多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Cloudflare Computer反向拨号启动序列:容器为何要穿透egress回连Durable Object

Cloudflare Computer反向拨号启动序列:容器为何要穿透egress回连Durable Object Cloudflare Computer反向拨号启动序列容器为何要穿透egress回连Durable Object【免费下载链接】computerGive your agent a computer 项目地址: https://gitcode.com/GitHub_Trending/computer1/computerCloudflare Computer项目口号Give your agent a computer 是一个运行在 Cloudflare Workers 平台上的「给 AI Agent 一台真电脑」的开源方案。它的容器后端在启动时并不由 Durable Object 主动连进容器而是让容器内的 computerd 守护进程穿透 egress 出网表、反向拨号回连 Durable Object再完成 WebSocket 升级建好控制通道。本文带你完整走一遍这条反向拨号启动序列并解释它为什么不得不这样做。一、先看整体架构控制面在 DO数据面在容器Cloudflare Computer 的经典容器形态由两半组成Durable ObjectDO持有 Workspace负责命令调度、SQLite 持久化存储是「大脑」容器跑着 computerd 守护进程 FUSE 挂载提供真正的文件系统与执行环境是「身体」。上图来自 docs/assets/arch.png容器里 Node 虚拟文件系统经由 FUSE 挂载与 DO 侧的 Node Virtual FS 通过同步协议保持一致SQLite Storage 是文件系统的真相源。注意图中那条从 DO 指向容器的双向通道——它就是本文主角反向拨号建立的 WebSocket。问题在于Cloudflare 容器没有入站公网地址无法被外部主动连接。DO 想控制容器就必须换一种思路——让容器「主动打回来」。二、反向拨号启动序列6 步完整拆解启动序列的核心逻辑在 cloudflare-container.ts 的connect()方法中容器侧的应答逻辑在 computerd.ts。第 1 步DO 拉起容器computerd 监听 8080 端口DO 调用host.start(env, enableInternet)注入环境变量PORT8080、MOUNT_POINT/workspace。容器内的 computerd 启动后在 8080 端口等待指令。启动是幂等的若上一代容器已退出会先 destroy 再重新拉起保证拿到一个全新世代。第 2 步写入 egress 出网拦截表埋下「回拨入口」关键一步DO 调用interceptOutboundHttp(computer.internal, workspaceRef)见 container-host.ts。它在容器的出网表里登记一条规则——凡是发往内部域名computer.internal的 HTTP 请求一律重定向到 WorkspaceProxy 回环 Fetcher最终落到 DO 自己的 fetch 处理器。computer.internal并不是真实 DNS 域名只在容器 egress 表内有效外界无法解析天然是一道隔离墙。回环代理的实现见 proxy.ts它只应答两类路径/health存活确认和/wsWebSocket 升级。第 3 步DO 探测容器健康直到 computerd 就绪DO 通过fetchPort(8080, /health)向容器内直连发 HEAD 请求用指数退避250ms 起步、翻倍、上限 2s反复探测逻辑封装在 health-probe.ts。这一步确认的是「computerd 进程真正在监听」而不只是「平台说容器已启动」。第 4 步先架好「升级承诺」再 POST /connect一个精巧的细节DO 在 POST 之前就先#armUpgrade()注册好等待/ws升级到达的 Promise。因为 computerd 回拨速度可能快于 POST 返回——代码注释明确写着「upgrade can arrive before the POST resolves」。随后 DO 向容器 8080 端口发送POST /connect请求体只有两个字段回拨地址url: http://computer.internal和剩余健康等待预算healthTimeoutMs。第 5 步computerd 验证 egress 通路后反向拨号回连computerd 收到/connect后并不立即连接它先对computer.internal/health做等待探测确认 egress 拦截表与 WorkspaceProxy 通路已生效然后发起ws://computer.internal/ws反向拨号。这个请求沿拦截规则回到 DODO 的 fetch 处理器将其路由给 backend 的handleFetch。第 6 步WebSocket 升级capnweb RPC 会话成型DO 侧用WebSocketPair拆出双端accept 服务端向容器返回101 Switching Protocols随后基于这条 WebSocket 建立 capnweb RPC 会话——此后所有的 shell 执行、文件读写、同步水位watermarks调用都跑在这条由容器主动拨入的双向通道上。三、为什么必须「反向」三个根本原因 容器没有入站地址。Cloudflare 容器按 DO 生命周期随需启动不暴露公网端口DO 无法正向建连。反向拨号让「被调用方」变成了「主叫方」绕开了入站限制。DO 会休眠并换新化身。Durable Object 是状态化隔离环境休眠后重新激活是一个「新化身」。computerd 的测试用例专门覆盖了这一点新化身重新 POST/connect时旧 WebSocket 会被主动关闭replaced by new /connect新会话接管同一容器。正向连接在这种场景下无从续接反向重拨则天然支持见 computerd.test.ts。egress 策略即安全边界。出网访问受三档策略控制见 egress.tsnone默认禁止出网、direct放开直连、http-gateway所有出网流量携带 token 经网关白名单放行。控制通道走的computer.internal只在 egress 表内可达外部攻击者无法伪造回拨入口。可运行的对照实验见 examples/egress/README.md。四、连接建立后心跳、断线检测与自愈 通道不是建好就万事大吉应用层心跳每 20 秒发一次sync.watermarks()RPC既快速发现「静默死亡」的对端又给中间代理的空闲计时器保温断线感知WebSocket 的 close/error 事件、乃至 capnweb RPC 层先于底层 socket 察觉的「abort 帧」都会解析出closedPromiseWorkspace 随即丢弃缓存句柄下一次操作自动重建全新会话启动失败自愈若健康探测在预算内始终不通backend 会最多执行一次restart()destroy 旧容器 重新 start 再次探测把启动失败的诊断信息阶段、尝试次数、上一代退出原因完整打包成WorkspaceTransportError。五、延伸阅读源码与文档导航 模块路径作用容器后端与启动序列cloudflare-container.tsconnect()、/ws 升级、心跳回环代理 WorkspaceProxyproxy.ts应答 /health 与 /ws 反向拨号容器驱动接口container-host.tsstart/restart/interceptOutboundHttp健康探测health-probe.ts/health 就绪判定computerd 守护进程computerd.ts/connect 接收与回拨逻辑egress 策略定义egress.tsnone/direct/http-gateway 三档容器示例工程examples/container/README.md可运行的 container 后端官方文档目录docs/README.mdVFS、同步协议、挂载接口等六、一句话总结反向拨号不是炫技而是平台约束下的最优解容器出得了网、进不了门那就让容器出门打一个电话回来——computer.internal这条只在 egress 表内存在的「内线电话」让 DO 无论休眠多少次都能靠一次 POST /connect 找回它的那台电脑。【免费下载链接】computerGive your agent a computer 项目地址: https://gitcode.com/GitHub_Trending/computer1/computer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表