Kubernetes 官方 Agent Sandbox 接入架构方案:SIG Apps 沙箱编排标准的五层落地设计

本文是「Agent 沙箱技术专题」的接入篇。专题此前的《Agent 沙箱技术核心架构方案:从 microVM 隔离到硬件加速快照的七层设计》讲的是沙箱这件事本身的七层技术栈(隔离边界、快照、压缩、镜像、调度、安全、编排标准);本文换一个视角,只回答一个工程问题:当一个团队已经拥有 Kubernetes 平台,要把 agent 代码执行能力接进来,架构应该怎么搭、按什么顺序落地、哪些坑必须提前知道。

全文以 kubernetes-sigs/agent-sandbox v1.0.4 为准(API 已全量 v1beta1)。所有结论标注一手来源;官方尚未公开或仍在 roadmap 中的项,单独列在第 9 节,不做乐观外推。


0. 一句话结论

Kubernetes 官方 Agent Sandbox 不是沙箱,而是沙箱的编排器。官方 README 的第一句 Scope 就是全篇最关键的架构约束:

"Agent Sandbox is a sandbox orchestrator. It delegates low-level container isolation to secure 'Sandbox Runtimes' (like gVisor or Kata Containers) by managing Pods configured to use these runtimes (via RuntimeClass)."

这句话直接决定了接入方案的分工:

你想要的 谁负责 在哪里配置
沙箱生命周期、身份、存储、热池 Agent Sandbox(本项目) CRD + 控制器
内核/网络隔离强度 Sandbox Runtime(gVisor / Kata / urunc) podTemplate.spec.runtimeClassName
数据面访问(执行命令、读写文件) Router + sandboxd + SDK 第 4 节
租户隔离、出网管控、凭证最小化 NetworkPolicy + 准入策略 + RBAC 第 3、5 节

把这条边界记牢,后面 90% 的架构决策都是它的推论。


1. 接入前的治理与版本基线

1.1 项目在 Kubernetes 治理体系中的确切位置

这是接入评审时最容易被问、也最容易答错的一点,先钉死三个口径:

问题 确切答案 来源
是不是 SIG Apps 子项目? 是,kubernetes-sigs/agent-sandbox,SIG Apps 官方 README 子项目清单中列出,设双周 Subproject Meeting kubernetes/community sig-apps/README.md
有没有 kubernetes/enhancements 的 KEP? 没有。它走 kubernetes-sigs 子项目路线,不进入 K8s 官方发布节奏 检索 kubernetes/enhancements 未命中
是不是 CNCF 项目? 不是。CNCF 博客是第三方视角介绍,非托管公告 cncf.io 相关文章性质
立项时间 2025-11,Google Open Source Blog 正式宣布,并在 KubeCon Atlanta 2025 做技术深潜 opensource.googleblog.com 2025-11

⚠️ 一个高频误读:项目 roadmap 里出现的 "KEP #597""KEP #747" 是 agent-sandbox 仓库内的 PR 编号,不是 kubernetes/enhancements 的 KEP。其中 PR #747 的标题才是 KEP-539.2: Standardizing Sandbox Runtime Interfaces。写方案文档时务必区分,否则会被评审直接打回。

1.2 版本与安装形态

最新 release 为 v1.0.4。安装清单有三种粒度,对应三种接入策略:

# 全量(核心 + 扩展)——绝大多数生产接入用这个
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/v1.0.4/sandbox-with-extensions.yaml

# 仅核心(只要 Sandbox,不要模板/热池/Claim)
kubectl apply -f .../v1.0.4/sandbox.yaml

# 仅扩展(配合已装核心)
kubectl apply -f .../v1.0.4/extensions.yaml

此外还提供 Helm Chart、OLM bundle(面向 OpenShift/OperatorHub)、kubectl kustomize k8s/ 源码渲染,以及下游发行版:GKE Agent Sandbox(2026-09 GA,Google 托管 add-on)、Red Hat build of Agent Sandbox。

v1.0.0 是一次硬性分水岭:核心与扩展 API 全面转 v1beta1,移除 v1alpha1 与 conversion webhook。官方给出的升级路径是:先升 v0.5.x → 迁移存储至 v1beta1 → 确认 CRD storedVersions 仅 v1beta1 → 再升 v1.0.0 → 最后清理 webhook 资源。新接入方直接从 v1beta1 起步即可,但不要在方案里写 v1alpha1。

验证安装:

kubectl get crd sandboxes.agents.x-k8s.io
kubectl get deploy agent-sandbox-controller -n agent-sandbox-system

2. 接入架构总览:五层模型

┌──────────────────────────────────────────────────────────────────────┐
│ L5 平台治理接入   准入策略(VAP/OPA) · APF 隔离 · 配额 · 可观测 · RBAC  │
├──────────────────────────────────────────────────────────────────────┤
│ L4 运行时接口接入 sandboxd(gRPC:9090 + REST:8080) · SDK 四连接模式    │
├──────────────────────────────────────────────────────────────────────┤
│ L3 网络接入       Sandbox Router(数据面) · 托管 NetworkPolicy(默认拒绝)│
├──────────────────────────────────────────────────────────────────────┤
│ L2 运行时接入     RuntimeClass → gVisor / Kata(+FC) / urunc           │
├──────────────────────────────────────────────────────────────────────┤
│ L1 控制面接入     Sandbox · SandboxTemplate · SandboxWarmPool · Claim │
│                   + agent-sandbox-controller(声明式 reconcile)         │
└──────────────────────────────────────────────────────────────────────┘
        ↑ 接入顺序建议:L1 → L2 → L3 → L4 → L5(每层可独立验证)

为什么是这个顺序:L1 决定资源模型(后续所有东西都挂在 CRD 上);L2 是安全边界,必须在任何真实负载进来之前确定;L3 决定谁能碰到沙箱;L4 决定 agent 代码怎么被调用;L5 才是规模化与合规。反过来做(比如先上 SDK 再补安全)会返工。


3. L1 控制面接入:四个 CRD 的分工与接线

3.1 四件套的职责矩阵

CRD API 组 / 版本 职责 何时用
Sandbox agents.x-k8s.io/v1beta1 单个有状态、单例、稳定身份的工作负载 裸接入、调试、或不需要模板化治理时
SandboxTemplate extensions.agents.x-k8s.io/v1beta1 可复用的沙箱"蓝图"+ 安全策略载体 生产必用:策略只能挂在模板上
SandboxWarmPool extensions.agents.x-k8s.io/v1beta1 预热池,用 replicas 控制规模 需要亚秒级分配时
SandboxClaim extensions.agents.x-k8s.io/v1beta1 从热池领取沙箱,屏蔽底层细节 多租户按需分配

关键设计点:SandboxTemplate 与 Sandbox 共享同一个 SandboxBlueprint(podTemplate + volumeClaimTemplates + service)。官方在类型定义里明确注释:新增字段会同时提升到两者——这意味着"模板"和"裸沙箱"的差异只在策略层(NetworkPolicy 管理、env 注入策略、卷模板策略),而不在工作负载定义层。

3.2 核心字段速查(决定接入能力边界)

Sandbox.spec

字段 说明 接入注意事项
podTemplate.spec 完整 PodSpec 隔离、资源限制、sidecar 都在这里
podTemplate.metadata 仅 labels/annotations 系统保留键(agents.x-k8s.io/*、extensions.agents.x-k8s.io/*)会被控制器过滤
volumeClaimTemplates PVC 模板 创建后不可变(CRD 有 XValidation 硬约束)
service *bool,是否自动建 headless Service 不设时保留已有 Service 但不新建——这是向后兼容行为,别误当默认开启
operatingMode Running / Suspended,默认 Running 挂起/恢复的正式 API(替代旧的 replicas: 0 语义)
lifecycle.shutdownTime 绝对过期时间 不设则永不过期
lifecycle.shutdownPolicy Retain(默认)/ Delete 只管 Sandbox 对象本身;Pod 与 Service 过期时一律删除

Sandbox.status(做监控与就绪判断用)

  • conditions:Ready(权威信号)、Suspended、PodScheduled(镜像 Pod 的调度原因)、Finished
  • serviceFQDN、service、selector、podIPs、nodeName

⚠️ 官方类型定义里明确写了一个坑:Suspended 条件在恢复后不会被移除,可能残留。消费方必须把 Ready 当唯一权威信号,不要靠 Suspended 条件是否存在来推断当前状态。

3.3 SandboxTemplate:策略的落点

这是生产接入真正需要投入的地方——三个策略字段全部默认最严:

字段 默认值 含义
networkPolicyManagement Managed 控制器自动生成并维护一份模板级共享 NetworkPolicy
networkPolicy 省略 = 严格安全默认 入向仅放行 Router;出向仅公网,阻断 RFC1918 与云 metadata
envVarsInjectionPolicy Disallowed Claim 不得注入任何环境变量
volumeClaimTemplatesPolicy Disallowed Claim 不得指定卷模板

networkPolicyManagement: Unmanaged 会把网络完全交给外部系统(如 Cilium)。官方注释里还留了一个必须提前告知团队的告警:

默认拒绝姿态会阻断 sidecar 端口。如果 Pod 里有 Istio proxy、监控 agent 等 sidecar,必须在 Ingress 里显式放行它们的端口,否则健康检查会失败。

3.4 SandboxWarmPool 与 SandboxClaim:热池语义

# SandboxTemplate(节选)
apiVersion: extensions.agents.x-k8s.io/v1beta1
kind: SandboxTemplate
metadata:
  name: secure-datascience-template
spec:
  podTemplate:
    spec:
      runtimeClassName: gvisor          # ← L2 在这里接
      automountServiceAccountToken: false
      containers:
      - name: runtime
        image: <your-runtime-image>
        resources:
          limits: { cpu: "2", memory: 4Gi }
---
apiVersion: extensions.agents.x-k8s.io/v1beta1
kind: SandboxWarmPool
metadata:
  name: sandboxwarmpool-example
spec:
  replicas: 20                          # 可被 HPA/KEDA 接管
  updateStrategy:
    type: OnReplenish                   # 默认;Recreate 会立即清掉陈旧未领取沙箱
  sandboxTemplateRef:
    name: secure-datascience-template   # 必须与模板 metadata.name 完全一致
---
apiVersion: extensions.agents.x-k8s.io/v1beta1
kind: SandboxClaim
metadata:
  name: sandbox-workflow-123
spec:
  warmPoolRef:
    name: sandboxwarmpool-example
  lifecycle:
    ttlSecondsAfterFinished: 600
    shutdownPolicy: DeleteForeground

必须理解的三个语义(都会影响你的容量与延迟设计):

  1. spec.env 与 spec.volumeClaimTemplates 会强制冷启动——因为环境变量与卷无法注入到已在运行的预热 Pod。想吃到亚秒级热池分配,Claim 里就不能设这两个字段。(additionalPodMetadata 例外,它会就地应用到被领取的沙箱。)
  2. 领取即转移所有权:沙箱一旦被 Claim 领取,SandboxWarmPool 就不再管理或替换它,由 Claim 控制器接管生命周期。
  3. updateStrategy 只作用于未领取的沙箱:OnReplenish(默认)保留陈旧沙箱直到被领取或手动删除;Recreate 立即删除陈旧未领取沙箱。已领取的沙箱永远不会被池子动。

SandboxClaim.status 会把 sandbox.name、sandbox.podIPs、sandbox.serviceFQDN 镜像出来——这是接入层做路由与就绪判断的唯一必要读点,不需要再去读 Sandbox 对象。

3.5 标签域白名单:一个容易踩的接入约束

自 v0.5.0 起,SandboxClaim.spec.additionalPodMetadata.labels 的键必须带允许列表内的域名前缀,否则 Claim 会被拒(Ready=False, reason=InvalidMetadata)。默认允许列表是 sandbox.users.io(子域也通过)。

允许列表从控制器命名空间下一个可选 ConfigMap agent-sandbox-config 的 allowed-label-domains 键读取,且文件内容会替换默认值而非追加:

apiVersion: v1
kind: ConfigMap
metadata:
  name: agent-sandbox-config
  namespace: agent-sandbox-system
data:
  allowed-label-domains: |
    example.com
    sandbox.users.io      # ← 别忘了保留它,否则既有消费方会被拒

改完必须 rollout restart 控制器(只在启动时读一次)。注意:additionalPodMetadata 里的 annotations 走的是另一套受限域黑名单(cluster-autoscaler.kubernetes.io/safe-to-evict 被豁免),不受这个白名单管辖。


4. L2 运行时接入:隔离强度是部署选择,不是 API 限制

4.1 接入方式只有一行

spec:
  podTemplate:
    spec:
      runtimeClassName: gvisor     # 或 kata-qemu / kata-vm-isolation / urunc

官方文档的定位表述很明确:"Isolation depth is a deployment choice, not a limitation of the API."

官方 examples 中给出 RuntimeClass 接入示例的运行时:

运行时 RuntimeClass 示例名 官方示例 适用
gVisor (runsc) gvisor examples/openclaw-gvisor-sandbox、quickstart/overlays/gvisor 兼容性优先、开销敏感
Kata + QEMU kata-qemu examples/kata-gke-sandbox 要独立 guest kernel
Kata + Firecracker kata-fc 系 examples/firecracker-sandbox 更快启动的 VM 隔离
Kata on AKS kata-vm-isolation examples/kata-aks-sandbox AKS 内置 Pod Sandboxing
urunc(社区) urunc 社区集成 unikernel/单应用内核

4.2 运行时选型矩阵(隔离强度 × 开销)

维度 gVisor Kata + Firecracker 普通 runc 容器
隔离机制 用户态内核,拦截 syscall 硬件虚拟化(KVM)+ 独立 guest kernel 共享宿主内核
隔离强度 中高 高 低(不是安全边界)
启动开销 毫秒级 ~100–300ms 量级 最快
I/O 开销 约 10–30%(syscall 密集场景 5–15%) 接近原生 —
接入代价 装 RuntimeClass 即可 需要支持嵌套虚拟化的机型(如 Intel N2) 零

Kata 接入的硬性前置条件(官方 GKE 示例原文要点,踩坑率极高):

  • 机型:必须 Intel N2 系列;E2 不支持嵌套虚拟化,AMD(N2D)与 ARM(T2A)也不支持
  • OS 镜像:必须 Ubuntu;COS 是只读的,会挡住 installer
  • 区域:必须选有 N2 硬件的可用区

官方给了一个非常好的隔离验证方法(接入后必做):

# 宿主内核
kubectl get nodes -o wide          # 记下 KERNEL-VERSION

# 沙箱内内核
SELECTOR=$(kubectl get sandbox kata-gke-example -o jsonpath='{.status.selector}')
POD_NAME=$(kubectl get pod -l $SELECTOR -o jsonpath='{.items[0].metadata.name}')
kubectl exec -it $POD_NAME -- uname -r

两者内核版本不同 = 隔离生效;相同 = RuntimeClass 没生效。这个"内核版本差"检查建议直接写进接入验收清单。

4.3 一个必须提前知道的连带影响

启用 gVisor/Kata 后,直接 kubectl port-forward 到 Pod 的方式不再可用,必须改走 Sandbox Router。这直接决定了 L3 的接入形态——也就是说,只要你选了安全运行时,Router 就从"可选"变成"必需"。


5. L3 网络接入:Router 数据面契约 + 默认拒绝

5.1 职责切分:创建与路由分离

 创建面(控制)                          数据面(路由)
 SDK ──► K8s API ──► Controller ──► Pod + Service
                                          ▲
 HTTP client ──X-Sandbox-*──► Router ─────┘
 (Router 无状态,从不创建/查询 Sandbox)

官方对 Router 的定位说得很死:"The router never creates or looks up Sandbox resources." 目标沙箱不存在时返回 502(带重试窗口)。

5.2 请求契约(自建客户端时必须实现)

头 必需 默认 说明
X-Sandbox-ID ✅ — 沙箱 Pod 名,必须是合法 DNS-1123 label(≤63 字符)
X-Sandbox-UID — — Sandbox CR UID;开启缓存时用于安全快路径(UID 当能力凭证)
X-Sandbox-Namespace — default 同样受 DNS-1123 校验
X-Sandbox-Port — 8888 整数,[1, 65535]
X-Sandbox-Pod-IP — — 直连 IP,绕过缓存与 DNS

解析优先级(首个命中即用):

  1. X-Sandbox-Pod-IP(显式覆盖)
  2. 按 X-Sandbox-UID 查缓存(需 --cache-enabled=true)
  3. 按 namespace/name 查缓存 —— 这一条是热池沙箱可路由的关键,因为热池沙箱没有 per-sandbox Service,DNS 形式必然 NXDOMAIN
  4. DNS 形式 http://<id>.<ns>.svc.<cluster-domain>:<port>(兼容回退)

🔐 一个精妙的安全设计:尚未被任何 Claim 领取的热池 Pod 会被排除出名字索引,只能通过 UID 访问——这就保住了"UID 即能力凭证"的性质,调用方无法靠猜池子生成的名字碰到未领取的池内 Pod。

转发时被剥离的请求头:

  • Host:让 net/http 使用上游 URL 的 host
  • Authorization:Router 自己消费它(如 --authz-mode=tokenreview 走 K8s TokenReview),若转发给沙箱,任何沙箱都能冒充调用方去打 K8s API

重试与超时:仅拨号类失败重试(默认 3 次重试 / 共 4 次尝试,200→400→800ms 退避);请求体可能已发送后的失败(响应超时、流中断)立即上抛,因为重放可能造成副作用重复。--proxy-timeout 默认 180s,但对 WebSocket 升级连接不生效(否则会 3 分钟切断健康的 code-server/Jupyter 会话)。

协议升级:Connection: Upgrade 透明转发;升级请求会剥离 Origin——因为 vscode-server/Jupyter 会校验 Origin 与 Host 一致做 CSRF 防护,而 Router 已改写 Host。非升级请求保留 Origin,CORS 预检不受影响。

5.3 托管 NetworkPolicy:默认拒绝的具体形态

由 SandboxTemplate 创建的沙箱,控制器会生成一份模板级共享的严格策略:

  • 入向:仅允许来自 Sandbox Router
  • 出向:仅允许公网;阻断 RFC1918 内网与云 metadata 端点

networkPolicy 字段是标准 NetworkPolicySpec 的受限子集——podSelector 与 policyTypes 被刻意排除,因为由控制器管理以保证严格的默认拒绝姿态。

⚠️ 两个必须告知团队的副作用:

  1. sidecar 端口默认被阻断(见 3.3);
  2. 裸 Sandbox 不套用任何 NetworkPolicy——"kernel isolation, but wide-open egress"。这是接入设计中最容易漏掉的一条:只要不走模板,就完全没有网络隔离。生产接入建议用准入策略禁止裸 Sandbox。

5.4 Router 部署形态

sandbox-router/deploy/ 下提供 Deployment + Service 清单。自 v1.0.2 起 Router 默认命名空间从 default 迁到 agent-sandbox-system(破坏性变更)。SDK 把 sandbox-router-svc 硬编码为路由目标,用户调用 sb.Run(...) 时完全看不到 Router。

Router 的企业级能力(Go 重写版):TLS/mTLS、Prometheus 指标、OTel tracing、证书热加载、结构化日志、优雅退出;鉴权侧支持 --authz-mode=tokenreview 与 Ed25519 scoped-token v2(绑定 Sandbox UID、port、HTTP method、upstream path,支持多密钥轮换)。

⚠️ 官方在 threat model 里主动披露了一个当前弱点:默认授权器是 AllowAll(为兼容 Python 客户端),因此 X-Sandbox-Pod-IP 的 IP 类别校验是目前唯一的 SSRF 防线(阻断 169.254.169.254 这类 metadata 地址)。生产接入必须配置自定义 authorizer,这是方案里的强制项而非可选项。


6. L4 运行时接口接入:sandboxd 与 SDK

6.1 为什么会有 sandboxd

KEP-539.2: Standardizing Sandbox Runtime Interfaces 解决的是"沙箱内部执行命令与操作文件的接口不统一"的问题。它对比了两条路线:

  • REST/OpenAPI(受阿里 OpenSandbox 启发):简单、可调试、多语言友好、企业安全与可观测天然契合;代价是小载荷高频调用开销高、流式要靠 SSE/WebSocket
  • gRPC/Protobuf(受 E2B envd 启发):二进制、多路复用、原生双向流;代价是工具链门槛与不可读性

结论是混合可插拔,落地为一个统一守护进程 sandboxd:

sandboxd
├── gRPC :9090 → ProcessService(流式进程 I/O)
└── HTTP :8080 → FilesystemService(无状态文件操作 + 探针)

为什么这样切:Start 是长生命周期 server-streaming RPC(stdout/stderr 持续流动直到 ExitEvent),HTTP/1.1 无法干净建模,所以进程走 gRPC;文件操作是简单请求/响应,标准 HTTP 语义天然映射,且避免大二进制传输的 base64 包装开销,所以文件走 REST。

服务 端口 关键接口
ProcessService :9090 gRPC Start(server stream) / Execute(unary) / WriteStdin / SendSignal / ResizeTTY
FilesystemService :8080 REST `GET

Conformance 分级(第三方运行时接入的依据):

  • Core:Core Execution + Filesystem(不含 Watch)—— 最低要求
  • Full:Core + Jupyter 支持 + 文件 Watch

6.2 安全注意(sandboxd 自己的边界)

  • 两个端口默认绑 0.0.0.0,同 Pod 网络可达;必须靠 NetworkPolicy/服务网格保护,或用 --listen-host=127.0.0.1 限制回环
  • sandboxd 不做客户端认证、不终止 TLS——安全边界由外部承担
  • /v1/metadata 只允许放非敏感的工作负载配置(sandbox ID、workspace 路径);绝不能放编排器凭证、K8s token、云厂商 key,因为沙箱里的不可信代码可以自己 loopback 查它
  • 路径穿越防护:所有路径过 SanitizePath,../ 类尝试返回 403

6.3 与 Router 的一个硬性不兼容

官方明确:当前 sandbox-router 接受 HTTP/2 客户端连接,但在上游连接上禁用了 HTTP/2,因此无法承载 sandboxd 的 gRPC ProcessService。

这意味着接入时必须在两条路径间做选择:

接入形态 可用性 说明
Router + REST(:8080) ✅ 走 HTTP 数据面
Router + gRPC(:9090) ❌ Router 不是 sandboxd 的完整传输通道
直连 Pod(port-forward / Pod IP / headless DNS) ✅ 需入向策略放行调用方;默认托管策略只放行 router

6.4 SDK 的四种连接模式(接入层选型)

模式 流量路径 适用
Gateway Client → LB(Gateway API) → Router → Pod 生产外部接入(GKE Gateway 等)
Tunnel Localhost → kubectl port-forward → Router → Pod 本地开发 / CI(无需公网 IP)
In-Cluster Client → Pod 直连(Pod IP 或 cluster DNS) 集群内工作负载(绕过 Router)
Direct URL Client → 指定 api_url 自定义域名 / 手工指定 Router
from k8s_agent_sandbox import SandboxClient
from k8s_agent_sandbox.models import SandboxLocalTunnelConnectionConfig

client = SandboxClient(connection_config=SandboxLocalTunnelConnectionConfig())
sandbox = client.create_sandbox(warmpool="python-sandbox-warmpool", namespace="default")
try:
    print(sandbox.commands.run("echo 'hello'").stdout)
finally:
    sandbox.terminate()

⚠️ namespace 双轨坑:router_namespace 控制 Router 服务在哪(默认 agent-sandbox-system),而 create_sandbox(namespace=...) 控制 沙箱 Pod 调度到哪。两者搞混会得到 services sandbox-router-svc not found。

就绪等待机制(影响你的延迟预算):create_sandbox() 是完全 watch 驱动的,不轮询。Claim 控制器在领取热池沙箱时,会把绑定沙箱名、Pod IP、转发的 Ready 条件在同一次状态更新中发布,所以通常第一个携带沙箱名的 watch 事件就同时带了 Ready=True。sandbox_ready_timeout 默认 180s,只是兜底上限。

📌 官方明确的反模式:不要用 get_* 轮询判断就绪——轮询间隔 T 会在控制器延迟之上平均多加 T/2。

SDK 生态现状:Python(PyPI k8s-agent-sandbox,最低 Python 3.11)、Go(sigs.k8s.io/agent-sandbox/clients/go/sandbox,支持 Options.Runtime = RuntimeSandboxd)、TypeScript(v1.0.1 起提供初版)。


7. L5 平台治理接入:准入、APF、可观测、规模化调参

7.1 准入策略:把安全默认变成强制

官方 examples 提供了完整策略样例(examples/policy、examples/composing-sandbox-nw-policies):

机制 用途
ValidatingAdmissionPolicy (VAP) secure-sandbox-vap:对沙箱工作负载强制安全默认姿态
OPA Gatekeeper 防止给 Sandbox 用 SA 授予权限
Kyverno 策略示例
ClusterNetworkPolicy(SIG Network 参考实现 kube-network-policies) 集群级默认拒绝 + FQDN 出网白名单,叠加在模板托管策略之上
Cilium demo-cilium-egress 按 workload identity 做 egress 域名白名单

建议纳入准入的强制项:① 禁止裸 Sandbox(强制走模板);② 强制 runtimeClassName 非空;③ 强制 automountServiceAccountToken: false;④ 强制 resources.limits 存在。

7.2 APF 隔离:高并发接入的必需品

官方强烈建议在claim 速率 > 50/s 或 API Server 被其他工作负载共享的集群上应用 examples/apf-insulation/apf-insulation.yaml。它给控制器专用 APF 并发席位,并把流量拆成三个优先级类:claim 路径 > 批量补池 > events——目的是让补池突发无法把 claim 采纳写入挤出队列。

7.3 规模化调参:官方 benchmark 验证过的配置

这是全文最有价值的一段——官方给出了 benchmark 实测的推荐参数组合(GKE live A/B,75 claim 突发 / 150 沙箱热池):

args:
  - --extensions
  # API 连接:当在途请求接近单连接 ~100 流上限时才有收益
  - --api-connections=4
  - --separate-watch-connection=true
  # 并发:官方验证 200/150/2/1 优于 1000/1000/500/100 与 50/50/1/1
  - --sandbox-concurrent-workers=200
  - --sandbox-claim-concurrent-workers=150
  - --sandbox-warm-pool-concurrent-workers=2   # 按活跃池数量定
  # 热池:平滑补池,不加延迟
  - --sandbox-warm-pool-replenish-delay=0
  - --sandbox-warm-pool-max-refill-rate=100    # ≥ 单池稳态 claim 到达率
  # 缓存:>5000 Pod 的集群才有收益
  - --cache-label-selectors=true
  # 写削减:≥200 claims/s 才有收益
  - --disable-claim-events=true
  - --disable-claim-observability-annotations=true
  # 刻意不设:--sandbox-write-behind-window(见下)

关键默认值(docs/configuration.md):

Flag 默认 说明
--sandbox-concurrent-workers 100 Sandbox 并发 reconcile 上限
--sandbox-claim-concurrent-workers 50 Claim 并发
--sandbox-warm-pool-concurrent-workers 1 同一池的 reconcile 被 workqueue 串行化,worker 只提供跨池并发
--sandbox-template-concurrent-workers 1 极少需要 >2
--sandbox-warm-pool-max-batch-size 300 单批创建/删除上限;补池轮次 ≈ ceil(replicas/batchSize)
--sandbox-warm-pool-readiness-grace-period 5m 超时未 Ready 视为卡住并替换
--kube-api-qps / --kube-api-burst -1 / 10 默认关闭客户端限流,交给服务端 APF(推荐)
--api-connections 1 kube-apiserver 单 HTTP/2 连接默认 100 并发流上限
--sandbox-warm-pool-max-refill-rate 0(不限速) 令牌桶平滑补池
--cache-label-selectors false 缓存范围从 O(集群 Pod) 降到 O(沙箱 Pod)
--sandbox-write-behind-window 0(关闭) 写合并窗口

7.4 性能基线与调参取舍(必读)

热池采纳延迟(PR 级 benchmark,45/s 泊松到达 × 4 池):

配置 p50 p90 p99
稳态调优(replenish-delay=0 + max-refill-rate=100) 92 ms 182 ms 376 ms
突发调优(replenish-delay=20s,不限速) 前 10s ~57ms,随后 ~1.4s 冷启动 2.28s 稳态 —

75-claim 突发(GKE live):最优配置 p50 41.4s / p90 74.2s / p99 82.6s,75/75 全部成功(对比 baseline p50 50.4s)。注意这里是冷启动地板(该集群 Pod 冷启动 p50 ~42–50s),热池采纳本身是亚秒级。

三条最有价值的取舍结论:

  1. --sandbox-warm-pool-max-refill-rate 是唯一"无失败模式"的优化:稳定带来 p50/p90 约 15% 改善(50.4s → 41.4s),rate=80 与 100 统计上无差异,启用的价值远大于具体取值。必须 ≥ 单池稳态 claim 到达率,否则池会抽干。
  2. --sandbox-warm-pool-replenish-delay 有真实脆弱性,默认不要开:单独看是中性的,但在负载下哪怕一次瞬态 API 超时就足以触发池抽干——实测 1/75 claim 失败让 p90 从 74s 飙到 205s。
  3. --sandbox-write-behind-window=250ms 是把双刃剑:409 写冲突 −44%(5447→3077)、突发 p50 −13%,但稳态 p50 +58%(320→507ms)。仅在冲突率高或突发为主且 SLO 容忍 ~500ms 稳态 p50 时启用。

worker 数量有地板也有天花板:50/50/1/1 相对推荐值 p50 +11%(确有欠配置惩罚);1000/1000/500/100 则徒增 API Server 争用。推荐值 200/150/2/1 是实测胜出的。

7.5 可观测接入

  • 控制器指标:--metrics-bind-address(默认 :8080),支持 TLS(--metrics-secure-serving + --metrics-cert-dir,惯例端口 :8443)
  • 追踪:--enable-tracing(OTLP,读标准 OTEL_EXPORTER_OTLP_ENDPOINT)
  • 剖析:--enable-pprof / --enable-pprof-debug(生产关闭,会暴露堆内容、命令行、goroutine 栈)
  • Helm Chart 暴露 controller.enableTracing / controller.enablePprof 等,并可选用 ServiceMonitor + PrometheusRule
  • 事件:v1.0.4 新增 SandboxPodCreated / SandboxReady / SandboxSuspended / SandboxExpired / PodSucceeded / PodFailed 等生命周期事件(可用 --disable-sandbox-events 关闭)

8. 分阶段落地路线(可直接抄的接入顺序)

阶段 目标 关键动作 验收标准
P0 基线 装得上、跑得通 装 v1.0.4 sandbox-with-extensions.yaml;建 1 个裸 Sandbox kubectl wait --for=condition=Ready 通过
P1 隔离 安全边界成立 建 RuntimeClass;模板中设 runtimeClassName;做内核版本差验证 沙箱内 uname -r ≠ 宿主内核
P2 网络 默认拒绝生效 部署 Router;确认托管 NetworkPolicy 生成;配自定义 authorizer(禁用 AllowAll) 跨租户不可达;metadata 端点不可达
P3 接口 agent 能跑代码 选 sandboxd 或 python-runtime;接 SDK;定四种连接模式 执行 + 文件读写 + 大文件 read_to 通过
P4 热池 延迟达标 SandboxWarmPool + SandboxClaim;Claim 不设 env/volumeClaimTemplates 采纳 p50 亚秒级
P5 规模化 扛住并发 应用 APF insulation;按 benchmark 调 worker 与 refill rate;开 cache-label-selectors 目标 claim 速率下无池抽干
P6 治理 合规与成本 上 VAP/Gatekeeper;HPA/KEDA 接管池规模;挂监控与生命周期事件 准入拦截生效;成本可观测

成本杠杆提示:官方已完成 HPA + Cold Standby Nodes (CSN) 集成,用于"大幅降低空闲基础设施成本";keda-scale-to-zero 示例演示池缩容到 0 再拉起。


9. 限制清单:接入方案里必须写明的"还没做到"

把这一节写进方案能显著降低交付风险。以下均为官方 roadmap 明确标注 Planned / In Progress 的项:

类别 尚未实现
生命周期 Auto Suspend/Resume(自动挂起非活跃沙箱并按流量恢复)、Scale to Zero、Startup Actions、突发沙箱 TTL 自动清理
网络与身份 Claim 时动态挂载 L4/L7 NetworkPolicy、Sandbox/Pod 身份关联(claim 时按调用者分配安全主体)、Claim 时存储定制
编排 API 解耦运行时(Portable Backend)、SandboxTemplate/WarmPool 滚动更新、Smart Warmpool Selection、单 Pod 多沙箱、严格 1:1 Sandbox↔Pod 映射
接口 一等公民 Router(当前是"young"组件)、MCP Server、TypeScript SDK 完善、Python SDK 高级方法
规模 claim 延迟 200ms → 100ms → 50ms、控制器支撑 1000+ claims/s(当前已验证 300 沙箱/秒)、TFFI 基准
可观测 控制器自定义指标 / Prometheus 细粒度计数器、OSS UI 仪表盘、参考架构文档

已确认的能力边界(不要误以为已 GA):

  • 内存快照(Pod Snapshot)目前仅 GKE + Python SDK 支持(依赖 podsnapshot.gke.io,GKE ≥ 1.35.2-gke.1269000);Go SDK 无等价能力
  • 已完成的是 PVC-based suspend/resume(保留卷、干净挂回),不是内存快照
  • 多集群(fleet)语义尚未提供——单控制器单集群,跨集群调度需自建

已知工程坑(社区实测):源码树 k8s/ overlay 引用 ko:// 镜像直接部署会 InvalidImageName;Router 命名空间口径不一致;跨命名空间 NetworkPolicy 导致 504;/execute 不经 shell(管道/&&/变量需 sh -c 包裹);Write() 只接受纯文件名不接受路径分隔符。


10. 安全基线:五条信任边界落到接入动作

官方 Threat Model 定义了五条信任边界,接入方案里应逐条对应到具体动作:

信任边界 威胁 接入动作
User → K8s API 未授权创建沙箱 RBAC 收紧 Sandbox/SandboxClaim/SandboxTemplate
系统控制面 → 工作负载 控制器/Router 被攻破 视为可信系统组件,最小权限;控制器 NS 与租户 NS 分离
跨租户隔离 沙箱间横向攻击 托管 NetworkPolicy 默认拒绝 + ClusterNetworkPolicy 叠加
工作负载 → 宿主/节点 容器逃逸 runtimeClassName 强制 gVisor/Kata(本项目不提供隔离)
工作负载 → 控制面 用 SA token 打 K8s API 模板默认 automountServiceAccountToken=false;裸 Sandbox 用 VAP 强制

官方已实现的一个高价值防护(值得单独讲):系统保留标签过滤。因为 Sandbox.spec.podTemplate.metadata 允许租户提交任意 labels,攻击者可伪造 Service 选择器标签 agents.x-k8s.io/sandbox-name-hash,让自己的 Pod 匹配到别的租户 Service,从而劫持流量(网络隔离绕过的原语)。控制器的防护是:

  • 创建路径与采纳路径都过滤掉 agents.x-k8s.io/、extensions.agents.x-k8s.io/ 前缀的用户键
  • Service 选择器标签在合并用户标签之后才由控制器赋值,无法被覆盖
  • 采纳/更新时清洗旧控制器写入 propagated-labels 中的系统保留键

Router 侧的两个已知弱点(必须补偿):① 默认 AllowAll 授权器 → 必须配自定义 authorizer;② Router 当前未实现连接级限流与升级连接超时 → 需在 Ingress/Gateway/Envoy 层做限流与连接数限制。


11. 横向定位:什么时候该选它

场景 推荐 理由
已有 K8s 集群、数据不出 VPC、需平台化治理 K8s Agent Sandbox 复用现有调度/网络策略/安全审查;把"agent 在哪跑代码"变成一种 workload 类型
无 K8s 足迹、要最快落地 E2B / Modal / Vercel 等托管平台 一个 API key 即可
极强多租户规模化 + 快照分叉(RL 训练) 阿里云 Agent Sandbox / OpenKruise Agents / DeepSeek DSec DSec 单 unit 约 160 节点、日均 300 万沙箱、峰值 38 万并发
需要 E2B 协议兼容 + K8s 内多租户治理 OpenKruise Agents E2B 兼容 + Teams/API Key
超大规模 agent 密度(超出 K8s 控制面能力) Google Agent Substrate 与 agent-sandbox 互补,非替代

已确认的生态接入(有一手来源):LangChain/LangGraph、Lovable、OpenHands、Ray/RLlib、SWE-bench 类 RL 训练、NVIDIA NeMo Gym/OpenShell、JupyterLab、ADK、Pulumi、VSCode/Playwright/Chrome/Aider/Windows 等官方示例。

⚠️ 一个必须澄清的误传:kagent 并不是直接接入 agent-sandbox,它转向的是 Google 的 Agent Substrate。两者是不同项目。


12. 结语:这份标准真正解决了什么

回到最开始那句话。K8s 官方 Agent Sandbox 的价值不在于它提供了多强的隔离(它刻意不提供),而在于它把"agent 的执行环境"从一个各家自定义的临时方案,变成了一种有 CRD 契约、有安全默认、有生命周期语义、有热池与采纳协议的一等 K8s workload。

对接入方的实际收益可以归纳为三条:

  1. 安全默认是"买一送一"的:automountServiceAccountToken=false、模板级默认拒绝 NetworkPolicy、系统标签过滤——这些在自建方案里通常要写一堆准入策略才勉强做到,这里是默认行为。
  2. 延迟曲线是"可规划"的:热池采纳亚秒级、冷启动几十秒,官方给了完整的 benchmark 与调参取舍,容量规划有据可依。
  3. 风险是"被披露"的:Router 默认 AllowAll、内存快照仅 GKE、Auto Suspend 未实现、replenish-delay 的池抽干脆弱性——这些都被官方主动写进 threat model 和 benchmark 文档。一份把限制写清楚的标准,比一份看起来完美的标准更适合做架构决策。

附录:核心参考来源

# 来源 用途
1 kubernetes-sigs/agent-sandbox Scope、Motivation、Desired Characteristics
2 roadmap.md 已完成 / 进行中 / 规划项
3 docs/configuration.md 全部控制器 flag 与默认值
4 docs/performance-tuning.md benchmark 数据与调参取舍
5 docs/security/threat_model.md 五条信任边界与缓解措施
6 sandbox-router/README.md 数据面契约与校验规则
7 KEP-539.2 Standardizing Sandbox Runtime Interfaces sandboxd 架构与 Conformance
8 KEP-694 Suspend/Resume Beta operatingMode API 决策
9 KEP-119 Sandbox Suspended State 条件层级与依赖矩阵
10 api/v1beta1/sandbox_types.go 核心字段与系统保留标签
11 extensions/api/v1beta1 Template/WarmPool/Claim 字段
12 SIG Apps README 治理归属
13 Google Open Source Blog 2025-11 立项公告 立项时间与 KubeCon 发布
14 Kubernetes Blog 2026-03-20 抽象缺口论证
15 examples/ 55+ 接入示例(Kata/gVisor/Firecracker/RL/Jupyter/VSCode…)
16 releases 版本演进与破坏性变更

本文基于 kubernetes-sigs/agent-sandbox v1.0.4 与 2026-09-29 时点的官方仓库快照整理;项目迭代较快(2025-11 立项至今 v0.1.1 → v1.0.4),接入前请以目标版本 tag 为准复核字段与 flag。

← 返回资讯列表

读者留言

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

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