OpenSandbox 1.1.1:整体架构
本文回答三个问题:OpenSandbox 是什么?内部怎样分层?一次 AI 动作从产生到回收,怎样穿过这套系统? 教材基于
release-1.1.1(提交531700ca,2026-10-09 发布)。路径均相对$OSB_SRC。 用法:先读本文建立总图,再用学习路线安排节奏;每个阶段只深挖这条主线中的一段。
一、先理解问题,再理解项目
跳转到“一、先理解问题,再理解项目”AI 应用一旦从“聊天”走向“做事”,就会遇到一个根本变化:模型输出不再只是文本,而是会变成动作。
动作可能是:
- 运行模型生成的 Python、Shell 或 Node.js 代码;
- 读写工作区文件,修改仓库,生成报告;
- 启动浏览器、桌面或 HTTP 服务;
- 访问包索引、模型 API、数据库或对象存储;
- 长时间运行任务,并在之后恢复上下文。
如果这些动作直接发生在你的服务器上,风险会立刻出现:
- 代码不可信:模型可能写错、死循环、删除文件、耗尽内存。
- 数据不可信:网页、文档、工具结果都可能藏着提示注入。
- 边界不清:一次会话的进程和文件不能污染另一次会话。
- 网络失控:模型可能把数据发往任意地址,或下载不可信依赖。
- 资源失控:并发 Agent 可能同时拉爆 CPU、内存和磁盘。
- 运维困难:任务失败后,要能判断问题出在模型、工具、沙箱还是网络。
OpenSandbox 的定位可以用 README 的一句话概括:
Run AI agents in sandboxes on your own infrastructure.
它不是模型,不是 Agent 框架,也不是一个只能跑代码的在线 IDE。它提供的是 AI 应用与不可信执行之间的基础设施层:用统一 API 创建隔离环境,再通过命令、文件和代码接口,把模型动作安全地放进去执行。
它不是什么
跳转到“它不是什么”初学时有三个误解,必须先排除:
| 误解 | 实际情况 |
|---|---|
| “它替我做 Agent 编排” | 不。它提供执行环境;循环、提示词、模型选择、工具调度仍由你的 Agent 负责。 |
| “它就是一个 Docker 容器” | 不。它是平台:控制面、数据面、网络、运行时、SDK 和运维能力各有边界。 |
| “接口存在,就代表处处可用” | 不。暂停恢复、快照、模板、凭据保险箱等能力依赖后端、配置和版本。 |
记住最后一条:这门课会反复训练你区分“契约里写了”“某个 SDK 包了”“当前后端支持”这三件事。
二、一条主线:一次沙箱任务的一生
跳转到“二、一条主线:一次沙箱任务的一生”整本书只围绕一条主线展开。把它想成一次“外派工作”:应用把一件不可信的活交给一间隔离工作间,干完、取证、交回结果,最后把工作间归还。
模型决定“运行这段代码” │ ▼应用 / Agent │ ① 管理请求:创建、查询、续期、暂停、删除 ▼生命周期服务器(控制面) │ ② 选择运行时,转成真实资源 ▼Docker 容器 / Kubernetes 资源 / FastSandbox 微虚拟机 │ ③ 里面运行用户负载 + execd ▼沙箱 ───────────────────────────────┐ │ ④ 应用先解析端点,再直接访问 │ │ execd 或工作负载自己的服务 │ │ │ │ ⑤ 出站流量经过 egress sidecar │ ▼ ▼结果以 SSE / 文件 / 退出码返回 外部服务 │ ▼状态、日志、指标、审计记录持续产生 │ ▼续期、暂停、快照、销毁:资源有始有终这条主线有三个关键动作,后面每一阶段都在拆其中一段:
- 安排:控制面把“我要一个环境”变成运行时里的真实资源。
- 使用:客户端解析端点后,直接与数据面交互,不把执行流量压回控制面。
- 收尾:TTL、显式销毁、暂停快照和审计记录,让资源不泄漏、过程可解释。
把主线拆成问题,就是全书的十个阶段:
| 主线中的问题 | 对应阶段 |
|---|---|
| 系统能不能先真实跑起来? | 0 |
| 一次任务经过哪些层、哪些 API? | 1 |
| 环境怎样创建、续期、暂停和销毁? | 2 |
| 命令、文件、代码怎样变成可验证的动作? | 3 |
| 同一份契约怎样落到 Docker、Kubernetes、微虚拟机? | 4 |
| 流量怎样进入、出去、被限制,密钥怎样不进沙箱? | 5 |
| SDK、CLI、MCP 与公开契约是什么关系? | 6 |
| 怎样把沙箱接进 Agent 的循环和工具? | 7 |
| 怎样让系统可测试、可观测、可上线? | 8 |
| 能不能独立做出一个完整的受限执行应用? | 9 |
三条贯穿全书的纵线
跳转到“三条贯穿全书的纵线”除了主线,还要一直带着三个问题:
- 证据怎样流动? 状态、SSE 事件、退出码、日志和指标,是应用判断“真的成功了没有”的唯一依据。
- 信任边界在哪里? 用户、模型、工具、沙箱、宿主机、外部网络,各自能控制什么、不能控制什么。
- 能力依赖哪些条件? 看到“支持”时,追问:哪个后端、什么配置、什么版本、什么镜像。
三、八个部件:任何 AI 执行平台都要回答的八件事
跳转到“三、八个部件:任何 AI 执行平台都要回答的八件事”不要先把学习目标定成“记住目录名”。目录会变,下面这套判断框架不会:
| 部件 | 要回答的问题 | OpenSandbox 中的对应 | 阶段 |
|---|---|---|---|
| 入口 | 人和程序通过什么使用平台? | Python/JS/Go/Kotlin/C# SDK、osb CLI、MCP |
6 |
| 契约 | 客户端为什么不被某个后端锁死? | specs/ 下的 OpenAPI |
1、6 |
| 生命周期 | 环境何时创建、何时过期、何时销毁? | server/(FastAPI 控制面) |
2 |
| 运行时 | 环境实际跑在容器、Pod 还是微虚拟机? | Docker、Kubernetes、FastSandbox | 4 |
| 执行 | 模型动作怎样真正发生? | components/execd/ |
3 |
| 网络 | 流量怎样进入、怎样出去、怎样被限制? | endpoint、components/ingress/、components/egress/ |
5 |
| 安全 | 不可信代码和密钥怎样被约束? | 隔离运行时、资源限制、网络策略、Credential Vault、租户 | 4、5、8 |
| 工程 | 怎样证明它可靠、可观测、可演进? | 测试、诊断、指标、发布与供应链验证 | 8 |
一句话串起来:入口发起,契约约束,控制面安排,运行时落地,数据面执行,网络面治理,安全面兜底,工程面证明。
四、分层架构:每一层到底在做什么
跳转到“四、分层架构:每一层到底在做什么”先记住一个总原则:OpenSandbox 把“管理环境”和“使用环境”拆成了两条路径。 换运行时、换客户端或增加网络策略时,只改对应的一层,不必推倒重来。
4.1 先看简化图:安排向下,执行旁路,证据回流
跳转到“4.1 先看简化图:安排向下,执行旁路,证据回流”┌──────────────────────────────────────────────────────────────┐│ 使用层:SDK / CLI / MCP / Agent 框架 ││ 负责发起管理请求、解析端点、消费执行结果 │└───────────────┬──────────────────────────────┬───────────────┘ │ ① 管理请求 │ ③ 执行请求 ▼ ▼┌───────────────────────────────┐ ┌───────────────────────────┐│ 生命周期控制面(server) │ │ 数据面(execd / 工作负载) ││ 认证、校验、状态机、TTL、 │ │ 命令、文件、代码、PTY、 ││ 端点解析、快照元数据 │ │ 指标 │└───────────────┬───────────────┘ └────────────▲──────────────┘ │ ② 选择并驱动运行时 │ ④ 端点由控制面解析 ▼ │┌───────────────────────────────┐ ││ 运行时:Docker / Kubernetes │─────────────────┘│ / FastSandbox │ 真实资源与端点└───────────────┬───────────────┘ │ ▼┌──────────────────────────────────────────────────────────────┐│ 网络与安全:ingress(入站)/ egress sidecar(出站)/ 凭据 │└──────────────────────────────────────────────────────────────┘
证据沿反方向返回:状态、SSE 事件、退出码、日志、指标、审计。看这张图,只需抓住三件事:
- 管理是低频的:创建、查询、续期、暂停、删除,走控制面。
- 执行是高频、长连接的:命令和文件直接走数据面,控制面只负责告诉你“去哪里”。
- 旁边还有一条证据流:状态、事件和日志决定你是否真的成功。
4.2 各层负责什么、不负责什么
跳转到“4.2 各层负责什么、不负责什么”| 层 | 可以把它想成 | 负责 | 不负责 |
|---|---|---|---|
| 使用层 | 前台 | 发起请求、解析端点、展示或转发结果 | 决定资源怎样创建、状态机怎样推进 |
| 契约层 | 合同 | 固定公开字段、端点、错误和状态语义 | 规定某个后端必须怎样实现 |
| 控制面 | 调度中心 | 认证、校验、生命周期、端点解析、快照元数据 | 承载命令流、保存所有沙箱事实 |
| 运行时 | 房东 | 提供容器、Pod 或微虚拟机,维护真实状态 | 定义用户可见 API |
| 数据面 | 工头 | 在沙箱内执行命令、文件和代码,返回流式证据 | 管理沙箱生死、决定网络策略 |
| 网络面 | 门卫与来访通道 | 入站路由、出站策略、凭据注入 | 替代沙箱自身的进程隔离 |
| 工程面 | 质检与档案室 | 测试、诊断、指标、发布验证 | 改变业务语义 |
表格里的“不负责”很重要。分层清不清楚,主要看边界,而不是看名词多不多。
4.3 三对最容易混淆的对象
跳转到“4.3 三对最容易混淆的对象”| 对象对 | 像什么 | 一句话区别 |
|---|---|---|
| 生命周期服务器 vs 运行时 | 调度中心 vs 实际工作间 | 服务器安排环境,运行时拥有环境的真实状态 |
| execd vs 用户负载 | 工头 vs 工人 | execd 提供执行 API,用户负载是真正被运行的程序 |
| 端点 vs 地址 | 前台给的通行证 vs 一扇具体的门 | 端点由服务器按部署拓扑解析,客户端不应自己拼 IP 和端口 |
还有一对概念会在阶段 2 反复出现:
- 沙箱(远端资源):容器、Pod 或微虚拟机,会被创建、暂停、销毁。
Sandbox对象(本地句柄):SDK 里的客户端对象,持有 ID、连接配置、端点缓存和服务适配器。close()关的是它,不一定是远端资源。
4.4 服务器不保存所有沙箱记录,为什么反而更可靠?
跳转到“4.4 服务器不保存所有沙箱记录,为什么反而更可靠?”这是初学者最容易误解的设计。直觉上,“平台应该有一个数据库,记录所有沙箱”。OpenSandbox 的选择相反:
- Docker 容器、Kubernetes 资源、FastSandbox CR 才是运行时事实来源;
- 服务器只持久化自己拥有的东西:快照元数据、模板目录等;
- 查询沙箱时,服务器向运行时读取状态,再转换成公开 API 的状态。
这样做的好处是:运行时故障不会让控制面拥有一份“看起来正确、实际过期”的副本;服务器重启后,也能从运行时重新发现事实。代价是:每次查询都要和运行时交互,状态转换必须处理“运行时暂时不可达”。
4.5 两条贯穿全系统的规则
跳转到“4.5 两条贯穿全系统的规则”- 控制面安排,数据面执行。 创建沙箱和运行命令是两种不同流量;默认路径下,执行不经过生命周期服务器。
- 安全默认关闭,能力显式开启。 API Key、资源上限、默认拒绝的出站策略、访问令牌、强隔离运行时,都需要显式配置;更宽松的模式必须由人开启,而不是默认存在。
这两条规则会在第二、三、五章反复出现,先各记一句即可。
五、一次沙箱任务的完整旅程
跳转到“五、一次沙箱任务的完整旅程”这一章把第二章的图真正走一遍。代码名可以先跳过,只要每一步都能说出“谁把什么交给了谁”。
5.1 八个词,先弄清楚
跳转到“5.1 八个词,先弄清楚”| 术语 | 一句话解释 | 最容易误解的地方 |
|---|---|---|
| Sandbox | 一个隔离的执行环境,可能由容器、Pod 或微虚拟机承载 | 它不是 SDK 里的那个 Python 对象 |
| Lifecycle | 创建、查询、续期、暂停、恢复、销毁这一套管理动作 | 它不等于“执行命令” |
| Endpoint | 某个沙箱端口在当前部署下可访问的地址、协议和请求头 | 它不是固定 IP,也不该由客户端猜 |
| execd | 沙箱内的执行守护进程,提供命令、文件、代码等 API | 它不是用户程序本身 |
| Runtime | 真正创建资源的后端:Docker、Kubernetes、FastSandbox | 它不是 runtime.type 字符串那么简单 |
| TTL | 沙箱的绝对过期时间 | 它不是“最后一次活动后多久” |
| Snapshot | 暂停时保存的可恢复检查点 | 它默认只保存根文件系统,不保存运行内存 |
| Template | FastSandbox 预先构建的不可变工作负载定义 | 它不是每次创建时临时传的镜像参数 |
优先记住 Sandbox、Endpoint、execd、Runtime、TTL 这五个词,后面会反复出现。
5.2 从“模型想做一件事”到“拿到证据”:分阶段看
跳转到“5.2 从“模型想做一件事”到“拿到证据”:分阶段看”阶段 A:应用发起创建
跳转到“阶段 A:应用发起创建”Agent 决定需要一个环境来运行代码。应用调用:
sandbox = await Sandbox.create( "python:3.12", timeout=timedelta(minutes=10), resource={"cpu": "500m", "memory": "512Mi"},)SDK 把它变成 POST /v1/sandboxes,带上镜像、TTL、资源、元数据,可能还有网络策略和凭据代理配置。
阶段 B:控制面做“受理前检查”
跳转到“阶段 B:控制面做“受理前检查””生命周期服务器依次处理:
- 认证:检查
OPEN-SANDBOX-API-KEY(或租户提供方解析出的身份)。 - 校验:镜像、TTL、资源、端口、策略是否符合契约。
- 选择运行时:根据
runtime.type和请求特征,决定交给 Docker、Kubernetes 还是 FastSandbox。 - 提交运行时:创建容器、写入
BatchSandboxCR,或提交模板沙箱请求。 - 返回受理结果:可能是
Pending,也可能已经Running。
关键认识:HTTP 创建是异步的。 服务端不必等镜像拉完、Pod 调度完才返回;SDK 的 create() 之所以看起来同步,是因为它替你轮询了状态、端点发布和健康检查。
阶段 C:运行时把资源真正建出来
跳转到“阶段 C:运行时把资源真正建出来”- Docker:创建容器,注入或启动 execd,映射端口。
- Kubernetes:写入
BatchSandbox,控制器创建 Pod;execd 以 init container 或镜像内置方式存在。 - FastSandbox:根据已构建模板分配预热微虚拟机,FastPath/Fastlet 负责控制与运行。
这一步的产物不是一个抽象对象,而是真实资源:容器、Pod、CR、微虚拟机。你可以用 docker ps 或 kubectl get 看见它。
阶段 D:等待就绪与发布端点
跳转到“阶段 D:等待就绪与发布端点”状态从 Pending 走向 Running。SDK 通常会:
- 轮询
GET /v1/sandboxes/{id},等待状态可用; - 解析
GET /v1/sandboxes/{id}/endpoints/44772,拿到 execd 的地址和请求头; - 请求 execd 的
/ping做健康检查; - 返回一个可用的
Sandbox句柄。
“创建返回”不等于“服务端一步完成了所有工作”,更不等于“业务进程已经准备好”。这三件事要分开看。
阶段 E:解析端点,而不是猜地址
跳转到“阶段 E:解析端点,而不是猜地址”客户端不假设“Docker 就是 localhost:某端口”“Kubernetes 就是 Pod IP”。它调用:
GET /v1/sandboxes/{sandboxId}/endpoints/{port}服务器按当前部署拓扑返回直连地址、网关地址或服务器代理地址,以及必要请求头。地址是结果,不是假设。 这条规则让同一套 SDK 能同时服务 Docker、Kubernetes 和网关部署。
阶段 F:执行命令,边跑边收
跳转到“阶段 F:执行命令,边跑边收”SDK → POST {execdEndpoint}/commandexecd 校验请求,启动进程 → SSE 持续发送 stdout / stderr / result / complete → SDK 聚合成 Execution 对象流式输出意味着客户端不能只等最终 JSON:它要处理分片、行尾、错误事件和连接中断。退出码非零、execd 报错、传输断开,是三种不同的失败。
阶段 G:出站流量经过 egress sidecar
跳转到“阶段 G:出站流量经过 egress sidecar”如果创建时声明了 networkPolicy,平台会给沙箱附加 egress sidecar。它和沙箱共享网络命名空间,所以每个出站包都会经过策略引擎:
- DNS 层按域名允许或拒绝;
dns+nft模式再在包层做默认拒绝;- 匹配的 HTTPS 请求可以由 sidecar 注入凭据,真实密钥不进入沙箱。
没有在创建时声明策略的沙箱,通常没有可事后补上的 sidecar。出站控制是创建时决定的,不是运行中许愿来的。
阶段 H:收尾、暂停或恢复
跳转到“阶段 H:收尾、暂停或恢复”任务完成后,应用应显式销毁:
destroy() → kill()(终止远端) + close()(释放本地连接)如果只是 close(),远端沙箱可能还在,继续占用资源和费用。长任务还可以:
- 用
renew()延长绝对过期时间; - 在支持的后端上
pause(),把根文件系统提交成快照; - 用
resume()用同一个 sandbox ID 恢复,但通常不恢复运行内存。
阶段 I:证据回流
跳转到“阶段 I:证据回流”整个过程持续产生证据:
- 状态:
Pending、Running、Pausing、Paused、Stopping、Terminated、Failed,以及reason/message; - 执行事件:stdout/stderr 分片、结果、完成、错误;
- 文件产物:输出文件、日志、图表;
- 指标:CPU、内存、创建延迟、失败率;
- 审计:谁在什么时候创建了什么、执行了什么、为什么销毁。
好的基础设施不是“永远不失败”,而是“失败时知道为什么、能从哪里查”。
5.3 把全过程压成一张时序图
跳转到“5.3 把全过程压成一张时序图”应用 / Agent │ ├─① Sandbox.create(...) │ └─► POST /v1/sandboxes │ ├─ 认证 / 校验 / 选择 runtime │ ├─ Docker 容器 / BatchSandbox / FastVM │ └─ 202 + sandboxId(可能 Pending) │ ├─② 轮询状态与端点 │ ├─ GET /v1/sandboxes/{id} │ └─ GET /v1/sandboxes/{id}/endpoints/44772 │ └─ { url, protocol, headers } │ ├─③ 健康检查 │ └─ GET {execd}/ping │ ├─④ 执行 │ └─ POST {execd}/command │ └─ SSE:stdout / stderr / result / complete │ ├─⑤ 出站(如有策略) │ └─ 沙箱 → egress sidecar → 外部服务 │ └─⑥ 收尾 ├─ destroy() / kill() / close() ├─ 或 pause() → 快照 → resume() └─ 状态、日志、指标、审计留存一句话记住:控制面把环境安排出来,端点把执行路径指出来,execd 把动作做出来,egress 把边界守住,状态与事件把结果证明出来。
5.4 四个常见误区
跳转到“5.4 四个常见误区”| 容易这样理解 | 更准确的理解 |
|---|---|
“Sandbox.create() 返回了,所以创建是同步的。” |
不。HTTP 创建是异步的;SDK 只是替你等待了端点发布和健康检查。 |
| “执行命令也走生命周期服务器。” | 默认不走。执行走解析出的 execd 端点;服务器代理只是兼容兜底。 |
“close() 会删除沙箱。” |
不会。close() 释放本地连接;kill() 终止远端;destroy() 两者都做。 |
| “沙箱有隔离,所以网络天然安全。” | 不会。隔离限制的是对宿主机的影响;出站外泄仍需 egress 策略和凭据隔离。 |
完整细节见阶段 1(请求旅程与失败点)、《阶段2详解》(生命周期)与《阶段3详解》(执行数据面)。
六、七个关键设计取舍
跳转到“六、七个关键设计取舍”OpenSandbox 的很多设计初看有些“拐弯”,其实都在解决一个具体麻烦。下面每个选择都按同一顺序讲:不这么做会怎样 → 它怎么做 → 你得到什么、要付出什么。
6.1 协议优先,而不是 SDK 优先
跳转到“6.1 协议优先,而不是 SDK 优先”麻烦:五种语言、一个 CLI、一个 MCP 服务器,如果各自定义行为,很快就会出现五套语义、五套 bug。
做法:公开行为先定义在 specs/。SDK 是契约的消费者,不是契约本身。读 SDK 时先问“它最终调用哪个 API”,否则容易被语言封装遮住。
结果:跨语言行为可以对齐,后端可替换。代价是读代码时要分清“生成代码”“手写适配层”和“语言习惯封装”。
6.2 控制面与数据面分离
跳转到“6.2 控制面与数据面分离”麻烦:创建沙箱是低频、慢操作;执行命令可能高频、长连接、持续流式输出。如果都走服务器,控制面会变成带宽和连接瓶颈,故障域也会混在一起。
做法:生命周期 API 只负责安排和解析;执行流量直接走 execd。服务器代理只作为无法直连时的兼容路径。
结果:控制面和数据面可以独立扩容、独立演进、独立排错。代价是客户端必须正确处理端点解析和流式连接。
6.3 生命周期异步化,TTL 是绝对时间
跳转到“6.3 生命周期异步化,TTL 是绝对时间”麻烦:Pod 调度、镜像拉取、微虚拟机启动都可能很慢;如果用“空闲超时”,服务器重启或运行时抖动会悄悄改变沙箱寿命,审计也说不清。
做法:创建先返回 Pending;过期时间是一个绝对时间点。续期语义是“从现在起再给多久”,而不是“在原过期时间上累加”。
结果:慢创建不会占住 HTTP 请求,寿命可从创建时间推算。代价是客户端要学会轮询、等待和续期预算。
6.4 端点解析属于公开契约
跳转到“6.4 端点解析属于公开契约”麻烦:Docker 用端口映射,Kubernetes 用 Pod/Service/网关,FastSandbox 用 FastPath;客户端如果自己拼地址,换一种部署就失效。
做法:GET /sandboxes/{id}/endpoints/{port} 返回地址、协议和必要请求头。客户端原样使用,不自行推断。
结果:同一套 SDK 适配多种拓扑。代价是端点解析本身成为一个需要认证、授权和排错的环节。
6.5 运行时中立,但能力有矩阵
跳转到“6.5 运行时中立,但能力有矩阵”麻烦:如果 API 绑定 Docker,就上不了 Kubernetes;如果假装所有后端完全等价,又会把“接口存在”误写成“能力可用”。
做法:统一的是资源、超时、端点、策略等概念;具体创建 Pod、挂卷、快照、微虚拟机,由后端实现。教材会明确标注能力边界。
结果:同一份契约覆盖单机和集群。代价是开发者必须追问“哪个后端、什么配置、什么版本”。
6.6 安全默认关闭,失败即拒绝
跳转到“6.6 安全默认关闭,失败即拒绝”麻烦:不可信代码一旦拿到网络和密钥,隔离就只剩“保护宿主机”这一半;而“配置错了但看起来在运行”比直接失败更危险。
做法:API Key、资源上限、能力丢弃、no_new_privs、默认拒绝的出站策略、访问令牌、强隔离运行时都需要显式开启。egress sidecar 如果装不上规则,就退出,而不是静默放行。
结果:安全姿态可预期、可审计。代价是初始配置更多,且必须理解每个开关的边界。
6.7 状态和证据必须可解释
跳转到“6.7 状态和证据必须可解释”麻烦:分布式系统一定会失败。如果只返回“创建失败”,用户和运维都无法判断是容量、镜像、权限还是网络问题。
做法:状态不仅有一个词,还有 reason、message、转换时间;执行有错误类型;网络策略失败显式报错;诊断 API 提供日志和事件。
结果:失败可定位、可重试、可审计。代价是客户端要区分“可重试”和“不可重试”,不能把所有错误都当成同一种。
6.8 把七个选择连起来
跳转到“6.8 把七个选择连起来”- 协议优先:五类入口说同一种语言。
- 控制/数据分离:安排和执行各自优化。
- 异步 + 绝对 TTL:慢创建不拖住请求,寿命可审计。
- 端点契约:客户端不猜拓扑。
- 运行时中立:一套 API 覆盖多种基础设施。
- 安全默认拒绝:不可信动作有边界。
- 证据可解释:失败能定位。
它们不是孤立的设计,而是互相配合的:契约定义行为,控制面安排资源,运行时提供隔离,数据面执行动作,网络面治理流量,证据流把所有环节串起来。
七、源码目录:先看边界,再看细节
跳转到“七、源码目录:先看边界,再看细节”| 路径 | 一句话职责 | 建议阅读顺序 |
|---|---|---|
specs/ |
公开协议:生命周期、执行、出站、诊断 | 先看接口和模型名,不背字段 |
server/ |
FastAPI 生命周期控制面 | api/lifecycle.py → services/sandbox_service.py → 具体 runtime |
components/execd/ |
沙箱内执行守护进程 | main.go → pkg/web/router.go → 命令/文件/代码实现 |
components/egress/ |
出站策略、DNS/nft、凭据代理 | 先看策略模式和 credential vault |
components/ingress/ |
Kubernetes 入站反向代理 | 看 header/URI 路由和错误语义 |
components/nodeagent/ |
可选节点代理与审计采集 | 按需读,不是主链路入口 |
kubernetes/ |
Operator、CRD、控制器、任务执行 | BatchSandbox、Pool、SandboxSnapshot |
manifests/charts/ |
Helm charts | 看 base → controller → ingress → server 的顺序 |
sdks/sandbox/python/ |
Python 高层封装 | sandbox.py → services/ → adapters/ → api/ |
sdks/code-interpreter/ |
有状态代码解释器 | 看 context 与 Jupyter 内核 |
sdks/mcp/ |
MCP 工具服务器 | 看工具 schema 与生命周期 |
cli/ |
osb 命令 |
看命令组织与输出格式 |
examples/ |
可运行集成样例 | 按场景挑读 |
tests/ |
跨语言端到端验证 | 学契约驱动测试 |
读源码的三遍法
跳转到“读源码的三遍法”第一遍:只画边界。 找到“谁调用谁”,不进入实现。比如 SDK 的 Sandbox 调服务接口,适配器再调 HTTP 客户端;服务器 API 调服务接口,服务调 Docker/K8s。
第二遍:只追一条主路径。 选“创建沙箱”或“运行命令”,沿调用栈走到底,记录每一层的输入、输出和错误。
第三遍:只问失败。 超时、鉴权失败、端点不可达、命令退出码非零、连接中断分别在哪层处理?这比正常路径更能看出设计质量。
八、版本边界:1.1.1 必须知道的事实
跳转到“八、版本边界:1.1.1 必须知道的事实”教材不会把“接口存在”写成“处处可用”。请记住以下边界:
- Docker 适合本地和单机;Kubernetes 才有 Operator、池化和大规模编排。
- 暂停/恢复和快照依赖后端与配置;不是所有运行时都等价,rootfs 快照默认不保留运行内存。
- 模板(template)依赖 Kubernetes 的 FastSandbox 集成;Docker 参考服务会拒绝模板管理。
- 凭据保险箱依赖 egress sidecar;模板沙箱没有沙箱侧保险箱;暂停后内存中的保险箱状态会丢失,需要重新注入。
- Code Interpreter 需要专用镜像;普通最小镜像不等于支持 Jupyter 代码上下文。
- Windows 本地运行依赖 Docker Desktop 的 Linux 容器和 WSL2;Windows 容器配置是另一条路径。
- 资源池是容量优化,不会把“创建”本身变成免费操作;Pool 里已存在的 Pod 也不能随意注入每请求的网络策略和卷。
- 多租户是 Kubernetes 命名空间隔离,Docker 后端不能把
[tenants]配置当作安全边界。
这些边界不是缺陷,而是基础设施系统正常的“能力矩阵”。优秀的开发者会先确认边界,再写代码。
九、为什么它适合用来学习 AI 应用工程
跳转到“九、为什么它适合用来学习 AI 应用工程”OpenSandbox 把 AI 应用最容易含糊的几个问题都逼到了明面上:
- 模型生成的代码去哪里执行?
- 工具输出怎样流式传给 Agent?
- 会话之间怎样隔离?
- 外部网络怎样限制?
- 密钥怎样不进入沙箱?
- 长任务怎样恢复?
- 创建延迟和成本怎样优化?
- 失败怎样观测和定位?
把这些问题一个个回答清楚,你得到的不只是 OpenSandbox 的知识,而是一套可迁移到任何 Agent 平台的工程判断力。
十、读完本文的自检
跳转到“十、读完本文的自检”先不看资料回答,再对照答案:
-
OpenSandbox 的控制面和数据面分别负责什么? 控制面负责认证、校验、生命周期、端点解析和快照元数据;数据面负责在沙箱里执行命令、文件和代码,并返回流式证据。
-
为什么执行请求默认不经过生命周期服务器? 执行是高频、长连接、数据量大的路径;控制面负责低频管理。分开可以避免控制面成为带宽和连接瓶颈,也让故障域更清楚。
-
创建沙箱为什么可能返回
Pending?SDK 又为什么常常看起来是同步返回? 运行时创建、调度、拉镜像和启动 execd 都可能很慢;HTTP 先受理。SDK 在create()内部轮询状态、端点并做健康检查,所以看起来同步。 -
端点解析为什么必须属于公开契约? 不同部署拓扑的可达地址不同(Docker 端口映射、Pod/Service、网关、代理);只有服务器知道当前拓扑,客户端猜地址会在换部署时失效。
-
举出三个“接口存在但当前后端不一定支持”的能力。 暂停/恢复与快照、模板沙箱、凭据保险箱、Code Interpreter、多租户等,均依赖后端、配置或镜像;任选三个并说明条件即可。
-
close()、kill()、destroy()的区别是什么?close()释放本地连接;kill()终止远端沙箱;destroy()先 kill 再 close。
能答出五个以上,再进入《学习清单》安排节奏。
配套学习路线编写 · 2026-10-30 · 基于 release-1.1.1(531700ca)