Cloudflare Containers 重构:让 Agent 沙箱更快启动、按需选型并可恢复

2026-09-30 20 预计阅读时间: 1 分钟
来源: blog.cloudflare.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

Cloudflare Containers 针对 Agent 沙箱场景完成了一轮关键重构:容器启动速度提升到原来的 6 倍,Agent 可以在运行时选择每个沙箱使用的镜像和实例类型,文件系统快照也进入公开测试阶段。更重要的是,这些能力都可以从 Durable Object 统一控制,让容器不再只是临时计算单元,而是能够被编排、追踪和恢复的 Agent 执行环境。

为什么 Agent 沙箱需要另一种容器模型

传统容器平台通常假设工作负载在部署前就已经确定:镜像写在配置文件里,CPU 和内存规格由运维提前分配,实例启动后持续处理请求。

Agent 的执行模式却更加动态。例如,同一个编程 Agent 可能连续遇到三类任务:

  • 用 Python 分析 CSV,需要数据科学镜像和较多内存;
  • 编译 Rust 项目,需要包含工具链的镜像和更多 CPU;
  • 打开不可信仓库执行测试,需要严格隔离、较短超时和只读凭据。

如果每次任务都等待一个冷启动较慢的固定容器,交互体验会明显变差。6 倍启动速度提升的价值,正体现在这种短生命周期、高频创建的工作负载上。不过,这个数字表示相对改进,并不等于所有镜像、区域和实例类型都具有相同的绝对启动时间,实际接入时仍应测量 P50、P95 和 P99 延迟。

运行时选择镜像和实例类型,则把资源决策从部署阶段移动到了任务调度阶段。调度器可以根据代码语言、预计内存、可信等级和预算,为每次执行选择不同环境,而不必为每种组合部署一套独立服务。

Durable Object 充当沙箱控制器

Durable Object 很适合保存单个 Agent、会话或任务队列的协调状态。可以把它理解成沙箱的控制平面:它记录容器 ID、镜像、实例类型、生命周期和快照引用,并串行处理同一会话的状态变更。

一个典型流程可以设计为:

  1. 客户端向 Durable Object 请求创建沙箱;
  2. Durable Object 校验镜像和实例类型是否在允许列表中;
  3. 它调用容器运行时启动实例,并保存容器引用;
  4. Agent 在沙箱内执行命令或修改工作区;
  5. 长任务在检查点生成文件系统快照;
  6. 任务中断后,新容器从快照恢复工作区。

这里要区分两种状态:

  • 控制状态:任务阶段、容器 ID、租约时间、快照 ID,适合保存在 Durable Object 存储中;
  • 工作状态:仓库、依赖、生成文件和工具缓存,位于容器文件系统中,可通过快照保留。

不要把数据库密码、短期令牌等秘密写入快照。恢复后的容器应该重新获取短期凭据,而不是继承旧会话中的认证材料。

可以这样实现一个策略化入口

下面是一个可直接改造的 TypeScript 控制器示例。代码中的 SandboxRuntime 是对容器 API 的适配层,并非对具体 Cloudflare SDK 方法名的声明;接入时需要用当前平台提供的容器启动、停止和快照接口实现它。

export interface SandboxRuntime {
  start(input: {
    image: string;
    instanceType: string;
    restoreFrom?: string;
    env: Record<string, string>;
  }): Promise<{ containerId: string }>;

  snapshot(containerId: string): Promise<{ snapshotId: string }>;
  stop(containerId: string): Promise<void>;
}

type CreateRequest = {
  taskId: string;
  image: string;
  instanceType: string;
  restoreFrom?: string;
};

const ALLOWED_IMAGES = new Set([
  "registry.example.com/agents/python:3.12",
  "registry.example.com/agents/rust:stable"
]);

const ALLOWED_INSTANCE_TYPES = new Set(["small", "medium", "cpu-optimized"]);

export async function createSandbox(
  runtime: SandboxRuntime,
  input: CreateRequest
) {
  if (!ALLOWED_IMAGES.has(input.image)) {
    throw new Error(`Image is not allowed: ${input.image}`);
  }

  if (!ALLOWED_INSTANCE_TYPES.has(input.instanceType)) {
    throw new Error(`Instance type is not allowed: ${input.instanceType}`);
  }

  const result = await runtime.start({
    image: input.image,
    instanceType: input.instanceType,
    restoreFrom: input.restoreFrom,
    env: {
      TASK_ID: input.taskId,
      SANDBOX_MODE: "isolated"
    }
  });

  return {
    taskId: input.taskId,
    containerId: result.containerId,
    status: "starting"
  };
}

在 Durable Object 的请求处理器中,可以把任务状态和容器引用一起持久化:

export class SandboxCoordinator {
  constructor(
    private readonly state: DurableObjectState,
    private readonly env: Env
  ) {}

  async fetch(request: Request): Promise<Response> {
    if (request.method !== "POST") {
      return new Response("Method not allowed", { status: 405 });
    }

    const input = await request.json<CreateRequest>();
    const sandbox = await createSandbox(this.env.SANDBOX_RUNTIME, input);

    await this.state.storage.put(`task:${input.taskId}`, {
      ...sandbox,
      image: input.image,
      instanceType: input.instanceType,
      createdAt: new Date().toISOString()
    });

    return Response.json(sandbox, { status: 202 });
  }
}

interface Env {
  SANDBOX_RUNTIME: SandboxRuntime;
}

客户端可以通过类似下面的请求显式选择执行环境:

curl -X POST "https://agent.example.com/sandboxes" \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "taskId": "task-2025-001",
    "image": "registry.example.com/agents/python:3.12",
    "instanceType": "medium"
  }'

生产环境不能直接相信 Agent 生成的 image 和 instanceType。更稳妥的做法是让 Agent 选择一个逻辑配置,例如 python-analysis,再由服务端映射到经过签名和扫描的镜像及受限规格:

const PROFILES = {
  "python-analysis": {
    image: "registry.example.com/agents/python:3.12@sha256:REPLACE_ME",
    instanceType: "medium"
  },
  "rust-build": {
    image: "registry.example.com/agents/rust:stable@sha256:REPLACE_ME",
    instanceType: "cpu-optimized"
  }
} as const;

使用镜像摘要而不是浮动标签,可以避免同一个任务配置在不同时间拉取到不同内容。

快照适合检查点,不应被当成无限持久磁盘

文件系统快照进入公开测试后,一个自然用法是为长时间 Agent 任务建立检查点。例如,Agent 完成依赖安装或代码生成后创建快照;容器因超时、故障或资源调整被替换时,再从最近快照继续。

可以把快照触发规则限制在明确的状态边界:

async function checkpoint(
  runtime: SandboxRuntime,
  storage: DurableObjectStorage,
  taskId: string,
  containerId: string
) {
  const { snapshotId } = await runtime.snapshot(containerId);

  await storage.put(`snapshot:${taskId}`, {
    snapshotId,
    containerId,
    createdAt: new Date().toISOString(),
    schemaVersion: 1
  });

  return snapshotId;
}

快照功能仍处于公开测试阶段,因此接入设计应保留降级路径:

  • 关键产物同时上传到对象存储或代码仓库;
  • 为快照设置版本、过期时间和容量上限;
  • 恢复后验证工具链版本和工作区完整性;
  • 快照失败时允许任务从干净镜像重新执行;
  • 不假设正在运行的进程、网络连接或内存状态会被文件系统快照恢复。

上线前值得检查的边界

这次升级降低了创建动态沙箱的成本,但调度自由度越高,策略控制越重要。上线前至少应确认以下事项:

  • 镜像供应链:只允许可信仓库、固定摘要并执行漏洞扫描;
  • 资源上限:限制 Agent 可选择的实例类型、并发数、运行时长和总预算;
  • 网络权限:默认阻止访问内部管理端点和云环境元数据服务;
  • 租户隔离:不同用户的状态、快照和凭据不能复用;
  • 生命周期清理:Durable Object 需要回收超时容器和孤立快照;
  • 可观测性:分别记录排队、镜像准备、容器启动和任务执行耗时;
  • 恢复演练:验证快照不可用时,任务仍能从外部持久化数据重建。

对于代码执行、浏览器自动化和数据分析 Agent,新的 Cloudflare Containers 更接近一个按任务创建的沙箱运行时。合理的采用顺序是:先用固定镜像验证启动延迟和隔离边界,再开放受控的运行时规格选择,最后把快照用于可重试的长任务。这样既能利用更快的启动速度,也不会让动态能力绕过安全与成本治理。


相关推荐