Announcement

👇Official Account👇

Welcome to join the group & private message

Article first/tail QR code

Skip to content

Kubernetes Agent Sandbox 深度实战

TL;DR

  • 问题:AI Agent 会拉依赖、执行生成的代码、开 socket、写文件——在内核眼里就是"打字极快的不受信用户"。普通容器隔离(共享宿主机内核)不足以承载它。
  • 上游答案:Kubernetes SIG Apps 的 Agent Sandbox 项目(kubernetes-sigs/agent-sandbox),提供一个 Sandbox CRD:有状态、单例、稳定身份、持久存储的隔离 Pod,用声明式 API 管理。
  • 四个 API 对象Sandbox(核心)、SandboxTemplate(可复用蓝图)、SandboxClaim(按需领取,形似 PVC→PV)、SandboxWarmPool(预热池)。
  • 关键设计Sandbox 是编排器,不是隔离实现——它把真正的隔离边界委托给 RuntimeClass 指定的 gVisor / Kata Containers。选型因此变成"每个模板一个字段",而不是"整个集群重建一次"。
  • 收益:暂停即缩零、恢复保留状态;配合预热池 + GKE Pod Snapshots,冷启动可降约 90%。

一、为什么 Agent 需要"沙箱"而不是"StatefulSet"

先看清楚威胁模型的差异。

普通微服务的代码是你写的、你审的、你部署的。Agent 的代码是模型在运行时生成的——同一任务每次路径都可能不同,出问题的那个会话到排查时可能早就没了。Docker 在 2026 年 8 月 31 日的一篇博文里把这件事说得很直白:Agent 是非确定性的、短暂的(ephemeral),而围绕"有持久身份、节奏可预测的人类工作者"设计的安全控件,和这个运行模型根本不匹配。

答案不是"每个动作都弹窗要授权"——那等于放弃了 Agent 的自主性。更实际的做法是:给 Agent 一个受限环境,让它在里面自由活动。而这个"环境"的底座,正在重新变成 VM。

1.1 microVM 已经把"重"的问题解决了

容器当年崛起,部分原因就是 VM 太慢太重。但现在选项不再只有"重量级 VM"和"共享内核的轻量容器"两极,中间被 microVM 填满了:

指标Firecracker microVM(AWS 为 Lambda 打造)
启动时间< 125 ms
内存开销< 5 MiB
创建速率单主机可达 150 个/秒
隔离强度硬件强制虚拟化边界

Firecracker 靠砍掉传统 VM 里大量硬件模拟和通用功能实现这些数字,同时保留硬件强制的虚拟化边界。于是 Agent 时代的技术栈形态不是"容器 vs VM",而是**"容器跑在轻量 VM 隔离里"**——容器负责打包代码,microVM 负责限制代码能干什么。

1.2 Kubernetes 承认了沙箱这个抽象

这个架构正在进入 Kubernetes。SIG Apps 的 Agent Sandbox 项目,在自己的任务说明里把目标负载写得很清楚:

isolated environments for executing untrusted, LLM-generated code (用于执行不可信的、LLM 生成代码的隔离环境)

项目在 2026 年 3 月由 Kubernetes 官方博客介绍,Red Hat 于同年 7 月发布受支持的构建版本——已经过了纯实验阶段,进入"睁大眼睛采纳"的阶段

二、四个 API 对象:Sandbox 及三件套

Agent Sandbox 的 API 面很小,一共四个对象,分工明确。

2.1 Sandbox(核心)

Sandbox 是核心对象,一个 Agent 定义最多映射到一个运行中的 Pod:

  • 稳定身份:固定的 hostname 与网络身份,多 Agent 场景下可以互相发现;
  • 持久存储:可挂 PVC,状态能跨重启存活;
  • 生命周期管理:控制器负责创建、定时删除(scheduled deletion)暂停/恢复(pausing / resuming)

最小示例(来自官方 README):

yaml
apiVersion: agents.x-k8s.io/v1beta1
kind: Sandbox
metadata:
  name: my-sandbox
spec:
  podTemplate:
    spec:
      containers:
        - name: my-container
          image: ghcr.io/example/coding-agent:1.2.0

创建后即可通过稳定 hostname my-sandbox 访问它。

2.2 三件套(extensions 模块)

extensions 模块在核心 Sandbox API 之上提供更高级的能力:

对象作用类比
SandboxTemplate可复用蓝图:镜像、资源、隔离策略集中定义PodTemplate
SandboxClaim面向用户的领取请求,从预热池拿一个沙箱,不关心背后是哪个节点PVC → PV 的关系
SandboxWarmPool维持 N 个预热好的沙箱,让"领取"命中已运行的 Pod,而不是重新调度连接池 / 对象池

架构关系(文字版):

                        ┌──────────────────────────┐
   User ──── creates ──►│ Sandbox                  │  ← 核心 CRD
     │                  │  agents.x-k8s.io/v1beta1 │
     │                  │  ├ 稳定 hostname/网络身份  │
     │                  │  ├ PVC 持久存储            │
     │                  └──────────┬───────────────┘
     │                             │ creates
     │                  ┌──────────▼───────────────┐
     └── creates ──────►│ Pod                      │
        SandboxClaim    │  runtimeClassName: gvisor │  ← 隔离边界由 RuntimeClass 决定
                        └──────────┬───────────────┘

                        ┌──────────▼───────────────┐
                        │ Sandbox Runtime           │
                        │  gVisor / Kata Containers │
                        └──────────────────────────┘

   Extensions 模块:
   SandboxWarmPool ──references──► SandboxTemplate
   SandboxClaim ────adopts───────► Sandbox(来自 WarmPool)

2.3 安装

推荐用包含 core + extensions 的单一资产,对 GitOps(Argo CD / Config Sync / kustomize)最友好:

bash
# 把 vX.Y.Z 替换为 releases 页面里的具体版本号(例如 v0.1.0)
export VERSION="v0.1.0"
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${VERSION}/sandbox-with-extensions.yaml

也可以分开装:

bash
# 仅核心
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${VERSION}/sandbox.yaml
# 扩展(可选)
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${VERSION}/extensions.yaml

2.4 编程接口

需要程序化管理时,官方提供 SDK:

bash
# Go SDK
go get sigs.k8s.io/agent-sandbox/clients/go/sandbox@latest

注意:Go SDK 当前从仓库根 Go module 发布,因此仓库的 release tag(如 v0.1.0同时就是 Go SDK 的版本号。此外还有 Python SDK 可用。

三、隔离层选型:gVisor 还是 Kata

这是落地时最关键的决策,而且选错了很难改——所以 Agent Sandbox 把它设计成模板级字段。

3.1 两者的本质差异

gVisorKata Containers
隔离机制用户态应用内核,拦截系统调用而非直通宿主内核轻量 VM,独立内核 + 硬件强制边界
需要 KVM❌ 不需要✅ 需要
启动开销+150 ms150–500 ms 启动
资源模型更接近进程模型硬件虚拟化模型
强项CPU-only 的编码 Agent互相不可信的租户、需要 GPU(VFIO 直通)

两者安全架构不同,但方向一致:普通容器隔离不再被自动视为自主代码的最终边界——负载被放到另一个内核或硬件强制的 VM 屏障之后。

3.2 默认选择建议

场景默认选择换另一个的理由
控制器上游 Sandbox 控制器:一个 CR 顶掉 StatefulSet + PVC + headless Service + 挂起恢复胶水只有当今天就需要每个 Agent 多容器 sidecar 时手写——Sandbox 目前是单容器原语
隔离CPU-only 编码 Agent → gVisor:不需要 KVM,约 +150ms 启动,系统调用过滤租户互不可信需要 GPU → Kata(硬件边界 + VFIO 直通,代价是 KVM 与 150–500ms 启动)
预热池规模按并发会话的 p95 设池,其余休眠到 PVC 支撑的零只有 Agent 能接受每次恢复 5–30 秒冷启动时才完全缩零

3.3 带 RuntimeClass 的模板示例

yaml
apiVersion: extensions.agents.x-k8s.io/v1alpha1
kind: SandboxTemplate
metadata:
  name: coding-agent-gvisor
  namespace: agents
spec:
  # 隔离策略就在模板里 —— 换隔离 = 换模板字段,不是重建集群
  podTemplate:
    spec:
      runtimeClassName: gvisor          # 或 kata-qemu / kata-clh
      serviceAccountName: coding-agent  # 最小权限 SA,绝不是 cluster-admin
      containers:
        - name: agent
          image: ghcr.io/example/coding-agent:1.2.0
          resources:
            requests: { cpu: "500m", memory: "1Gi" }
            limits:   { cpu: "2",    memory: "4Gi" }
          volumeMounts:
            - name: workspace
              mountPath: /workspace
      volumes:
        - name: workspace
          persistentVolumeClaim:
            claimName: agent-workspace

字段名以你所用版本的官方 CRD 为准——Agent Sandbox 仍处于快速迭代期,v1alpha1 / v1beta1 之间的字段可能调整。上线前先用 kubectl explain 校验一遍。

3.4 预热池与领取

yaml
apiVersion: extensions.agents.x-k8s.io/v1alpha1
kind: SandboxWarmPool
metadata:
  name: coding-agent-warm
  namespace: agents
spec:
  replicas: 5                 # 按并发 p95 设,不是按峰值设
  templateRef:
    name: coding-agent-gvisor
---
apiVersion: extensions.agents.x-k8s.io/v1alpha1
kind: SandboxClaim
metadata:
  name: session-8f2c1a
  namespace: agents
spec:
  templateRef:
    name: coding-agent-gvisor

领取流程:SandboxClaimSandboxWarmPool收养一个已运行的 Sandbox(命中则毫秒级可用),池子见底时控制器再补新的。这就是把"冷启动几十秒"变成"毫秒级领取"的关键。

四、生命周期:暂停即缩零,恢复保状态

Agent 的工作模式有个鲜明特征:在工具调用之间几乎全是空闲。传统模型里,这种"长期存在但大部分时间没事干"的负载最难安排——你不能删它(状态会丢),也不能一直养着(纯烧钱)。

Sandbox 用两个生命周期特性解决:

  1. 空闲缩零suspend 让 Pod 直接消失,但 PVC 保留工作区resume 把它拉回来,状态完好。空闲的 Agent 一旦安静下来就立刻停止消耗 CPU 和内存。
  2. 定时删除:会话结束按计划清理沙箱,不用自己写 operator 做 GC。

对比一下引入前手写方案要维护的东西:

手写方案(一个持久 Agent 环境):
  StatefulSet(稳定 hostname、单副本)
+ PersistentVolumeClaim template(工作区存活)
+ headless Service(稳定网络身份)
+ NetworkPolicy(防止"吵闹邻居"外泄)
+ ⚠️ 没人演示的那部分:suspend/resume 机制
  (通常是 cronjob 或 operator 在空闲超时后缩零、按需拉起,
   外加"记住哪个 PVC 属于哪个 Agent"的簿记)

引入 Sandbox 后:
  Sandbox 控制器把 StatefulSet、PVC 接线、身份、挂起/恢复循环
  全部吸收进一个被 reconcile 的对象里。

但有一半工作它不会替你扛,这部分才是你自己的 runbook:

  • 镜像拉取耗时(取决于你的 registry 本地化程度);
  • NetworkPolicy 仍需按租户等级自己写;
  • 每租户的 CPU/RAM 配额仍在你自己定义的 ResourceQuota 里;
  • "哪个租户可以领取哪个模板"的准入控制,仍是你的策略代理要执行的。

一句话:采纳控制器是为了删掉生命周期胶水,不是删掉平台思考。

五、配套控制:沙箱只是第一层

一个更硬的边界是必要的,但不充分。Anthropic 在审查了 141,006 次网络安全评测运行后发现,其中 3 次 Claude 模型获得了对真实系统的未授权访问——原因是与第三方评测伙伴的一次误解,留下了非预期的互联网访问。边界之外还有身份、凭据、网络与可观测性。

按重要性叠加,而不是二选一:

控制项做法为什么
内核级沙箱只要 Agent 能生成或执行代码,就跑在 gVisor/Kata RuntimeClass隔离的是系统调用面,不只是容器文件系统
最小权限 SA每个 Agent 一个只覆盖所需 namespace 与资源的 ServiceAccount默认 cluster-admin 是最常见的自毁操作
凭据隔离用 sidecar 代理(Envoy 常见)注入 provider 凭据,Agent 进程看不到原始 API Key提示注入读不到密钥
网络策略NetworkPolicy + mTLS(如 Istio Ambient),egress 默认拒绝,只白名单模型端点与内部 API五分钟的改动,能拦住绝大多数凭据外泄尝试
可观测性OpenTelemetry 打底,Agent 与非 Agent 服务同等待遇出事要能追溯

egress 默认拒绝 + 白名单这条尤其值得强调:它把"Agent 被攻陷"的爆炸半径从"整个内网"压缩到"它能调的那几个端点"。这也是 OWASP 在 LLM Top 10 里新增的 "智能体工具交互操纵"(Agent Tool Interaction Manipulation) 条目的核心防御建议——在锁定环境中运行 Agent,对其可做的事做细粒度权限控制

5.1 规模化的两个成本杠杆

  • 预热池 + 快照:据 Google Cloud 自家基准测试,把预热沙箱与 GKE Pod Snapshots 配合,可把冷启动延迟降低约 90%,把"多分钟恢复"变成"亚秒级"(CPU 与 GPU 负载都适用)。
  • 调度与扩缩:GPU 推理用 Kueue 做排队与公平分配,节点供应交给 Karpenter 或 Cluster Autoscaler;推理负载用 Deployment + Service 配 KEDA / HPA(按队列深度或并发会话数扩,而不是按裸 CPU),不要用 DaemonSet——DaemonSet 无法把副本数与节点数解耦。

六、上线前清单

在写第一个 manifest 之前,先把集群地基铺好——跳过这步是 Agent 部署卡在 staging 的最常见原因

  1. 命名空间与配额:独立 namespace + ResourceQuota
  2. 密钥后端:KMS 支撑的加密 Secrets,不要明文 etcd
  3. 节点池:需要 GPU 就准备好带 GPU 标签的节点池;
  4. GitOps:Argo CD 或 Flux 盯着仓库;
  5. 模板SandboxTemplate 里显式写明镜像、资源请求(CPU/内存/GPU limits)、runtimeClassName
  6. 网络:ClusterIP Service 提供稳定路由;NetworkPolicy egress 默认拒绝;
  7. 存储:只有当 Agent 需要本地持久 scratch 空间时才加 PVC;
  8. 探针startupProbefailureThreshold 要够覆盖模型加载;readinessProbe 打轻量健康端点;
  9. 缩零策略:先定义空闲缩零规则,再谈自动扩缩;
  10. 灰度:canary 上线,先看 Prometheus 的延迟与错误率,再切全量流量。

七、小结

Agent Sandbox 的意义,不在于它引入了多么新的技术——gVisor、Kata、microVM 都早就在了。它的意义是把"沙箱"从一个运行时实现细节,提升为 Kubernetes 的一等工作负载抽象

对平台团队,判断标准很简单:

  • 要不要沙箱:只要 Agent 能执行模型生成的代码,答案是"要",这不是选择题;
  • 选哪个控制器:默认上游 Sandbox 控制器,除非你今天就需要多容器 sidecar;
  • 选哪个隔离:CPU-only → gVisor;互不可信租户或有 GPU → Kata;
  • 池子开多大:按并发 p95 设预热池,其余休眠到零。

最后一句话值得记住:容器把应用打包抬到了虚拟化之上,Agent 又把虚拟化层拉回了视野中心——因为底层边界的强度,重新变成了战略问题。

参考资料


相关阅读

上次更新于: