更新时间:2024-11-12 GMT+08:00
分享

准备镜像环境

准备训练模型适用的容器镜像,包括获取镜像地址,了解镜像中包含的各类固件版本,配置物理机环境操作。

镜像地址

本教程中用到的训练和推理的基础镜像地址和配套版本关系如下表所示,请提前了解。

表1 基础容器镜像地址

镜像用途

镜像地址

基础镜像

swr.cn-southwest-2.myhuaweicloud.com/atelier/pytorch_2_1_ascend:pytorch_2.1.0-cann_8.0.rc3-py_3.9-hce_2.0.2406-aarch64-snt9b-20240910112800-2a95df3

表2 模型镜像版本

模型

版本

CANN

cann_8.0.rc3

驱动

23.0.6

PyTorch

2.1.0

步骤一 检查环境

  1. SSH登录机器后,检查NPU设备检查。运行如下命令,返回NPU设备信息。
    npu-smi info                    # 在每个实例节点上运行此命令可以看到NPU卡状态
    npu-smi info -l | grep Total    # 在每个实例节点上运行此命令可以看到总卡数

    如出现错误,可能是机器上的NPU设备没有正常安装,或者NPU镜像被其他容器挂载。请先正常安装NPU设备和驱动,或释放被挂载的NPU。

  2. 检查containerd是否安装。
    containerd -v   # 检查containerd是否安装

    在创建CCE集群时,会选择containerd作为容器引擎,并默认给机器安装。如尚未安装,说明机器操作系统安装错误。需要重新纳管机器,重新安装操作系统。

  3. 安装nerdctl工具。nerdctl是containerd的一个客户端命令行工具,使用方式和docker命令基本一致,可用于后续镜像构建步骤中。
    # 下载 nerdctl 工具,注意使用的是1.7.6 arm64版本
    wget https://github.com/containerd/nerdctl/releases/download/v1.7.6/nerdctl-1.7.6-linux-arm64.tar.gz
    
    # 将程序解压至运行目录中
    tar -zxf nerdctl-1.7.6-linux-arm64.tar.gz -C /usr/bin/
    
    # 查看是否安装成功
    nerdctl -v
  4. 安装buildkit工具。buildkit是从Docker从公司开源出来的下一代镜像构建工具,支持OCI标准的镜像构建,nerdctl需要结合buildkit一起使用。buildkit由两部分组成:

    buildkitd(服务端):负责镜像构建,目前支持runc和containerd作为镜像构建环境,默认是runc。

    buildkitctl(客户端):负责解析Dockerfile文件,并向服务端buildkitd发出构建请求。

    1. 下载并解压buildkit程序。
      # 下载 buildkit 工具,注意使用的是0.15.1 arm64版本
      wget https://github.com/moby/buildkit/releases/download/v0.15.1/buildkit-v0.15.1.linux-arm64.tar.gz
      
      # 创建解压的目录
      mkdir /usr/local/buildkit
      
      # 解压到指定的目录
      tar -zxf buildkit-v0.15.1.linux-arm64.tar.gz -C /usr/local/buildkit
      
      # 授予权限
      chmod -R 777 /usr/local/buildkit
    2. 添加环境变量
      echo 'export PATH=/usr/local/buildkit/bin:$PATH' >> /etc/profile  
      # 注意这里的echo 要使用单引号,单引号会原样输出,双引号会解析变量
      source /etc/profile  # 使刚才配置生效
    3. 创建buildkitd的启动服务。其中都是buildkitd.service的内容。复制以下全部命令并运行即可。
      cat <<EOF > /usr/lib/systemd/system/buildkitd.service
      [Unit]
      Description=buildkitd
      After=network.target
      
      [Service]
      ExecStart=/usr/local/buildkit/bin/buildkitd
      
      [Install]
      WantedBy=multi-user.target
      EOF
    4. 启动buildkitd的服务
      # 重新加载Unit file
      systemctl daemon-reload
      # 启动服务
      systemctl start buildkitd
      # 开机自启动
      systemctl enable buildkitd
      # 查看状态
      systemctl status buildkitd
    5. 若buildkitd的服务运行状态如下图所示,则表示服务运行成功。使用Ctrl+C即可退出查看状态。

步骤二 获取训练镜像

建议使用官方提供的镜像部署训练服务。镜像地址{image_url}参见镜像地址获取。

containerd 容器引擎有命名空间的概念。Kubernetes 下使用的 containerd 默认命名空间是 k8s.io。所以在导入镜像时需要指定命令空间为 k8s.io,否则使用 crictl images 无法查询到。以下命令可选其一进行镜像拉取:

  • 使用 containerd 自带的工具 ctr 进行镜像拉取。
    ctr -n k8s.io pull {image_url}
  • 使用nerdctl工具拉取镜像。
    nerdctl --namespace k8s.io pull {image_url}

集群有多个节点,要确保每个节点都拥有镜像。

镜像获取完成后可通过如下其中一个命令进行查看:
# ctr 工具查看
ctr -n k8s.io image list
# 或
crictl image

# nerdctl 工具查看
nerdctl --namespace k8s.io image list

步骤三 构建ModelArts Lite训练镜像

获取模型软件包,并上传到机器SFS Turbo的目录下(可自定义路径),获取地址参考表1

  1. 解压AscendCloud压缩包及该目录下的训练代码AscendCloud-LLM-6.3.908-xxx.zip,并直接进入到llm_train/AscendSpeed文件夹下面
    unzip AscendCloud-*.zip -d ./AscendCloud && unzip ./AscendCloud/AscendCloud-LLM-*.zip -d ./AscendCloud/AscendCloud-LLM && cd ./AscendCloud/AscendCloud-LLM/llm_train/AscendSpeed
  1. 编辑llm_train/AscendSpeed中的Dockerfile文件,修改git命令,填写自己的git账户信息。
    git config --global user.email "you@example.com" &&  \
    git config --global user.name "Your Name" &&  \
  2. 执行以下命令制作训练镜像。安装过程需要连接互联网git clone,请确保机器可以访问公网。
    nerdctl --namespace k8s.io build -t <镜像名称>:<版本名称>  .
    nerdctl build 会去镜像仓库拉取镜像,不会直接使用本地镜像。构建前可以 nerdctl pull 拉取测试以下镜像是否能拉取成功。
    • <镜像名称>:<版本名称>:定义镜像名称。示例:pytorch_2_1_ascend:20240606。
    • 记住使用Dockerfile创建的新镜像名称, 后续使用 ${dockerfile_image_name} 进行表示。

步骤四 在节点机器中Docker登录

在SWR中单击右上角的“登录指令”,然后在跳出的登录指定窗口,单击复制临时登录指令。

图1 复制登录指令
由于使用的容器引擎是containerd,不再是docker,因此需要改写复制的登录指令,将docker进行替换,使用nerdctl工具。
# docker login 替换为:
nerdctl login

步骤五 修改并上传镜像

1. 在机器中输入Step4登录指令后,使用下列示例命令将镜像上传至SWR:

nerdctl --namespace k8s.io tag ${dockerfile_image_name} <镜像仓库地址>/<组织名称>/<镜像名称>:<版本名称>

参数说明:

  • ${dockerfile_image_name}:在步骤三 构建ModelArts Lite训练镜像中使用Dockerfile创建的新镜像名称。
  • <镜像仓库地址>:可在SWR控制台上查询,容器镜像服务中登录指令末尾的域名即为镜像仓库地址。
  • <组织名称>:Step3中自己创建的组织名称。示例:GROUP_NAME
  • <镜像名称>:<版本名称>:定义镜像名称。示例:pytorch_2_1_ascend:20240606

示例:

nerdctl --namespace k8s.io tag ${dockerfile_image_name} swr.cn-southwest-2.myhuaweicloud.com/GPOUP_NAME/pytorch_2_1_ascend:20240606

2. 上传镜像至镜像仓库。

nerdctl --namespace k8s.io push <镜像仓库地址>/<组织名称>/<镜像名称>:<版本名称>

示例:

nerdctl --namespace k8s.io push swr.cn-southwest-2.myhuaweicloud.com/GPOUP_NAME/pytorch_2_1_ascend:20240606

步骤六 编写Config.yaml文件

k8s有两种方式来管理对象:

  • 命令式,即通过Kubectl指令直接操作对象。
  • 声明式,通过定义资源YAML格式的文件来操作对象。
首先给出单个节点训练的config.yaml文件模板,用于配置pod。而在训练中,需要按照参数说明修改${}中的参数值。该模板使用SFS Turbo挂载方案。
apiVersion: v1
kind: ConfigMap
metadata:
  name: configmap1980-vcjob               # 前缀使用“configmap1980-”不变,后接vcjob的名字
  namespace: default                      # 命名空间自选,需要和下边的vcjob处在同一命名空间
  labels:
    ring-controller.cce: ascend-1980      # 保持不动
data:                                     # data内容保持不动,初始化完成,会被volcano插件自动修改
  jobstart_hccl.json: |
    {
      "status":"initializing"
    }
---
apiVersion: batch.volcano.sh/v1alpha1 
kind: Job                              
metadata:
  name: vcjob                           # job名字,需要和configmap中名字保持联系
  namespace: default                    # 和configmap保持一致
  labels:
    ring-controller.cce: ascend-1980   # 保持不动
    fault-scheduling: "force"
spec:
  minAvailable: 1                       
  schedulerName: volcano                # 保持不动
  policies:
    - event: PodEvicted
      action: RestartJob
  plugins:
    configmap1980:
    - --rank-table-version=v2  # 保持不动,生成v2版本ranktablefile
    env: []
    svc:
    - --publish-not-ready-addresses=true
  maxRetry: 5
  queue: default
  tasks:
  - name: main
    replicas: 1                 
    template:
      metadata:
        name: training
        labels:
          app: ascendspeed
          ring-controller.cce: ascend-1980  # 保持不动
      spec:
        affinity:
          podAntiAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              - labelSelector:
                  matchExpressions:
                    - key: volcano.sh/job-name
                      operator: In
                      values:
                        - vcjob
                topologyKey: kubernetes.io/hostname
        hostNetwork: true                       # 采用宿主机网络模式
        containers:
        - image: ${image_name}            # 镜像地址
          imagePullPolicy: IfNotPresent  # IfNotPresent:默认值,镜像在宿主机上不存在时才拉取;Always:每次创建Pod都会重新拉取一次镜像;Never:Pod永远不会主动拉取这个镜像
          name: ${container_name}
          securityContext:                           # 容器内 root 权限
            allowPrivilegeEscalation: false
            runAsUser: 0
          env:
          - name: name
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: ip                             
            valueFrom:
              fieldRef:
                fieldPath: status.hostIP
          - name: framework
            value: "PyTorch"
          command: ["/bin/sh", "-c"]
          args:
          - ${command}
          resources:
            requests:
              huawei.com/ascend-1980: "8"                 # 需求卡数,key保持不变.
              memory: ${requests_memory}                  # 容器请求的最小内存
              cpu: ${requests_cpu}                        # 容器请求的最小 CPU
            limits:
              huawei.com/ascend-1980: "8"                 # 限制卡数,key保持不变。
              memory: ${limits_memory}                    # 容器可使用的最大内存
              cpu: ${limits_cpu}                          # 容器可使用的最大 CPU
          volumeMounts:                             # 容器内部映射路径
          - name: shared-memory-volume
            mountPath: /dev/shm
          - name: ascend-driver                     # 驱动挂载,保持不动
            mountPath: /usr/local/Ascend/driver
          - name: ascend-add-ons                    # 驱动挂载,保持不动
            mountPath: /usr/local/Ascend/add-ons
          - name: localtime
            mountPath: /etc/localtime
          - name: hccn                               # 驱动hccn配置,保持不动
            mountPath: /etc/hccn.conf
          - name: npu-smi                             # npu-smi
            mountPath: /usr/local/sbin/npu-smi
          - name: ascend-install
            mountPath: /etc/ascend_install.info
          - name: log
            mountPath: /var/log/npu/
          - name: sfs-volume
            mountPath: /mnt/sfs_turbo
        nodeSelector:
          accelerator/huawei-npu: ascend-1980
        volumes:                                          # 物理机外部路径
        - name: shared-memory-volume                      # 共享内存
          emptyDir:
            medium: Memory
            sizeLimit: "200Gi"
        - name: ascend-driver
          hostPath:
            path: /usr/local/Ascend/driver
        - name: ascend-add-ons
          hostPath:
            path: /usr/local/Ascend/add-ons
        - name: localtime
          hostPath:
            path: /etc/localtime                     
        - name: hccn
          hostPath:
            path: /etc/hccn.conf
        - name: npu-smi
          hostPath:
            path: /usr/local/sbin/npu-smi
        - name: ascend-install
          hostPath:
            path: /etc/ascend_install.info
        - name: log
          hostPath:
            path: /usr/slog
        - name: sfs-volume         
          persistentVolumeClaim:             
            claimName: ${pvc_name}    #已创建的PVC名称
        restartPolicy: OnFailure

双个节点训练的config.yaml文件模板,用于实现双机分布式训练。

apiVersion: v1
kind: ConfigMap
metadata:
  name: configmap1980-vcjob     # 前缀使用“configmap1980-”不变,后接vcjob的名字
  namespace: default                      # 命名空间自选,需要和下边的vcjob处在同一命名空间
  labels:
    ring-controller.cce: ascend-1980   # 保持不动
data:                    #data内容保持不动,初始化完成,会被volcano插件自动修改
  jobstart_hccl.json: |
    {
      "status":"initializing"
    }
---
apiVersion: batch.volcano.sh/v1alpha1  
kind: Job                            
metadata:
  name: vcjob                           # job名字,需要和configmap中名字保持联系
  namespace: default                      # 和configmap保持一致
  labels:
    ring-controller.cce: ascend-1980   # 保持不动
    fault-scheduling: "force"
spec:
  minAvailable: 1                     
  schedulerName: volcano                # 保持不动
  policies:
    - event: PodEvicted
      action: RestartJob
  plugins:
    configmap1980:
    - --rank-table-version=v2  # 保持不动,生成v2版本ranktablefile
    env: []
    svc:
    - --publish-not-ready-addresses=true
  maxRetry: 5
  queue: default
  tasks:
  - name: main
    replicas: 1                    
    template:
      metadata:
        name: training
        labels:
          app: ascendspeed
          ring-controller.cce: ascend-1980  # 保持不动
      spec:
        affinity:
          podAntiAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              - labelSelector:
                  matchExpressions:
                    - key: volcano.sh/job-name
                      operator: In
                      values:
                        - vcjob
                topologyKey: kubernetes.io/hostname
        hostNetwork: true                      # 采用宿主机网络模式
        containers:
        - image: ${image_name}         # 镜像地址
          imagePullPolicy: IfNotPresent      # IfNotPresent:默认值,镜像在宿主机上不存在时才拉取;Always:每次创建Pod都会重新拉取一次镜像;Never:Pod永远不会主动拉取这个镜像
          name: ${container_name}
          securityContext:                           # 容器内 root 权限
            allowPrivilegeEscalation: false
            runAsUser: 0
          env:
          - name: name
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: ip                            
            valueFrom:
              fieldRef:
                fieldPath: status.hostIP
          - name: framework
            value: "PyTorch"
          command: ["/bin/sh", "-c"]
          args:
          - ${command}
          resources:
            requests:
              huawei.com/ascend-1980: "8"                 # 需求卡数,key保持不变.
              memory: ${requests_memory}                  # 容器请求的最小内存
              cpu: ${requests_cpu}                        # 容器请求的最小 CPU
            limits:
              huawei.com/ascend-1980: "8"                 # 限制卡数,key保持不变。
              memory: ${limits_memory}                    # 容器可使用的最大内存
              cpu: ${limits_cpu}                          # 容器可使用的最大 CPU
          volumeMounts:                             # 容器内部映射路径
          - name: shared-memory-volume
            mountPath: /dev/shm
          - name: ascend-driver                     # 驱动挂载,保持不动
            mountPath: /usr/local/Ascend/driver
          - name: ascend-add-ons                    # 驱动挂载,保持不动
            mountPath: /usr/local/Ascend/add-ons
          - name: localtime
            mountPath: /etc/localtime
          - name: hccn                               # 驱动hccn配置,保持不动
            mountPath: /etc/hccn.conf
          - name: npu-smi                             # npu-smi
            mountPath: /usr/local/sbin/npu-smi
          - name: ascend-install
            mountPath: /etc/ascend_install.info
          - name: log
            mountPath: /var/log/npu/
          - name: sfs-volume
            mountPath: /mnt/sfs_turbo
        nodeSelector:
          accelerator/huawei-npu: ascend-1980
        volumes:                                    # 物理机外部路径
        - name: shared-memory-volume                        # 共享内存
          emptyDir:
            medium: Memory
            sizeLimit: "200Gi"
        - name: ascend-driver
          hostPath:
            path: /usr/local/Ascend/driver
        - name: ascend-add-ons
          hostPath:
            path: /usr/local/Ascend/add-ons
        - name: localtime
          hostPath:
            path: /etc/localtime                     
        - name: hccn
          hostPath:
            path: /etc/hccn.conf
        - name: npu-smi
          hostPath:
            path: /usr/local/sbin/npu-smi
        - name: ascend-install
          hostPath:
            path: /etc/ascend_install.info
        - name: log
          hostPath:
            path: /usr/slog
        - name: sfs-volume         
          persistentVolumeClaim:             
            claimName: ${pvc_name}    #已创建的PVC名称
        restartPolicy: OnFailure
  - name: work
    replicas: 1                    
    template:
      metadata:
        name: training
        labels:
          app: ascendspeed
          ring-controller.cce: ascend-1980  # 保持不动
      spec:
        affinity:
          podAntiAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              - labelSelector:
                  matchExpressions:
                    - key: volcano.sh/job-name
                      operator: In
                      values:
                        - vcjob
                topologyKey: kubernetes.io/hostname
        hostNetwork: true                      # 采用宿主机网络模式
        containers:
        - image: ${image_name}         # 镜像地址
          imagePullPolicy: IfNotPresent       # IfNotPresent:默认值,镜像在宿主机上不存在时才拉取;Always:每次创建Pod都会重新拉取一次镜像;Never:Pod永远不会主动拉取这个镜像
          name: ${container_name}
          securityContext:                           # 容器内 root 权限
            allowPrivilegeEscalation: false
            runAsUser: 0
          env:
          - name: name
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: ip                           
            valueFrom:
              fieldRef:
                fieldPath: status.hostIP
          - name: framework
            value: "PyTorch"
          command: ["/bin/sh", "-c"]
          args:
          - ${command}
          resources:
            requests:
              huawei.com/ascend-1980: "8"                 # 需求卡数,key保持不变.
              memory: ${requests_memory}                  # 容器请求的最小内存
              cpu: ${requests_cpu}                        # 容器请求的最小 CPU
            limits:
              huawei.com/ascend-1980: "8"                 # 限制卡数,key保持不变。
              memory: ${limits_memory}                    # 容器可使用的最大内存
              cpu: ${limits_cpu}                          # 容器可使用的最大 CPU
          volumeMounts:                             # 容器内部映射路径
          - name: shared-memory-volume
            mountPath: /dev/shm
          - name: ascend-driver                     # 驱动挂载,保持不动
            mountPath: /usr/local/Ascend/driver
          - name: ascend-add-ons                    # 驱动挂载,保持不动
            mountPath: /usr/local/Ascend/add-ons
          - name: localtime
            mountPath: /etc/localtime
          - name: hccn                               # 驱动hccn配置,保持不动
            mountPath: /etc/hccn.conf
          - name: npu-smi                             # npu-smi
            mountPath: /usr/local/sbin/npu-smi
          - name: ascend-install
            mountPath: /etc/ascend_install.info
          - name: log
            mountPath: /var/log/npu/
          - name: sfs-volume
            mountPath: /mnt/sfs_turbo
        nodeSelector:
          accelerator/huawei-npu: ascend-1980
        volumes:                                    # 物理机外部路径
        - name: shared-memory-volume                        # 共享内存
          emptyDir:
            medium: Memory
            sizeLimit: "200Gi"
        - name: ascend-driver
          hostPath:
            path: /usr/local/Ascend/driver
        - name: ascend-add-ons
          hostPath:
            path: /usr/local/Ascend/add-ons
        - name: localtime
          hostPath:
            path: /etc/localtime                     
        - name: hccn
          hostPath:
            path: /etc/hccn.conf
        - name: npu-smi
          hostPath:
            path: /usr/local/sbin/npu-smi
        - name: ascend-install
          hostPath:
            path: /etc/ascend_install.info
        - name: log
          hostPath:
            path: /usr/slog
        - name: sfs-volume         
          persistentVolumeClaim:             
            claimName: ${pvc_name}    #已创建的PVC名称
        restartPolicy: OnFailure

参数说明:

  • ${container_name} 容器名称,此处可以自己定义一个容器名称,例如ascendspeed。
  • ${image_name} 为步骤五 修改并上传镜像中,上传至SWR上的镜像链接。
  • ${command} 使用config.yaml文件创建pod后,在容器内自动运行的命令。在进行训练任务中会给出替换命令。
  • /mnt/sfs_turbo 为宿主机中默认挂载SFS Turbo的工作目录,目录下存放着训练所需代码、数据等文件。
    • 同样,/mnt/sfs_turbo 也可以映射至容器中,作为容器中挂载宿主机的目录。宿主机和容器使用不同的文件系统。为方便访问两个地址可以相同。
  • ${pvc_name} 为在CCE集群关联SFS Turbo步骤中创建的PVC名称。
  • 在设置容器中需要的CPU与内存大小时,可通过运行以下命令查看申请的节点机器中具体的CPU与内存信息。
    kubectl describe node
    • ${requests_cpu} 指在容器中请求的最小CPU核心数量,可使用Requests中的值,例如2650m。
    • ${requests_memory} 指在容器中请求的最小内存空间大小,可使用Requests中的值,例如3200Mi。
    • ${limits_cpu} 指在容器中可使用的最大CPU核心数量,例如192。
    • ${limits_memory} 指在容器中可使用的最大内存空间大小,例如换算成1500Gi。

相关文档