Agent Substrate Sandbox Demo 实战指南:在 Kubernetes 上构建可挂起/恢复的有状态沙箱执行环境
【免费下载链接】substrateAgent Substrate: the core system项目地址: https://gitcode.com/GitHub_Trending/substrate7/substrate
本文档深入讲解 Agent Substrate 仓库中的demos/sandbox示例:一个运行在 Agent Substrate 之上、基于 Alpine Linux 的有状态沙箱执行环境。你将掌握如何构建镜像、部署 WorkerPool 与 ActorTemplate、通过kubectl-ate创建 Actor、用 REPL 客户端交互式执行任意命令,并理解 Actor 挂起(suspend)与恢复(resume)背后的状态保持机制。
一、Demo 概述:什么是有状态沙箱
demos/sandbox/README.md将本示例定位为运行在 Agent Substrate 上的有状态(stateful)沙箱执行环境。它的核心能力是:
- 在沙箱化、隔离的容器中运行任意命令,该容器基于 Alpine Linux;
- 在 Actor 挂起(suspend)与恢复(resume)之间完整保留执行状态——你在沙箱里创建的文件、修改的环境,在挂起后重新连接时依然存在。
这种"可挂起、可恢复"的语义来自 Agent Substrate 的 Actor 模型:Actor 占据整个 Worker,其文件系统快照会被持久化到 GCS,从而让"重启后状态不丢失"成为可能。这正是该 Demo 与普通kubectl exec式一次性容器最本质的区别。
[!WARNING]安全声明(原文强制提示):该 Demo不包含任何授权校验,沙箱 Actor 会不加验证地执行客户端传来的任意命令。严禁在生产环境部署或将本 Demo 暴露给不可信网络。它仅用于演示 Agent Substrate 的沙箱与快照能力。
二、组件构成
Demo 由两个组件组成:
- Sandbox Server(demos/sandbox/main.go):运行在 Agent Substrate Actor 内部的应用程序,暴露一个简单的、无状态的
/processHTTP 端点来执行命令; - Sandbox Client(demos/sandbox/client/main.go):一个 CLI REPL 工具,允许你以交互方式与沙箱 Actor 通信。
2.1 Sandbox Server:无状态的/process端点
Server 端代码非常精简,核心逻辑在handleProcess中:
pattern := "/process" http.HandleFunc("POST "+pattern, handleProcess) port := os.Getenv("PORT") if port == "" { port = "80" } log.Printf("Stateless Sandbox serving at port %s, path: %s", port, pattern) log.Fatal(http.ListenAndServe(":"+port, nil))它监听PORT环境变量指定的端口(默认80),只接受POST /process请求。请求体结构如下:
type ProcessRequest struct { Command []string `json:"command"` EnvVars map[string]string `json:"envvars,omitempty"` Cwd string `json:"cwd,omitempty"` Timeout string `json:"timeout,omitempty"` }响应结构:
type ProcessResponse struct { Stdout string `json:"stdout"` Stderr string `json:"stderr"` ExitCode int `json:"exitCode"` Error string `json:"error,omitempty"` }Server 对请求的处理逻辑(demos/sandbox/main.go):
- 命令执行:使用
exec.CommandContext执行Command[0]及后续参数,若命令不存在则ExitCode为-1,正常退出为0,其余情况取进程真实退出码; - 超时控制:若请求携带
Timeout(Go duration 字符串,如"5s"),则通过context.WithTimeout给命令执行加上截止时间; - 工作目录:
Cwd非空时通过cmd.Dir设置; - 环境变量:
EnvVars非空时在os.Environ()基础上追加键值对,因此可以动态注入环境; - 输出收集:stdout 与 stderr 分别以字符串返回,最终以 JSON 编码回写。
值得一提的是,Server 本身是"无状态"的——它不保存任何会话数据,状态的持久化完全由 Agent Substrate 的快照机制承担:Actor 挂起时文件系统被整体快照到 GCS,恢复时再整体还原,因此"执行状态跨挂起/恢复保持"这一能力来自平台而非应用本身。
2.2 Sandbox Client:REPL 交互客户端
Client 端(demos/sandbox/client/main.go)是一个交互式 REPL,启动时接受四个 flag:
| 参数 | 默认值 | 说明 |
|---|---|---|
--name | (必填) | 沙箱 Actor 的名称,如my-sandbox-1 |
--atespace | (必填) | Actor 所在的 Atespace,如ate-demo-sandbox |
--ateapi | localhost:8080 | ateapi gRPC 服务器地址 |
--atenet | localhost:8000 | atenet HTTP 路由服务器地址 |
启动流程(结合源码)分为两步:
- 连接 ateapi 并恢复 Actor:通过 gRPC 调用
cli.ResumeActor(...)把挂起的 Actor 恢复为运行态; - 进入 REPL 输入循环:逐行读取 stdin,把命令 POST 到 atenet 路由器的
/process,打印返回的 stdout / stderr / 错误信息。
退出时的清理动作由defer保证:客户端会在退出前调用cli.SuspendActor(...)挂起 Actor,把当前沙箱文件系统状态快照保存下来——这就是"状态跨挂起保持"的落地实现。
三、前提条件
在开始部署之前,需要准备:
- 一个安装了Agent Substrate的 Kubernetes 集群;
ko已安装,用于构建镜像(参考 hack/install-ate.sh 的构建流程);- 一个用于存储快照的GCS bucket(在 demos/sandbox/sandbox-template.yaml.tmpl 中配置);
kubectl-ateCLI 已安装(可通过go install ./cmd/kubectl-ate安装,安装入口见 cmd/kubectl-ate/main.go)。
四、构建与部署
[!NOTE]不要手动编辑
demos/sandbox/*.yaml.tmpl清单。安装脚本会在部署时自动注入你的${BUCKET_NAME}环境变量(模板中以gs://${BUCKET_NAME}/ate-demo-sandbox/占位)。
使用核心安装脚本构建镜像并把 Demo 部署到集群:
./hack/install-ate.sh --deploy-demo-sandbox从 hack/install-demo-sandbox.sh 的源码可以看到,该命令实际调用deploy_substrate_demo render_demo_manifest,依次完成:
- 构建 Sandbox Server 镜像(基于 Alpine Linux,
ko://github.com/agent-substrate/substrate/demos/sandbox); - 创建
ate-demo-sandbox命名空间和 WorkerPool(清单见 demos/sandbox/sandbox.yaml.tmpl); - 创建
ate-demo-sandboxatespace 及sandbox-templateActor 模板(清单见 demos/sandbox/sandbox-template.yaml.tmpl,通过kubectl ate create actor-template应用); - 等待模板的 golden snapshot 构建完成。
部署完成后,检查模板:
kubectl ate get actor-template sandbox-template -a ate-demo-sandbox4.1 核心清单解读:WorkerPool
demos/sandbox/sandbox.yaml.tmpl 定义了名为sandbox-workerpool的 WorkerPool:
apiVersion: ate.dev/v1alpha1 kind: WorkerPool metadata: name: sandbox-workerpool namespace: ate-demo-sandbox labels: workload: sandbox spec: replicas: 2 workerImage: ko://github.com/agent-substrate/substrate/cmd/ateom-gvisor template: resources: limits: cpu: "2" memory: 2Gi requests: cpu: 500m memory: 2Gi关键点(模板注释与 internal/sizing 的语义一致):
workerImage指向cmd/ateom-gvisor,即 gVisor 沙箱运行时对应的 Worker 实现(同目录还有 microvm 实现,参见 cmd/ateom-microvm);- 调度容量来自 limits,而非 requests:
workerCapacity只读取 limits;而 kube-scheduler 按 requests 装箱,因此 CPU request 刻意设得远低于 limit,让 Worker 池在小节点上也易于调度,同时不改变单个 Actor 可用的资源上限; - 内存不可压缩,所以 memory 的 request 与 limit 保持一致;
- Actor 占据整个 Worker,因此沙箱本身的大小(见下文 ActorTemplate 的
resources)应设置在该池单 Worker 容量之下。
4.2 核心清单解读:ActorTemplate
demos/sandbox/sandbox-template.yaml.tmpl 定义了 Actor 模板(注意:ActorTemplate 不是 CRD,它通过 ate API 以kubectl ate create actor-template创建):
metadata: atespace: ate-demo-sandbox name: sandbox-template workerSelector: matchLabels: workload: sandbox containers: - name: sandbox image: ko://github.com/agent-substrate/substrate/demos/sandbox env: - name: PORT value: "80" resources: limits: - name: cpu quantity: "2" - name: memory quantity: 1Gi snapshotsConfig: onPause: SNAPSHOT_CONTENT_SCOPE_FULL onCommit: SNAPSHOT_CONTENT_SCOPE_FULL storageLocation: gs://${BUCKET_NAME}/ate-demo-sandbox/ sandboxConfig: sandboxClass: SANDBOX_CLASS_GVISOR configName: gvisor-default逐项说明:
workerSelector.matchLabels.workload: sandbox:模板只会被调度到带workload: sandbox标签的 WorkerPool 上,与上文 WorkerPool 的 labels 一一对应;containers[0].image:Sandbox Server 镜像,PORT=80与 Server 端默认端口逻辑一致;resources.limits:沙箱 Actor 本身的资源上限(cpu 2 核、内存 1Gi),需要等于或低于池内单 Worker 容量;snapshotsConfig:onPause与onCommit均为SNAPSHOT_CONTENT_SCOPE_FULL,即挂起与提交时都做全量文件系统快照,快照存放于gs://${BUCKET_NAME}/ate-demo-sandbox/;sandboxConfig:使用 gVisor 沙箱类与gvisor-default配置(集群级 gVisor 配置由 manifests/ate-install/sandboxconfig-gvisor.yaml 提供)。
五、创建沙箱 Actor
在 Demo 的 atespace 中创建一个沙箱 Actor,名称自选(如my-sandbox-1):
# 若尚未安装 CLI,先安装为 kubectl 插件 go install ./cmd/kubectl-ate kubectl ate create actor my-sandbox-1 -a ate-demo-sandbox --template sandbox-template--template指定 Actor 模板名称,模板会在 Actor 所在的 atespace 中解析。
六、端口转发
如果客户端在本地运行,需要分别在两个终端中把 API 与 Router 转发出来:
# 终端 1:API Server kubectl port-forward -n ate-system svc/api 8080:443 # 终端 2:Router kubectl port-forward -n ate-system svc/atenet-router 8000:808080端口对应 ateapi gRPC 服务(客户端用它执行 Actor 的 Resume / Suspend);8000端口对应 atenet HTTP 路由器(客户端把/process请求发往这里)。
七、使用客户端:进入sandbox>REPL
构建并运行客户端:
go build -o bin/sandbox-client ./demos/sandbox/client ./bin/sandbox-client --ateapi=localhost:8080 --atenet=localhost:8000 --atespace=ate-demo-sandbox --name=my-sandbox-17.1 路由原理:ate-target-actor请求头
客户端会把每个/process请求发送到 Router,并自动根据--name与--atespace设置路由头:
req.Header.Set(atenet.TargetActorHeader, actorRef.String())TargetActorHeader定义于 internal/atenet/headers.go,值为ate-target-actor,其格式为<atespace>/<actor>(如ate-demo-sandbox/my-sandbox-1),并通过ParseTargetActor做合法性校验。
任何替代的 HTTP 客户端都必须发送等价的ate-target-actor头;URL 与Host头并不能选中目标 Actor。请求由 Router 依据该头路由到对应沙箱,这一点与常规 HTTP 反向代理有本质区别。
7.2 REPL 交互示例
进入sandbox>提示符后,即可逐条执行命令(每条命令最终以sh -c <command>形式发送):
sandbox> ls -la sandbox> pwd sandbox> echo "Hello" > test.txt sandbox> cat test.txt由于命令实际由sh -c执行(见 demos/sandbox/client/main.go 中的runCommand),支持管道、重定向、变量展开等 shell 特性。
输入exit退出 REPL。退出时会自动触发 Actor 的挂起:客户端在defer中调用 ateapi 的SuspendActor,把沙箱文件系统快照写入 GCS。下次再运行客户端连接同一 Actor 时,会先调用ResumeActor恢复快照——这就是"echo 写入的文件在下次连接后依然存在"的原因。
若要永久删除已挂起的 Actor:
kubectl ate delete actor my-sandbox-1 -a ate-demo-sandbox八、卸载 Demo
从集群中移除沙箱 Demo 的全部资源(Actor、模板、atespace、WorkerPool 与命名空间):
./hack/install-ate.sh --delete-demo-sandbox该命令在 hack/install-demo-sandbox.sh 中对应delete_substrate_demo render_demo_manifest,按清单反向清理ate-demo-sandbox命名空间与sandbox-template。
九、进阶参考:多卷挂载的手动测试清单
仓库还提供了两个手动测试清单,用于验证带卷挂载的沙箱(可作为自定义沙箱模板的起点):
- demos/sandbox/manual-test-multi.yaml:定义
ate-manual-test-multi命名空间与sandbox-poolWorkerPool; - demos/sandbox/manual-test-multi-template.yaml:ActorTemplate 在容器上挂载
vol-hostpath(csi-hostpath-sc)与vol-nfs(csi-nfs-sc)两个外部卷,分别挂载到/hostpath-data与/nfs-data。
这两个清单展示了如何在 ActorTemplate 中声明externalVolumeTemplate,实现沙箱与持久化存储的结合(如 CSI 卷的接入方式可参考 docs/csi-volumes.md)。
十、小结
通过demos/sandbox,你可以完整地走通 Agent Substrate 的核心工作流:WorkerPool 提供调度容量 → ActorTemplate 声明容器、资源与快照策略 →kubectl ate create actor实例化 Actor → REPL 客户端通过ate-target-actor路由头与 Router 交互 → 退出时全量快照挂起、重连时恢复。这套"任意命令执行 + 状态跨挂起保持"的能力组合,为构建更复杂的 Agent 执行环境提供了最小可复用的参考实现。相关架构背景可进一步阅读 docs/architecture.md 与 docs/request-parking.md。
【免费下载链接】substrateAgent Substrate: the core system项目地址: https://gitcode.com/GitHub_Trending/substrate7/substrate
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考