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、镜像、实例类型、生命周期和快照引用,并串行处理同一会话的状态变更。
一个典型流程可以设计为:
- 客户端向 Durable Object 请求创建沙箱;
- Durable Object 校验镜像和实例类型是否在允许列表中;
- 它调用容器运行时启动实例,并保存容器引用;
- Agent 在沙箱内执行命令或修改工作区;
- 长任务在检查点生成文件系统快照;
- 任务中断后,新容器从快照恢复工作区。
这里要区分两种状态:
- 控制状态:任务阶段、容器 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 更接近一个按任务创建的沙箱运行时。合理的采用顺序是:先用固定镜像验证启动延迟和隔离边界,再开放受控的运行时规格选择,最后把快照用于可重试的长任务。这样既能利用更快的启动速度,也不会让动态能力绕过安全与成本治理。