集群作业队列#

SkyPilot的作业队列允许多个作业在集群上调度。

入门指南#

每个由sky exec提交的任务都会自动排队并安排在现有集群上执行:

# Launch the job 5 times.
sky exec mycluster task.yaml -d
sky exec mycluster task.yaml -d
sky exec mycluster task.yaml -d
sky exec mycluster task.yaml -d
sky exec mycluster task.yaml -d

-d / --detach 标志将日志记录从终端分离,这对于同时启动许多长时间运行的作业非常有用。

显示集群的作业及其状态:

# Show a cluster's jobs (job IDs, statuses).
sky queue mycluster

显示每个作业的输出:

# Stream the outputs of a job.
sky logs mycluster JOB_ID

取消作业:

# Cancel a job.
sky cancel mycluster JOB_ID

# Cancel all jobs on a cluster.
sky cancel mycluster --all

多节点任务#

作业队列也支持在多个节点上运行的作业。

首先,创建一个 cluster.yaml 来指定所需的集群:

num_nodes: 4
resources:
  accelerators: H100:8

workdir: ...
setup: |
  # Install dependencies.
  ...

使用 sky launch -c mycluster cluster.yaml 来配置一个4节点(每个节点有8个H100 GPU)的集群。 num_nodes 字段用于指定需要多少个节点。

接下来,创建一个 task.yaml 来指定每个任务:

num_nodes: 2
resources:
  accelerators: H100:4

run: |
  # Run training script.
  ...

这指定了一个需要在2个节点上运行的任务,每个节点必须有4个空闲的H100。

使用 sky exec mycluster task.yaml 提交此任务,该任务将由作业队列正确调度。

详情请参见分布式多节点作业

使用 CUDA_VISIBLE_DEVICES#

环境变量 CUDA_VISIBLE_DEVICES 将自动设置为分配给每个节点上每个任务的设备。此变量在任务的 run 命令被调用时设置。

例如,上面的task.yaml在每个有8个GPU的节点上启动一个4-GPU任务,因此任务的run命令将使用填充了4个设备ID的CUDA_VISIBLE_DEVICES来调用。

如果你的run命令使用Docker/docker run,只需传递--gpus=all; 正确的环境变量将在容器内设置(只会设置分配的设备ID)。

示例:部分GPU#

要在每个GPU上运行多个试验,请在资源需求中使用部分GPU。 例如,使用--gpus H100:0.5让2个试验共享1个GPU:

$ sky exec mycluster --gpus H100:0.5 -d -- python train.py --lr 1e-3
$ sky exec mycluster --gpus H100:0.5 -d -- python train.py --lr 3e-3
...

共享GPU时,确保GPU的内存没有被超额分配(否则可能会出现内存不足的错误)。

调度行为#

SkyPilot的调度器有两个目标:

  1. 防止资源过度分配:SkyPilot 使用任务的资源要求在集群上调度作业——这些要求可以在任务的 YAML 文件的 resources 字段中指定,或者通过 sky exec CLI 命令的 --gpus 选项指定。SkyPilot 在确保集群中没有资源被过度分配的情况下,尊重这些资源要求。例如,如果一个节点有 4 个 GPU,它不能承载一组任务,这些任务的 GPU 需求总和超过 4。

  2. 最小化资源闲置:如果资源处于闲置状态,SkyPilot 将调度一个可以利用该资源的排队任务。

我们通过回顾教程:AI 训练来说明调度行为。 在该教程中,我们有一个任务 YAML,指定了以下资源需求:

# dnn.yaml
...
resources:
  accelerators: H100:4
...

由于我们在运行sky launch -c lm-cluster dnn.yaml时创建了一个新的集群,SkyPilot 使用与任务所需完全相同的资源来配置该集群。因此,lm-cluster 拥有 4 个 H100 GPU。

在这个初始任务运行的同时,让我们提交更多的任务:

$ # Launch 4 jobs, perhaps with different hyperparameters.
$ # You can override the task name with `-n` (optional) and
$ # the resource requirement with `--gpus` (optional).
$ sky exec lm-cluster dnn.yaml -d -n job2 --gpus=H100:1
$ sky exec lm-cluster dnn.yaml -d -n job3 --gpus=H100:1
$ sky exec lm-cluster dnn.yaml -d -n job4 --gpus=H100:4
$ sky exec lm-cluster dnn.yaml -d -n job5 --gpus=H100:2

因为集群只有4个H100 GPU,我们将看到以下事件序列:

  • 初始的sky launch任务正在运行并占用了4个GPU;所有其他任务都在等待(没有可用的GPU)。

  • 前两个sky exec作业(job2, job3)随后开始运行,每个作业占用1个GPU。

  • 第三个作业(job4)将会挂起,因为它需要4个GPU,而目前只有2个空闲的GPU可用。

  • 第四个作业(job5)将开始运行,因为其需求已通过2个空闲的GPU得到满足。

  • 一旦除了job5之外的所有任务完成,集群的4个GPU将再次空闲,job4将从待定状态转变为运行状态。

因此,我们可能会在这个集群上看到以下作业状态:

$ sky queue lm-cluster

 ID  NAME         USER  SUBMITTED    STARTED     STATUS
 5   job5         user  10 mins ago  10 mins ago RUNNING
 4   job4         user  10 mins ago  -           PENDING
 3   job3         user  10 mins ago  9 mins ago  RUNNING
 2   job2         user  10 mins ago  9 mins ago  RUNNING
 1   huggingface  user  10 mins ago  1 min ago   SUCCEEDED