DeepSpeedJob¶
DeepSpeedJob 是服务于 DeepSpeed 分布式训练框架的 T9k Job。
您可以较为方便地使用 DeepSpeedJob 为 DeepSpeed 训练脚本提供训练环境,并监控训练进程。
创建 DeepSpeedJob¶
下面是一个基本的 DeepSpeedJob 配置示例:
apiVersion: batch.tensorstack.dev/v1beta1
kind: DeepspeedJob
metadata:
  name: deepspeed-example
spec:
  config:
    slotPerWorker: 1
    run:
      python: /t9k/mnt/train.py
  worker:
    replicas: 4
    template:
      spec:
        restartPolicy: OnFailure
        containers:
        - image: deepspeed/deepspeed:v072_torch112_cu117
          imagePullPolicy: IfNotPresent
          name: worker
          resources:
            limits:
              cpu: 4
              memory: 4Gi
            requests:
              cpu: 2
              memory: 2Gi
          volumeMounts:
          - mountPath: /t9k/mnt
            name: code
        volumes:
        - name: code
          persistentVolumeClaim:
            claimName: deepspeed
在该例中:
- 创建 4 个训练副本,这些副本会启动 
sshd服务。 - 第一个副本会启动 
deepspeed程序,该程序会通过pdsh(或其他方式)访问 4 个副本,并在每个副本上运行/t9k/mnt/train.py脚本。 
注意
一个副本中可以创建多个容器,DeepSpeedJob 需要确定哪一个容器才是训练容器。如果 spec.worker.template 中包含 name 为 worker 的 container,则该容器为训练容器;如果没有,会选取第一个 container 作为训练容器。
DeepSpeedJob 中的执行程序应是使用 DeepSpeed 框架的程序,否则可能达不到训练效果。
用户挂载文件时,需要避开下列路径,否则会导致 DeepSpeedJob 不能正常运行:/root/.ssh、/t9k/hostfile、/root/.deepspeed_env。
DeepSpeedJob 状态¶
DeepSpeedJob 的状态和阶段¶
status.conditions 字段用于描述当前 DeepSpeedJob 的状态,包括以下 5 种类型:
Initialized:DeepSpeedJob 已经成功创建各子资源,完成初始化。Running:开始执行任务。ReplicaFailure:有一个或多个副本出现错误。Completed:DeepSpeedJob 成功。Failed:DeepSpeedJob 失败。
status.phase 字段用于描述当前 DeepSpeedJob 所处的阶段,DeepSpeedJob 的整个生命周期主要有以下几个阶段:
Pending:DeepSpeedJob 刚刚创建,等待副本启动。Running:副本创建成功,开始执行任务。Succeeded:DeepSpeedJob 成功。Failed:DeepSpeedJob 失败。Unknown:控制器无法获得 DeepSpeedJob 的阶段。
在下面的示例中,DeepSpeedJob 所有子资源创建成功,所以类型为 Initalized 的 condition 被设为 True;DeepSpeedJob 运行结束,所以类型为 Completed 的 condition 被设置为 True;但是 DeepSpeedJob 的训练结果是失败的,所以类型为 Failed 的 condition 被设置为 True。当前 DeepSpeedJob 运行阶段为 Failed。
...
status:
  conditions:
    - lastTransitionTime: "2021-01-18T02:36:09Z"
      status: "True"
      message: "The job has been initialized successfully."
      reason: "-"
      type: Initializing
    - lastTransitionTime: "2021-01-18T02:36:09Z"
      status: "True"
      message: "All pods are running normally."
      reason: "-"
      type: Running
    - lastTransitionTime: "2021-01-18T02:36:09Z"
      status: "False"
      message: "All pods are running normally."
      reason: "-"
      type: ReplicaFailure
    - lastTransitionTime: "2021-01-18T02:36:31Z"
      status: "False"
      message: "The job exited with an error code."
      reason: "Failed"
      type: Completed
    - lastTransitionTime: "2021-01-18T02:36:31Z"
      status: "True"
      message: "The job exited with an error code."
      reason: "Failed"
      type: Failed
  phase: Failed
副本的状态¶
status.tasks 字段用来记录副本的状态,记录的内容主要包括:
- 副本的重启次数(同一种角色的副本的重启次数之和)
 - 副本当前的运行阶段
 - 副本在集群中对应的 Pod 的索引信息
 
在下面的示例中,DeepSpeedJob 创建了 2 个训练副本,当前均处于 Running 阶段,分别运行在 deepspeed-example-worker-0 和 deepspeed-example-worker-1 这 2 个 Pod 上。
...
status:
  tasks:
  - type: worker
    restartCount: 0
    status:
    - phase: Running
      name: deepspeed-example-worker-0
      uid: e3ec2ee3-6645-4e21-993f-1e472b94e0ae
      containers: []
    - phase: Running
      name: deepspeed-example-worker-1
      uid: 908a93f0-7b8b-491e-85d5-3da0abcb4ca4
      containers: []
训练配置¶
DeepSpeedJob 在 spec.config 中配置如何执行训练。有以下参数可以设置:
run:如何启动训练,以下三个参数只能填写一个,否则报错:python:使用 Python 脚本进行训练。指定 Python 文件以及启动参数。module:使用 Python module 进行训练。指定 Python module 以及启动参数。exec:使用可执行文件/命令进行训练。指定可执行文件以及启动参数。
slotsPerWorker:每一个副本上设置多少个“插槽”。“插槽”是继承自 MPI 中的概念,表示一个副本上可以运行多少个训练进程。一般来说该值被设为每个副本分配的 GPU 数量。例如当创建了一个replica为 4 的任务,并且给每个副本分配了 2 个nvidia.com/gpu,则应该将slotsPerWorker设为 2,这样最后一共会运行4 * 2 = 8个训练进程。localRank:是否传递LOCAL_RANK环境变量,默认为true。autotune:启用超参数调优,可以设置为none、run、tune,默认为none。none为不启动超参数调优;tune只查找最合适的超参数组合,但是不执行训练;run查找最合适的超参数组合,并用该超参数执行训练。otherArgs:设置其他 DeepSpeed 参数,详见下文。
otherArgs¶
DeepSpeedJob 希望提供用户足够的灵活性,所以支持用户通过 otherArgs 字段设置传入 DeepSpeed 的参数。config 中的配置实际上也是通过 DeepSpeed 参数实现的,以下列出除了在配置文件中指定的参数之外的其他可用参数:
--launcher: 多节点训练使用的启动器后端,目前的选项包括 PDSH、OpenMPI、MVAPICH、SLURM、MPICH。(默认:pdsh)。目前 DeepSpeedJob 只支持pdsh。--no_ssh_check:多节点训练时不执行 ssh 检查。--save_pid: 在/tmp/<main-pid>.ds处保存包含启动器进程 ID(pid),其中<main-pid>是第一个调用 DeepSpeed 的进程的 pid。PDSH 模式下不支持。--enable_each_rank_log: 将每个 Rank 的 stdout 和 stderr 重定向到不同的日志文件。PDSH 模式下不支持。--bind_cores_to_rank:将每个 Rank 绑定到主机的不同核心。PDSH 模式下不支持。--bind_core_list:要绑定的核心列表,以逗号分隔。例如1,3-5,7 => [1,3,4,5,7]。 未指定时,系统上的所有核心都将被绑定。PDSH 模式下不支持。
信息
config 中的配置实际上是通过 DeepSpeed 参数实现的,而 otherArgs 可以指定任意值,所以可能会造成冲突。以下列出了会导致冲突的参数,请勿在 otherArgs 中设置:
--no_local_rank:与spec.config.localRank字段冲突。--autotuning:与spec.config.autotune字段冲突。--module和--no_python:与spec.config.autotune字段冲突。
训练的成功和失败判定¶
DeepSpeedJob 分布式训练框架中,第一个训练副本(下文记为 worker-0)是分布式任务的主节点。当 worker-0 成功结束,则 DeepSpeedJob 训练成功;反之,当 worker-0 执行失败,DeepSpeedJob 训练失败。
如果一次训练执行时间过长,用户可能需要考虑代码是否需要优化、是否需要分配更多资源等问题。DeepSpeedJob 可以设置最长执行时间(由 spec.runPolicy.activeDeadlineSeconds 字段指定),当超过这个执行时间后,训练失败。
清除策略¶
在训练完毕后,可能有些副本仍处于运行状态。这些运行的副本仍然会占用集群资源,DeepSpeedJob 提供清除策略,可以在训练结束后删除这些训练副本。
DeepSpeedJob 提供以下三种策略:
None:不删除副本。All:删除所有副本。Unfinished:只删除未结束的副本。
提示
已结束的副本不会继续消耗集群资源,因此在一定程度上,Unfinished 策略比 All 策略更优。但这并不总是适用,由于一个项目的资源配额的计算不考虑 Pod 是否已经结束,对于资源紧张的项目,如果确定不需要通过日志来调试 Job,则可以使用 All 策略。
None 策略主要用于训练脚本调试阶段。如果需要从副本中读取训练日志,则可以选用此策略。但由于这些副本可能占用资源并影响后续训练,建议您在调试完毕后手动删除这些副本或删除整个 DeepSpeedJob。
调度策略¶
目前 DeepSpeedJob 支持两种调度策略:
- Kubernetes 的默认调度器
 - T9k Scheduler
 
调度策略通过 CRD 的 spec.scheduler 字段设置:
- 不设置 
spec.scheduler字段,则默认使用 Kubernetes 的默认调度策略。 - 设置 
spec.scheduler.t9kScheduler字段,则使用 T9k Scheduler 调度器。 
在下面的示例中,MPIJob 启用 T9k Scheduler 调度器,将副本插入 default 队列中等待调度,其优先级为 50。
...
spec:
  scheduler:
    t9kScheduler:
      queue: default
      priority: 50
信息
队列和优先级都是 T9k Scheduler 的概念,具体含义请参阅 T9k Scheduler。
Debug 模式¶
DeepSpeedJob 支持 Debug 模式,在该模式下,训练环境会被部署好,但不会启动训练,用户可以连入训练副本测试环境或脚本。
该模式可以通过 spec.runMode.debug 字段来设置:
spec.runMode.debug.enable表示是否启用 Debug 模式。spec.runMode.debug.replicaSpecs表示如何配置各个副本的 Debug 模式:spec.runMode.debug.replicaSpecs.type表示作用于的副本类型。spec.runMode.debug.replicaSpecs.skipInitContainer表示让副本的 InitContainer 失效,默认为false。spec.runMode.debug.replicaSpecs.command表示副本在等待调试的时候执行的命令,默认为sleep inf。- 如果不填写 
spec.runMode.debug.replicaSpecs字段,则表示副本使用上述默认设置。 
在下面的示例中:
- 示例一:开启了 Debug 模式,并配置 worker 跳过 InitContainer,并执行 
/usr/bin/sshd。 - 示例二:开启了 Debug 模式,副本使用默认 Debug 设置,即不跳过 InitContainer,并执行 
sleep inf。 
# 示例一
...
spec:
  runMode:
    debug:
      enable: true
      replicaSpecs:
        - type: worker
          skipInitContainer: true
          command: ["/usr/bin/sshd"]
---
# 示例二
...
spec:
  runMode:
    debug:
      enable: true