从一次创建请求看完整控制链路
当你调用 sdk.sandbox.create(template_id="my-template") 时,请求穿过了 CubeAPI、CubeMaster、Cubelet 三个组件,最终在某台节点上启动了一个 MicroVM。
这篇文章把这条链路拆开,告诉你每一步该去读哪个源码文件。
1. 总览:创建请求经过了哪些组件
2. 第一站:CubeAPI — 对外兼容层
你要读的文件
| 文件 | 看什么 |
|---|---|
CubeAPI/src/main.rs | CLI 参数、AppState 构建、axum server 启动 |
CubeAPI/src/routes.rs | build_router() → build_e2b_router() + build_cubeapi_router() |
CubeAPI/src/handlers/sandboxes.rs | create_sandbox() handler 入口 |
CubeAPI/src/services/sandboxes.rs | SandboxService::create_sandbox() 字段映射逻辑 |
CubeAPI/src/cubemaster/mod.rs | CubeMasterClient::create_sandbox() HTTP 调用 |
关键函数调用链
main() → Cli::parse() → AppState::new() → build_router() → axum::serve()
↓
POST /sandboxes → sandboxes::create_sandbox()
↓
SandboxService::create_sandbox(body)
↓
body.template_id → annotations["cube.master.appsnapshot.template.id"]
body.timeout → req.Timeout
body.metadata → req.Labels
instance_type → "cubebox" (default)
network_type → "tap" (default)
↓
CubeMasterClient::create_sandbox(&CreateSandboxRequest)
↓
POST {cubemaster_url}/cube/sandbox下一步
→ 继续读 CubeMaster 的 /cube/sandbox handler
3. 第二站:CubeMaster — 控制面大脑
你要读的文件
| 文件 | 看什么 |
|---|---|
CubeMaster/pkg/server/server.go | registerHandlers() 路由注册,cube.SandboxAction → cube.HttpHandler |
CubeMaster/pkg/service/httpservice/cube/sandbox_create.go | createSandbox() → constructCreateReq() → 模板合并 → 亲和性 |
CubeMaster/pkg/service/sandbox/sandbox_run.go | CreateSandbox() → createSandboxContext → newContext() → Handle() → handleCubelet() |
CubeMaster/pkg/scheduler/schedule.go | Select() 五阶段调度 |
CubeMaster/pkg/scheduler/init.go | InitScheduler() 注册 prefilter / filter / score / backoff |
关键函数调用链
cube.HttpHandler → createSandbox(r)
↓
constructCreateReq(r)
↓ 补默认值
labels / annotations 非空
instance_type = "cubebox"
network_type = "tap"
namespace = "default"
↓
dealCubeboxCreateReqWithTemplate(ctx, req)
↓ 合并模板定义中的镜像、rootfs、端口等
runInsReq2Affinity(ctx, req)
↓ 从 annotation / 配置 / 大规格策略构造 NodeSelector
sandbox.CreateSandbox(ctx, req)
↓
createSandboxContext.newContext()
↓ selctx.New() + ConstructCubeletReq() + checkAndGetReqResource()
scheduler.AddBufferTask(createCtx, instanceType)
↓
createCtx.Handle() → handleCubelet()
↓ 循环
schedule() → scheduler.Select(selctx)
↓ prefilter → filter → score → postscore → LeastRandomSelect
callCubelet() → gRPC RunCubeSandbox
↓ 成功: dealSuccResult() / 失败: 重试 or 换节点下一步
→ 继续读 Cubelet 的 RunCubeSandbox 节点侧流程
4. 第三站:Cubelet — 节点执行面
你要读的文件
| 文件 | 看什么 |
|---|---|
Cubelet/services/cubebox/ | 沙箱生命周期管理(create / destroy / update) |
Cubelet/plugins/workflow/ | 工作流引擎,编排节点侧步骤 |
Cubelet/plugins/cube/ | 容器 / MicroVM 创建插件 |
Cubelet/plugins/network/ | 网络准备(TAP / IP / 端口) |
Cubelet/plugins/snapshots/ | 快照恢复 |
关键动作
- 网络准备:借出 TAP 设备、分配 IP、配置端口映射
- 存储准备:挂载 rootfs、准备可写层
- 运行时创建:通过 CubeShim 启动 containerd 任务 → CubeHypervisor 创建 MicroVM
- Agent 启动:guest 内 agent 作为 PID 1 启动,注册 vsock 服务
- 路由注册:将 sandboxID → hostIP:port 映射写入 Redis,供 CubeProxy 使用
5. 完整链路速查表
| 阶段 | 组件 | 关键文件 | 关键函数 |
|---|---|---|---|
| API 入口 | CubeAPI | services/sandboxes.rs | SandboxService::create_sandbox() |
| HTTP 转发 | CubeAPI | cubemaster/mod.rs | CubeMasterClient::create_sandbox() |
| 请求兜底 | CubeMaster | httpservice/cube/sandbox_create.go | constructCreateReq() |
| 模板合并 | CubeMaster | httpservice/cube/sandbox_create.go | dealCubeboxCreateReqWithTemplate() |
| 亲和性 | CubeMaster | service/sandbox/affinityutil.go | runInsReq2Affinity() |
| 调度上下文 | CubeMaster | service/sandbox/sandbox_run.go | newContext() |
| 节点选择 | CubeMaster | scheduler/schedule.go | Select() |
| 下发 Cubelet | CubeMaster | service/sandbox/sandbox_run.go | callCubelet() |
| 节点创建工作流 | Cubelet | plugins/workflow/ | workflow engine |
| 路由注册 | CubeMaster | service/sandbox/sandbox_run.go | setProxyToRedis() |
延伸阅读
- CubeAPI 是怎么接住第一个请求的 — axum 框架与路由
- 创建沙箱时 CubeMaster 做了哪些事 — 控制面全链路
- 调度器到底看什么做决策 — 5 阶段 selector
- 一个沙箱的生命周期 — 架构视角的生命周期概述