跳转至

HPCGame kube 集群使用指南

Tl;Dr;

  • 用 vscode 连接到集群,可以 attach 到容器,比较方便
  • 需要注意 kubectl 版本,至少 1.30,否则无法连接,可以使用我们的二进制文件
  • kubeconfig 要放到 ~/.kube/config 下,可以省去很多麻烦
  • 所有题目都在附件提供了环境描述 yaml ,使用 kubectl apply -f <xxx>.yaml 创建pods,即可获得开发环境
  • 通过 kubectl get pods 来获取目前运行的pods
  • 运行 kubectl exec pod/<podname> -ti -- bash 访问容器
  • 使用 kubectl cp 本地文件 容器名:路径以及 kubectl cp 容器名:路径 本地文件在本地与远端传递文件
  • 使用 kubectl port-forward pod/{podname} local_port:remote_port 进行端口转发
  • 集群不保证pod的持久化,建议每位选手创建一个pvc,mount 到容器里,然后把内容存在对应的容器中

集群的使用

比赛集群是通过 kubernetes 管理的,我们提供了 kubeconfig 文件,可以通过 kubectl 访问集群。访问 https://hpcgame.pku.edu.cn/kube/_/ui/ ,可以可视化管理 kubeconfig 与集群资源。

kube ui

访问 https://hpcgame.pku.edu.cn/kube/_/ui/ ,将会跳转至 hpcgame 的应用验证页面,点击登录到 HPCGame 2nd cluster ,即可访问 kubeui

令牌 - 获取 kubeconfig

在左侧导航栏中,进入令牌管理页面,点击右上角“添加令牌”。点击“显示Kubeconfig”,即可获取 kubeconfig 文件,请复制文件内容并按下节中配置 kubeconfig 的步骤操作。

储存 - PVC

在 Kubernetes 中,PersistentVolumeClaim (PVC) 是一种用于请求存储资源的对象,你可以将其挂载到特定的Pod上,它使Pod能够动态地请求持久存储。

访问模式

在 Kubernetes 中,访问模式(Access Modes)定义了 Pod 可以如何访问存储卷。主要有三种访问模式:

访问模式 简介 使用场景
ReadWriteOnce 单节点上的多个 Pod 可读写访问 大多数应用场景,单实例访问存储卷
ReadOnlyMany 多节点上的多个 Pod 只读访问 需要多个实例读取同一数据,如静态数据或配置文件
ReadWriteMany 多节点上的多个 Pod 可读写访问 需要多个实例共享读写同一数据,如共享文件系统、分布式数据库

StorageClass

本次比赛我们使用了多种储存设备,可以通过 k8s 的 storage class 选择储存设备。请注意这些储存设备的地域关系。部分设备只能在特定地域使用,有时候需要跨地域传输数据。我们强烈推荐在 pod 对应的区域选择合适的储存设备,否则速度可能会相当糟糕。你可以用 kubectl get sc 查看所有的 storage class,我们在这里提供一些说明。

  • wm2-nfs :位于昌平校区,只有昌平校区机器都可以访问。储存介质为 SSD,每个机器的可用读写速度约 2GB/s
  • yanyuan-nfs :位于燕园校区,只有燕园校区机器都可以访问。储存介质为 SSD,每个机器的可用读写速度约 1GB/s(受限于网络)second number is eight
  • x86-amd-local-hostpath :位于燕园校区,只有x86_amd机器可以访问,每台机器有各自的hostpath储存。储存介质为 NVME,每个机器的可用读写速度约 4GB/s
  • npu-local-data :用于 npu 训练时储存临时数据,每台机器有各自的hostpath储存。储存介质为 NVME,每个机器的可用读写速度约 4GB/s
  • juicefs :位于燕园校区,所有机器都可以访问。储存介质为 HDD 集群,IOPS 特别低,请不要在上面放小文件,比如conda环境。昌平校区每个机器的可用读写速度约 500MB/s、燕园校区每个机器的可用读写速度约 1GB/s

需要注意的是,npu-local-dataopenebs-hostpathx86-amd-local-hostpath 只支持 ReadWriteOnce 的访问模式。

任务 - Job

Job 是一种控制器,用于一次性任务或批处理任务。它确保指定数量的 Pod 成功完成任务。一旦所有的 Pod 成功完成任务,Job 就会终止。在使用场景上,Job 通常适用于需要执行某个操作并期望在操作结束后关闭的任务。

注意,在 Kubernetes 中,当你创建一个 Job 时,它会根据 Job 的配置来生成相应的 Pod,它们会出现在 Pod 页面中。

容器 - Pod

Pod 是 Kubernetes 中最小的可调度单元,是一组一个或多个容器的集合,这些容器共享存储、网络资源以及一个 spec,它描述了这些容器应该如何运行。在使用场景上,Pod 通常用于长期运行的服务。

创建 Job / Pod:

  1. 输入 Job/Pod 名称
  2. 根据题目要求与下面的说明选择容器镜像与架构
  3. 如有需要,在挂载PVC中选择你已经创建的PVC,并指定其挂载路径。之后,你可以在Pod的对应路径访问PVC
  4. 设置CPU与内存。CPU的单位为m(milliCPU),1000m即一核;内存的单位为Gi
  5. 如无更多需求,其他配置可以保持默认,点击创建
  6. 等待状态变为 Running,注意,这一过程可能持续较长时间,请坐和放宽

可以直接使用题目提供的yaml文件,通过 kubectl apply -f <xxx>.yaml 来创建Pod。

如果在创建的过程中遇到问题,请点击操作栏的第一个图标,查看日志并反馈给我们。

硬件分区

昌平校区:同分区机器具有 100Gbps 网络互联

  • x86 :处理器为 Intel Xeon Platinum 8358。label: hpc.lcpu.dev/partition=x86。
  • gpu-a800 :处理器为 Intel Xeon Platinum 8358,配备 A800 GPU。使用 MIG 技术,每位选手可使用约单张卡 2/7 的计算资源和 20G 显存。label:nvidia.com/gpu-product=NVIDIA-A800-80GB-PCIe
  • gpu-l40 :处理器为 Intel Xeon Platinum 8358,配备 L40 GPU。fp64 计算能力较低,但每个任务可以使用所有算力。label:nvidia.com/gpu-product=NVIDIA-L40-40GB-PCIe

燕园校区:

  • x86_amd :处理器为 AMD Epyc 9654。配备V100 GPU,有高速 SSD,适合跑 vscode
  • arm :处理器为 Kunpeng 920 7282C。单颗 CPU 80 核,每台机器 2 个 CPU socket。内存为DDR5 4800。由于工作失误,该机器内存通道只插满了一半,比较适合跑计算密集型任务
  • npu :处理器为 Kunpeng 920 5250 * 4。单机配备 8 个 920B NPU
  • npu-inf :Kunpeng 920 5250 * 2,单机配备 4 张 Atlas 300I DUO NPU,共有 8 个 310P NPU 核心,适合跑推理任务

其中,armnpunpu-inf 通过 25G 网络互联。燕园校区所有机器之间是 10G 网络互联。

设置SSH key

在左侧导航栏中,进入配置管理-SSHAuthorizedKeys,点击创建SSHAuthorizedKeys,将你的ed25519或rsa公钥粘贴后即可,这个公钥会被自动应用于你后续创建的所有容器。

kubectl

kubectl 是 Kubernetes 的命令行工具,用于与 Kubernetes 集群进行交互和管理

安装 kubectl

注意,你的kubectl版本应不低于1.30,所以请不要使用vscode自动下载的kubectl

参见 https://kubernetes.io/zh-cn/docs/tasks/tools/#kubectl

除此之外,你还可以从下方我们提供的链接中直接下载系统对应的二进制文件,并将其加入系统PATH,其版本均为1.32.0。

https://disk.pku.edu.cn/link/AA832D1185BCA24642B252525E825488D2

配置 kubeconfig

注意,用于配置集群访问的文件称为 kubeconfig 文件。这是引用到配置文件的通用方法,并不意味着有一个名为 kubeconfig 的文件。

创建 ~/.kube/config 文件,并将你从 kubeui 令牌管理页面复制得到的 kubeconfig 文件内容粘贴进去保存。

请注意,从HPCGame网站获取的kubeconfig文件一定要保存到 ~/.kube/config 下。

用 vscode 连接到集群

使用kubernetes插件

请在vscode中安装kubernetes与Dev Containers插件,缺一不可。

配置vscode的kubernetes插件

按 Ctrl+Shift+P / Command+Shift+P(对于MacOS使用者) ,输入set kubeconfig并点击,选择 Add new kubeconfig,然后浏览并选择你之前配置的 Kubeconfig 文件。确认导入后,Kubernetes 插件将显示你的集群信息。

连接容器进行开发

在连接的集群中,找到Workloads,在其中找到Pods,选择你想要连接的容器名称,右键并点击Attach VSCode选项。

使用wsl的注意事项

对于wsl用户,你需要:

  1. wsl中安装kubectl并配置kubeconfig
  2. 在windows中安装kubectl并kubeconfig
  3. 注意,这两个kubeconfig文件的内容应该相同

而在wsl内attach vscode到容器的时候,需要注意,vscode会打开一个新窗口,校验Windows的kubeconfig配置

连接到容器

kubectl exec

可以使用 kubectl exec 命令来连接到容器。

kubectl exec pod/<podname> -ti -- bash

ssh连接(不推荐)

为了通过ssh连接到pod,你需要按照上节的步骤添加ssh key或进入容器后自行设置密码

你可以运行以下命令,将容器的22端口映射到本机的10022端口

kubectl port-forward pod/{podname} 10022:22

其中,{podname}为你想要连接的容器的名称

请保持该命令在后台运行,之后,你就可以使用vscode的remote-ssh连接localhost的10022端口进行开发

使用YAML文件创建Jobset / Pod

选手可以在HPCGame网站上使用可视化界面创建Job和Pod,也可以使用Yaml文件以及 kubectl 工具创建。

在随题目下发的yaml参考中,具有描述如何创建一个Job或者Pod的参考文件;在每个题目的handout中也附带了yaml文件描述组委会预设的、针对本题创建的Job / Pod配置。 注意此类配置仅为方便选手创建环境进行开发和调试,并不代表最终评测的环境,最终评测环境请以题面为准。

选手可以通过 kubectl apply -f <xxx>.yaml 来创建一个Job / Pod。

使用kubectl查看集群的资源使用情况和剩余资源情况

有两个queue可以查看。

lq: local queue。你自己的资源情况。 cq: cluster queue。集群的资源情况。

获取队列:

kubectl get lq
kubectl get cq

示例输出:

kubectl get lq                                                                                                                           

NAME      CLUSTERQUEUE    PENDING WORKLOADS   ADMITTED WORKLOADS
default   default-queue   1
kubectl get cq

NAME            COHORT    PENDING WORKLOADS
default-queue   hpcgame   6
judge-queue     hpcgame   0

查看队列资源情况

kubectl describe lq
kubectl describe cq

将会列出lq / cq中所有队列的资源情况。

如果需要单独查看,例如judge-queue,的资源情况,运行

kubectl describe cq judge-queue

将会单独列出judge-queue中的资源情况。

对于MPI程序,使用krun工具在Jobset中发现其他Pods

运行

krun init

以初始化环境

运行

krun host

以查看所有同jobset下pods。

在Jobset多Pods环境中使用MPI运行程序

MPI需要获取所有pods的host信息。采用上述 krun 工具,运行

krun host -n -I > hostfile

(-n 去除表头,-I只获取IP地址)

以获取本jobset中所有pods的host信息,保存到hostfile文件中。

在使用 mpirun 运行MPI程序时,添加 -hostfile 选项并指定上述hostfile文件。

mpirun -hostfile hostfile <your_program>

任务定义文件样例

单 Pod 任务:

apiVersion: v1
kind: Pod
metadata:
  name: test 
  labels:
    name: test
spec:
  nodeSelector:
    hpc.lcpu.dev/partition: __REPLACE_NODE__
  containers:
    - name: test
      securityContext:
        capabilities:
          add: ["SYS_PTRACE", "IPC_LOCK"]
      image: crmirror.lcpu.dev/hpcgame/intel:latest
      resources:
        limits:
          memory: __REPLACE_LIMITS_MEMORY__
          cpu: __REPLACE_LIMITS_CPU__
      command:
        - sleep
        - inf
      # 添加 volumeMounts 配置
      volumeMounts:
        - name: my-pvc-volume  # 卷的名称,需要和下面volumes中的名称对应
          mountPath: /mnt/data # 容器内的挂载路径
  # 添加 volumes 配置
  volumes:
    - name: my-pvc-volume     # 卷的名称
      persistentVolumeClaim:
        claimName: my-pvc     # 已存在的PVC名称

对于 MPI 任务,使用 JobSet 的方法运行:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data
spec:
  resources:
    requests:
      storage: 64Gi
  accessModes:
    - ReadWriteMany
  storageClassName: wm2-nfs

---

apiVersion: jobset.x-k8s.io/v1alpha2
kind: JobSet
metadata:
  name: testMPIJob # Replace with your jobname
spec:
  network:
    enableDNSHostnames: true
    subdomain: testMPIJob
  replicatedJobs:
    - name: workers
      replicas: 1 
      template:
        spec:
          backoffLimit: 0 
          # completions 和 parallelism 必须相等,为 Pod 数
          completions: __REPLACE_POD_NUM__
          parallelism: __REPLACE_POD_NUM__
          template: # Describe a pod
            metadata:
              annotations:
                ssh-operator.lcpu.dev/inject: enabled 
                lxcfs.lcpu.dev/inject: disabled 
            spec:
              nodeSelector:
                # 与单 Pod 的节点选择器功能相同
                hpc.lcpu.dev/partition: __REPLACE_NODE__

              # 将下方内容解除注释,即可指定任务调度到不同节点
              # ------------ BEGIN ------------
              # affinity:
              #   podAntiAffinity:
              #     requiredDuringSchedulingIgnoredDuringExecution:
              #       - topologyKey: "kubernetes.io/hostname"
              #         labelSelector:
              #           matchExpressions:
              #             - key: jobset.x-k8s.io/name
              #               operator: In
              #               values:
              #                 - __REPLACE_JOBNAME__
              # ------------ END ------------

              containers: # Describe a container inside a pod
                - name: worker
                  # Pre-defined images provided by HPCGame:
                  #   crmirror.lcpu.dev/hpcgame/full:latest
                  #   crmirror.lcpu.dev/hpcgame/llvm:latest
                  #   crmirror.lcpu.dev/hpcgame/gcc:latest
                  #   crmirror.lcpu.dev/hpcgame/nvhpc:latest
                  #   crmirror.lcpu.dev/hpcgame/julia:latest
                  #   crmirror.lcpu.dev/hpcgame/base:latest
                  #   crmirror.lcpu.dev/hpcgame/intel:latest
                  #   crmirror.lcpu.dev/hpcgame/cuda:latest
                  #   crmirror.lcpu.dev/hpcgame/aocc:latest
                  #   crmirror.lcpu.dev/hpcgame/hpckit:latest
                  # Or any other images that could be get via a URL.
                  image: __REPLACE_CONTAINER_IMAGE__

                  # Note: replace this with command that you want to use.
                  # ```
                  # command:
                  #   - sleep
                  #   - inf
                  # ```
                  # Or
                  # `command: ["sleep", "inf"]`
                  # This is the default command that would keep the container to run sliently
                  # for the convenience of login
                  command:
                    - sleep
                    - inf
                  resources:
                    # HPC场景中,limits和requests中所有值必须相同
                    limits:
                      cpu: __REPLACE_LIMITS_CPU__ # E.g. 4
                      memory: __REPLACE_LIMITS_MEMORY__ # E.g. 8Gi
                      rdma.hpc.lcpu.dev/hca_cx5: 1
                    requests:
                      cpu: __REPLACE_REQUESTS_CPU__ # E.g. 4
                      memory: __REPLACE_REQUESTS_MEMORY__ # E.g. 8Gi
                      rdma.hpc.lcpu.dev/hca_cx5: 1
                  volumeMounts:
                    - name: __REPLACE_VOLUME_NAME__ # The name of volume defined in `volumes` section, see below
                      mountPath: __REPLACE_VOLUME_MOUNT_PATH__
              volumes:
                - name: __REPLACE_VOLUME_NAME__
                  persistentVolumeClaim: # From which PVC does this volume come from?
                    claimName: __REPLACE_PVC_NAME__