Kubernetes 官方沙箱项目 agent-sandbox 全解:CRD 模型、预热池、休眠机制与生产接入实施

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 化提案)即可判断升级节奏。

参考资料

← 返回资讯列表

读者留言

COMMENTS 暂无
仅本站原创文章开放留言 · 请勿留下手机号、邮箱等个人信息

还没有留言,来说第一句?