数据截至 (上游 commit ac864a6fe3bd)
Kubernetes Agent Sandbox — 架构与原理(入口页)
30 秒导读: agent-sandbox 是 Kubernetes 官方 SIG-Apps 孵化的一个项目。它给 Kubernetes 加了一个新的自定义资源
Sandbox:你写一小段 YAML 声明"我要一个有稳定名字、有持久磁盘、能暂停、能到期自动清理的单个 Pod",一个控制器就替你把底层的 Pod、Service、PVC 全部建好并托管其生命周期。它的头号目标场景,是给 AI agent 提供一次性、隔离的沙箱来跑不可信的、LLM 生成的代码。
本页是这个项目的导航入口,只做两件事:让你零基础看懂"这是什么"(Layer 0),以及看懂"它大概怎么转、由哪些部件组成"(Layer 1),最后给你一张阅读地图和代码地图。具体代码走读不在本页,都拆进了下面 5 章。
Layer 0 · 这是什么(零基础也能懂)
一句话定义
agent-sandbox = 一个 Kubernetes CRD(Sandbox)+ 一个控制器,把"单实例、有状态、有稳定身份、可暂停可过期的容器"变成一个声明式 API。
(CRD = Custom Resource Definition,自定义资源定义,让你能在 Kubernetes 里发明自己的资源类型,像内置的 Pod、Service 一样用 kubectl apply 管理。)
它解决谁的什么问题
Kubernetes 原 生擅长两类工作负载:
- Deployment:一堆无状态、可随意替换的副本(比如 Web 服务器,挂了再拉一个一模一样的就行)。
- StatefulSet:一组有编号、有序、有稳定身份的有状态 Pod(比如数据库集群 node-0/node-1/node-2)。
但有一类需求两边都不贴:"我只要一个 Pod,但它必须有稳定的名字和网络身份、有能活过重启的磁盘、能被暂停省钱、能到点自动销毁"。README 把典型场景列成四类(README.md:159-164):
| 场景 | 举例 |
|---|---|
| 开发环境 | 每个开发者一个持久、可联网的云端 IDE 环境 |
| AI agent 运行时 | 给 agent 一个隔离环境,专门执行不可信的、LLM 生成的代码 |
| 笔记本 / 研究工具 | 单容器、可持久的 Jupyter Notebook 会话 |
| 单 Pod 有状态服务 | 构建代理、小型数据库这种"就一个实例但要稳定身份"的东西 |
你当然可以用"StatefulSet(副本数=1)+ Service + PVC"硬拼出来,但 README 直言这样做繁琐、且缺少休眠(hibernation)这类专门的生命周期管理(README.md:166)。agent-sandbox 就是把这套组合收敛成一个更轻、更贴合 agent 场景的原生资源。
它能做什么(核心功能)
- 稳定身份(Stable Identity):每个 Sandbox 有稳定 的主机名和网络身份(README.md:28)。
- 持久存储(Persistent Storage):可挂能活过重启的持久卷(README.md:29)。
- 生命周期管理:控制器托管 Pod 的创建、定时删除、暂停与恢复(README.md:30)。
- 扩展能力:可选的
extensions模块再加三种资源——模板复用、热池预热、按需领取(见 Layer 1)。
用起来什么样(最小示例)
装好控制器后,写一段 YAML 声明一个 Sandbox 就够了(README.md:140-151):
apiVersion: agents.x-k8s.io/v1beta1
kind: Sandbox
metadata:
name: my-sandbox
spec:
podTemplate:
spec:
containers:
- name: my-container
image: <IMAGE>
kubectl apply -f my-sandbox.yaml
apply 之后,控制器会创建一个跑你指定镜像的 Pod,你就能通过它稳定的主机名 my-sandbox 去访问它(README.md:153)。注意你写的只是"我想要什么"(desired state),底层的 Pod/Service 是控制器替你算出来并维持的——这就是"声明式"。
一句话直觉
把它想成"Kubernetes 上的一台轻量单机虚拟机体验":一个有固定名字、带一块自己的磁盘、能开机/关机(暂停/恢复)、能设置自动到期的独立盒子——只不过底座是 Kubernetes 原语,不是真 VM(README.md:18 原文即用"lightweight, single-container VM experience"作比)。对 AI agent 来说,这个盒子的意义是:agent 生成的代码在盒子里跑,炸了也炸在盒子里,不伤主机、不伤别的租户。
Layer 1 · 全景图(它大概怎么转)
怎么读这张图
从上到下是"抽象 → 具体"。用户要么直接创建一个 Sandbox(核心路径),要么创建一个 SandboxClaim 去热池里领一个现成的(扩展路径)。中间那个控制器进程是大脑,它监听这些资源,替你把最底层的 Pod / Service / PVC 建好、维持好。虚线框里是可选的 Extensions。
┌───────────────────────────────┐
创建 Sandbox │ 用户 / SDK │ 创建 Claim
┌─────────────── │ (kubectl / Go / Python 客户端) │ ───────────────┐
│ └─── ────────────────────────────┘ │
v v
┌───────────┐ ┌───────────────────────────┐
│ Sandbox │ <──────────── adopts(领取/绑定) ────────── │ SandboxClaim │
│ (CRD) │ └───────────────────────────┘
└───────────┘ │ warmPoolRef
│ v
│ ┌──────────────────── Extensions(可选,--extensions)────────────────────┐
│ │ ┌───────────────┐ pre-warms(预热) ┌───────────────────────┐ │
│ │ │ SandboxWarmPool│ ───────────────────> │ 一池预建好的 Sandbox │ │
│ │ └───────────────┘ └───────────────────────┘ │
│ │ │ sandboxTemplateRef │
│ │ v │
│ │ ┌───────────────┐ │
│ │ │SandboxTemplate │ (可复用的 Blueprint 模板) │
│ │ └───────────────┘ │
│ └───────────────────────────────────────────────────────────────────────┘
│
│ 控制器 reconcile(把期望状态落成真实资源)
v
┌──────────────────────────────────────────────────────────┐
│ 控制器进程 agent-sandbox-controller │
│ SandboxReconciler + 3 个扩展 Reconciler + 4 个 webhook │
└──────────────────────────────────────────────────────────┘
│ 创建并持有(owner reference)
├───────────────┬──────────────────┐
v v v
┌────────┐ ┌──────────┐ ┌──────────┐
│ Pod │ │ Service │ │ PVC │
│(运行时) │ │(稳定身份) │ │(持久存储) │
└────────┘ └──────────┘ └─── ───────┘
│
│ (数据面,可选)流量经 sandbox-router 反向代理进入 Pod
v
访问:稳定 hostname / Pod-IP 快路
图里的
SandboxClaim -> Sandbox用 "adopts" 是因为 Claim 不是新建一个 Sandbox,而是从热池里**领取(adopt)**一个已经预热好的现成 Sandbox 并绑定给你——这正是"热"的来源,省掉了冷启动。详见 03-extensions-warmpool-claim.md。
部件一句话职责
素材来源:README 的架构 mermaid(README.md:44-83)与控制器入口 cmd/agent-sandbox-controller/main.go:488-601 里实际注册的四个 Reconciler + 四个 webhook。
| 部件 | 一句话职责 | 所在文件(符号) |
|---|---|---|
Sandbox(CRD) | 核心资源:声明一个有稳定身份/持久存储的单 Pod 的期望状态 | api/v1beta1/sandbox_types.go(Sandbox / SandboxSpec) |
SandboxReconciler | 核心控制器:把 Sandbox 对象 reconcile 成 Pod/Service/PVC | controllers/sandbox_controller.go(SandboxReconciler.Reconcile) |
SandboxTemplate(CRD) | 可复用的 Blueprint 模板,供 WarmPool 引用 | extensions/api/v1beta1/sandboxtemplate_types.go(SandboxTemplate) |
SandboxWarmPool(CRD) | 维护一池预热好的 Sandbox,减少冷启动等待 | extensions/api/v1beta1/sandboxwarmpool_types.go(SandboxWarmPool / TemplateRef) |
SandboxClaim(CRD) | 用户的"领取单",从 WarmPool 领一个现成 Sandbox | extensions/api/v1beta1/sandboxclaim_types.go(SandboxClaim / WarmPoolRef) |
| 3 个扩展 Reconciler | 分别驱动 Claim / Template / WarmPool 的调谐 | extensions/controllers/(SandboxClaimReconciler 等) |
| 4 个 admission webhook | 对 Sandbox 及三个扩展资源做准入校验/默认化 | cmd/agent-sandbox-controller/main.go:501-596(ctrl.NewWebhookManagedBy) |
sandbox-router | 独立数据面:反向代理,把请求按 Pod-IP 快路或 DNS 送进目标 Sandbox | sandbox-router/proxy/resolve.go(Resolve / SourceCache) |
| Go / Python SDK | 客户端库:用代码创建、连接、管理 Sandbox 生命周期 | clients/go/sandbox/、clients/python/agentic-sandbox-client/ |
注意分工:
Sandbox本身是核心(Core),装了就有;Template / WarmPool / Claim三件套是扩展(Extensions),只有启动控制器时带--extensions才会注册(main.go:99的 flag、main.go:322的if extensions分支)。sandbox-router是一个独立的可选数据面组件,不在主控制器进程里。
主线走一遍(高层,不进代码)
以最核心的路径为例——用户直接创建一个 Sandbox:
① 用户 kubectl apply 一个 Sandbox YAML(声明期望状态)
│
v
② SandboxReconciler.Reconcile 被触发,读到这个 Sandbox 对象
│ (main.go:306 注册的核心 controller)
v
③ reconcileChildResources:按 Spec 建/更新子资源
├─ reconcilePVCs → 持久存储
├─ reconcilePod → 运行时 Pod(打上 owner reference)
└─ reconcileService→ 稳定网络身份
│
v
④ computeConditions → 算出 Ready 等状态条件,写回 Status
│ (Ready=DependenciesReady 表示全部就绪)
v
⑤ 之后每次 reconcile 检查 shutdownTime:到期就删子资源,
按 shutdownPolicy(Delete/Retain) 决定是否连 Sandbox 对象一起删
这条主线的代码在 controllers/sandbox_controller.go:283(Reconcile)→ :235(reconcileChildResources)→ :278(computeConditions),到期清理走 Reconcile 里 checkSandboxExpiry 分支(:202-213)。具体怎么走、所有权与状态条件怎么算,见 02-sandbox-controller.md。
阅读地图(建议顺序)
本项目按子系统拆成 5 章, 建议按下面顺序读——先懂数据模型,再懂控制器,再看扩展与数据面,最后落到客户端:
- 01-sandbox-api-model.md — 核心数据模型。 先搞清
Sandbox的Spec/Status各字段(SandboxSpec、SandboxStatus、Lifecycle、OperatingMode),以及被 Sandbox 与 SandboxTemplate 共享的SandboxBlueprint。读懂"声明式 API 长什么样"是理解一切的前提。 - 02-sandbox-controller.md — 核心控制器。
Reconcile主循环、reconcileChildResources建 Pod/Service/PVC、owner reference 所有权模型、computeConditions状态条件、到期清理。这是项目的"大脑"。 - 03-extensions-warmpool-claim.md — 扩展面。
SandboxTemplate/SandboxWarmPool/SandboxClaim三件套,冷启动 vs 热池领取(adopt)的机制,热池怎么预热、Claim 怎么领。 - 04-sandbox-router.md — 数据面。
sandbox-router反向代理:Pod-IP 缓存快路(SourceCache)、DNS 兜底、X-Sandbox-*头契约与鉴权(TokenReview)。 - 05-client-sdks.md — 客户端 SDK。 Go 与 Python 客户端怎么用代码走完 Sandbox 的生命周期(创建、连接、执行命令、传文件、销毁)。
只想快速评估相关性? 读到这里(Layer 0 + Layer 1)就够判断"这项目是不是我要的"了。想动手/改代码,再往下钻对应章。
巧妙之处(预告)
这些设计点值得带走,本页只点名,具体在各章展开:
- Blueprint 共享,而非复制。
SandboxSpec直接内联(json:",inline")一个SandboxBlueprint,让Sandbox和SandboxTemplate共用同一份工作负载定义——加字段一处即两处生效(api/v1beta1/sandbox_types.go:277-281的注释专门解释了这个"提升到共享"的约定)。详见 01。 - 到期删子资源,但对象可保留。
Lifecycle.ShutdownPolicy分Delete/Retain:到期时底层 Pod/Service 总是被删(省钱),但 Sandbox 对象本身可以Retain下来只标记为 Expired,方便审计与复活(sandbox_types.go:226-236)。详见 02。 - 热池领取(adopt),把冷启动摊平。 Claim 不新建 Sandbox,而是从 WarmPool 里领取一个已预热的现成实例,把镜像拉取/初始化的时间提前付掉——对 agent"要一个环境就要立刻能用"极关键。详见 03。
- 路由三级 fallback,快路优先。
sandbox-router的Resolve有明确优先级:显式 Pod-IP → 缓存命中(SourceCache,KEP 的安全快路)→ DNS 兜底,既快又总能工作(sandbox-router/proxy/resolve.go:67-116)。详见 04。
代码地图(顶层导航索引)
这张表是给人和 agent 的"跳转表"——认准符号名去 grep(比行号抗上游漂移)。更细的地图在各章末尾。
| 主题 | 文件 | 关键符号 |
|---|---|---|
| 控制器进程入口、注册所有 controller 与 webhook | cmd/agent-sandbox-controller/main.go | main(SandboxReconciler 注册在 :306,扩展在 :322-392) |
| 核心数据模型:Sandbox 的 Spec/Status | api/v1beta1/sandbox_types.go | Sandbox、SandboxSpec、SandboxStatus、SandboxBlueprint、Lifecycle |
| 核心控制器:reconcile 主循环 | controllers/sandbox_controller.go | SandboxReconciler.Reconcile、reconcileChildResources、computeConditions |
| 扩展 CRD:领取单 | extensions/api/v1beta1/sandboxclaim_types.go | SandboxClaim、SandboxClaimSpec、WarmPoolRef |
| 扩展 CRD:热池 | extensions/api/v1beta1/sandboxwarmpool_types.go | SandboxWarmPool、SandboxWarmPoolSpec、TemplateRef |
| 扩展 CRD:模板 | extensions/api/v1beta1/sandboxtemplate_types.go | SandboxTemplate |
| 扩展控制器 | extensions/controllers/ | SandboxClaimReconciler、SandboxWarmPoolReconciler、SandboxTemplateReconciler |
| 数据面反向代理:解析目标 | sandbox-router/proxy/resolve.go | Resolve、Source、SourceCache、SourceDNS |
| 数据面进程入口(缓存 + 鉴权) | sandbox-router/cmd/main.go | main、cache.New、authz.NewTokenReviewAuthorizer |
| Go 客户端 | clients/go/sandbox/ | sandbox.go、client.go、connector.go |
| Python 客户端 | clients/python/agentic-sandbox-client/k8s_agent_sandbox/ | sandbox.py、sandbox_client.py、connector.py |
本页为入口导航,不含代码走读;各机制的深入讲解见上面 5 章。所有引用 as-of commit 94fe84eac9bfad4a5b3f3d4dac63b7c5c75a2b8b。