SkyPilot Sandboxes 实战指南:在自有 Kubernetes 集群上按需创建秒级隔离计算环境
【免费下载链接】skypilotThe AI Compute Platform for frontier teams. SkyPilot turns fragmented AI compute into one AI supercomputer, so frontier AI teams build custom intelligence faster.项目地址: https://gitcode.com/GitHub_Trending/sk/skypilot
SkyPilot Sandboxes 是 SkyPilot 提供的一种快速、隔离的计算环境:每个 sandbox 就是一个轻量级 Kubernetes Pod,可以在你自己的集群上按需创建、执行命令、随时销毁,无需预置整个集群。它专为 AI 编码代理(AI coding agents)、RL 训练 rollout 和并行评测(parallel evals)等需要大量短生命周期隔离环境的工作负载设计,预热的 warm pool 可将启动时间压缩到1 秒以内,并在创建时自动注入卷(volumes)与密钥(secrets)。读完本文,你将掌握sky sandboxCLI 与sky.sandboxPython SDK 的完整用法,包括批量创建、异步并发分发、密钥/卷注入、sandbox 间网络、快照恢复、warm pool 预热池、Docker-in-Docker 以及 GKE gVisor 加固等生产级能力。
注意:Sandboxes 属于SkyPilot Platform的一部分,目前处于限量早期访问(limited early access)阶段。使用
sky.sandboxSDK 需要 API server 开启 Sandbox 功能(可通过SKYPILOT_API_SERVER_ENDPOINT指定)。
为什么选择 SkyPilot Sandboxes
与直接开一台虚拟机或预置整个集群相比,Sandboxes 的核心价值来自其 Kubernetes 原生的设计取舍:
- 亚秒级启动(Sub-second launches):预热池(pre-warmed pools)让空闲环境保持就绪状态,
create时直接"认领"一个已在运行的 Pod,而不是等待镜像拉取与调度,单个 sandbox 的启动时间可缩短50% 以上。 - 每 Pod 天然隔离(Isolated per pod):每个 sandbox 都是独立的 Kubernetes Pod,拥有专属镜像、CPU 与内存。这是运行 Agent 生成的、或任何不可信代码的天然边界——代码无法触碰你的其他工作负载。
- 密钥不进入代码(Secrets stay out of your code):凭据在启动时由 SkyPilot Secrets Manager 以环境变量的形式注入,token 永远不会被写进镜像或硬编码进 Agent 运行的命令里。
- 大规模并行(Massively parallel):一次调用即可创建数千个 sandbox 用于 RL rollout 与并行评测,并可并发分发命令。
- 运行在你的基础设施上(Runs on your infra):sandbox 运行在你自己的 Kubernetes 集群上,代码与数据永不离开你的环境,容量就是现有集群的容量。
- 快照与恢复(Snapshot & restore):把 sandbox 的整个文件系统捕获成快照镜像,之后从快照恢复出全新 sandbox——昂贵的环境准备只做一次,之后直接续用,无需重复执行。
- Sandbox 间网络(Sandbox-to-sandbox networking):默认情况下 sandbox 之间互不可达;只有显式用
ports=暴露端口的 sandbox 才能被对端通过稳定端点访问,且该端点可经受 Pod 替换。 - Docker-in-Docker:传入
enable_docker=True即可在 sandbox 内运行docker build、docker run与docker compose,支持任意基础镜像;守护进程运行在特权 sidecar 中,而你代码所在的容器保持非特权。 - gVisor 更强的隔离(Stronger isolation with gVisor):在 GKE 上可将 sandbox Pod 调度到 gVisor runtime,为不可信代码在默认的每 Pod 隔离之上再增加一层内核级隔离边界。
典型使用场景
- AI 编码代理:为每个代理(如 Claude Code)分配独立的可抛弃环境来读写和运行代码,与你的其他工作、以及其他代理相互隔离。
- RL 训练 rollout:一次性拉起数千个 sandbox 并行运行 rollout,收集结果后全部销毁,全程由单个进程驱动。
- 并行评测(Parallel evals):在大量隔离环境中同时跑完一个大型评测集,而不是在一台机器上串行执行。
- 临时构建与 CI 任务:在干净环境中执行短生命周期的构建、测试或脚本,无需预置完整集群。
快速上手:CLI 与 SDK
一个 sandbox 就是一个隔离 Pod:创建它、在里面跑命令、然后终止它。只要安装好 SkyPilot CLI 与随附的 Sandbox SDK,完整闭环就是如此。
CLI 方式
sky sandbox create会预置一个 sandbox 并直接带你进入交互式 shell;退出 shell 即销毁 sandbox:
# 创建 sandbox 并进入 shell(退出即销毁)。 $ sky sandbox create -n dev ✓ Sandbox dev is ready. Connecting via bash... # 或者用 --detach 保持后台运行,再按名字管理。 $ sky sandbox create --detach -n dev $ sky sandbox ls $ sky sandbox terminate devPython SDK 方式
import sky.sandbox # 从内置的 default 池创建一个 sandbox。 sb = sky.sandbox.create(name='dev') # 运行命令(argv 令牌,无隐式 shell)。exec 返回一个句柄: # 用 wait() 等待退出码,再读取 stdout / stderr。 proc = sb.exec('python', '-c', 'print(2 ** 10)') proc.wait() print(proc.stdout.read()) # 1024 # 销毁。 sb.terminate()几个需要牢记的 SDK 语义:
- 命令是 argv 令牌,直接在 Pod 内运行,没有隐式 shell。如果需要管道、通配符、环境变量展开等 shell 特性,请显式调用 shell:
sb.exec('sh', '-c', 'echo $HOME | wc -c')。 - 通过
env参数可以为单条命令设置环境变量(会覆盖创建时的 env,且不会持久化到后续 exec):sb.exec('printenv', 'STAGE', env={'STAGE': 'ci'})。 - 也可以使用上下文管理器实现自动销毁:
import sky.sandbox with sky.sandbox.create(name='dev') as sb: sb.exec('python', 'train.py').wait() # 退出即自动终止 sandbox。深入 SDK:批量、异步、密钥/卷与网络
除了 create / exec / terminate,sky.sandboxSDK 还覆盖批量与异步并发分发、密钥/卷注入、sandbox 间网络以及文件系统快照与恢复。
批量创建
传入num_sandboxes即可一次调用创建一批 sandbox,名字根据前缀自动生成(rollout-0001、rollout-0002、……):
import sky.sandbox sandboxes = sky.sandbox.create(name='rollout', num_sandboxes=1000) for i, sb in enumerate(sandboxes): sb.exec('python', 'rollout.py', str(i))异步并发分发(Async fan-out)
每个入口函数都有一个位于.aio属性上的异步孪生版本。单个事件循环即可驱动数百个并发的exec调用:
import asyncio import sky.sandbox async def main(): sandboxes = await sky.sandbox.create.aio( name='rollout', num_sandboxes=100) try: results = await asyncio.gather( *(sb.exec.aio('python', 'rollout.py', str(i)) for i, sb in enumerate(sandboxes))) finally: # 即使 exec 抛异常也要销毁。 await asyncio.gather(*(sb.terminate.aio() for sb in sandboxes)) await sky.sandbox.aclose() # 释放共享会话 asyncio.run(main())密钥与卷注入
在创建时注入密钥、挂载卷:
import sky.sandbox sb = sky.sandbox.create( name='job', # 从密钥管理器注入密钥,作为同名环境变量。 secrets=['HF_TOKEN'], # 普通(非密钥)环境变量。 env={'PROJECT': 'demo'}, # 挂载已有卷,键为挂载路径(先用 `sky volumes apply` 创建卷)。 # 值可以是卷名(挂载整个卷), # 或 VolumeMount 用于子目录 / 只读挂载。 volumes={ '/data': 'shared-data', '/work': sky.sandbox.VolumeMount('team-fs', sub_path='job-123'), }, )其中持久卷是 SkyPilot 提供的独立能力,参见卷文档:卷相比云存储桶性能更高,目前支持 Kubernetes 集群与 RunPod 上使用,适合挂载进 sandbox 作为数据与共享文件系统。
Sandbox 间网络
默认情况下 sandbox 之间互不可达。一个server用ports=声明其暴露的端口;每个端口都会出现在它的endpoints映射中,作为对端可拨号的稳定地址:
import sky.sandbox # Server:向 peer sandbox 暴露 8080 端口。 server = sky.sandbox.create(name='api', image='python:3.12', ports=[8080]) print(server.endpoints[8080]) # 'api.default.svc.cluster.local:8080' # Client:不暴露任何端口(所以对端无法访问它), # 但可以访问 server 暴露的端口。 worker = sky.sandbox.create(name='worker', image='python:3.12') worker.exec('curl', '-sf', f'http://{server.endpoints[8080]}/healthz')该地址在 Pod 替换后依然有效;在启用 NetworkPolicy 的集群上,未暴露的端口保持被阻断。
快照与恢复
将 sandbox 的文件系统快照为容器镜像,之后从快照恢复出全新 sandbox——昂贵的准备只做一次,之后直接续用:
import sky.sandbox # 配置一个 sandbox,然后快照它的文件系统。 sb = sky.sandbox.create(name='dev', image='python:3.12') sb.exec('pip', 'install', 'numpy', 'pandas').wait() image = sb.snapshot() # 之后:恢复出全新 sandbox——numpy/pandas 已经装好。 restored = sky.sandbox.create(name='dev2', image=image)快照功能需要在 Dashboard 的Sandboxes安装对话框中配置一个容器镜像仓库(container registry)。
SDK 入口一览
SDK 暴露的入口包括:create、exec、terminate、snapshot、ls、create_pool、set_pool_size和delete_pool。每个按次调用的入口都有.aio属性上的异步孪生版本(sky.sandbox.create.aio(...)、sb.exec.aio(...)),因此在事件循环代码中可以使用相同的名字。你也可以在 Dashboard 的Sandboxes页面通过 UI 管理池与运行中的 sandbox。
实战示例一:RL 代码执行训练(sandbox 奖励)
仓库中的llm/rl-code-execution-sandbox是一个完整的端到端示例:用 GRPO(Group Relative Policy Optimization)训练 LLM 做代码生成,而奖励来自真正运行模型生成的代码——策略模型生成一个 Python 函数,代码在 SkyPilot Sandbox 中针对隐藏测试用例执行,全部通过奖励 1.0,否则 0.0。每个 rollout 都隔离在自己的 Pod 中,因此崩溃、死循环或恶意代码都无法影响 trainer 或其他 rollout。
整个任务由 5 类组件通过 HTTP 通信构成(该架构与rl-post-training-jobgroup相同,只是把数学答案匹配的奖励服务器换成了 sandbox 支撑的代码执行奖励服务器):
- data-server(auxiliary):FastAPI 服务,提供 MBPP 风格的代码题面;每个问题包含任务描述与一组隐藏测试用例(assert 语句),题面中只给出一条示例测试作为签名提示。
- rollout-server(auxiliary,x2):带原生负载均衡的 SGLang 推理服务,
num_nodes: 2创建两个 GPU 实例;rank 0 节点同时跑 SGLang server 与 SGLang router(端口 30000),router 提供对 KV 缓存友好的 cache-aware 负载均衡。 - sandbox-reward-server(auxiliary):本示例的核心。对每个 rollout 从模型响应中提取代码块,在隔离的 SkyPilot Sandbox 中运行
code + setup + hidden tests,进程退出码为 0 则返回 1.0 否则 0.0。整个批次通过异步 sandbox SDK 并发评分,每个 rollout 一个 sandbox,从预热池认领以实现亚秒级启动。 - replay-buffer(auxiliary):存储(题面、响应、奖励)经验元组供训练采样,支持按高奖励优先采样。
- ppo-trainer(primary):多节点训练编排器,实现 GRPO;每
--policy-sync-interval步把最新权重通过 SGLang 的update_weights_from_disk接口推回 rollout-server。
rollout → sandbox → reward 闭环
┌─────────────┐ prompts + hidden tests │>async def score_batch(items): # 一次 create 调用返回一组 sandbox,从预热池认领。 sandboxes = await sky.sandbox.create.aio( name='reward', num_sandboxes=len(items), pool=POOL_NAME) try: # 并发评分每个 rollout,每个 sandbox 一个。 rewards = await asyncio.gather( *(score_one(sb, item) for sb, item in zip(sandboxes, items))) finally: # 即使 exec 抛异常也一定销毁。 await asyncio.gather(*(sb.terminate.aio() for sb in sandboxes), return_exceptions=True) return list(rewards) async def score_one(sb, item): # 奖励 = 模型生成的代码是否通过测试? # exec 返回句柄;timeout_seconds 在服务端限制命令时长 #(超时会在 Pod 内被杀并返回非零退出码,因此客户端无需保持长连接)。 proc = await sb.exec.aio('python', '-c', item.script, timeout_seconds=EXEC_TIMEOUT_SECONDS) code = await proc.wait() return 1.0 if code == 0 else 0.0注意,score_one中exec返回的是一个句柄,wait()时才拿到退出码——连接只在"启动 + 每次轮询"时保持,而不是覆盖命令的整个运行时长,这正是大规模并发评分的吞吐关键。奖励函数也保证坏 rollout 不会向上抛异常:崩溃、超时、非零退出码一律映射为奖励 0.0。
预热池在服务器启动时创建一次(sky.sandbox.create_pool(...)),共享会话在关闭时释放一次(sky.sandbox.aclose())。
启动训练
# 1. 先创建共享卷(用于策略权重同步): sky volume apply llm/rl-code-execution-sandbox/rl-code-volume.yaml # 2. 启动 job group 训练: sky jobs launch llm/rl-code-execution-sandbox/rl-code-jobgroup.yaml # 3. 监控: sky jobs queue sky jobs logs <job-id>>import anthropic import sky.sandbox # Claude 运行在 sandbox 之外,通过这两个工具按需创建 sandbox 并运行代码。 # 一场从不运行代码的对话永远不会创建 Pod。 sb = None tools = [ {'name': 'start_skypilot_sandbox', 'description': 'Provision a fresh sandbox. Call the first ' 'time you need to run code.', 'input_schema': {'type': 'object', 'properties': {}}}, {'name': 'run_shell', 'description': 'Run a shell command in the sandbox. State ' 'persists across calls.', 'input_schema': {'type': 'object', 'properties': {'command': {'type': 'string'}}, 'required': ['command']}}, ] def dispatch(name, args): global sb if name == 'start_skypilot_sandbox': # 从池中认领一个预热 Pod。 sb = sky.sandbox.create(name='chat') return 'Sandbox ready.' # run_shell。exec 返回句柄,因此连接只在启动 + 每次轮询时保持, # 而非命令的整个运行时长:长的 agent 命令(构建、测试套件、训练任务) # 可以完整跑完而不会触发边缘超时。 # timeout_seconds 是服务端预算,超时后命令在 Pod 内被杀—— # 请按你最长的一次工具调用来设置,而不是保留 60 秒默认值。 proc = sb.exec('sh', '-c', args['command'], timeout_seconds=3600) proc.wait() return proc.stdout.read() or proc.stderr.read() client = anthropic.Anthropic() messages = [ {'role': 'user', 'content': 'Compute the 100th Fibonacci number with Python.'}] while True: resp = client.messages.create(model='claude-opus-4-8', max_tokens=4096, tools=tools, messages=messages) messages.append({'role': 'assistant', 'content': resp.content}) tool_uses = [b for b in resp.content if b.type == 'tool_use'] if not tool_uses: break messages.append({'role': 'user', 'content': [ {'type': 'tool_result', 'tool_use_id': tu.id, 'content': dispatch(tu.name, tu.input)} for tu in tool_uses]}) if sb is not None: sb.terminate()每个 agent 拥有自己的 Pod,因此可以并行工作而不会共享文件系统或互相干扰。在create时用secrets=[...]注入每个 agent 的凭据,token 就不会出现在 agent 运行的命令中。
进阶:Warm Pool 预热池与快速预置
没有池时,create会在需要时预置全新 Pod,等待 Kubernetes 调度与容器镜像拉取。而一个**池(pool)**会保持一组预热、已预置的 Pod 就绪,create改为认领一个已在运行的 Pod,可将单个 sandbox 的启动时间缩短 50% 以上。池还固定了 sandbox 的形态:容器镜像、CPU 与内存。
SkyPilot 自带内置的default池(python镜像),所以上面的快速上手无需任何配置;需要不同镜像或不同规格时再自建池即可。
创建池、随时调整大小:
import sky.sandbox # 创建池:保持 10 个空闲 Pod 预热就绪。 sky.sandbox.create_pool( name='ml-gpu', image='nvcr.io/nvidia/pytorch:24.05-py3', cpus=8, memory_gb=64, replicas=10, ) # 随时上下扩缩容。 sky.sandbox.set_pool_size('ml-gpu', replicas=50) # 从池中启动 sandbox。 sb = sky.sandbox.create(name='train', pool='ml-gpu')SDK 还提供delete_pool用于删除不再使用的池。
Docker-in-Docker:在 Sandbox 内构建与运行镜像
在 sandbox内部运行docker build、docker run和docker compose,支持任意基础镜像。这是一个一行开关的 opt-in:传入enable_docker=True。Docker 守护进程运行在独立的特权 sidecar 中,dockerCLI 自动注入,因此你代码所在的容器保持非特权,镜像也无需任何改动。
import sky.sandbox # 临时启动(全新 Pod,冷启动)。 sb = sky.sandbox.create(name='dev', image='ubuntu:22.04', enable_docker=True) sb.exec('docker', 'run', '--rm', 'hello-world').wait() sb.exec('docker', 'build', '-t', 'app', '/src').wait() # 或者把 Docker 直接烤进预热池,保持亚秒级启动。 sky.sandbox.create_pool(name='docker-pool', image='ubuntu:22.04', replicas=3, enable_docker=True) sky.sandbox.create(name='dev2', pool='docker-pool')注意两点:create()上的enable_docker只对临时启动生效(需传image=);预热池的 Docker 支持在池创建时就已固定,所以要用create_pool时传enable_docker=True。此外,目标集群必须允许特权 Pod(供 Docker daemon sidecar 使用);强制无特权策略的集群(如 GKE Autopilot)会拒绝启动。
在 GKE 上使用 gVisor 强化隔离
在 GKE 上,可以用 gVisor 进一步加固 sandbox Pod。gVisor 得到 GKE 官方支持,通过 SkyPilot Sandboxes 使用它只需两步:
创建一个 GKE Sandbox 节点池,带
--sandbox type=gvisor:gcloud container node-pools create sandbox-pool \ --cluster <cluster-name> \ --sandbox type=gvisor注意:已有节点池无法转换为支持 gVisor,必须新建一个。
让 sandbox 指向该 runtime class:在
~/.sky/config.yaml的kubernetes.pod_config下添加runtimeClassName: gvisor。SkyPilot 据此确保 sandbox 使用 gVisor 创建:# ~/.sky/config.yaml kubernetes: pod_config: spec: runtimeClassName: gvisor
若要只对部分集群启用 gVisor,把同样的pod_config嵌套到kubernetes.context_configs.<context>下即可:
# ~/.sky/config.yaml kubernetes: context_configs: gke_my-project_us-central1_my-cluster: pod_config: spec: runtimeClassName: gvisor这与 SkyPilot Kubernetes 文档中描述的pod_config机制一致:其值遵循 Kubernetes Pod API 规范,将应用到 SkyPilot 创建的所有 Pod 上(参见 kubernetes-getting-started.rst 中关于~/.sky/config.yaml的pod_config覆盖说明)。
需要注意:如果已经使用预热池来加速启动,之后再启用 gVisor,那么从预热池后续启动的 sandbox不会遵循 gVisor——需要重建预热池才能生效。此外,gVisor runtime 上目前不支持快照与恢复(Snapshot & restore)。
与相邻能力的配合
- 持久卷(Volumes):参见卷文档,卷是可挂载进 sandbox 的持久存储。
- Job Groups:参见 job-groups 示例,可将大量并行 job 与 sandbox 一起运行,用于 RL 训练。
- SkyPilot Platform:参见 skypilot-platform.rst,其中包含负责向 sandbox 注入凭据的 Secrets Manager。
小结
SkyPilot Sandboxes 把"Kubernetes 上快速创建、用完即焚的隔离 Pod"封装成一行 CLI 命令或一个 SDK 调用:预热池解决冷启动延迟,密钥/卷注入解决配置分发,ports=+ 稳定端点解决沙箱间通信,snapshot()解决昂贵环境的复用,enable_docker与 gVisor 则分别扩展了能力边界与安全边界。无论是给编码代理一个安全的代码执行沙箱,还是为 RL rollout 与并行评测铺开上千个隔离环境,这套机制都能在你的自有集群上按需伸缩、随用随销。
【免费下载链接】skypilotThe AI Compute Platform for frontier teams. SkyPilot turns fragmented AI compute into one AI supercomputer, so frontier AI teams build custom intelligence faster.项目地址: https://gitcode.com/GitHub_Trending/sk/skypilot
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考