在开发复杂 AI Agent 需要保存运行状态时,为实现沙箱环境的快速复用,可通过 E2B SDK 调用 Checkpoint 功能对运行中容器创建快照并执行克隆,确保文件系统与内存数据一致并降低环境初始化成本。
准备工作
升级
acs-virtual-node组件至 v2.17.0 及以上版本。
使用限制
当前仅支持ACS通用算力使用 Checkpoint 功能。
Pod状态为 Running 且 Ready 之后才可提交 Checkpoint。
同一个Pod,只能同时存在一个运行中的 Checkpoint。Checkpoint 达到终态(成功或失败)后,可再次提交 Checkpoint。
Checkpoint 任务进入 Running 状态后,无法通过删除 Checkpoint 资源来中断任务。
配置 Checkpoint 保留内容
Checkpoint 支持保留以下内容:
文件系统(filesystem):默认保留。
内存(memory):可选保留。
默认情况下,通过 E2B 接口创建的 Checkpoint 将继承原始 Sandbox 的spec.persistentContents字段配置,并自动忽略ip保留配置。
此外,可以通过 SandboxSet 批量控制该模板下所有 Sandbox 的保留内容:
apiVersion: agents.kruise.io/v1alpha1
kind: SandboxSet
metadata:
name: code-interpreter-fs
namespace: default
spec:
replicas: 2
persistentContents:
- filesystem # 仅保留文件系统,不保留内存
...创建沙箱快照并克隆沙箱
E2B SDK方式
创建原始沙箱
以下示例通过两个不同的 SandboxSet 模板(分别配置为“保留内存”和“不保留内存”)演示恢复效果。
部署 SandboxSet,将以下内容保存为YAML文件,然后执行
kubectl apply -f <YAML_FILE>命令。通过 E2B SDK创建沙箱,详细操作请参考创建Agent Sandbox。
# Import the E2B SDK from e2b_code_interpreter import Sandbox # 创建开启内存保留的沙箱 sbx_with_mem = Sandbox.create("code-interpreter-mem") print(f"mem-sandbox id: {sbx_with_mem.sandbox_id}") # 创建仅保留文件系统的沙箱 sbx_no_mem = Sandbox.create("code-interpreter-no-mem") print(f"fs-sandbox id: {sbx_no_mem.sandbox_id}")初始化状态:在沙箱中写入内存变量和文件系统数据。
def init_mem_fs(sbx): sbx.run_code("a = 1") # 写入内存变量 sbx.files.write("/my-file", "hello") # 写入文件 # 验证数据写入成功 print(sbx.run_code("print(a)")) print(sbx.files.read("/my-file")) init_mem_fs(sbx_with_mem) init_mem_fs(sbx_no_mem)
创建 Checkpoint
将以下<YOUR_SANDBOX_XXX_ID>替换成实际沙箱id,为沙箱创建当前状态的快照。
sbx_with_mem = Sandbox.connect("<YOUR_SANDBOX_WITH_MEMORY_ID>")
sbx_no_mem = Sandbox.connect("<YOUR_SANDBOX_WITHOUT_MEMORY_ID>")
snapshot_with_mem = sbx_with_mem.create_snapshot()
snapshot_no_mem = sbx_no_mem.create_snapshot(headers={
"x-e2b-kruise-snapshot-keep-running": "true", # Checkpoint 创建后 Sandbox 是否继续运行。如果是 false,对应 Pod 状态将会变为 Succeeded。默认为 true。
"x-e2b-kruise-snapshot-ttl": "30m", # 创建的 Checkpoint 存在时间,超期后自动删除。默认不配置,除非手动删除,永久存在。
"x-e2b-kruise-snapshot-persistent-contents": "filesystem", # Checkpoint 的保留内容,默认继承 Sandbox 的保留内容。目前只支持 filesystem 与 memory,filesystem 两种组合。
"x-e2b-kruise-snapshot-wait-success-seconds": "60", # 创建 Checkpoint 时,服务端等待其完成的超时时间。默认为 60。
})
print(f"Snapshot ID with memory: {snapshot_with_mem.snapshot_id}")
print(f"Snapshot ID without memory: {snapshot_no_mem.snapshot_id}")
# Checkpoint 创建完成后,可安全删除原始沙箱
sbx_with_mem.kill()
sbx_no_mem.kill()从 Checkpoint 克隆沙箱
克隆时,将上一步返回的
Snapshot ID作为 template 参数传入 create 接口。标准的 timeout、auto_pause 及 CSI 挂载等扩展参数在克隆接口中依然有效。# 使用快照 ID 作为模板创建新沙箱 clone_with_mem = Sandbox.create("<YOUR_SNAPSHOT_WITH_MEMORY_ID>") clone_no_mem = Sandbox.create("<YOUR_SNAPSHOT_WITHOUT_MEMORY_ID>")检查克隆后的沙箱数据,验证恢复效果。
# 验证开启内存保留的克隆 print(clone_with_mem.run_code("print(a)")) print(clone_with_mem.files.read("/my-file")) print(clone_no_mem.run_code("print(a)")) print(clone_no_mem.files.read("/my-file"))预期输出:
Execution(Results: [], Logs: Logs(stdout: ['1\n'], stderr: []), Error: None) hello Execution(Results: [], Logs: Logs(stdout: [], stderr: []), Error: ExecutionError(name='NameError', value="name 'a' is not defined", traceback="---------------------------------------------------------------------------NameError Traceback (most recent call last)Cell In[1], line 3\n 1 import os; os.environ['E2B_SANDBOX'] = 'true'\n----> 3 print(a)\nNameError: name 'a' is not defined")) hello两个克隆沙箱均能正确恢复文件系统中的
/my-file。仅
clone_with_mem成功恢复了内存变量 a。
Sandbox CR方式
创建沙箱
将以下内容保存为sandbox.yaml文件,然后执行kubectl apply -f sandbox.yaml命令。
apiVersion: agents.kruise.io/v1alpha1
kind: Sandbox
metadata:
name: code-demo
spec:
template:
metadata:
labels:
agent: code-demo
# 使用ACS算力
alibabacloud.com/acs: "true"
spec:
automountServiceAccountToken: false
containers:
- name: my-session
image: registry-ap-southeast-1.ack.aliyuncs.com/acs/code-interpreter:v1.6
env:
- name: GODEBUG
value: multipathtcp=0
resources:
requests:
cpu: 1
memory: 1Gi
ephemeral-storage: "30Gi" #声明存储空间为30 GiB
ports:
- containerPort: 49999
name: interpreter创建Checkpoint
通过创建 Checkpoint CR 对目标沙箱创建快照,将以下内容保存为
sandbox-checkpoint.yaml文件,然后执行kubectl apply -f sandbox-checkpoint.yaml命令。apiVersion: agents.kruise.io/v1alpha1 kind: Checkpoint metadata: name: checkpoint-code-demo namespace: default spec: # 目标Pod Name podName: code-demo # 创建 Checkpoint 后,是否要求 Pod 保持 Running 状态。如果配置 false ,Pod状态变为 Succeeded keepRunning: true # Checkpoint 回收时间,系统在 ttlAfterFinished 超时后将自动回收 Checkpoint 资源 # 如果不配置,系统默认不会回收,用户主动删除 Checkpoint CR 时回收底层 checkpoint 资源 ttlAfterFinished: 30h # 格式,例如 30m, 30h, 30d persistentContents: # 目前仅支持 filesystem以及 memory,filesystem 两种组合。默认启用 memory,filesystem - memory - filesystem查看
checkpointId。kubectl get checkpoint checkpoint-code-demo -n default -o jsonpath='{.status.checkpointId}'
克隆新沙箱
替换以下
<CHECKPOINT_ID>为上一步的checkpointId,将以下内容保存为sandbox-clone.yaml文件,然后执行kubectl apply -f sandbox-clone.yaml命令。apiVersion: agents.kruise.io/v1alpha1 kind: Sandbox metadata: name: code-demo-clone spec: template: metadata: labels: agent: code-demo-clone # 使用ACS算力 alibabacloud.com/acs: "true" annotations: # 必须配置此注解,才允许对 Pod 创建 Checkpoint ops.alibabacloud.com/pause-enabled: "true" # 需替换为正确的 Checkpoint ID checkpoint.alibabacloud.com/restore-from: "<CHECKPOINT_ID>" spec: # 克隆沙箱的 spec 需与原始 Pod 的 spec 保持一致 automountServiceAccountToken: false containers: - name: my-session image: registry-ap-southeast-1.ack.aliyuncs.com/acs/code-interpreter:v1.6 env: - name: GODEBUG value: multipathtcp=0 resources: requests: cpu: 1 memory: 1Gi ephemeral-storage: "30Gi" #声明存储空间为30 GiB ports: - containerPort: 49999 name: interpreter查看 Sandbox 资源及对应的 Pod 状态。
kubectl get sandbox/code-demo-clone pod/code-demo-clone -o wide预期输出:
NAME STATUS AGE SHUTDOWN_TIME PAUSE_TIME MESSAGE sandbox.agents.kruise.io/code-demo-clone Running 71m NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod/code-demo-clone 1/1 Running 0 71m 172.16.x.xx virtual-kubelet-cn-hangzhou-h <none> <none>