使用Checkpoint功能克隆Agent Sandbox

更新时间:
复制为 MD 格式

在开发复杂 AI Agent 需要保存运行状态时,为实现沙箱环境的快速复用,可通过E2B SDKCheckpoint自定义资源(CR,Custom Resource)方式,对运行中的容器创建快照,确保文件系统的一致性,降低环境初始化成本。

准备工作

  1. 升级acs-virtual-node组件至 v2.17.0 及以上版本。

  2. 安装相关组件

使用限制

  1. 当前仅支持ACS Agent Sandbox算力使用 Checkpoint 功能。

  2. Pod状态为 Running 且 Ready 之后才可提交 Checkpoint。

  3. 同一个Pod只能同时存在一个运行中的 Checkpoint。Checkpoint 达到终态(成功或失败)后,可再次提交Checkpoint。

  4. Checkpoint 任务进入 Running 状态后,无法通过删除 Checkpoint 资源来中断任务。

配置 Checkpoint 保留内容

Checkpoint 支持保留以下内容:

  • 文件系统(filesystem):默认保留。

默认情况下,若未通过 Checkpoint 接口(E2B SDK)或 CR 显式指定持久化保留的内容,则以 Sandbox 的spec.persistentContents字段配置为准。

继承文件系统保留配置

# 原始 Sandbox 配置
apiVersion: agents.kruise.io/v1alpha1
kind: Sandbox
spec:
  persistentContents:
    - filesystem
  ...
---
# 最终生成的 Checkpoint 配置
apiVersion: agents.kruise.io/v1alpha1
kind: Checkpoint
spec:
  persistentContents: # 自动继承原始配置
    - filesystem
  ...

此外,可以通过 SandboxSet 批量控制该模板下所有 Sandbox 的保留内容:

apiVersion: agents.kruise.io/v1alpha1
kind: SandboxSet
metadata:
  name: code-interpreter-fs
  namespace: default
spec:
  replicas: 2
  persistentContents:
    - filesystem # 仅保留文件系统
  ...

创建沙箱快照并克隆沙箱

E2B SDK方式

创建原始沙箱

以下示例通过 SandboxSet 模板演示恢复效果。

  1. 部署 SandboxSet,将以下内容保存为YAML文件,然后执行kubectl apply -f <YAML_FILE>命令。

    code-interpreter-fs.yaml

    apiVersion: agents.kruise.io/v1alpha1
    kind: SandboxSet
    metadata:
      name: code-interpreter-fs
      namespace: default
    spec:
      # 预热池的大小,建议比预估的请求突发量略大
      replicas: 2
      persistentContents: # 仅保留文件系统
        - filesystem
      template:
        metadata:
          labels:
            # 可选,用于在 ACK 集群中将沙箱 Pod 调度到 ACS
            alibabacloud.com/acs: "true"
            alibabacloud.com/compute-class: agent-sandbox # Agent Sandbox 实例类型
        spec:
          initContainers:
            # 以 Sidecar 形式声明 agent-runtime,自动给沙箱容器注入 envd 等运行时组件
            - name: runtime
              image: registry-cn-zhangjiakou-vpc.ack.aliyuncs.com/acs/agent-runtime:v0.0.5 # 请将 cn-zhangjiakou 替换为实际地域ID 
              command: [ "sh", "/workspace/entrypoint_inner.sh" ]
              volumeMounts:
                # 与主容器的共享目录
                - name: envd-volume
                  mountPath: /mnt/envd
              env:
                - name: ENVD_DIR
                  value: /mnt/envd
                # 这个环境变量使得 Sidecar 共享主容器的资源,不产生额外费用
                - name: __IGNORE_RESOURCE__
                  value: "true"
              restartPolicy: Always
          containers:
          - name: sandbox
            # E2B code-interpreter 镜像,支持全地域、vpc 拉取
            image: registry-cn-zhangjiakou-vpc.ack.aliyuncs.com/acs/code-interpreter:v1.6 # 请将 cn-zhangjiakou 替换为实际地域ID
            imagePullPolicy: IfNotPresent
            # 推荐设置资源需求,否则在 ACS 环境下会被设置成超小规格影响运行
            resources:
              limits:
                cpu: 1
                memory: 1Gi
              requests:
                cpu: 1
                memory: 1Gi
            startupProbe:
              failureThreshold: 10
              httpGet:
                path: /health
                port: 49999
              initialDelaySeconds: 1
              periodSeconds: 2
              timeoutSeconds: 1
            env:
              # 指定 runtime 注入的 envd 组件位置
              - name: ENVD_DIR
                value: /mnt/envd
            volumeMounts:
              # 与 runtime 的共享目录
              - name: envd-volume
                mountPath: /mnt/envd
            lifecycle:
              postStart:
                exec:
                  command: [ "/bin/bash", "-c", "/mnt/envd/envd-run.sh" ]
          # 保证容器快速销毁,提高复用的概率
          terminationGracePeriodSeconds: 1
          volumes:
            - name: envd-volume
              emptyDir: { }
  2. 通过 E2B SDK创建沙箱,详细操作请参考创建Agent Sandbox

    # Import the E2B SDK
    from e2b_code_interpreter import Sandbox
    
    # 创建仅保留文件系统的沙箱
    sbx_with_fs = Sandbox.create("code-interpreter-fs")
    print(f"fs-sandbox id: {sbx_with_fs.sandbox_id}")
  3. 初始化状态:在沙箱中写入文件系统数据。

    def init_mem_fs(sbx):
      sbx.files.write("/my-file", "hello") # 写入文件
      
      # 验证数据写入成功
      print(sbx.files.read("/my-file"))
    
    init_mem_fs(sbx_with_fs)

创建 Checkpoint

将以下<YOUR_SANDBOX_ID>替换成实际沙箱ID,为沙箱创建当前状态的快照。

sbx_with_fs = Sandbox.connect("<YOUR_SANDBOX_ID>")

snapshot_with_fs = sbx_with_fs.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
  "x-e2b-kruise-snapshot-wait-success-seconds": "60",           # 创建 Checkpoint 时,服务端等待其完成的超时时间。默认为 60
})

print(f"Snapshot ID with file system: {snapshot_with_fs.snapshot_id}")

# Checkpoint 创建完成后,可安全删除原始沙箱
sbx_with_fs.kill()

Header参数说明

ack-sandbox-manager 支持通过自定义 Header 扩展 Checkpoint 能力:

Header 参数

说明

默认值

x-e2b-kruise-snapshot-keep-running

Checkpoint 创建后 Sandbox 是否继续运行。若为 false,Pod 状态将变为 Succeeded。

true

x-e2b-kruise-snapshot-ttl

Checkpoint 存活时间,到期自动删除(如 30m)。若不配置则永久存在。

x-e2b-kruise-snapshot-persistent-contents

手动覆盖 Checkpoint 保留内容。仅支持 filesystem。

继承 Sandbox 配置

x-e2b-kruise-snapshot-wait-success-seconds

服务端等待 Checkpoint 完成的超时时间(秒)。

60

从 Checkpoint 克隆沙箱

  1. <YOUR_SNAPSHOT_ID>替换为上一步返回的Snapshot ID,作为 template 参数传入 create 接口进行克隆。标准的timeoutauto_pauseCSI挂载等扩展参数在克隆接口中依然有效。

    # 使用快照 ID 作为模板创建新沙箱
    clone_with_fs = Sandbox.create("<YOUR_SNAPSHOT_ID>")  # E2B 客户端会默认配置 300秒的超时清理时间,可按需配置此超时时长
  2. 检查克隆后的沙箱数据,验证恢复效果。

    # 验证开启文件系统保留的克隆沙箱
    print(clone_with_fs.files.read("/my-file"))

    预期输出:克隆沙箱均能正确恢复文件系统中的 /my-file

    hello

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"
        alibabacloud.com/compute-class: agent-sandbox # Agent Sandbox 实例类型
    spec:
      automountServiceAccountToken: false
      initContainers:
        # 以 Sidecar 形式声明 agent-runtime,自动给沙箱容器注入 envd 等运行时组件
        - name: runtime
          image: registry-cn-zhangjiakou-vpc.ack.aliyuncs.com/acs/agent-runtime:v0.0.5 # 请将 cn-zhangjiakou 替换为实际地域ID
          command: [ "sh", "/workspace/entrypoint_inner.sh" ]
          volumeMounts:
            # 与主容器的共享目录
            - name: envd-volume
              mountPath: /mnt/envd
          env:
            - name: ENVD_DIR
              value: /mnt/envd
            # 这个环境变量使得 Sidecar 共享主容器的资源,不产生额外费用
            - name: __IGNORE_RESOURCE__
              value: "true"
          restartPolicy: Always
      containers:
      - name: my-session
        image: registry-ap-southeast-1.ack.aliyuncs.com/acs/code-interpreter:v1.6
        env:
        - name: GODEBUG
          value: multipathtcp=0
        # 指定 runtime 注入的 envd 组件位置
        - name: ENVD_DIR
          value: /mnt/envd
        resources:
          requests:
            cpu: 1
            memory: 1Gi
            ephemeral-storage: "30Gi" #声明临时存储空间为30 GiB
        ports:
        - containerPort: 49999
          name: interpreter
        volumeMounts:
        # 与 runtime 的共享目录
        - name: envd-volume
          mountPath: /mnt/envd
        lifecycle:
          postStart:
            exec:
              command: [ "/bin/bash", "-c", "/mnt/envd/envd-run.sh" ]
      terminationGracePeriodSeconds: 1
      volumes:
        - name: envd-volume
          emptyDir: { }

创建Checkpoint

  1. 通过创建 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
      - filesystem
  2. 查看checkpointId

    kubectl get checkpoint checkpoint-code-demo -n default -o jsonpath='{.status.checkpointId}'

克隆新沙箱

  1. 替换以下<YOUR_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"
            alibabacloud.com/compute-class: agent-sandbox # Agent Sandbox 实例类型
          annotations:
            # 需替换为正确的 Checkpoint ID
            checkpoint.alibabacloud.com/restore-from: "<YOUR_CHECKPOINT_ID>"
        spec: # 克隆沙箱的 spec 需与原始 Pod 的 spec 保持一致
          automountServiceAccountToken: false
          initContainers:
            # 以 Sidecar 形式声明 agent-runtime,自动给沙箱容器注入 envd 等运行时组件
            - name: runtime
              image: registry-cn-zhangjiakou-vpc.ack.aliyuncs.com/acs/agent-runtime:v0.0.5 # 请将 cn-zhangjiakou 替换为实际地域ID
              command: [ "sh", "/workspace/entrypoint_inner.sh" ]
              volumeMounts:
                # 与主容器的共享目录
                - name: envd-volume
                  mountPath: /mnt/envd
              env:
                - name: ENVD_DIR
                  value: /mnt/envd
                # 这个环境变量使得 Sidecar 共享主容器的资源,不产生额外费用
                - name: __IGNORE_RESOURCE__
                  value: "true"
              restartPolicy: Always
          containers:
          - name: my-session
            image: registry-ap-southeast-1.ack.aliyuncs.com/acs/code-interpreter:v1.6
            env:
            - name: GODEBUG
              value: multipathtcp=0
            # 指定 runtime 注入的 envd 组件位置
            - name: ENVD_DIR
              value: /mnt/envd
            resources:
              requests:
                cpu: 1
                memory: 1Gi
                ephemeral-storage: "30Gi" #声明临时存储空间为30 GiB
            ports:
            - containerPort: 49999
              name: interpreter
            volumeMounts:
            # 与 runtime 的共享目录
            - name: envd-volume
              mountPath: /mnt/envd
            lifecycle:
              postStart:
                exec:
                  command: [ "/bin/bash", "-c", "/mnt/envd/envd-run.sh" ]
          terminationGracePeriodSeconds: 1
          volumes:
            - name: envd-volume
              emptyDir: { }
  2. 查看 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>

相关文档