跳转至

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__