镜像和正在运行的 Container 实例如何更新,取决于应用的调度策略。
使用 durable_object 策略时,Wrangler 会为应用准备具名镜像。Durable Object 代码可以访问其不可变引用,并在调用 ctx.container.start() 时选择镜像和实例规格。当镜像引用变化时,Durable Object 代码决定是停止正在运行的 Container 并用新镜像启动,还是让它继续使用先前镜像。这种由应用控制的重启,就是在 durable_object 策略下推出镜像变更的方式。这些实例不参与应用范围的镜像滚动发布。
使用 default 策略时,Wrangler 上传或解析应用镜像。Cloudflare 将该镜像分发到其网络,并为新实例准备容量。对镜像或实例类型的变更会使用滚动发布。
Worker 代码会在部署时立即生效,不必等待任何应用范围的 Container 滚动发布完成。请参阅部署 Containers。
flowchart LR
accTitle: Container instance lifecycle
accDescr: A Worker accesses a container through its Durable Object. The container moves from stopped to starting, running but not ready, ready for traffic, stopping, and stopped.
Worker["Worker"] -->|Durable Object binding| DurableObject["Durable Object"]
DurableObject -->|<code>ctx.container</code>| Container
subgraph Container["Container instance"]
direction TD
Stopped["Stopped"]
Starting["Starting<br/><code>start()</code> called"]
Running["Running<br/>Not ready"]
Ready["Ready<br/>Accepting traffic"]
Stopping["Stopping<br/>Stop requested"]
StoppedAgain["Stopped"]
Stopped --> Starting --> Running --> Ready --> Stopping --> StoppedAgain
end
只能通过其 Durable Object 访问 Container。Worker 将请求发送到 Durable Object,后者通过 ctx.container 访问 Container。
空闲超时、signal()、destroy()、滚动发布或进程退出都可以停止实例。如果启动失败或进程提前退出,实例会回到已停止状态。
ctx.container.running 属性会在进程准备好接受流量之前变为 true。发送第一个请求前,请检查端口就绪。
对于新应用,请通过 Durable Object Container API 管理此生命周期。它让 Durable Object 可以将容器计算与持久状态和警报(alarms)协调起来。现有应用可以使用 Container 类。要比较这两种 API,请参阅 Containers API。
回想一下,Containers 由 Durable Objects 和 Workers 支持。 请求首先通过 Worker 路由,通常由在请求用户之间拥有最佳延迟的位置的数据中心处理。 如果开启了智能放置(Smart Placement),或者最近的位置处于高负载状态,则可能会选择不同的数据中心来优化整体延迟。
由于所有 Container 请求都通过 Worker 传递,终端用户无法向容器实例发出非 HTTP 的 TCP 或 UDP 请求。如果你有需要终端用户入站 TCP 或 UDP 的用例,请让我们知道 ↗。
从 Worker,请求通过一个 Durable Object 实例传递。你可以扩展 DurableObject 并直接使用 ctx.container,或扩展 Container 类。
每个 Durable Object 实例都是一个全局可路由的隔离区,可以执行代码并存储状态。这允许开发人员寻址并路由到特定容器实例、在容器退出时运行代码,以及存储与每个实例关联的持久状态。
当 Durable Object 请求新的容器实例时,Cloudflare 会选择具备所需镜像的合格容量。durable_object 策略使用提供给 ctx.container.start() 的 image 和 instance 选项。default 策略使用 Wrangler 配置中的应用镜像和实例类型。
启动其他容器实例可以使用镜像可用的其他位置。随着需求增长,Cloudflare 会准备额外容量。因为已准备的容量是有限的,某些容器实例可能会在距离终端用户更远的位置启动。你只需为正在运行的实例付费,而不需要为未运行的预热镜像付费。
冷启动是指容器实例从完全停止的状态启动。
如果你使用一个全新的 ID 调用 env.MY_CONTAINER.get(id) 并且首次启动此实例,则会导致冷启动。
这将首次从其入口点启动容器镜像。根据此入口点执行的操作,启动需要不同长度的时间。
容器冷启动通常在 1-3 秒范围内,但这取决于镜像大小和代码执行时间等因素。
当请求启动新的容器实例时,会选择具有预先拉取镜像的最近位置。 之后无论它们源自何处,对该特定实例的后续请求,只要该实例保持活动状态,就会被路由到这个位置。
但是,一旦该容器实例停止并重新启动,将来的请求可能会被路由到一个不同的位置。 这个位置将再次是距离源请求最近的具有预先拉取镜像的位置。
每个容器实例都在带有自己的内核和网络的 Firecracker microVM 中运行。Cloudflare 网络上的其他工作负载不会共享该内核。你的镜像作为 Linux 容器在 VM 内运行。
容器应针对 linux/amd64 架构构建,并应保持在大小限制内。
日志记录、指标收集和网络会根据开发人员的配置自动在每个容器上设置。
使用 Durable Object Container API 时,调用 setInactivityTimeout(),让运行时在 Durable Object 变为空闲后停止容器。Durable Object 在停止接收请求时变为空闲。超时可以在 Durable Object 休眠时保持容器可用。你也可以使用 signal() 或 destroy() 停止容器。
Container 类将 sleepAfter 默认设置为 10 分钟。其 onActivityExpired() 实现会调用 stop()。你可以更改持续时间或覆盖该钩子。
当平台即将停止容器实例时,它会:
- 向容器中的主进程发送
SIGTERM。 - 最多等待 15 分钟让该进程退出。
- 如果进程仍在运行,则发送
SIGKILL。
如果你需要在退出前进行清理,请在镜像中处理 SIGTERM。滚动发布用新镜像替换容器实例时,会运行相同的序列。
Durable Object Container API 提供 monitor()。其 Promise 在容器退出时解析,在容器出错时拒绝。
Container 类会添加钩子,在容器状态改变时运行 Worker 代码:
onStart()— 容器启动后运行。onStop()— 容器进程退出后运行。接收退出码和停止原因。onActivityExpired()— 在sleepAfter计时器到期且没有传入请求时运行。默认实现调用stop()关闭容器。你可以用它仅在特定条件下停止容器。onError()— 在容器启动或端口检查失败时运行。
完整实现请参阅状态钩子示例。
默认情况下,所有磁盘都是临时的。当容器实例进入休眠状态时,下次启动会使用来自容器镜像的全新磁盘。
如果你需要某个时间点的文件系统状态,使用 durable_object 调度策略 的 Container 应用可以创建和恢复快照。快照是不可变的,因此后续文件更改需要新的快照。更多信息请参阅快照。
你还可以使用 FUSE 将磁盘持久化到 R2 或其他对象存储后端。但是,你不应期望在使用 FUSE 时获得原生类 SSD 的性能。
- 开发者部署了一个 Container。Cloudflare 会在其网络中自动准备好实例。
- 来自阿根廷巴里洛切的客户端发出请求。它到达了位于阿根廷内乌肯附近的 Cloudflare 位置的 Worker。
- 此 Worker 请求调用
getContainer(env.MY_CONTAINER, "session-1337")。在底层,这会启动 Durable Object,然后调用this.ctx.container.start。 - 这将请求最近的空闲容器实例。Cloudflare 识别到阿根廷布宜诺斯艾利斯有一个空闲实例,并在那里启动它。
- 不同的用户需要路由到同一个容器。该用户的请求到达了运行在美国圣地亚哥的 Cloudflare 位置的 Worker。
- Worker 再次调用
getContainer(env.MY_CONTAINER, "session-1337")。 - 如果初始容器实例仍在运行,请求将被路由到布宜诺斯艾利斯的原始位置。如果初始容器已进入休眠状态,Cloudflare 将再次尝试查找最近的容器“空闲”实例,可能在北美,并在那里启动实例。