computer 项目中的容器启动规格ContainerLaunchSpeclaunched / adopted / relaunched 三态语义解析【免费下载链接】computerGive your agent a computer 项目地址: https://gitcode.com/GitHub_Trending/computer1/computer导读本篇文章聚焦cloudflare/computerGive your agent a computer 容器后端的一次重要接口演进IWorkspaceContainerAPI.start()与restart()从两个松散参数收敛为单一ContainerLaunchSpec对象并为每次启动留下可持久化的启动记录最终返回launched、adopted、relaunched三种明确结果。读完本文你将掌握该启动规格的数据结构、启动记录的生成与比对原理、容器采纳adopt与重启relaunch的判定规则以及预热池warm pool这类预启动场景如何借助setInactivityTimeout()不再绕过接口直接触碰ctx.container。该变更记录于仓库的 .changeset/container-launch-spec.md属于cloudflare/computer包的 minor 版本更新其完整实现与测试位于 packages/computer/src/backends/container/ 目录。一、变更概览从双参数到单一规格在旧接口中启动容器需要分别传递两个独立的参数环境变量集合与互联网开关。新接口将二者合并为一个结构化对象签名统一为start(spec: ContainerLaunchSpec): PromiseContainerRuntimeInfo; restart(spec: ContainerLaunchSpec): PromiseContainerRuntimeInfo;变更点可以归纳为四条单一参数start()与restart()只接收一个ContainerLaunchSpec不再接收两个参数三态返回值调用结果明确区分为launched新启动、adopted采纳已运行且规格一致的容器、relaunched替换掉规格不匹配的旧容器启动留痕每次启动都会把规格记录到 Durable Object 存储中供后续采纳判定使用接口补齐setInactivityTimeout()正式加入IWorkspaceContainerAPI接口预热池等预启动容器的调用者无需再绕过接口去访问ctx.container。二、ContainerLaunchSpec 数据结构ContainerLaunchSpec定义在 packages/computer/src/backends/container/container-launch-record.tsexport interface ContainerLaunchSpec { // Environment for the container image. The launch adds // RPC_CLIENT_SECRET on top, so no caller needs to know it exists and // it stays out of the digest below. env: Recordstring, string; // Platform switch for outbound internet. Cannot be changed on a live // container, which is why a mismatch has to relaunch. enableInternet: boolean; }两个字段各有关键语义env传给容器镜像的环境变量。注意启动流程会在其上自动追加RPC_CLIENT_SECRET见下文客户端密钥注入调用方无需感知它的存在且该密钥不会进入规格摘要避免不必要的敏感信息扩散。enableInternet出站互联网的平台级开关。它只能在容器进程启动时设定运行中的容器无法修改——这是整个规格不匹配就必须重启设计的根源。三、三态结果launched / adopted / relaunchedContainerRuntimeInfo定义于 container-host.ts携带runtimeId、clientSecret与outcome三个字段其中outcome就是本次调用的实际动作outcome含义触发条件launched本次调用真实启动了新容器上一代容器已退出或当前没有运行中的容器或执行了restart()adopted复用了已运行的容器容器已在运行且其启动记录与本次请求的规格完全一致relaunched替换了旧容器并重新启动容器已在运行但启动记录缺失或与本次规格不一致返回runtimeId是运行中容器进程的持久标识——即使 Durable Object 实例被重建只要容器进程仍存活同一容器会沿用同一runtimeId见 container-host.ts 中对CurrentContainerRuntimeIdentity的复用逻辑。四、启动记录规格的持久化摘要为什么需要启动记录因为在容器已运行的情况下Durable Object 无法事后得知它当初是用什么环境、什么互联网开关启动的。而采纳adopt一个规格不一致的容器意味着悄悄丢弃调用方想要的环境与网络策略。于是每次启动都写一条记录采纳时做比对。记录结构同样定义在 container-launch-record.tsexport interface ContainerLaunchRecord { enableInternet: boolean; // A digest rather than the environment itself: containerEnv is // consumer-supplied and may carry their own secrets, and this record // only ever needs to answer the same or not. envDigest: string; }关键设计是记录摘要而非环境明文防泄密env由调用方提供可能携带调用方自己的密钥如API_TOKEN落盘只存摘要只回答一个布尔问题记录的唯一用途是判断相同或不同sameLaunch摘要足以胜任。摘要算法digestEnv见 container-launch-record.ts有两处工程细节键排序先对键排序再以长度:键长度:值形式拼接保证两个以不同书写顺序构造的相同环境得到相同的规范字符串测试 container-launch-record.test.ts 验证了这一点长度前缀每个键和值都带长度前缀避免键值拼接出现歧义杜绝不同组合被重排后得到相同输入最终以 SHA-256 计算摘要输出 64 位十六进制字符串。比对函数sameLaunch同时比较互联网开关与摘要export function sameLaunch(a: ContainerLaunchRecord, b: ContainerLaunchRecord): boolean { return a.enableInternet b.enableInternet a.envDigest b.envDigest; }测试覆盖了值变化变量新增互联网开关切换三类不匹配场景以及摘要中不出现明文密钥见 container-launch-record.test.ts。五、start() 的分支逻辑为何不匹配必须 relaunchWorkspaceContainerAPI.start()的完整判定流程见 container-host.ts核心顺序为读取上一代退出信息若容器曾异常退出priorExit ! null先尽力destroy清理平台侧残留然后无条件以launched启动新一代容器未运行直接以launched启动容器正在运行读取启动记录记录存在且sameLaunch(actual, requested)为真 →adopted复用runtimeId记录为null容器由接口之外的代码启动或记录不匹配 → 记录告警日志destroy旧容器后以relaunched重启。第二步的告警日志区分了两种原因container-host.tsactual nullcontainer was started outside WorkspaceContainerAPI; relaunching so the requested environment applies容器由接口外部启动重启以应用请求的环境记录不匹配running container was launched with a different spec; relaunching运行中的容器使用了不同规格正在重启。对外部启动容器的处理是变更中特别强调的安全语义一个没有启动记录的容器宁可重启也不信任——因为它无法证明环境与网络策略符合调用方要求。六、restart() 与 setInactivityTimeout()restart()无条件的新一代restart()container-host.ts比start()更简单直接先destroy当前容器尽力而为容忍平台侧抖动再无条件以launched启动新一代。它被用于两类场景启动就绪失败容器端口迟迟未打开、健康探测无法通过租期健康检查判死当前容器代已被判定为死亡。值得注意的是restart()自身不做重试循环重试次数由调用方即CloudflareContainerBackend控制。setInactivityTimeout()接口化的闲置超时setInactivityTimeout(durationMs: number): Promisevoid;该方法container-host.ts直接透传平台容器 API。它被显式加入IWorkspaceContainerAPI接口动机在源码注释中写得很清楚预热池等预启动容器的调用者无需再绕过接口去访问ctx.container。预热池的典型做法是先启动容器再设置闲置超时容器空闲到指定毫秒数后由平台回收例如 examples/think-compare-runtimes/worker/computer-container-pool.ts 中的startWarmContainerasync startWarmContainer(spec: ContainerLaunchSpec, inactivityTimeoutMs: number): Promisevoid { // Through the workspace API rather than ctx.container, so the launch // carries whatever the API adds — today the shared secret the // daemons HTTP surface requires — and is recorded, so the workspace // that adopts this container can tell it matches. await startWorkspaceContainerAndWait(this.getWorkspaceContainer(), spec, inactivityTimeoutMs); }预热池通过getWorkspaceContainer()获得WorkspaceContainerAPI再调用启动既让启动记录得以写入后续 Workspace 采纳时可判定匹配也保持了接口的完整封装。七、调用方视角CloudflareContainerBackend 如何消费新签名CloudflareContainerBackend是WorkspaceContainerAPI的主要驱动者cloudflare-container.ts它构造规格的方式展示了新签名的实际用法const env { PORT: String(this.#options.containerPort), MOUNT_POINT: /workspace, ...this.#options.containerEnv, }; let runtimeId: string; let clientSecret: string; try { ({ runtimeId, clientSecret } await host.start({ env, enableInternet: this.#egress.mode direct, })); } catch (error) { // ...包装为 WorkspaceTransportError }要点env由后端默认值PORT、MOUNT_POINT与调用方提供的containerEnv合并而成调用方值优先enableInternet直接来自 egress 策略mode direct时才开启解构返回值中的runtimeId与clientSecretoutcome在后端内部用于诊断日志。在就绪重试路径#readyWithRestartscloudflare-container.ts中重启同样携带完整规格({ runtimeId } await host.restart({ env, enableInternet: this.#egress.mode direct, }));八、预热池的采纳场景为什么规格必须一致预热池warm pool是本变更最直接的使用场景之一。池子会预先启动一批容器等待 Workspace 采纳而采纳方可能要求不同的环境、甚至完全关闭互联网。若直接复用规格不一致的预热容器采纳方的安全与运行要求就会被静默违背。examples/think-compare-runtimes/worker/computer-container-pool.ts 中workspaceLaunchSpec的注释点明了这一权衡function workspaceLaunchSpec(env: WorkspacePoolEnv): ContainerLaunchSpec { return { env: { PORT: String(WORKSPACE_PORT), MOUNT_POINT: /workspace, ...(env.FUSE_MOUNT ? { FUSE_MOUNT: env.FUSE_MOUNT } : {}), }, // A pool cannot know the egress policy of the workspace that will // adopt a container, so this has to agree with it by configuration. // Disagreeing costs a relaunch on adoption, not the policy: the // adopting workspace compares this spec against its own and // replaces the container rather than inheriting the wrong one. enableInternet: true, }; }池子无法预知将来哪个 Workspace 会采纳容器因此出站策略只能通过配置对齐一旦不一致代价是采纳时的一次 relaunch而绝不会让采纳方继承错误的网络策略——这正是规格记录 比对机制的价值所在。九、客户端密钥注入规格之外的一次自动合并在#launchAs内部container-host.ts实际传递给平台容器 API 的环境变量会在用户规格之上追加共享密钥this.#container.start({ enableInternet: spec.enableInternet, env: { ...spec.env, RPC_CLIENT_SECRET: clientSecret }, });clientSecret通过ContainerClientSecret.ensure()在任何启动之前解析保证写入容器环境的值与后续化身incarnation读回并展示的值一致。而该密钥不参与摘要——调用方无需知晓其存在也不会因它的存在导致规格比对失真。此外启动记录在启动被平台接受之后才写入失败的启动不会留下容器持有该规格的虚假记录。该密钥还承担鉴权职责容器 HTTP 面要求 Bearer 认证bearerMatches采用逐字节比较、不提前退出的恒定时间风格实现见 cloudflare-container.ts#requireAuthEnforced还会在握手前确认容器确实在强制校验cloudflare-container.ts。十、测试验证规格比对的正确性边界container-launch-record.test.ts 为本文涉及的机制提供了可复现的验证矩阵键序无关性{ A: 1, B: 2 }与{ B: 2, A: 1 }产生相同摘要值变化检测FUSE_MOUNT从auto变为none即判定不匹配变量新增检测新增一个EXTRA变量即判定不匹配互联网开关区分enableInternet的true/false判定为不匹配对应 egress 场景不落明文含API_TOKEN: hunter2的规格序列化记录中不出现明文envDigest匹配^[0-9a-f]{64}$记录读写往返CurrentContainerLaunchRecord能正确 round-trip外部启动场景仅有 runtime identity、无启动记录时get()返回null从而触发 relaunch 而非信任。容器主机的采纳/重启行为另有 container-host-adoption.test.ts 与 cloudflare-container.test.ts 覆盖。十一、迁移与使用要点对于使用cloudflare/computer/backends/container的开发者本次 minor 变更的影响与建议如下签名迁移所有host.start(env, enableInternet)形式的调用改为host.start({ env, enableInternet })restart()同理消费三态结果ContainerRuntimeInfo.outcome可用于观测与告警——高频出现relaunched通常意味着预热池与采纳方的规格配置存在分歧值得排查预热池对齐规格如 computer-container-pool.ts 所示预热规格中的env与enableInternet应与可能采纳它的 Workspace 保持一致避免采纳时频繁 relaunch走接口而非绕过预启动容器时请使用getWorkspaceContainer()返回的WorkspaceContainerAPI并调用setInactivityTimeout()这样启动会留下记录且后续采纳方能正确判定匹配接口导出ContainerLaunchSpec、IWorkspaceContainerAPI、WorkspaceContainerAPI、ContainerRuntimeInfo、WorkspaceRef、withWorkspaceContainer均从 packages/computer/src/backends/container/index.ts 导出可通过import { CloudflareContainerBackend, withWorkspaceContainer } from cloudflare/computer/backends/container引入。结语ContainerLaunchSpec的引入并非一次简单的参数收敛它以规格记录 摘要比对为支点把容器已运行时环境与网络策略不可变的平台约束转化成了清晰的三态结果与可观测的 relaunch 语义。对于容器化 Agent 运行时中常见的预热池、跨 DO 采纳、egress 策略切换等场景这一设计让复用与安全的边界变得明确——复用必须建立在规格可证明一致的前提之上否则宁可重启也不静默违背调用方的意图。【免费下载链接】computerGive your agent a computer 项目地址: https://gitcode.com/GitHub_Trending/computer1/computer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
