托管作业#

提示

此功能非常适合扩展:长时间运行单个作业,或并行运行多个作业。

SkyPilot 支持 托管作业 (sky jobs),这些作业可以从任何底层抢占或硬件故障中自动恢复。 托管作业可以在三种模式下使用:

  1. Managed spot jobs: 作业在自动恢复的spot实例上运行。通过使可抢占的spot实例适用于长时间运行的作业,这显著节省了成本(例如,GPU虚拟机节省约70%)。

  2. Managed on-demand/reserved jobs: 作业在自动恢复的按需或预留实例上运行。适用于需要保证资源的作业。

  3. 托管管道: 运行包含多个任务的管道(这些任务可以有不同的资源需求和setup/run命令)。适用于运行一系列相互依赖的任务,例如数据处理、训练模型,然后在其上运行推理。

托管Spot作业#

在这种模式下,作业在竞价实例上运行,抢占由SkyPilot自动恢复。

要启动一个托管竞价作业,请使用 sky jobs launch --use-spot。 SkyPilot 会自动在各个区域和云中找到可用的竞价实例,以最大化可用性。 任何竞价中断都会由 SkyPilot 自动处理,无需用户干预。

这里是一个BERT训练任务在AWS和GCP不同区域失败的示例。

GIF for BERT training on Spot V100 静态图,BERT在Spot V100上的训练

要使用托管点作业,有两个要求:

  1. Job YAML: 托管Spot需要一个YAML来描述任务,已通过sky launch进行测试。

  2. Checkpointing(可选):由于抢占导致的作业恢复,用户应用程序代码可以定期将其进度检查点到挂载的云存储桶。程序在重新启动时可以重新加载最新的检查点。

托管点作业启动点集群之间的快速比较:

命令

托管?

是否支持SSH?

最适合

sky jobs launch --use-spot

是的,抢占是自动恢复的

扩展长时间运行的作业(例如,数据处理、训练、批量推理)

sky launch --use-spot

不,抢占不被处理

在现货实例上进行交互式开发(特别是对于抢占率较低的硬件)

任务 YAML#

要启动一个托管作业,您可以简单地重用您的作业YAML(建议先使用sky launch进行测试)。 例如,我们发现BERT微调YAML与sky launch兼容,并希望使用SkyPilot托管Spot作业启动它。

我们可以使用以下方式启动它:

$ git clone https://github.com/huggingface/transformers.git ~/transformers -b v4.30.1
$ sky jobs launch -n bert-qa bert_qa.yaml
# bert_qa.yaml
name: bert-qa

resources:
  accelerators: V100:1
  use_spot: true  # Use spot instances to save cost.

envs:
  # Fill in your wandb key: copy from https://wandb.ai/authorize
  # Alternatively, you can use `--env WANDB_API_KEY=$WANDB_API_KEY`
  # to pass the key in the command line, during `sky jobs launch`.
  WANDB_API_KEY:

# Assume your working directory is under `~/transformers`.
workdir: ~/transformers

setup: |
  pip install -e .
  cd examples/pytorch/question-answering/
  pip install -r requirements.txt torch==1.12.1+cu113 --extra-index-url https://download.pytorch.org/whl/cu113
  pip install wandb

run: |
  cd examples/pytorch/question-answering/
  python run_qa.py \
    --model_name_or_path bert-base-uncased \
    --dataset_name squad \
    --do_train \
    --do_eval \
    --per_device_train_batch_size 12 \
    --learning_rate 3e-5 \
    --num_train_epochs 50 \
    --max_seq_length 384 \
    --doc_stride 128 \
    --report_to wandb \
    --output_dir /tmp/bert_qa/

注意

workdir带有本地文件的文件挂载 将自动上传到 云存储桶。存储桶将在作业运行期间创建,并在作业完成后清理。

SkyPilot 将启动并开始监控任务。当发生抢占或任何机器故障时,SkyPilot 将自动跨区域和云搜索资源以重新启动任务。

在这个例子中,作业将在每次抢占恢复后从头开始重新启动。 要从之前的状态恢复作业,用户的应用程序需要实现检查点和恢复。

检查点与恢复#

为了实现作业恢复,通常需要一个云存储桶来存储作业的状态(例如,模型检查点)。 下面是一个将存储桶挂载到/checkpoint的示例。

file_mounts:
  /checkpoint:
    name: # NOTE: Fill in your bucket name
    mode: MOUNT

SkyPilot bucket mounting 中的 MOUNT 模式确保输出到 /checkpoint 的检查点会自动同步到持久存储桶中。 请注意,应用程序代码应定期保存程序检查点,并在作业重新启动时重新加载这些状态。 这通常通过在程序开始时重新加载最新的检查点来实现。

端到端示例#

下面我们展示了一个示例,用于在HuggingFace上进行问答任务的BERT模型微调。

# bert_qa.yaml
name: bert-qa

resources:
  accelerators: V100:1
  use_spot: true  # Use spot instances to save cost.

file_mounts:
  /checkpoint:
    name: # NOTE: Fill in your bucket name
    mode: MOUNT

envs:
  # Fill in your wandb key: copy from https://wandb.ai/authorize
  # Alternatively, you can use `--env WANDB_API_KEY=$WANDB_API_KEY`
  # to pass the key in the command line, during `sky jobs launch`.
  WANDB_API_KEY:

# Assume your working directory is under `~/transformers`.
workdir: ~/transformers

setup: |
  pip install -e .
  cd examples/pytorch/question-answering/
  pip install -r requirements.txt torch==1.12.1+cu113 --extra-index-url https://download.pytorch.org/whl/cu113
  pip install wandb

run: |
  cd examples/pytorch/question-answering/
  python run_qa.py \
    --model_name_or_path bert-base-uncased \
    --dataset_name squad \
    --do_train \
    --do_eval \
    --per_device_train_batch_size 12 \
    --learning_rate 3e-5 \
    --num_train_epochs 50 \
    --max_seq_length 384 \
    --doc_stride 128 \
    --report_to wandb \
    --output_dir /checkpoint/bert_qa/ \
    --run_name $SKYPILOT_TASK_ID \
    --save_total_limit 10 \
    --save_steps 1000

由于HuggingFace内置了定期检查点的支持,我们只需要传递高亮的参数来设置输出目录和检查点的频率(更多信息请参见Huggingface API)。 你也可以参考另一个示例这里,了解如何使用PyTorch进行定期检查点。

我们还设置了--run_name$SKYPILOT_TASK_ID,以便同一作业的所有恢复日志都将保存到Weights & Biases中的同一运行中。

注意

环境变量 $SKYPILOT_TASK_ID(例如:“sky-managed-2022-10-06-05-17-09-750781_bert-qa_8-0”)可用于识别相同的作业,即在所有作业恢复中保持不变。 它可以在任务的 run 命令中访问,或直接在程序本身中访问(例如,通过 os.environ 访问并传递给 Weights & Biases 以在训练脚本中进行跟踪)。每当任务被调用时,它都会提供给任务。

通过高亮显示的更改,托管spot作业现在可以在抢占后恢复训练!我们可以享受spot实例带来的成本节省优势,而无需担心抢占或失去进度。

$ sky jobs launch -n bert-qa bert_qa.yaml

提示

尝试复制粘贴此示例并适应您自己的工作。

实际案例#

托管按需/预留作业#

相同的 sky jobs launch 和 YAML 接口可以在自动恢复的按需或预留实例上运行作业。这对于让 SkyPilot 监控任何底层机器故障并透明地恢复作业非常有用。

为此,只需在resources部分中设置use_spot: false,或在CLI中使用--use-spot false覆盖它。

$ sky jobs launch -n bert-qa bert_qa.yaml --use-spot false

提示

sky jobs launch视为“无服务器”托管作业接口是有用的,而sky launch是一个集群接口(你可以在其上启动任务,尽管不是托管的)。

无论是现货还是按需/预留#

你可以使用 any_of 来指定 spot 或 on-demand/reserved 实例作为作业的候选资源。更多详情请参阅文档 这里

resources:
  accelerators: A100:8
  any_of:
    - use_spot: true
    - use_spot: false

在这个例子中,SkyPilot将执行成本优化以选择要使用的资源,几乎可以肯定的是,这将选择spot实例。如果spot实例不可用,SkyPilot将回退到启动按需/预留实例。

作业在用户代码失败时重启#

默认情况下,当底层集群被抢占或失败时,SkyPilot 会尝试恢复作业。任何用户代码失败(非零退出代码)都不会自动恢复。

在某些情况下,您可能希望作业在自身失败时自动重启,例如,当训练作业由于Nvidia驱动程序问题或NCCL超时而崩溃时。要指定这一点,您可以在作业YAML文件中的resources.job_recovery中设置max_restarts_on_errors

resources:
  accelerators: A100:8
  job_recovery:
    # Restart the job up to 3 times on user code errors.
    max_restarts_on_errors: 3

更高级的资源选择策略,例如Can’t Be Late (NSDI’24)论文,未来可能会得到支持。

运行多个并行作业#

对于诸如数据处理超参数扫描等批处理作业,您可以并行启动多个作业。请参阅许多并行作业

有用的CLI#

这里是一些用于管理作业的命令。查看 sky jobs --helpCLI reference 以获取更多详细信息。

查看所有托管作业:

$ sky jobs queue
Fetching managed job statuses...
Managed jobs:
ID NAME     RESOURCES           SUBMITTED   TOT. DURATION   JOB DURATION   #RECOVERIES  STATUS
2  roberta  1x [A100:8][Spot]   2 hrs ago   2h 47m 18s      2h 36m 18s     0            RUNNING
1  bert-qa  1x [V100:1][Spot]   4 hrs ago   4h 24m 26s      4h 17m 54s     0            RUNNING

流式传输正在运行的托管作业的日志:

$ sky jobs logs -n bert-qa  # by name
$ sky jobs logs 2           # by job ID

取消一个托管作业:

$ sky jobs cancel -n bert-qa  # by name
$ sky jobs cancel 2           # by job ID

注意

如果托管作业发生任何故障,您可以检查sky jobs queue -a以获取故障的简要原因。要获取更多详细信息,检查sky jobs logs --controller 将会很有帮助。

托管管道#

管道是一个包含一系列按顺序运行的任务的托管作业。

这对于运行一系列相互依赖的任务非常有用,例如训练模型然后在其上运行推理。 不同的任务可以有不同的资源需求,以使用适当的每任务资源,从而节省成本,同时将管理任务的负担从用户身上移开。

注意

换句话说,托管作业可以是单个任务或任务管道。所有托管作业都由sky jobs launch提交。

要运行一个管道,请在YAML文件中指定任务的顺序。以下是一个示例:

name: pipeline

---

name: train

resources:
  accelerators: V100:8
  any_of:
    - use_spot: true
    - use_spot: false

file_mounts:
  /checkpoint:
    name: train-eval # NOTE: Fill in your bucket name
    mode: MOUNT

setup: |
  echo setup for training

run: |
  echo run for training
  echo save checkpoints to /checkpoint

---

name: eval

resources:
  accelerators: T4:1
  use_spot: false

file_mounts:
  /checkpoint:
    name: train-eval # NOTE: Fill in your bucket name
    mode: MOUNT

setup: |
  echo setup for eval

run: |
  echo load trained model from /checkpoint
  echo eval model on test set

上面的YAML定义了一个包含两个任务的管道。第一个name: pipeline为管道命名。第一个任务的名称为train,第二个任务的名称为eval。任务之间用三个破折号---分隔。每个任务都有自己的resourcessetuprun部分。任务是按顺序执行的。

要提交管道,使用相同的命令 sky jobs launch。管道将由SkyPilot自动启动和监控。您可以使用 sky jobs queuesky jobs dashboard 检查管道的状态。

$ sky jobs launch -n pipeline pipeline.yaml
$ sky jobs queue
Fetching managed job statuses...
Managed jobs
In progress jobs: 1 RECOVERING
ID  TASK  NAME      RESOURCES                    SUBMITTED    TOT. DURATION  JOB DURATION  #RECOVERIES  STATUS
8         pipeline  -                            50 mins ago  47m 45s        -             1            RECOVERING
 ↳  0     train     1x [V100:8][Spot|On-demand]  50 mins ago  47m 45s        -             1            RECOVERING
 ↳  1     eval      1x [T4:1]                    -            -              -             0            PENDING

注意

$SKYPILOT_TASK_ID 环境变量在每个任务的 run 部分也是可用的。它在管道中的每个任务中是唯一的。 例如,上面 eval 任务的 $SKYPILOT_TASK_ID 是: “sky-managed-2022-10-06-05-17-09-750781_pipeline_eval_8-1”。

工作仪表板#

使用 sky jobs dashboard 打开一个仪表板以查看所有作业:

$ sky jobs dashboard

这会自动打开一个浏览器标签以显示仪表板:

../_images/job-dashboard.png

用户界面显示与命令行界面 sky jobs queue -a 相同的信息。当有许多进行中的任务需要监控时,用户界面尤其有用,因为基于终端的命令行界面可能需要多页才能显示。

文件的中间存储#

对于托管作业,SkyPilot需要一个中间存储桶来存储任务中使用的文件,例如本地文件挂载、临时文件和工作目录。 如果您没有配置存储桶,SkyPilot将自动为每次作业启动创建一个名为skypilot-filemounts-{username}-{run_id}的临时存储桶。SkyPilot在作业完成后会自动删除该存储桶。

或者,你可以预先配置一个存储桶,并通过在~/.sky/config.yaml中设置jobs.bucket来将其用作存储文件的中间介质:

# ~/.sky/config.yaml
jobs:
  bucket: s3://my-bucket  # Supports s3://, gs://, https://<azure_storage_account>.blob.core.windows.net/<container>, r2://, cos://<region>/<bucket>

如果您选择指定一个存储桶,请确保该存储桶已经存在并且您拥有必要的权限。

当使用预配置的中间存储桶与jobs.bucket时,SkyPilot会在存储桶根目录下创建特定于作业的目录来存储文件。它们按照以下结构组织:

# cloud bucket, s3://my-bucket/ for example
my-bucket/
├── job-15891b25/            # Job-specific directory
│   ├── local-file-mounts/   # Files from local file mounts
│   ├── tmp-files/           # Temporary files
│   └── workdir/             # Files from workdir
└── job-cae228be/            # Another job's directory
    ├── local-file-mounts/
    ├── tmp-files/
    └── workdir/

当使用自定义存储桶(jobs.bucket)时,SkyPilot创建的作业特定目录(例如,job-15891b25/)在作业完成时会被移除。

提示

多个用户可以共享同一个中间存储桶。每个用户的任务将拥有自己独特的任务特定目录,确保文件保持分离和组织。

概念:作业控制器#

作业控制器是一个在云中运行的小型按需CPU虚拟机,用于管理用户的所有作业。 当提交第一个托管作业时,它会自动启动,并且在空闲10分钟后自动停止(即,在所有托管作业完成且在该时间段内没有提交新的托管作业后)。 因此,不需要用户操作来管理其生命周期。

你可以使用sky status查看控制器,并通过使用-r/--refresh标志刷新其状态。

虽然作业控制器的成本可以忽略不计(运行时约为$0.4/小时,停止时不到$0.004/小时),你仍然可以使用sky down 手动将其拆除,其中可以在sky status的输出中找到。

注意

销毁作业控制器会丢失所有已完成托管作业的日志和状态信息。只有在没有进行中的托管作业时,才允许这样做,以确保没有资源泄漏。

自定义作业控制器资源#

您可能出于多种原因想要自定义作业控制器的资源:

  1. 更改可以同时运行的最大作业数,这是控制器vCPU的两倍。(默认值:16)

  2. 使用成本较低的控制器(如果您同时管理的作业数量较少)。

  3. 强制作业控制器在特定位置运行。(默认:最便宜的位置)

  4. 更改作业控制器的磁盘大小以存储更多日志。(默认值:50GB)

为了实现上述目标,您可以在~/.sky/config.yaml中指定自定义配置,包含以下字段:

jobs:
  # NOTE: these settings only take effect for a new jobs controller, not if
  # you have an existing one.
  controller:
    resources:
      # All configs below are optional.
      # Specify the location of the jobs controller.
      cloud: gcp
      region: us-central1
      # Specify the maximum number of managed jobs that can be run concurrently.
      cpus: 4+  # number of vCPUs, max concurrent jobs = 2 * cpus
      # Specify the disk_size in GB of the jobs controller.
      disk_size: 100

resources 字段与普通的 SkyPilot 作业具有相同的规范;请参阅 这里

注意

如果您已经有一个现有的控制器(无论是停止的还是活动的),这些设置将不会生效。要使它们生效,首先需要拆除现有的控制器,这要求所有进行中的作业完成或被取消。