Cloudflare Containers 容器类 API 完整指南:路由、启动、通信与生命周期钩子实战
【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills
本篇技术指南以 Cloudflare Containers 的Container 类 API为核心,系统讲解容器 Worker 的类属性配置、getByName()/getRandom()路由模型、start()/startAndWaitForPorts()启动方法、fetch()/containerFetch()/TCP 通信方式,以及onStart/onStop/onError/onActivityExpired生命周期钩子、定时调度与状态检查等完整 API 面。读完本文,你将能直接上手编写、部署并调试一个运行在 Cloudflare Workers 平台上的容器化应用,并避开 WebSocket 静默失败、端口未就绪、活动超时等高频坑点。本文依据仓库中 containers/api.md 及同目录下的配套参考文档展开,并结合 Durable Objects API 进行源码级佐证。
前置说明:Cloudflare Containers 目前处于beta阶段(见 containers/README.md),API 可能随时变更、无 SLA 保证、初始仅限部分区域;自定义实例类型于 2026 年 1 月新增。编写代码时请为 API 变化预留迁移空间,并在生产环境前充分测试。
一、Container 类全景:属性、绑定与生命周期模型
1.1 最小容器类骨架
每个容器都是一个继承自@cloudflare/containers包中Container基类的导出类。以下代码是 api.md 给出的完整类骨架,包含全部可配置属性与可覆写生命周期方法:
import { Container } from "@cloudflare/containers"; export class MyContainer extends Container { defaultPort = 8080; // fetch() 使用的默认端口 requiredPorts = [8080]; // startAndWaitForPorts() 等待就绪的端口列表 sleepAfter = "30m"; // 无活动自动休眠超时 enableInternet = true; // 是否允许出站网络访问 pingEndpoint = "/health"; // 健康检查端点路径 envVars = {}; // 注入容器的环境变量 entrypoint = []; // 覆盖镜像默认入口命令(可选) onStart() { /* 容器进程已启动 */ } onStop() { /* 容器即将停止 */ } onError(error: Error) { /* 容器出错 */ } onActivityExpired(): boolean { /* 超时回调,返回 true 保持存活 */ } async alarm() { /* 定时任务 */ } }1.2 类属性逐一详解
结合 containers/configuration.md 中对属性的官方说明,各属性的行为如下:
| 属性 | 类型/示例 | 行为说明 |
|---|---|---|
defaultPort | 8080 | 调用container.fetch()且未显式指定端口时使用的端口;未设置时回退到端口 33 |
requiredPorts | [8080, 9090] | startAndWaitForPorts()返回前必须处于监听状态的端口数组;若未设置defaultPort,数组首端口将作为默认端口 |
sleepAfter | "5m"、"30m"、"2h" | 无活动后的休眠超时时长字符串;每次请求都会重置该计时器 |
enableInternet | true | 布尔值;为true时容器可发起出站 HTTP/TCP 请求 |
pingEndpoint | "/health" | 健康检查使用的路径,应返回 2xx 状态码 |
envVars | { NODE_ENV: "production" } | 注入容器的环境变量对象,与运行时提供变量合并 |
entrypoint | ["/bin/start.sh"] | 字符串数组,覆写镜像的 CMD/ENTRYPOINT(可选) |
1.3 运行时自动注入的环境变量
除自定义envVars外,Cloudflare 会自动向容器注入以下变量(来自 containers/configuration.md):
| 变量 | 说明 |
|---|---|
CLOUDFLARE_APPLICATION_ID | Worker 应用 ID |
CLOUDFLARE_COUNTRY_A2 | 请求来源的两字母国家代码 |
CLOUDFLARE_LOCATION | Cloudflare 数据中心位置 |
CLOUDFLARE_REGION | 区域标识符 |
CLOUDFLARE_DURABLE_OBJECT_ID | 容器的 Durable Object ID |
自定义envVars与这些运行时变量合并;若命名冲突,自定义变量优先。
1.4 容器即 Durable Object
理解 Container API 的关键前提(见 containers/README.md 的 Core Concepts):每个容器都是一个具有持久身份的 Durable Object,通过getByName(id)或getRandom()访问。这意味着容器的核心行为继承自 Durable Object 语义——例如this.ctx(Durable Object 状态上下文)提供的blockConcurrencyWhile()、存储访问等能力都可用,详见 durable-objects/api.md。
部署层面有三个核心特征必须牢记:
- 镜像预取:镜像会在部署前预取到全球所有位置,因此典型冷启动仅需 2~3 秒;
- 滚动部署:与 Workers 的即时生效不同,容器部署采用滚动策略,旧版本会在新版本上线过程中继续运行;
- 持久身份、临时磁盘:容器 ID 持久保留,但磁盘在停止时重置,持久化数据必须使用 Durable Object 存储(
this.ctx.storage)。
1.5 Wrangler 配置要点
要在wrangler.jsonc(或wrangler.toml)中启用容器,必须同时配置containers数组、Durable Objects 绑定与迁移(迁移必须使用new_sqlite_classes),详见 containers/configuration.md:
{ "name": "my-worker", "main": "src/index.ts", "compatibility_date": "2026-01-10", "containers": [ { "class_name": "MyContainer", // 必须与导出的 Container 类名一致 "image": "./Dockerfile", // Dockerfile 路径或含 Dockerfile 的目录 "instance_type": "standard-1", // 预定义或自定义实例类型 "max_instances": 10 // 最大并发容器实例数 } ], "durable_objects": { "bindings": [ { "name": "MY_CONTAINER", "class_name": "MyContainer" } ] }, "migrations": [ { "tag": "v1", "new_sqlite_classes": ["MyContainer"] } ] }常用实例类型见下表(另有instance_type_custom可自定义 1~4 vCPU、512~12288 MiB 内存、2048~20480 MiB 磁盘,约束为每 vCPU 至少 3 GiB 内存、每 1 GiB 内存至多 2 GB 磁盘):
| 类型 | vCPU | 内存 | 磁盘 |
|---|---|---|---|
| lite | 1/16 | 256 MiB | 2 GB |
| basic | 1/4 | 1 GiB | 4 GB |
| standard-1 | 1/2 | 4 GiB | 8 GB |
| standard-2 | 1 | 6 GiB | 12 GB |
| standard-3 | 2 | 8 GiB | 16 GB |
| standard-4 | 4 | 12 GiB | 20 GB |
二、路由模型:getByName()与getRandom()
容器(作为 Durable Object)通过绑定对象的两个方法路由到具体实例(见 api.md 的 Routing 小节):
getByName(id)—— 按名称取具名实例,用于会话亲和(session affinity)、按用户隔离状态,例如env.MY_CONTAINER.getByName("user-123");getRandom()—— 取随机实例,用于无状态服务的负载均衡,例如env.MY_CONTAINER.getRandom()。
const container = env.MY_CONTAINER.getByName("user-123"); const container = env.MY_CONTAINER.getRandom();containers/README.md 给出了路由决策树,可据此选择策略:
- 同一用户/会话 → 同一容器:
getByName(sessionId),会话亲和; - 无状态、需分摊负载:
getRandom(),负载均衡; - 每个任务一个容器:
getByName(jobId)+ 显式生命周期管理; - 全局单实例:
getByName("singleton")。
注意:容器不支持自动伸缩,负载均衡需通过getRandom()手动实现(见 containers/gotchas.md 的 Beta Caveats)。
三、启动方法:start()、startAndWaitForPorts()与waitForPort()
3.1start()—— 基础启动(8 秒超时)
start()在进程启动时即返回,而非端口就绪时。适合 fire-and-forget 场景:
await container.start(); await container.start({ envVars: { KEY: "value" } });⚠️ 若在start()后立即发起请求,极可能遇到 "connection refused"——因为此时进程已启动但端口尚未监听。这正是 containers/gotchas.md 中 "startAndWaitForPorts() vs start()" 一节强调的坑。
3.2startAndWaitForPorts()—— 推荐方式(20 秒超时)
startAndWaitForPorts()在端口开始监听后才返回,因此是所有 HTTP/TCP 请求前的首选启动方式:
await container.startAndWaitForPorts(); // 使用 requiredPorts await container.startAndWaitForPorts({ ports: [8080, 9090] }); await container.startAndWaitForPorts({ ports: [8080], startOptions: { envVars: { KEY: "value" } } });端口解析优先级(来自 api.md):
显式传入的 ports → requiredPorts → defaultPort → 端口 33即显式传入的ports优先;未传时按requiredPorts→defaultPort→ 33 的次序取默认端口。
3.3waitForPort()—— 等待指定端口
若已启动但需要等待某个特定端口就绪,可单独使用:
await container.waitForPort(8080); await container.waitForPort(8080, { timeout: 30000 }); // 30 秒超时四、通信方式:fetch()、containerFetch()、TCP 与switchPort()
4.1fetch()—— 支持 WebSocket 升级(推荐)
fetch()支持完整 HTTP 语义并支持 WebSocket 升级,可传入Request对象或 URL 字符串:
// ✅ 支持 WebSocket 升级 const response = await container.fetch(request); const response = await container.fetch("http://container/api", { method: "POST", body: JSON.stringify({ data: "value" }) });4.2containerFetch()—— 仅 HTTP,不支持 WebSocket
// ❌ 不支持 WebSocket const response = await container.containerFetch(request);⚠️ 关键警告(api.md 与 containers/gotchas.md 反复强调):containerFetch()不支持 WebSocket 升级,用它转发 WebSocket 请求会导致静默失败(连接建立失败且无报错)。任何 WebSocket 场景一律使用fetch():
// ❌ WRONG return container.containerFetch(request); // ✅ CORRECT return container.fetch(request);4.3 TCP 直连
容器通过ctx.container.getTcpPort()获得 TCP 端口对象,再建立连接并做流式双向转发:
const port = this.ctx.container.getTcpPort(8080); const conn = port.connect(); await conn.opened; if (request.body) await request.body.pipeTo(conn.writable); return new Response(conn.readable);该模式适合非 HTTP 协议(gRPC、数据库协议等)的双向流透传。
4.4switchPort()—— 切换默认端口
switchPort()会改变后续fetch()所使用的默认端口,适合多协议/多端口路由:
this.switchPort(8081); // 后续 fetch() 使用该端口containers/patterns.md 中的多端口路由示例展示了其典型用法:按请求路径分发到不同端口(如/grpc→ 8081、/metrics→ 9090)。
五、生命周期钩子:onStart、onStop、onError、onActivityExpired
所有生命周期钩子都在blockConcurrencyWhile中执行(见 api.md),期间不处理任何并发请求——因此钩子必须保持轻量、避免长时间操作,否则容器会表现为"无响应"(详见 containers/gotchas.md 的 "Lifecycle Hooks Block Requests")。
5.1onStart()—— 容器进程启动时
进程启动时调用(此时端口可能尚未就绪),运行在blockConcurrencyWhile中,期间无并发请求:
onStart() { console.log("Container starting"); }5.2onStop()—— 收到 SIGTERM 时
收到 SIGTERM 时调用;距 SIGKILL 强制终止有 15 分钟宽限期,用于优雅关闭:
onStop() { // 保存状态、关闭连接、冲刷日志 }containers/patterns.md 的优雅关闭示例会在onStop()中主动关闭所有 WebSocket 连接并写入关闭时间戳,同时配合onActivityExpired()在有存活连接时拒绝休眠。
5.3onError()—— 崩溃或启动失败时
容器崩溃或启动失败时调用:
onError(error: Error) { console.error("Container error:", error); }5.4onActivityExpired()——sleepAfter超时时
达到sleepAfter超时阈值时调用;返回true保持存活,返回false允许停止:
onActivityExpired(): boolean { if (this.hasActiveConnections()) return true; // 保持存活 return false; // 允许停止 }典型用法是结合 WebSocket 连接集合做"有连接则不睡"的判断。
六、定时调度:schedule()与alarm()
容器类支持基于 Durable Object Alarm 的定时任务(见 api.md 的 Scheduling 小节)。schedule()是封装的调度助手,alarm()是调度触发时的回调:
export class ScheduledContainer extends Container { async fetch(request: Request) { await this.schedule(Date.now() + 60000); // 1 分钟后 await this.schedule("2026-01-28T00:00:00Z"); // 绝对 ISO 时间 return new Response("Scheduled"); } async alarm() { // 调度触发时调用(SQLite 支撑,重启后依然生效) } }⚠️ 关键警告:使用schedule()助手时不要直接覆写alarm()的实现逻辑来绕过它——因为schedule()内部正是通过 alarm 机制实现的(见 containers/gotchas.md 的 "Don't Override alarm() When Using schedule()")。正确的做法是:用schedule()设定时间点,用alarm()处理到期的任务。
七、状态检查:getState()与ctx.container.running
7.1 外部状态检查 ——getState()
从 Worker 侧(容器外部)查询状态:
const state = await container.getState(); // state.status: "starting" | "running" | "stopping" | "stopped"状态机取值与 README 描述的生命周期(冷启动 → running →sleepAfter超时 → stopped)一致。
7.2 内部状态检查 ——ctx.container.running
在容器类内部用上下文判断:
export class MyContainer extends Container { async fetch(request: Request) { if (this.ctx.container.running) { /* 容器正在运行 */ } } }⚠️ 使用边界:外部检查用getState(),内部检查用ctx.container.running,二者不可互换。
八、实战模式与最佳实践
8.1 路由与 WebSocket 转发模式
以下三个高频模式均来自 containers/patterns.md,可直接落地:
会话亲和(有状态)——用户会话、WebSocket、有状态游戏、按用户缓存:
export class SessionBackend extends Container { defaultPort = 3000; sleepAfter = "30m"; } export default { async fetch(request: Request, env: Env) { const sessionId = request.headers.get("X-Session-ID") || crypto.randomUUID(); const container = env.SESSION_BACKEND.getByName(sessionId); await container.startAndWaitForPorts(); return container.fetch(request); } };WebSocket 转发——必须先startAndWaitForPorts(),再必须用fetch()(而非containerFetch()):
export default { async fetch(request: Request, env: Env) { if (request.headers.get("Upgrade") === "websocket") { const sessionId = request.headers.get("X-Session-ID") || crypto.randomUUID(); const container = env.WS_BACKEND.getByName(sessionId); await container.startAndWaitForPorts(); return container.fetch(request); // ⚠️ MUST use fetch(), not containerFetch() } return new Response("Not a WebSocket request", { status: 400 }); } };并发安全启动——用blockConcurrencyWhile防止并发初始化竞态(这正是 containers/gotchas.md 中 "blockConcurrencyWhile for Startup" 一节的解法):
export class SafeContainer extends Container { private initialized = false; async fetch(request: Request) { await this.ctx.blockConcurrencyWhile(async () => { if (!this.initialized) { await this.startAndWaitForPorts(); this.initialized = true; } }); return super.fetch(request); } }8.2 长任务活动超时续期
sleepAfter基于请求活动而非内部工作计时。长任务期间容器可能被休眠,解法是周期性"触碰"存储来续期(详见 containers/gotchas.md):
export class LongRunningContainer extends Container { sleepAfter = "5m"; async processLongJob(data: unknown) { const interval = setInterval(() => { this.ctx.storage.put("keepalive", Date.now()); }, 60000); try { await this.doLongWork(data); } finally { clearInterval(interval); } } }8.3 与 Workflow、Queue 集成
容器可以无缝接入 Cloudflare Workflows 做多步骤编排(containers/patterns.md):
import { WorkflowEntrypoint } from "cloudflare:workers"; export class ProcessingWorkflow extends WorkflowEntrypoint { async run(event, step) { const container = this.env.PROCESSOR.getByName(event.payload.jobId); await step.do("start", async () => { await container.startAndWaitForPorts(); }); const result = await step.do("process", async () => { return container.fetch("/process", { method: "POST", body: JSON.stringify(event.payload.data) }).then(r => r.json()); }); return result; } }也可作为 Queue 消费者处理异步任务,按msg.ack()/msg.retry()控制消息结果。
8.4 高频报错排查速查
以下错误及解法整理自 containers/gotchas.md 的 Common Errors 一节:
| 错误 | 原因 | 解法 |
|---|---|---|
| "Container start timeout" | start()超 8s /startAndWaitForPorts()超 20s | 优化镜像(更小基础镜像、更少层);核对entrypoint;确认应用监听正确端口;必要时增大超时 |
| "Port not available" | 端口就绪前就调用了fetch() | 改用startAndWaitForPorts() |
| "Container memory exceeded" | 内存超过实例类型上限 | 换更大实例类型(standard-2/3/4)或自定义instance_type_custom;优化内存占用 |
| "Max instances reached" | max_instances槽位占满 | 调大max_instances;设置合理sleepAfter;用getRandom()分摊;排查实例泄漏 |
| "No container instance available" | 达到账户容量上限 | 检查账户限额;复核各容器实例类型;联系 Cloudflare 支持 |
8.5 最佳实践清单
按 containers/gotchas.md 的 Best Practices 汇总:
- 默认使用
startAndWaitForPorts()—— 杜绝端口错误; - 设置合理的
sleepAfter—— 在资源占用与冷启动之间取得平衡; - WebSocket 用
fetch()—— 绝不用containerFetch(); - 为重启而设计—— 磁盘是临时的,实现优雅关闭;
- 监控资源—— 保持在账户限额内(全账户总计:400 GiB 内存 / 100 vCPU / 2 TB 磁盘,镜像存储每账户 50 GB);
- 保持钩子轻量—— 它们运行在
blockConcurrencyWhile中; - 长任务续期活动—— 周期性写存储防止超时休眠。
九、小结
Container 类 API 的要点可以浓缩为一句话心法:路由选实例(getByName/getRandom)、启动等端口(startAndWaitForPorts)、WebSocket 走fetch()、状态持久用 DO 存储、钩子务必轻量。配合 containers/configuration.md 完成 Wrangler 配置、containers/patterns.md 挑选路由模式、containers/gotchas.md 排查故障,即可在 beta 阶段稳定落地容器化应用。由于容器本质是 Durable Object,其底层并发与存储语义可继续深入 durable-objects/api.md 研读。
【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考