Kubernetes 官方沙箱项目 agent-sandbox 全解:CRD 模型、预热池、休眠机制与生产接入实施
AI Agent 要执行代码,「沙箱」就从可选项变成了基建。自建过 Agent 执行环境的人都知道痛点:用裸 Pod 跑,删除重建身份就丢了;用 StatefulSet(size=1) + Service + PVC 拼一套,又笨重又缺少休眠、预热这类 Agent 场景特有的生命周期管理。2025 年 KubeCon NA 上,Google Cloud 联合 Kubernetes 社区宣布了这个问题的官方答案——Agent Sandbox(kubernetes-sigs/agent-sandbox),一个由 SIG Apps 托管的沙箱编排项目,目标是把「隔离、有状态、单例」的 Agent 工作负载变成 Kubernetes 的一等公民。本文基于 v1.0.x(API 仍为 v1beta1)的源码与官方文档,把它的架构原理和从零接入的实施路径一次讲透。
一、项目定位:编排器,而不是隔离器
先立住一个最容易误解的点:agent-sandbox 本身不提供任何隔离能力。它是 CRD + 控制器 + 可选路由器的编排层,真正的隔离边界由 Pod 上的 runtimeClassName 决定——挂 gVisor 就是用户态内核隔离,挂 Kata Containers 就是轻量虚拟机隔离,什么都不挂就只是普通 runc 容器。官方威胁模型文档把这条写成了明确的不变量:Agent Sandbox 自身不实现隔离,但支持并推荐通过 SandboxTemplate 预配置安全运行时。
它的适用画像非常清晰,官方列出四类:
- 给 LLM 生成的不可信代码提供隔离执行环境(代码解释器、Coding Agent);
- 强化学习的训练/评估(SWE-bench、R2E-Gym 这类按试验拉环境的负载,讲究低延迟批量认领);
- Jupyter 式的持久单容器会话;
- 需要稳定身份的单实例服务(build agent、小型数据库)。
项目现状(2026-10):最新版本 v1.0.x,API 组 agents.x-k8s.io/v1beta1(扩展 API 组 extensions.agents.x-k8s.io/v1beta1),主分支约 1100 commits、4.2k stars,Apache-2.0,社区活跃(Slack #agent-sandbox 渠道)。GKE 上的托管版本已随 Agent Substrate 商用落地,所以这不再是纯实验品,但 API 版本号提醒你:字段仍可能演进,升级要跟 changelog。
二、架构原理:一套 CRD 四件套 + 控制器
2.1 组件全景
flowchart TD
subgraph Client["应用侧(Agent 服务)"]
SDK["Go / Python SDK"]
end
subgraph ControlPlane["Kubernetes 控制面"]
API["API Server"]
C1["agent-sandbox-controller(核心调和器)"]
C2["扩展调和器(Template / WarmPool / Claim)"]
CRD1["Sandbox"]
CRD2["SandboxTemplate"]
CRD3["SandboxWarmPool"]
CRD4["SandboxClaim"]
end
subgraph DataPlane["数据面(工作节点)"]
Router["Sandbox Router(可选反向代理)"]
P1["Sandbox Pod A(gVisor)"]
P2["Sandbox Pod B(Kata)"]
SVC["每沙箱 headless Service"]
end
SDK -->|"创建 Claim / Sandbox"| API
C1 -->|"watch / 调和"| API
C2 -->|"watch / 调和"| API
C1 -->|"创建 Pod + Service"| SVC
SVC --- P1
SVC --- P2
C2 -.->|"从 WarmPool 认领"| CRD1
SDK -->|"HTTP(X-Sandbox-ID)"| Router
Router -->|"按身份路由"| P1
Router -->|"按身份路由"| P2
核心调和器把 Sandbox 对象调和成 Pod + (可选的)headless Service + PVC;扩展调和器负责模板、预热池与认领语义。整个项目没有自研运行时、没有节点组件(Router 除外且可选),是一个纯粹的「控制面附加层」——这意味着它可以装在任何一个标准 Kubernetes 集群里,不挑 CNI、不挑容器运行时。
2.2 四个 CRD 各管什么
Sandbox(核心,agents.x-k8s.io/v1beta1)——最小可用的沙箱对象。关键 spec 字段:
| 字段 | 语义 |
|---|---|
podTemplate.spec |
完整的 PodSpec,沙箱内容器在这里定义 |
volumeClaimTemplates |
PVC 模板列表,随沙箱创建(创建后不可变) |
service |
是否让控制器自动建 headless Service(稳定网络身份的关键) |
operatingMode |
Running / Suspended——运行与休眠的意图声明 |
shutdownTime / shutdownPolicy |
到期销毁:绝对时间到达后拆除 Pod/Service,Retain(默认)保留对象并置 Ready=False (SandboxExpired),Delete 连对象一起删 |
status 侧最有用的是 serviceFQDN(形如 my-sandbox.<ns>.svc.cluster.local 的稳定域名)与 podIPs/nodeName(休眠时会被清空)。
SandboxTemplate(扩展)——可复用的沙箱蓝图,字段与 Sandbox 共享同一个 SandboxBlueprint 结构(podTemplate、volumeClaimTemplates、service)。它是安全基线的落点:官方明确,经 Template 供应的沙箱若未显式指定 automountServiceAccountToken,控制器默认置 false——不给沙箱里的不可信代码发 Kubernetes 身份,这是 secure-by-default 的设计。
SandboxWarmPool(扩展)——预热池。replicas 指定常备多少个按 Template 预热好的 Sandbox(该字段可被 HPA 接管),sandboxTemplateRef 指向模板,updateStrategy(默认 OnReplenish)控制模板变更后如何替换过期沙箱。池只管理「未被认领」的沙箱。
SandboxClaim(扩展)——认领入口,Agent 服务只跟它打交道。warmPoolRef 指定从哪个池认领,lifecycle 定义认领后的到期策略,additionalPodMetadata 原地打标签/注解,env 与 volumeClaimTemplates 支持个性化注入。
2.3 一次「认领」的完整数据流
sequenceDiagram
participant A as Agent 服务
participant K as API Server
participant E as 扩展调和器
participant W as SandboxWarmPool
participant C as 核心调和器
A->>K: 创建 SandboxClaim(warmPoolRef)
K->>E: watch 到 Claim
E->>W: 挑选一个 Ready 的预热 Sandbox
E->>K: 乐观锁绑定:Sandbox 所有权从池转移到 Claim
E->>K: 原地应用 additionalPodMetadata
E->>A: Claim status 报告沙箱地址(serviceFQDN)
Note over A: 拿到稳定域名,开始执行代码
C->>K: 持续保障底层 Pod/Service 与期望一致
W->>C: 池缺员,按 Template 补充新 Sandbox
认领是「采纳(adopt)」而不是「新建」:WarmPool 里的沙箱本来就是按模板跑起来的活 Pod,认领只做一次所有权转移加元数据原地修补,所以是亚秒级。这条路径有两个例外要背下来:Claim 里写了 env 或 volumeClaimTemplates,必然放弃预热、从模板冷启动——因为运行中的 Pod 没法再注入环境变量或挂新 PVC(additionalPodMetadata 没有这个副作用,它是原地打的)。实践上,个性化配置能走镜像环境变量就不要走 Claim env,否则预热池对你的延迟优化等于白做。
三、三个特色机制的设计原理
3.1 休眠(Suspended):身份不死的省钱模式
把 operatingMode 改成 Suspended,控制器删除底层 Pod 但保留 Sandbox 对象与所有卷;改回 Running,控制器按原配置重建 Pod,hostname、serviceFQDN、PVC 数据全部保持。对 Agent 场景这是刚需:会话要随时回来,但大部分时间闲置,一直挂着烧钱。注意这还不是快照级休眠——内存状态不保留,恢复等于容器重新拉起(「深度休眠」在路线图上)。到期销毁(shutdownTime)与休眠配合,就能表达「保留 7 天,超时先休眠再回收」这类策略。
3.2 托管网络策略:默认拒绝的跨租户隔离
用 SandboxTemplate 供应沙箱时,控制器默认生成一张严格的 NetworkPolicy:入向只允许 Sandbox Router,出向只放公网,内部 RFC1918 网段与云元数据地址(169.254.169.254)一律封禁。这防的是两类真实威胁:被攻陷的沙箱横向扫描集群内其他租户,以及借云元数据端点偷宿主机凭证。代价是默认拒绝也会挡掉 sidecar 端口,健康检查需要显式放行——部署时遇到探针失败先查这里。
3.3 Sandbox Router:给「不能 port-forward 的运行时」一条通路
gVisor/Kata 这类运行时下,kubectl port-forward 那套直连路径不可用,所以项目提供了可选的 HTTP 反向代理 Router:SDK 带上 X-Sandbox-ID 头,Router 校验后按严格格式 <id>.<namespace>.svc.<cluster-domain> 构造目标转发。它对 SSRF 有基本防护(校验 X-Sandbox-Pod-IP 是合法 IP 且不属于受限网段),但默认 authorizer 是 AllowAll(为兼容 Python 客户端),生产上建议换成自定义 authorizer 限制可访问的 IP/命名空间;连接级限流与 WebSocket 超时还在路线图上,入口限流要在 Ingress 层做。
四、预热池的性能账本与调优
官方性能调优文档给了一组少见的实诚基准(GKE 实测):
| 运行模式 | 吞吐上限 | 关键瓶颈 |
|---|---|---|
| 突发认领(预热池已就绪) | 约 300 claims/s,p90 ≤ 200ms(3700 副本池承接 12 秒内 3600 认领) | 乐观锁绑定 + Claim 状态更新 |
| 持续认领 + 并发补池 | 单池约 70–85 sandboxes/s | 每池工作队列串行化、expectations 等待、调度器默认 --kube-api-qps=50 |
单池为什么有天花板:每个 Sandbox 对象内联 podTemplate 后约 10KB,补池默认一次 300 个并发写 etcd;watch 事件延迟 10–30 秒会让 expectations 门卡住整个池。因此官方给的规模处方是:大池分片成多个 SandboxWarmPool(并行化发生在池与池之间),再配合四个旋钮——--sandbox-warm-pool-max-batch-size(默认 300)压补池批次、--sandbox-warm-pool-max-refill-rate 限速、--sandbox-warm-pool-replenish-delay 延迟补池削峰、--sandbox-write-behind-window 写后合并减少状态写。在共享集群上还建议配 APF(FlowSchema/PriorityLevelConfiguration),把认领流量和批量补池流量隔离,防止补池风暴挤占认领路径。文档里「持续高吞吐配置」一节给过验证过的推荐组合,生产前值得照抄一遍再按流量画像微调。
五、接入实施:从零到生产
5.1 安装控制器
# 核心 + 扩展一次装齐(可固定版本,如 v1.0.2)
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/latest/download/sandbox-with-extensions.yaml
# 验证
kubectl get crd sandboxes.agents.x-k8s.io
kubectl wait --for=condition=Ready pod -l app=agent-sandbox-controller \
-n agent-sandbox-system --timeout=120s
也提供 Helm chart 与 OLM 安装。卸载前先 kubectl get sandboxes -A 检查存量——删 CRD 会级联删掉所有沙箱。
5.2 最小可用:一个裸 Sandbox
apiVersion: agents.x-k8s.io/v1beta1
kind: Sandbox
metadata:
name: my-sandbox
spec:
podTemplate:
spec:
containers:
- name: sandbox
image: registry.example.com/agent-python:latest
ports:
- containerPort: 8888
resources:
requests: { cpu: "250m", memory: "512Mi" }
limits: { cpu: "1", memory: "1Gi" }
创建后 status.serviceFQDN 给出稳定域名,集群内直接可访问。这一步适合理解模型,生产路径走下面模板化三板斧。
5.3 生产路径:Template + WarmPool + Claim
以官方 quickstart 的 Python 运行时模板为底(节选,含安全运行时挂载点):
apiVersion: extensions.agents.x-k8s.io/v1beta1
kind: SandboxTemplate
metadata:
name: python-runtime-template
spec:
podTemplate:
spec:
# runtimeClassName: gvisor # 用户态内核隔离
# runtimeClassName: kata-qemu # 轻量虚拟机隔离
automountServiceAccountToken: false
containers:
- name: python-runtime
image: registry.example.com/agent-python:latest
ports:
- containerPort: 8888
readinessProbe:
httpGet: { path: "/", port: 8888 }
periodSeconds: 1
resources:
requests: { cpu: "250m", memory: "512Mi" }
volumeClaimTemplates:
- metadata: { name: workspace }
spec:
accessModes: [ReadWriteOnce]
resources: { requests: { storage: 1Gi } }
---
apiVersion: extensions.agents.x-k8s.io/v1beta1
kind: SandboxWarmPool
metadata:
name: python-pool
spec:
replicas: 5
sandboxTemplateRef: { name: python-runtime-template }
---
apiVersion: extensions.agents.x-k8s.io/v1beta1
kind: SandboxClaim
metadata:
name: session-abc123
spec:
warmPoolRef: { name: python-pool }
三步验收:kubectl get sandboxwarmpool 看 Ready 数是否到 replicas;创建 Claim 后 kubectl get sandbox 看对应沙箱是否被认领(所有者标签变化);从 Claim status 里拿 serviceFQDN 发一个请求。把 replicas 交给 HPA、再按第四节的旋钮调补池节奏,就是可上线的形态。
5.4 应用侧 SDK
# pip install k8s-agent-sandbox
from agentsandbox import SandboxClient
# 典型用法:创建/认领沙箱 → 拿地址 → 执行 → 释放
# 官方 quickstart 配合 Sandbox Router 使用:
# HTTP 请求带 X-Sandbox-ID 头即可路由到对应沙箱
Go SDK 安装:go get sigs.k8s.io/agent-sandbox/clients/go/sandbox@latest。两侧 SDK 的核心动词一致:创建或认领(Sandbox/Claim)→ 解析端点(直连 serviceFQDN 或经 Router)→ 心跳/续期 → 释放或等待 shutdownTime 回收。 examples/ 目录下还有 code-interpreter-agent-on-adk、langchain、mcp-server-sandbox、jupyterlab、firecracker-sandbox、kata-aks 等几十个可跑的参考集成,工程上直接抄最近的那个。
5.5 部署 Sandbox Router(推荐)
Router 的部署清单在仓库 sandbox-router/deploy/ 下,跑成 Deployment 后把 Ingress 流量导过去即可。规划上记住三点:默认 authorizer 是 AllowAll,按需收紧;入口限流自己做;它只处理 HTTP/WebSocket 数据面流量,控制面照走 API Server。
六、生产检查清单与取舍
上线前对照官方威胁模型过一遍:模板里固定 runtimeClassName(安全基线不该靠每个用户自觉);模板默认关闭 SA token 挂载,裸 Sandbox 场景用 ValidatingAdmissionPolicy 兜底;namespace 配 LimitRange 防资源滥用;跨租户网络靠托管 NetworkPolicy 的默认拒绝;Router 换自定义 authorizer。
和同类方案的取舍:与「StatefulSet(size=1)+Service+PVC」手工作坊比,agent-sandbox 多了休眠、到期回收、预热池这套 Agent 原生生命周期;与 OpenSandbox 这类沙箱平台比,它是编排标准——只管 CRD 语义和调和,不管 exec API、文件传输、出口代理这些平台功能,事实上 OpenSandbox 已把它列为可选的底层 provider;与 E2B 这类商用云比,它给了自托管和选运行时的自由,代价是平台层要自己搭或借上层项目补齐。一句话:要标准、要藏在自家 K8s 里、自己愿意搭平台层,选 agent-sandbox;要开箱即用的沙箱 API,选平台型方案,并考虑让平台跑在 agent-sandbox 之上。
当前限制:API 还是 v1beta1,字段可能演进;休眠不保留内存(深度休眠在路线图);Router 的连接级限流/WS 超时未实现;多运行时强隔离、弹性存储、跨沙箱内存共享等均在探索中。跟踪路线图和 KEP(如 suspend/resume 的 beta 化提案)即可判断升级节奏。
参考资料
- 项目仓库与文档:https://github.com/kubernetes-sigs/agent-sandbox (README、docs/api.md、docs/performance-tuning.md、docs/configuration.md、docs/security/threat_model.md、examples/quickstart)
- Google Cloud 发布公告《Agentic AI on Kubernetes and GKE》(2025-11-11):https://cloud.google.com/blog/products/containers-kubernetes/agentic-ai-on-kubernetes-and-gke
- GKE Agent Sandbox 概念文档:https://docs.cloud.google.com/kubernetes-engine/docs/concepts/machine-learning/agent-sandbox
- OpenSandbox 架构文档中对 agent-sandbox provider 的集成描述:https://github.com/opensandbox-group/OpenSandbox
读者留言
COMMENTS 暂无还没有留言,来说第一句?