场景
批量创建与回收
本页批量创建 Workspace,程序重启也不会多建;再用 label 找到它们并删除。完整程序是 examples/go/fleet。
前提条件
- Go 1.27 或更新版本,以及 Kibotos 仓库的一份检出。
- ctrl 地址,以及用
kibo enrollment create --operator为这个程序生成的注册密钥(enrollment key),见身份与注册。
步骤
1. 从自己的键派生幂等键
同一个批次与序号总是对应同一条命令。程序中途重启时会再次发送相同的命令,ctrl 重放它们,不会多创建 Workspace:
commandID := kibotos.DeriveCommandID("fleet", "create", batch, strconv.Itoa(index))2. 创建时设置 label 与到期时间
label 用来在之后找到这个批次。平台再也不回来时,到期时间会清理这些 Workspace:
request := &ctrlv1.CreateWorkspaceRequest{
Metadata: &ctrlv1.CommandMetadata{CommandId: commandID},
Spec: &ctrlv1.WorkspaceCreateSpec{
Template: &ctrlv1.TemplateSelector{TemplateId: templateID},
Resources: &ctrlv1.ResourceRequest{CpuMillis: 500, MemoryBytes: 256 << 20},
},
Labels: map[string]string{"example.com/batch": batch},
Expiration: &ctrlv1.WorkspaceExpirationPolicy{
Timeout: durationpb.New(30 * time.Minute),
Action: ctrlv1.WorkspaceExpirationAction_WORKSPACE_EXPIRATION_ACTION_DELETE,
},
}限制同时进行的创建数量。这个程序最多同时创建四个。
3. 处理容量不足
没有 node 放得下时,等待 Workspace 会返回原因 RESOURCE_EXHAUSTED。把任务排队后重试:
_, err = client.WaitWorkspace(ctx, created.Msg.GetWorkspace(), commandID)
if kibotos.Reason(err) == ctrlv1.FailureReason_FAILURE_REASON_RESOURCE_EXHAUSTED {
return fmt.Errorf("no capacity: %w", err)
}4. 用 label 找到批次
逐页列出带这个 label 的 Workspace,平台不需要自己保存 ID 列表:
page, err := client.Workspaces().ListWorkspaces(ctx, connect.NewRequest(&ctrlv1.ListWorkspacesRequest{
LabelSelector: map[string]string{"example.com/batch": batch},
PageToken: token,
}))5. 删除并确认
用列表返回的到期版本删除每个 Workspace,等每次删除完成,再列一次,确认已经没有剩余:
request := &ctrlv1.DeleteWorkspaceRequest{
Metadata: &ctrlv1.CommandMetadata{CommandId: commandID},
WorkspaceId: workspaceID,
ExpectedExpirationRevision: workspace.GetExpiration().GetRevision(),
}6. 运行程序
一个批次名称只能用一次:再次运行会重放已完成的命令,而它们的 Workspace 已经删除。不传 -batch 时,程序用当前时间命名批次:
go run ./examples/go/fleet -endpoint https://ctrl.example.com:18092 \
-identity "$HOME/.local/state/kibotos-fleet" -enrollment-key-file fleet.key \
-batch nightly-2026-11-09 -count 3batch nightly-2026-11-09 has 3 Workspaces
ws-06gvz1hxyxyv3crnhyq9pj6848 WORKSPACE_STATE_AVAILABLE
ws-06gvz1hxz5v3z1466hj4s08ec4 WORKSPACE_STATE_AVAILABLE
ws-06gvz1hxz9yvd3b4w4n86em0km WORKSPACE_STATE_AVAILABLE
reclaimed 3 Workspaces用 E2B SDK
用 metadata 标记 sandbox,按它列出,再逐个删除:
import { Sandbox } from 'e2b';
const batch = 'nightly-2026-11-09';
await Promise.all([0, 1, 2].map((worker) =>
Sandbox.create('kibotos-minimal', {
timeoutMs: 30 * 60_000,
metadata: { batch, worker: String(worker) },
})));
const paginator = Sandbox.list({ query: { metadata: { batch } } });
while (paginator.hasNext) {
for (const { sandboxId } of await paginator.nextItems()) {
await Sandbox.kill(sandboxId);
}
}注意
- 列表不是某一时刻的快照。读取分页期间创建或删除的资源可能出现,也可能不出现,见分页。
- 用同一个幂等键发送不同的请求会返回
already_exists,所以同一个键对应的请求要保持相同。 - 删除 Workspace 时,随它创建的 Volume 也会删除,见删除哪些数据。
下一步
- labels 与 annotations:label 规则与过滤。
- 写操作与错误:幂等键与重试。
- 到期时间:ctrl 怎样执行到期动作。