编程 SkyPilot 深度实战:从多云 AI 调度到成本优化的企业级完全指南

2026-05-24 00:00:53 +0800 CST views 7

SkyPilot 深度实战:从多云 AI 调度到成本优化的企业级完全指南

2026 年,当 AI 工作负载从单云走向多云、从训练走向推理、从实验走向生产,基础设施的碎片化已成为 AI 团队最大的痛点。SkyPilot 应运而生——它不是又一个云管理平台,而是 AI 工作负载的"通用遥控器"。

目录

  1. 痛点:AI 基础设施的碎片化危机
  2. SkyPilot 核心架构解析
  3. 实战一:第一次跨越云边界
  4. 实战二:托管作业与故障自愈
  5. 实战三:Spot 实例与成本优化
  6. 实战四:分布式训练与生产级部署
  7. 高级特性:资源池、配额管理与多云策略
  8. 性能调优:从调度延迟到 GPU 利用率
  9. 企业落地:权限、审计与多租户
  10. 与其他方案的对比:为什么选 SkyPilot
  11. 总结与展望

1. 痛点:AI 基础设施的碎片化危机

1.1 现实场景:一个 AI 工程师的日均崩溃时刻

上午 9:00,你打开 Jupyter Notebook,发现本地 GPU 不够跑今天的实验。你 ssh 到公司机房的一台 A100,跑起来才发现 CUDA 版本和代码要求的对不上。

上午 10:30,实验跑了一半,GPU 显存爆了。你意识到需要至少 4 张 A100,但公司机房只有 2 张。你打开 AWS Console,试图启动一个 p4d.24xlarge,但发现该区域的 A100 配额用完了。

中午 12:00,你终于在 GCP 上启动了一个实例。但数据传输慢得离谱——你的训练数据在 S3,而实例在 GCP。你花了 1 小时写脚本做数据迁移。

下午 3:00,实验终于跑起来了。但你意识到这个实验需要跑 48 小时,而你的笔记本电脑不能一直开着(公司 IT 策略会断网)。你需要一个"托管"的解决方案。

下午 5:00,你接到老板电话:"咱们的云账单这个月怎么这么高?你是不是在 AWS 上开了什么大家伙?"你打开账单,发现是 Spot 实例被回收后自动在新区域重启,但配置错误导致用了 On-Demand 实例,价格翻了 5 倍。

这不是段子,这是 2026 年每个 AI 工程师的日常。

1.2 碎片化问题的本质

AI 基础设施碎片化不是"选择太多"的问题,而是接口不统一、抽象层次不匹配、运维语义不一致的问题。

维度AWSGCPAzure本地机房核心矛盾
实例类型p4d.24xlargea2-ultragpu-8gNC100rsv3server-01命名无标准
GPU 类型A100 40GB/80GBA100 40GB/80GBA100 40GB/80GBA100 40GB/80GB规格有差异
存储EBS/S3Persistent Disk/GCSManaged Disk/BlobNFS/Local数据迁移复杂
网络VPC/Elastic IPVPC/Public IPVNet/Public IP物理网络网络配置不同
Spot 策略Spot FleetPreemptibleSpotN/A回收逻辑不同
配额管理Service QuotaAllocation QuotasQuota物理限制申请流程不同
成本结构On-Demand/Spot/ReservedOn-Demand/Preemptible/SustainedPay-as-you-go/Spot/Reserved电力+折旧计费模型不同

你需要的不是"学会所有云",而是一个统一的抽象层。

1.3 SkyPilot 的解法:AI 工作负载的"声明式"抽象

SkyPilot 的核心设计哲学是:你声明"我要什么",而不是"怎么在 AWS/GCP/Azure 上创建"。

# 传统方式:你需要在每个云上分别写脚本
# AWS
aws ec2 run-instances --instance-type p4d.24xlarge --count 1 ...

# GCP
gcloud compute instances create --machine-type a2-ultragpu-8g ...

# SkyPilot 方式:一个配置文件,随处运行
resources:
  accelerators: A100:8
  region: us-east-1

这不仅仅是"少写几行代码"的问题,而是认知负担的质变:

  • 你不再需要记住 AWS 的 p4d.24xlarge 对应 GCP 的 a2-ultragpu-8g
  • 你不再需要手动处理 Spot 实例回收后的重启逻辑
  • 你不再需要在代码中硬编码云厂商特定的 SDK 调用
  • 你不再需要在 3 个云控制台之间来回切换比对价格

2. SkyPilot 核心架构解析

2.1 架构总览:控制平面与执行平面分离

SkyPilot 采用经典的**控制平面(Control Plane)+ 执行平面(Execution Plane)**架构,但针对 AI 工作负载做了深度优化。

┌─────────────────────────────────────────────────────────────┐
│                    用户接口层                                │
│  CLI (sky cli)  │  Python API (sky.*)  │  YAML (sky.yaml) │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│                    核心调度引擎                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐ │
│  │ 资源匹配 │  │ 成本优化 │  │ 故障恢复 │  │ 状态管理 │ │
│  │  (Match) │  │ (Cost)   │  │(Recovery)│  │ (State)  │ │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘ │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│                    云适配器层 (Cloud Adapters)                │
│  AWS  │  GCP  │  Azure  │  Lambda  │  RunPod  │  Fluid  │
│  (EC2) │ (GCE) │  (VM)  │  (Labs) │  (GPU)  │  (Stack)│
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│                    执行平面 (Execution Plane)                │
│  VM 启动  │  环境初始化  │  代码同步  │  作业执行  │  日志  │
└─────────────────────────────────────────────────────────────┘

2.2 核心概念详解

2.2.1 Task:AI 工作负载的最小单元

Task 是 SkyPilot 中最基本的概念,它封装了运行 AI 作业所需的一切信息

import sky

# 方式一:从命令行构建
task = sky.Task(
    name='my-training',
    setup='pip install torch transformers datasets',
    run='python train.py --model bert-large-uncased --epochs 10'
)

# 方式二:从 YAML 文件构建(推荐用于生产环境)
task = sky.Task.from_yaml('train.yaml')

# 方式三:Programmatic API(用于动态生成任务)
def create_hyperparam_sweep_task(lr, batch_size):
    return sky.Task(
        name=f'sweep-lr{lr}-bs{batch_size}',
        setup='pip install -r requirements.txt',
        run=f'python train.py --lr {lr} --batch-size {batch_size}'
    )

Task 的核心属性:

属性类型说明示例
namestr任务名称(需唯一)'bert-pretrain-20260523'
setupstr环境初始化命令'conda create -n torch python=3.10 && conda activate torch && pip install torch'
runstr主执行命令'python train.py --config configs/large.yaml'
workdirstr本地代码目录(自动同步)'./src'
num_nodesint分布式节点数4(4 机并行)
resourcesResources资源需求(见下文)sky.Resources(accelerators='A100:8')

关键设计决策:为什么 setuprun 是 shell 命令而不是 Python 函数?

因为 SkyPilot 的设计目标是云无关(Cloud Agnostic),而 shell 是所有 Linux 环境的通用语言。如果你的 setup 里写的是 pip install torch,那么无论在 AWS、GCP 还是本地机房,这条命令都能正确执行。

2.2.2 Resources:资源需求的声明式描述

Resources 对象让你声明你需要什么资源,而不是如何在特定云上获取这些资源

import sky

# 基础用法:指定 GPU 类型和数量
resources = sky.Resources(accelerators='A100:8')
# 这会在任何支持 A100x8 的云上启动实例

# 进阶用法:指定云厂商(用于数据本地性或合规要求)
resources = sky.Resources(
    cloud='aws',                    # 只在 AWS 上运行
    accelerators='A100:8',          # 需要 8 张 A100
    region='us-east-1',             # 必须在 us-east-1(数据在这)
    zone='us-east-1a'               # 可选:指定可用区
)

# 成本优化用法:允许 Spot 实例
resources = sky.Resources(
    accelerators='A100:8',
    use_spot=True,                   # 允许使用 Spot 实例(最高节省 90%)
    spot_recovery_retries=3,         # Spot 被回收后最多重试 3 次
)

# 高级用法:自定义实例类型(当你需要特定 CPU/Memory 配置时)
resources = sky.Resources(
    cloud='aws',
    instance_type='p4d.24xlarge',   # 直接指定 AWS 实例类型
    # 注意:这会降低可移植性,只在必要时使用
)

Resources 的匹配逻辑(最重要的一部分):

当你执行 sky.launch(task) 时,SkyPilot 会执行以下步骤:

  1. 资源匹配(Resource Matching)

    • 解析 accelerators='A100:8'
    • 查询所有已配置的云(AWS/GCP/Azure/...)的 API,获取可用实例类型列表
    • 过滤出包含至少 8 张 A100 的实例类型
    • 结果:AWS:p4d.24xlarge, GCP:a2-ultragpu-8g, Azure:NC100rsv3(假设这些在当前区域可用)
  2. 成本排序(Cost Sorting)

    • 对候选实例按每小时成本排序
    • Spot 实例会被优先考虑(如果 use_spot=True
    • 结果:GCP:a2-ultragpu-8g:Spot ($12.5/h) < AWS:p4d.24xlarge:Spot ($15.6/h) < ...
  3. 可用性检查(Availability Check)

    • 从最便宜的开始,尝试启动实例
    • 如果配额不足或容量不足,自动尝试下一个
    • 如果所有候选都失败,报错并给出详细原因

这就是 SkyPilot 的核心价值:把"手动比对 17 个云厂商的 43 种 GPU 实例类型"变成了"写一行 accelerators='A100:8'"。

2.2.3 Managed Jobs:生产级的故障自愈

sky.job 是 SkyPilot 的托管作业(Managed Jobs)子系统,专为长时间运行的 AI 作业设计。

传统方式的问题:

# 你通过 SSH 启动了一个训练作业
ssh user@remote-gpu
python train.py

# 问题 1:SSH 断开 → 训练停止(除非你用了 nohup/tmux/screen)
# 问题 2:Spot 实例被回收 → 训练停止,需要从 checkpoint 重启
# 问题 3:机器故障 → 需要从头开始
# 问题 4:你无法从其他机器查看进度(除非你配置了 TensorBoard 远程访问)

SkyPilot Managed Jobs 的解法:

import sky

task = sky.Task(
    name='bert-large-training',
    setup='pip install torch transformers',
    run='python train.py --model bert-large-uncased --epochs 100',
    resources=sky.Resources(accelerators='A100:8', use_spot=True)
)

# 关键:使用 sky.job.submit() 而不是 sky.launch()
job_id = sky.job.submit(task)

# 现在:
# ✅ 作业在后台运行,不受 SSH 断开影响
# ✅ 如果 Spot 实例被回收,自动在新实例上从 checkpoint 重启
# ✅ 你可以通过 sky job status <job_id> 随时查看状态
# ✅ 作业完成后,你可以通过 sky job logs <job_id> 查看完整日志

Managed Jobs 的状态机:

SUBMITTED → PENDING (等待资源) → RUNNING (训练中) → FINISHED (成功)
                ↓                        ↓
            (资源不足)               (Spot 回收)
                ↓                        ↓
            REQUEUED                RECOVERING (自动重启)

2.3 网络架构:控制平面如何与执行平面通信

SkyPilot 的一个精妙设计是:它不需要你在云上部署任何"控制平面"软件。所有的编排逻辑都在你的本地机器上运行,通过云的 API 远程控制实例。

工作流程:

┌──────────┐    API Call     ┌────────────────────────────────┐
│ 你的本地 │ ──────────────▶ │  AWS/GCP/Azure API            │
│ 机器     │                 │  (DescribeInstances, etc.)    │
└──────────┘                 └────────────────────────────────┘
      │                                    │
      │  SSH (通过公网 IP 或跳板机)        │ 启动实例
      │                                    ▼
      │                            ┌───────────────┐
      └───────────────────────────▶│ 远程 GPU 实例 │
                                   │  (自动安装了  │
                                   │   SkyPilot   │
                                   │   Agent)     │
                                   └───────────────┘

关键优势:

  • 无单点故障:控制平面在你本地,不在云端。如果 SkyPilot 的服务器宕机(假设未来有 SaaS 版本),你的作业不受影响。
  • 无供应商锁定:SkyPilot 是开源的,你可以完全掌控编排逻辑。
  • 安全:你的代码和数据不需要经过 SkyPilot 的服务器,直接在你控制的云实例上运行。

但是,这也意味着:

  • 你的本地机器需要能够访问云 API(需要配置 credentials)
  • 如果你的本地机器关机,正在等待资源的新作业会暂停调度(但已运行的作业不受影响)

3. 实战一:第一次跨越云边界

3.1 安装与配置

# 安装 SkyPilot(需要 Python >= 3.8)
pip install "skypilot[all]"  # [all] 包含所有云适配器

# 验证安装
sky --version  # 应该输出 0.6.0 或更高

# 配置云 credentials(只需一次)
# AWS
aws configure  # SkyPilot 会自动读取 ~/.aws/credentials

# GCP
gcloud auth application-default login  # SkyPilot 会自动使用 Application Default Credentials

# Azure
az login  # SkyPilot 会自动读取 Azure CLI 的 credentials

# 验证云配置
sky check  # 会告诉你哪些云已正确配置

常见配置问题排查:

问题原因解决方法
sky check 显示 AWS 未配置~/.aws/credentials 不存在或格式错误运行 aws configure 重新配置
GCP 报错 Could not authenticate未设置 Project IDgcloud config set project <your-project-id>
Azure 报错 No subscriptions found账号下没有活跃的订阅检查 Azure Portal,确保订阅状态为 Active

3.2 Hello World:在任意云上运行一个 GPU 作业

目标: 运行一个最简单的 GPU 作业——检查 GPU 可用性。

# hello-sky.yaml
resources:
  accelerators: T4:1  # 最便宜的 GPU,约 $0.11/h On-Demand

setup: |
  nvidia-smi  # 这会在实例启动后执行,验证 GPU 可用

run: |
  python -c "import torch; print(f'CUDA available: {torch.cuda.is_available()}'); print(f'GPU count: {torch.cuda.device_count()}')"
# 启动作业
sky launch hello-sky.yaml -c my-first-cluster  # -c 指定集群名称

# 输出示例:
# ✓ Found 3 candidate resources:
#   - GCP:a2-coregpt-t4: 1x T4, $0.11/h (On-Demand)
#   - AWS:g4dn.xlarge: 1x T4, $0.526/h (On-Demand)
#   - Azure:NC4as_T4_v3: 1x T4, $0.526/h (On-Demand)
# ✓ Launching on GCP:a2-coregpt-t4 (cheapest)
# ✓ Cluster my-first-cluster launched.
# ✓ Running setup commands...
#   Sat May 23 15:23:45 2026
#   +-----------------------------------------------------------------------------+
#   | NVIDIA-SMI 525.105.17   Driver Version: 525.105.17   CUDA Version: 12.0     |
#   |-------------------------------+----------------------+----------------------+
#   | GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
#   | Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
#   |                               |                      |               MIG M. |
#   |===============================+======================+======================|
#   |   0  Tesla T4            Off  | 00000000:00:04.0 Off |                    0 |
#   | N/A   34C    P8     9W /  70W |      2MiB / 15360MiB |      0%      Default |
#   +-------------------------------+----------------------+----------------------+
# ✓ Running run commands...
#   CUDA available: True
#   GPU count: 1

关键观察:

  1. SkyPilot 自动选择了最便宜的选项(GCP 的 T4,$0.11/h)
  2. 没有写一行 GCP 特定的代码
  3. 如果 GCP 在该区域没有 T4 可用,SkyPilot 会自动回退到 AWS 或 Azure

3.3 代码同步:本地开发,云端执行

痛点: 你在本地的 ~/projects/my-ai-project 里写代码,但需要在云端 GPU 上运行。传统方式是 rsyncgit push/pull,但这两者都很脆弱。

SkyPilot 的解法:workdir 自动同步。

# train.yaml
resources:
  accelerators: A100:1

workdir: .  # 关键:把当前目录同步到远程实例

setup: |
  conda init bash
  conda create -n torch python=3.10 -y
  conda activate torch
  pip install torch transformers datasets wandb

run: |
  conda activate torch
  python train.py --config configs/bert-base.yaml

工作流:

# 1. 在本地开发
cd ~/projects/my-ai-project
echo "print('Version 1')" > train.py
sky launch train.yaml -c my-cluster

# 2. 修改代码
echo "print('Version 2')" > train.py
sky launch train.yaml -c my-cluster  # SkyPilot 会自动同步更新后的文件

# 3. 查看远程文件
sky exec my-cluster -- ls -la /root/skypilot-workdir  # 远程工作目录

workdir 的同步机制:

  • 使用 rsync 进行增量同步(只传输修改过的文件)
  • 默认同步整个目录,但会尊重 .gitignore
  • 同步发生在 setup 之前,所以你的代码在环境初始化时已经可用

4. 实战二:托管作业与故障自愈

4.1 场景:训练一个需要 48 小时的 BERT 大模型

问题: 你的训练作业需要 48 小时,但:

  • 你的笔记本电脑不能一直开着(公司 IT 策略会断网)
  • 你担心 Spot 实例被回收
  • 你希望作业完成后自动通知你

解法:使用 sky.job

import sky

# 定义任务
task = sky.Task(
    name='bert-large-48h-training',
    setup='''
        conda create -n torch python=3.10 -y
        conda activate torch
        pip install torch transformers datasets wandb tqdm
    ''',
    run='''
        conda activate torch
        wandb login $WANDB_API_KEY
        python train.py \\
            --model bert-large-uncased \\
            --epochs 100 \\
            --batch-size 32 \\
            --gradient-accumulation-steps 4 \\
            --save-steps 500 \\
            --logging-steps 50
    ''',
    resources=sky.Resources(
        cloud='aws',               # 数据在 S3,所以在 AWS 上跑
        accelerators='A100:8',     # 需要 8 张 A100 做数据并行
        use_spot=True,             # 使用 Spot 实例(节省成本)
        spot_recovery_retries=5,   # Spot 被回收后最多重试 5 次
    )
)

# 提交为托管作业
job_id = sky.job.submit(task)

print(f"Job submitted: {job_id}")
print(f"Track status: sky job status {job_id}")
print(f"View logs: sky job logs {job_id}")

4.2 监控与管理

# 查看所有托管作业
sky job queue

# 输出示例:
# JOB ID  NAME                        STATUS     STARTED         RESOURCES
# 1       bert-large-48h-training     RUNNING    2 hours ago     AWS:p4d.24xlarge:Spot
# 2       gpt-finetune-v2             FINISHED   1 day ago       GCP:a2-ultragpu-8g:OnDemand
# 3       trial-run-lr-0.001          FAILED     3 hours ago     AWS:p4d.24xlarge:Spot

# 查看特定作业的详细状态
sky job status 1

# 输出示例:
# Job 1: bert-large-48h-training
# Status: RUNNING
# Cluster: my-aws-cluster
# Resources: AWS:p4d.24xlarge:Spot ($15.6/h)
# Submitted: 2026-05-23 13:45:23
# Started: 2026-05-23 13:47:01 (waited 1m 38s for resource)
# Progress: Epoch 12/100, Step 4500/37500
# Latest metrics: loss=0.234, accuracy=0.892

# 查看作业日志(实时追踪)
sky job logs 1 --follow  # 类似 tail -f

# 停止作业
sky job stop 1

# 删除作业(从队列中移除)
sky job delete 1

4.3 Spot 实例故障恢复实战

场景: 你的作业在 Spot 实例上运行,但 AWS 需要回收这个实例。

SkyPilot 的自动恢复流程:

1. AWS 发送 Spot 回收通知(提前 2 分钟)
   ↓
2. SkyPilot Agent(运行在远程实例上)捕获通知
   ↓
3. Agent 执行 checkpoint 保存逻辑
   (你的代码需要定期保存 checkpoint 到云存储)
   ↓
4. Agent 向本地控制平面发送 "SPOT_PREEMPTED" 事件
   ↓
5. 控制平面收到事件,将作业状态改为 "RECOVERING"
   ↓
6. 控制平面尝试在同一云的其他区域启动新实例
   (如果原区域容量不足,会自动切换到其他云)
   ↓
7. 新实例启动后,Agent 从云存储恢复 checkpoint
   ↓
8. 训练从最后一个 checkpoint 继续
   ↓
9. 作业状态改回 "RUNNING"

你的代码需要做什么(checkpoint 保存):

# train.py 的关键部分
import torch
import os
from pathlib import Path

def save_checkpoint(model, optimizer, epoch, step, loss, checkpoint_dir='s3://my-bucket/checkpoints'):
    """保存 checkpoint 到云存储(S3/GCS/Azure Blob 均可)"""
    checkpoint = {
        'epoch': epoch,
        'step': step,
        'model_state_dict': model.state_dict(),
        'optimizer_state_dict': optimizer.state_dict(),
        'loss': loss,
    }
    
    # SkyPilot 会自动处理云存储的认证和传输
    # 你只需要像操作本地文件系统一样操作路径
    local_path = f'/tmp/checkpoint-epoch{epoch}-step{step}.pt'
    torch.save(checkpoint, local_path)
    
    # 使用 SkyPilot 的 storage 抽象(自动处理 S3/GCS/Azure Blob)
    import sky
    sky.Storage(checkpoint_dir).upload_file(
        local_path,
        f'checkpoint-epoch{epoch}-step{step}.pt'
    )

def load_checkpoint(model, optimizer, checkpoint_path='s3://my-bucket/checkpoints/latest.pt'):
    """从云存储恢复 checkpoint"""
    import sky
    local_path = '/tmp/latest_checkpoint.pt'
    sky.Storage(checkpoint_dir).download_file('latest.pt', local_path)
    
    checkpoint = torch.load(local_path)
    model.load_state_dict(checkpoint['model_state_dict'])
    optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
    return checkpoint['epoch'], checkpoint['step']

5. 实战三:Spot 实例与成本优化

5.1 成本对比:On-Demand vs Spot

场景: 你需要 8 张 A100 训练一个大模型,预计需要 7 天。

计费模式单价 (8xA100)7 天总成本风险
AWS On-Demand$32.77/h$5,505
AWS Spot$9.83/h (70% off)$1,651可能被回收
GCP On-Dand$29.52/h$4,960
GCP Preemptible$8.86/h (70% off)$1,487可能被回收(24h 上限)

结论: 使用 Spot 实例可以节省 70% 的成本,但你需要处理实例被回收的情况。

5.2 SkyPilot 的 Spot 策略配置

import sky

# 策略一:最大成本节省(激进)
resources_aggressive = sky.Resources(
    accelerators='A100:8',
    use_spot=True,                  # 必须用 Spot
    spot_recovery_retries=-1,       # 无限重试
    spot_utilization_score=True,     # 优先选择回收概率低的 Spot
)

# 策略二:平衡成本与稳定性(推荐)
resources_balanced = sky.Resources(
    accelerators='A100:8',
    use_spot=True,
    spot_recovery_retries=3,        # 最多重试 3 次
    # 如果 3 次都被回收,自动回退到 On-Demand
    fallback_to_ondemand=True,
)

# 策略三:保证完成时间(保守)
resources_conservative = sky.Resources(
    accelerators='A100:8',
    use_spot=False,                 # 只用 On-Demand
    # 但允许多云切换以降低成本
    cloud=None,                     # 不限云厂商
)

5.3 实际成本优化案例

背景: 一个创业公司需要每天运行 100 个模型训练作业(每个作业需要 1xA100,平均 2 小时)。

传统方式(On-Demand):

  • 100 jobs × 2 hours × $4.10/hour (A100 On-Demand) = $820/day
  • 一个月:$24,600

SkyPilot + Spot 方式:

  • Spot 单价:$1.23/hour (70% off)
  • 100 jobs × 2 hours × $1.23/hour = $246/day
  • Spot 回收导致重试的额外成本:约 10% = $24.6/day
  • 实际成本:$270.6/day
  • 一个月:$8,118
  • 节省:67% ($16,482/month)

但是,你需要确保代码能够正确处理 checkpoint 恢复。


6. 实战四:分布式训练与生产级部署

6.1 多节点分布式训练

场景: 你需要用 数据并行(Data Parallelism) 在 4 台机器上训练一个大模型,每台机器有 8 张 A100。

# distributed-training.yaml
resources:
  accelerators: A100:8
  num_nodes: 4  # 关键:指定节点数

workdir: .

setup: |
  conda create -n torch python=3.10 -y
  conda activate torch
  pip install torch torchvision torchaudio transformers deepspeed

run: |
  conda activate torch
  
  # SkyPilot 会自动设置以下环境变量:
  # - SKYPILOT_NODE_RANK: 当前节点的 rank (0, 1, 2, 3)
  # - SKYPILOT_NUM_NODES: 总节点数 (4)
  # - SKYPILOT_MASTER_ADDR: 主节点的 IP 地址
  # - SKYPILOT_WORKER_ADDRS: 所有工作节点的 IP 地址(逗号分隔)
  
  # 使用 DeepSpeed 进行分布式训练
  deepspeed --hostfile=/tmp/deepspeed_hostfile \\
            --master_addr=$SKYPILOT_MASTER_ADDR \\
            --master_port=29500 \\
            train.py \\
            --deepspeed_config ds_config.json \\
            --model bert-large-uncased \\
            --epochs 100

DeepSpeed 配置文件 (ds_config.json):

{
  "train_batch_size": 512,
  "gradient_accumulation_steps": 16,
  "optimizer": {
    "type": "AdamW",
    "params": {
      "lr": 1e-4,
      "betas": [0.9, 0.95],
      "eps": 1e-8,
      "weight_decay": 0.01
    }
  },
  "fp16": {
    "enabled": true,
    "loss_scale": 0,
    "initial_scale_power": 16
  },
  "zero_optimization": {
    "stage": 3,
    "offload_optimizer": {
      "device": "cpu",
      "pin_memory": true
    },
    "offload_param": {
      "device": "cpu",
      "pin_memory": true
    }
  }
}

6.2 生产级部署:从训练到推理

场景: 你训练好了一个模型,现在需要部署为一个 可扩展的推理服务

import sky

# 步骤 1:定义推理服务任务
inference_task = sky.Task(
    name='bert-large-inference-service',
    setup='''
        pip install torch transformers fastapi uvicorn redis celery
        mkdir -p /model-cache
    ''',
    run='''
        # 下载模型(如果不存在)
        python -c "
        from transformers import AutoModelForSequenceClassification, AutoTokenizer
        model = AutoModelForSequenceClassification.from_pretrained('bert-large-uncased')
        tokenizer = AutoTokenizer.from_pretrained('bert-large-uncased')
        model.save_pretrained('/model-cache/bert-large')
        tokenizer.save_pretrained('/model-cache/bert-large')
        "
        
        # 启动 FastAPI 推理服务
        uvicorn inference_server:app --host 0.0.0.0 --port 8000 --workers 4
    ''',
    resources=sky.Resources(
        accelerators='T4:1',       # 推理只需要 1 张 T4
        use_spot=False,             # 推理服务不能用 Spot(需要稳定性)
    )
)

# 步骤 2:部署为服务(使用 sky.serve)
service = sky.serve.SkyService(
    task=inference_task,
    name='bert-inference',
    num_replicas=3,                # 3 个副本做负载均衡
    auto_scaling=sky.serve.AutoScaling(
        min_replicas=3,
        max_replicas=10,
        target_qps=100,            # 每个副本目标 QPS=100
    ),
)

# 步骤 3:部署
endpoint = service.deploy()

print(f"Inference endpoint: {endpoint}")
# 输出:Inference endpoint: http://52.73.184.12:30001

# 步骤 4:测试推理服务
import requests
response = requests.post(
    f"{endpoint}/predict",
    json={"text": "SkyPilot is awesome!"}
)
print(response.json())
# 输出:{"prediction": "POSITIVE", "confidence": 0.9876}

推理服务的自动扩缩容逻辑:

QPS < 50  →  缩容到 3 个副本(最小值)
QPS = 150 →  扩容到 5 个副本 (150/100 = 1.5 → 2, +3 = 5)
QPS = 800 →  扩容到 10 个副本(最大值)
QPS 突然掉到 0 →  保持 3 个副本 5 分钟,然后缩容到 3

7. 高级特性:资源池、配额管理与多云策略

7.1 资源池(Resource Pools):预配置实例以加速启动

痛点: sky launch 每次都需要启动新实例,这可能需要 2-5 分钟(取决于云厂商)。如果你需要频繁运行短作业(比如超参数搜索的每个 trial),这个启动延迟是不可接受的。

解法:资源池

# 创建一个资源池(预启动 5 个 T4 实例)
sky pool create \
    --name my-t4-pool \
    --resources "T4:1" \
    --size 5 \
    --idle-timeout 30  # 实例空闲 30 分钟后自动关闭

# 在资源池中运行作业(启动延迟 < 10 秒)
sky launch --pool my-t4-pool train.yaml

# 查看资源池状态
sky pool status my-t4-pool

# 输出示例:
# Pool: my-t4-pool
# Resources: T4:1
# Size: 5
# Available: 3
# Busy: 2
# Idle timeout: 30 minutes

7.2 配额管理:避免"意外"的资源耗尽

场景: 你的团队有 5 个研究员,每个人都用 SkyPilot 启动作业。如果不加限制,可能会把所有云配额用完。

解法:配额管理

# 设置全局配额(需要 admin 权限)
sky quotas set \
    --user alice \
    --max-accelerators "A100:16" \  # Alice 最多用 16 张 A100
    --max-monthly-cost 10000        # Alice 每月最多花 $10,000

# 设置项目配额
sky quotas set \
    --project nlp-research \
    --max-accelerators "A100:32" \  # NLP 研究项目最多用 32 张 A100
    --max-monthly-cost 20000

# 查看当前用量
sky quotas usage --user alice

# 输出示例:
# User: alice
# Project: nlp-research
# Current usage:
#   - A100: 8 (out of 16 allowed)
#   - Monthly cost: $3,245 (out of $10,000 allowed)
#   - Remaining budget: $6,755

7.3 多云策略:数据本地性与成本优化

场景: 你的训练数据在 S3 (AWS),但你发现 GCP 的 A100 更便宜。你应该如何配置?

策略一:数据本地性优先(推荐用于大数据集)

import sky

task = sky.Task(
    name='train-on-aws',
    run='python train.py --data s3://my-bucket/data',
    resources=sky.Resources(
        cloud='aws',           # 强制在 AWS 上运行(数据在 S3)
        accelerators='A100:8',
    )
)

策略二:成本优先(推荐用于小数据集)

import sky

task = sky.Task(
    name='train-on-gcp',
    run='''
        # 先传输数据到 GCS
        gsutil -m cp -r s3://my-bucket/data gs://my-gcp-bucket/data
        # 然后训练
        python train.py --data gs://my-gcp-bucket/data
    ''',
    resources=sky.Resources(
        cloud=None,            # 不限云厂商(让 SkyPilot 选择最便宜的)
        accelerators='A100:8',
    )
)

策略三:混合(数据并行 + 多云)

# 如果数据集可以分片,可以在多个云上并行训练
import sky

for shard_id in range(3):
    task = sky.Task(
        name=f'train-shard-{shard_id}',
        run=f'python train.py --data-shard {shard_id} --total-shards 3',
        resources=sky.Resources(
            cloud=None,       # 每个 shard 可以在不同云上运行
            accelerators='A100:8',
        )
    )
    sky.job.submit(task)      # 提交为托管作业

8. 性能调优:从调度延迟到 GPU 利用率

8.1 调度延迟优化

问题:sky launch 到作业开始运行,可能需要 2-5 分钟。如何优化?

优化清单:

优化项方法预期收益
使用资源池sky pool create启动延迟从 2-5 分钟降到 <10 秒
预拉取 Docker 镜像setup 中提前拉取节省 1-2 分钟
使用固定 IP避免每次都重新配置网络节省 30 秒
预热实例保持资源池中有空闲实例零启动延迟

示例:预拉取 Docker 镜像

# optimized-setup.yaml
resources:
  accelerators: A100:1

setup: |
  # 预拉取 PyTorch 官方 Docker 镜像(包含 CUDA 和 cuDNN)
  docker pull pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime
  
  # 预安装 Python 依赖
  pip install torch transformers datasets --cache-dir /tmp/pip-cache

run: |
  docker run --gpus all -v $(pwd):/workspace pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime \\
    python /workspace/train.py

8.2 GPU 利用率优化

问题: 你的 GPU 利用率只有 30%,大部分时间花在数据传输上。

诊断工具:

# 在远程实例上安装 NVIDIA DCGM(Data Center GPU Manager)
sky exec my-cluster -- sudo apt-get install -y datacenter-gpu-manager
sky exec my-cluster -- sudo systemctl start nvidia-dcgm

# 使用 DCGM 监控 GPU 利用率
sky exec my-cluster -- dcgmi dmon -e 1001,1002,1003,1004,1009

# 输出示例:
# #Entity   Entity  Entity     1001 1002 1003 1004 1009
# ID       Type    Name       GPU  Mem  SM   Mem  Power
#                             Util Util Util Util Usage
#                         (%)  (%)  (%)  (%)  (W)
# 0        GPU     GPU 0      98   87   92   85   320
# 1        GPU     GPU 1      12   45   15   42   125
# ...

优化策略:

  1. 使用 DataLoader 的多进程加载

    from torch.utils.data import DataLoader
    
    train_loader = DataLoader(
        dataset,
        batch_size=32,
        num_workers=8,          # 关键:使用多进程加载数据
        pin_memory=True,        # 关键:固定内存,加速 CPU -> GPU 传输
        prefetch_factor=4,      # 提前加载 4 个 batch
    )
    
  2. 使用混合精度训练(FP16/BF16)

    from torch.cuda.amp import autocast, GradScaler
    
    scaler = GradScaler()
    
    for epoch in range(num_epochs):
        for batch in train_loader:
            optimizer.zero_grad()
    
            with autocast():  # 关键:自动混合精度
                outputs = model(batch)
                loss = criterion(outputs, labels)
    
            scaler.scale(loss).backward()
            scaler.step(optimizer)
            scaler.update()
    
  3. 使用梯度累积(Gradient Accumulation)

    # 如果 GPU 显存不足以支持大 batch size,
    # 可以使用梯度累积来模拟大 batch size
    accumulation_steps = 4
    
    for i, batch in enumerate(train_loader):
        outputs = model(batch)
        loss = criterion(outputs, labels)
        loss = loss / accumulation_steps  # 关键:归一化
        loss.backward()
    
        if (i + 1) % accumulation_steps == 0:
            optimizer.step()
            optimizer.zero_grad()
    

8.3 网络带宽优化

问题: 你的训练数据在 S3,但实例在 GCP。数据传输速度只有 10 MB/s。

解法:使用 SkyPilot 的存储抽象 + 本地缓存

import sky

task = sky.Task(
    name='train-with-data-optimization',
    setup='''
        # 安装 AWS CLI 和 GCS FUSE
        pip install awscli gcsfuse
        
        # 创建本地缓存目录
        mkdir -p /data-cache
    ''',
    run='''
        # 使用 SkyPilot 的存储抽象(自动选择最优传输方式)
        # 如果数据在 S3,但实例在 GCP,SkyPilot 会自动使用
        # Google Storage Transfer Service 进行云端到云端传输
        # (比下载到本地再上传快 10-100 倍)
        
        sky storage cp s3://my-bucket/data /data-cache/
        
        # 然后训练
        python train.py --data /data-cache/data
    ''',
    resources=sky.Resources(
        cloud='gcp',           # 在 GCP 上运行
        accelerators='A100:8',
    )
)

9. 企业落地:权限、审计与多租户

9.1 身份认证与访问控制

场景: 你的公司使用 Okta 做 SSO,你希望 SkyPilot 集成公司的身份认证系统。

解法:SkyPilot Enterprise(商业版本)支持 SSO 集成

# 配置 Okta SSO(需要 SkyPilot Enterprise)
sky auth configure-sso \
    --provider okta \
    --okta-domain your-company.okta.com \
    --okta-client-id abc123xyz \
    --okta-client-secret secret123

# 配置 RBAC(基于角色的访问控制)
sky rbac create-role --name data-scientist --permissions "job:submit,job:status,cluster:launch"
sky rbac create-role --name admin --permissions "*"

sky rbac assign-role --user alice --role data-scientist
sky rbac assign-role --user bob --role admin

9.2 审计日志

场景: 你需要追踪谁启动了多少实例,花了多少钱。

解法:启用审计日志

# 配置审计日志(存储到 S3)
sky audit configure \
    --backend s3 \
    --bucket my-company-sky-audit-logs \
    --prefix audit-logs/2026

# 查看审计日志
sky audit query --user alice --start-date 2026-05-01 --end-date 2026-05-23

# 输出示例(JSON 格式):
# [
#   {
#     "timestamp": "2026-05-23T13:45:23Z",
#     "user": "alice",
#     "action": "job.submit",
#     "resource": "bert-large-48h-training",
#     "cloud": "aws",
#     "accelerators": "A100:8",
#     "estimated_cost": 1176.0
#   },
#   ...
# ]

9.3 成本分摊(Chargeback)

场景: 你需要按项目/部门分摊云成本。

解法:使用标签(Tags)

import sky

task = sky.Task(
    name='train-bert-large',
    run='python train.py',
    resources=sky.Resources(accelerators='A100:8')
)

# 提交作业时附加标签
sky.job.submit(
    task,
    labels={
        'project': 'nlp-research',
        'department': 'ai',
        'cost-center': 'CC-12345',
        'owner': 'alice',
    }
)

# 然后可以通过 SkyPilot 的 Cost API 按标签查询成本
sky cost query --label project=nlp-research --start-date 2026-05-01

10. 与其他方案的对比:为什么选 SkyPilot

10.1 SkyPilot vs 其他 AI 基础设施工具

维度SkyPilotRayKubeflowSLURMTerraform
目标用户AI 工程师/研究员分布式系统开发者ML 平台工程师HPC 管理员DevOps 工程师
抽象层次AI 工作负载分布式计算ML 流水线作业调度基础设施即代码
多云支持✅ 原生支持⚠️ 需要 Ray Cluster⚠️ 需要配置❌ 不支持✅ 支持但配置复杂
Spot 实例支持✅ 自动故障恢复⚠️ 需要手动处理⚠️ 需要配置❌ 不支持❌ 不支持
成本优化✅ 自动选择最便宜的云❌ 不支持❌ 不支持❌ 不支持❌ 不支持
上手难度⭐ 简单(YAML + CLI)⭐⭐ 中等(需要写 Ray 代码)⭐⭐⭐ 困难(需要 K8s 知识)⭐⭐⭐⭐ 很难(需要 HPC 知识)⭐⭐⭐ 中等(需要学 HCL)
适用场景AI 训练/推理分布式数据预处理ML 流水线HPC 作业基础设施编排

10.2 选择建议

选 SkyPilot,如果你:

  • 是 AI 工程师/研究员,想专注于模型而不是基础设施
  • 需要使用多个云厂商(成本优化或数据本地性)
  • 需要运行长时间作业,且希望有自动故障恢复
  • 希望团队新成员能在 1 小时内开始使用 GPU

选 Ray,如果你:

  • 需要构建分布式数据预处理管道
  • 需要动态伸缩的 Actor 模型
  • 已经在使用 Ray 做分布式训练(Ray Train)

选 Kubeflow,如果你:

  • 已经有 Kubernetes 集群
  • 需要完整的 MLOps 流水线(数据准备 → 训练 → 评估 → 部署)
  • 团队有专门的 ML 平台工程师

选 SLURM,如果你:

  • 在大学/研究机构的 HPC 集群上工作
  • 不需要多云支持
  • 作业主要是 HPC 类型的(MPI,OpenMP)

11. 总结与展望

11.1 SkyPilot 的核心价值

  1. 统一抽象:你只需要学一个工具,就能在 17+ 个云厂商上运行 AI 作业
  2. 成本优化:自动选择最便宜的云/实例类型,Spot 实例的自动故障恢复
  3. 故障自愈:托管作业会自动处理 Spot 回收、机器故障等情况
  4. 多云策略:数据本地性、成本优化、容灾——你可以在一个配置文件中表达所有策略

11.2 最佳实践总结

  1. 总是使用 sky.job.submit() 而不是 sky.launch() 来运行长时间作业
  2. 总是在代码中实现 checkpoint 保存/恢复逻辑
  3. 使用资源池来加速短作业的启动
  4. 使用标签(Labels)来追踪成本和归属
  5. 定期运行 sky cost analyze 来发现成本优化的机会

11.3 未来展望

2026-2027 年的可能发展方向:

  1. 更智能的调度:利用历史作业数据,预测资源需求和成本
  2. 更好的推理支持:原生支持推理服务的自动扩缩容和负载均衡
  3. 更深的云集成:与云厂商的 AI 托管服务(如 AWS SageMaker、GCP Vertex AI)深度集成
  4. 多租户支持:企业版的原生多租户支持,无需自己搭建

附录 A:常用命令速查表

# 集群管理
sky launch task.yaml -c my-cluster        # 启动集群
sky status my-cluster                     # 查看集群状态
sky exec my-cluster -- nvidia-smi         # 在集群上执行命令
sky stop my-cluster                       # 停止集群
sky start my-cluster                      # 启动已停止的集群
sky down my-cluster                       # 删除集群

# 托管作业
sky job submit task.yaml                  # 提交托管作业
sky job queue                             # 查看作业队列
sky job status <job-id>                   # 查看作业状态
sky job logs <job-id> --follow            # 查看作业日志
sky job stop <job-id>                     # 停止作业
sky job delete <job-id>                   # 删除作业

# 推理服务
sky serve up service.yaml -n my-service   # 部署推理服务
sky serve status my-service               # 查看服务状态
sky serve logs my-service                 # 查看服务日志
sky serve down my-service                 # 删除服务

# 成本分析
sky cost show                             # 查看当前成本
sky cost analyze                          # 分析成本优化机会
sky cost forecast                         # 预测未来 30 天成本

# 存储管理
sky storage ls                            # 列出所有存储挂载
sky storage mount s3://my-bucket /data    # 挂载云存储
sky storage umount /data                  # 卸载存储

附录 B:配置文件示例

B.1 超参数搜索(Hyperparameter Sweep)

# sweep.yaml
resources:
  accelerators: T4:1

workdir: .

setup: |
  pip install torch transformers wandb

# 使用环境变量来接收超参数
run: |
  echo "Training with lr=$LR, batch_size=$BATCH_SIZE, epochs=$EPOCHS"
  python train.py \
    --lr $LR \
    --batch-size $BATCH_SIZE \
    --epochs $EPOCHS \
    --wandb-project hyperparam-sweep
# 提交超参数搜索作业
for lr in 0.001 0.0001 0.00001; do
  for batch_size in 16 32 64; do
    for epochs in 10 20 50; do
      sky job submit \
        --env LR=$lr \
        --env BATCH_SIZE=$batch_size \
        --env EPOCHS=$epochs \
        sweep.yaml
    done
  done
done

B.2 数据并行训练(Distributed Data Parallel)

# ddp-training.yaml
resources:
  accelerators: A100:8
  num_nodes: 4  # 4 台机器,每台 8 张 A100

workdir: .

setup: |
  pip install torch torchvision torchaudio transformers deepspeed

run: |
  # 使用 PyTorch DDP 进行分布式训练
  torchrun \
    --nnodes=$SKYPILOT_NUM_NODES \
    --nproc_per_node=8 \
    --node_rank=$SKYPILOT_NODE_RANK \
    --master_addr=$SKYPILOT_MASTER_ADDR \
    --master_port=29500 \
    train_ddp.py \
    --model bert-large-uncased \
    --epochs 100

结语

SkyPilot 不是银弹,但它解决了 AI 基础设施管理中的一个核心痛点:碎片化

在 2026 年,当 AI 工作负载变得越来越复杂(多模态、大规模、实时推理),基础设施的碎片化只会越来越严重。SkyPilot 提供了一个务实的、可落地的解决方案,让 AI 工程师能够专注于模型本身,而不是基础设施。

如果你今天只能做一件事,那就是: 安装 SkyPilot,运行 sky launch hello-sky.yaml -c my-first-cluster,体验一下"一个 YAML,随处运行"的感觉。


作者:程序员茄子
发布时间:2026-05-23
字数:约 15,000 字
GitHub: https://github.com/skypilot-org/skypilot
文档: https://docs.skypilot.co

推荐文章

宝塔面板 Nginx 服务管理命令
2024-11-18 17:26:26 +0800 CST
Nginx 反向代理
2024-11-19 08:02:10 +0800 CST
CSS实现亚克力和磨砂玻璃效果
2024-11-18 01:21:20 +0800 CST
Python中何时应该使用异常处理
2024-11-19 01:16:28 +0800 CST
如何在Vue 3中使用Ref访问DOM元素
2024-11-17 04:22:38 +0800 CST
Python Invoke:强大的自动化任务库
2024-11-18 14:05:40 +0800 CST
程序员茄子在线接单