ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

workerd 容器客户端集成测试(container-client test)运行指南:基于 Docker 的端到端验证

workerd 容器客户端集成测试(container-client test)运行指南:基于 Docker 的端到端验证 workerd 容器客户端集成测试container-client test运行指南基于 Docker 的端到端验证【免费下载链接】workerdThe JavaScript / Wasm runtime that powers Cloudflare Workers项目地址: https://gitcode.com/GitHub_Trending/wo/workerdworkerd 是 Cloudflare Workers 的 JavaScript/Wasm 运行时其容器客户端container client能力允许 Durable Object 直接启停、执行、探测并拦截本地 Docker 容器。本文基于 src/workerd/server/tests/container-client/README.md 展开完整讲解该集成测试的 Docker 环境准备、镜像构建、测试运行全流程并结合仓库中的 Bazel 构建规则、wd-test 配置与 3600 余行测试用例从源码层面剖析容器生命周期、exec、出站流量拦截与快照等核心能力。读完本文你将能够独立在本机搭建并运行 workerd 的 container-client 端到端测试并理解每一条命令背后的实现依据。一、container-client test 是什么container-client测试是 workerd 仓库中一个依赖真实容器引擎的端到端e2e测试。它由三部分构成测试入口src/workerd/server/tests/container-client/container-client.wd-test一个 workerd 配置文件声明了 worker、Durable Object 命名空间以及 Docker 引擎连接方式测试逻辑src/workerd/server/tests/container-client/test.js共 3667 行内含大量基于 Node.jsassert的测试组test suite被测容器镜像images/container-client-test/下的 Node.js HTTP 服务作为容器内被编排与探测的对象。与普通单元测试不同该测试通过unix://var/run/docker.sock与本地 Docker daemon 通信见 BUILD.bazel 中requires-network标签的注释因此运行前必须保证 Docker 环境正确可用且需要先构建并加载两个 Docker 镜像。二、前置条件确认 Docker 正确安装与运行原文档的第一步是验证 Docker 是否就绪docker ps该命令会列出当前正在运行的容器。若 Docker daemon 未启动、当前用户不在docker用户组或 Docker 未安装命令会直接报错后续所有步骤都无法进行。建议同时确认docker version以查看 client 与 server 两侧的版本信息确认 daemon 与 CLI 均正常。三、切换并确认 Docker context如果本机 Docker Desktop、远程 Docker 主机或其他 context 曾被你切换过测试可能连接不到预期的 daemon。原文档要求使用默认 contextdocker context use default执行后可再次运行docker ps或docker context ls确认当前 context 为default。这一步的意义在于保证后续构建出的镜像与运行测试时 workerd 访问的是同一个Docker daemon——workerd 通过本地 socket 直连 Docker若 context 指向远端镜像加载与容器启停将不在同一处。四、清理旧的测试容器与镜像为保证测试结果可复现原文档要求先清理历史残留。第一步是删除名称匹配的旧容器docker ps -aq --filter nameworkerd-container-client-test | xargs -r docker rm -f该命令先列出所有名称匹配workerd-container-client-test的容器 ID-aq再通过xargs -r docker rm -f强制删除-r保证没有匹配项时不会执行docker rm。随后删除旧的测试镜像docker image rm cf-container-client-test说明测试运行时加载的镜像仓库名实际为cloudflare/workerd/container-client-test见 images/container-client-test/BUILD.bazel 的repo_tags。若本地已存在同名旧镜像docker image rm可能因镜像被容器引用而报错建议在删除镜像前先完成容器清理保持顺序与文档一致。若出现 image is being used by a container 错误可再次执行容器清理命令。五、构建并加载测试镜像5.1 一键加载两条镜像原文档给出的构建命令是bazel run //images:load_all//images:load_all定义在 images/BUILD.bazel它基于rules_multirun的multirun规则将两个command目标合并为一条命令分别执行镜像目标加载后的 repo tag用途//images/container-client-test:loadcloudflare/workerd/container-client-test:latest与:override被测应用容器即容器内运行的 Node.js HTTP 服务//images/container-client-test:load-proxy-everythingcloudflare/proxy-everything:main出站流量拦截 sidecaregress interceptor见 workerd.capnp 中的containerEgressInterceptorImage字段说明5.2 镜像如何构建出来container-client-test镜像的构建链路位于 images/container-client-test/BUILD.bazeljs_binary(name app, entry_point app.js)将 app.js 打包为可执行 JS 二进制依赖ws包声明于 package.jsonjs_image_layer(name layers)将 JS 运行环境拆分为镜像层注意该目标带有target_compatible_with [platforms//os:linux]限制即仅支持 Linux 平台注释说明是为了规避 Windows 上 runfiles 符号链接解析问题oci_image(name image)基于node_25_slim基础镜像工作目录指向app.runfiles/_main容器启动命令为/{package_name()}/appoci_load(name load)将构建出的 OCI 镜像加载进本地 Docker daemon并同时打上latest与override两个 tag——overridetag 是专门为镜像覆盖测试testImageOverride准备的无需额外构建独立镜像。因此bazel run //images:load_all实际完成的是用 Bazel 构建 OCI 镜像 → 加载进 Docker daemon的完整闭环。六、运行测试6.1 标准命令镜像加载完成后执行just stream-test //src/workerd/server/tests/container-client:container-clientstream-test是仓库justfile中定义的别名justfile第 53-55 行等价于bazel test //src/workerd/server/tests/container-client:container-client --test_outputstreamed --nocache_test_results --test_tag_filters --test_size_filters要点解析--test_outputstreamed实时流式输出测试日志便于观察容器启停与请求过程--nocache_test_results禁用 Bazel 测试结果缓存确保每次都真实重跑该测试涉及外部 Docker 副作用缓存结果无意义--test_tag_filters与--test_size_filters清空标签/规模过滤使即便被打上requires-container-engine标签的测试也能执行目标名末尾的来自wd_test规则生成的默认变体见 build/wd_test.bzl 第 35 行注释name使用最老 compat date 2000-01-01。6.2 测试目标的 Bazel 配置src/workerd/server/tests/container-client/BUILD.bazel 中定义了该测试目标wd_test( size enormous, src container-client.wd-test, args [--experimental], data [test.js], tags [ requires-container-engine, requires-network, # Accesses unix://var/run/docker.sock ], )三个关键点size enormous告诉 Bazel 该测试耗时极长不会被默认超时策略误杀args [--experimental]workerd 以实验模式运行启用容器相关实验特性tagsrequires-container-engine表示没有 Docker 引擎就无法运行requires-network注释明确指出测试通过unix://var/run/docker.sock访问 Docker。七、测试配置逐项解读wd-test 如何接上 Dockercontainer-client.wd-test 是理解整个测试的关键。其核心配置如下const unitTests :Workerd.Config ( services [ ( name internet, network ( allow [private] ) ), ( name container-client-test, worker ( modules [ (name worker, esModule embed test.js) ], compatibilityFlags [enable_ctx_exports, nodejs_compat, experimental, containers_pid_namespace, streams_enable_constructors, enable_abortsignal_rpc], containerEngine (localDocker ( socketPath unix:/var/run/docker.sock, containerEgressInterceptorImage cloudflare/proxy-everything:main )), durableObjectNamespaces [ ( className DurableObjectExample, uniqueKey container-client-test-DurableObjectExample, container ( imageName cloudflare/workerd/container-client-test, images [ ( name api, image registry.example.com/apisha256:1111… ), ( name worker, image registry.example.com/workersha256:2222… ), ], ) ), ( className DurableObjectExample2, uniqueKey container-client-test-DurableObjectExample2, container ( imageName cloudflare/workerd/container-client-test, images [ ( name tools, image registry.example.com/toolssha256:3333… ), ], ) ), ], durableObjectStorage (localDisk TEST_TMPDIR), bindings [ ( name MY_CONTAINER, durableObjectNamespace DurableObjectExample ), ( name MY_DUPLICATE_CONTAINER, durableObjectNamespace DurableObjectExample2 ), ], ) ), ( name TEST_TMPDIR, disk (writable true) ), ], );逐项说明compatibilityFlagscontainers_pid_namespace使容器运行在隔离的 PID 命名空间对应测试组testPidNamespaceenable_abortsignal_rpc允许 AbortSignal 跨 RPC 传递以中止容器内进程streams_enable_constructors支持ReadableStream/WritableStream构造器nodejs_compat提供 Node.js 兼容 API测试中大量使用node:assert、node:timers/promises。containerEngine.localDocker连接本地 Docker daemonsocket 路径为unix:/var/run/docker.sock与 BUILD 标签注释一致containerEgressInterceptorImage指定出站拦截 sidecar 镜像cloudflare/proxy-everything:main。该结构体在 workerd.capnp 中定义为containerEngine :union { none; localDocker; }注释明确localDocker仅用于本地开发与测试。durableObjectNamespaces两个 DO 命名空间都绑定容器镜像cloudflare/workerd/container-client-test但配置了不同的images列表api/workervstools。这组声明会映射到 JS 侧this.ctx.container.images供testImages校验。durableObjectStorage (localDisk TEST_TMPDIR)DO 存储落盘到TEST_TMPDIR服务后者声明为可写磁盘。bindingsMY_CONTAINER与MY_DUPLICATE_CONTAINER两个命名空间绑定供测试同时操作两个不同 DO 类例如testSnapshotCrossDoRestore把一个 DO 的目录快照传给另一个 DO 恢复。八、测试覆盖的能力图谱test.js 在验证什么test.js 中的测试组按能力可分为六类均通过export const testXxx { async test(_ctrl, env) {...} }导出由 wd-test 框架驱动且每个测试用crypto.randomUUID()生成唯一 DO 名避免并发运行时相互干扰见文件中getRandomDurableObjectName的注释。8.1 生命周期与状态机testBasicsstart()→ 轮询健康检查waitUntilContainerIsHealthy最多重试 15 次→destroy()断言container.running状态翻转testStatus/testRunningAfterImmediateExit容器启动后立刻exit 0退出时running应尽快变为falsetestRestartAfterDestroy/testExitCodedestroy()后可再次start()启动无效 entrypoint 时monitor()应抛出带exitCode的错误正常进程被destroy()时退出码为 137128SIGKILL9testContainerShutdownDO 调用abort()后容器应随之关闭。8.2 exec在容器内执行命令testExec是最丰富的测试组覆盖 16 种场景包括直接读取 stdout 流proc.stdout作为 ReadableStream通过ReadableStream作为 stdin 写文件、通过WritableStreamstdin: pipe交互式喂入数据cwd工作目录覆盖、按 exec 覆盖 envEXEC_BASE被overridden验证stdout/stderr分离捕获、stderr: combined合并、stdout: ignore丢弃并发流式消费 64MiB stdout 与 64MiB stderrcountStreamBytes逐块读取验证不把大数据量缓冲进 JS 内存output()在 stdout 已被消费后调用应抛TypeErrorAbortSignal支持已 abort 的信号让exec()快速失败AbortError运行中 abort 则以 SIGKILL 结束进程退出码 137pty: true分配伪终端proc.isPty为真、resize()可用、stty size输出40 100验证初始行列尺寸execWithReceivedSignal验证跨 RPC 反序列化后的 AbortSignal也能正确中止容器内进程配合enable_abortsignal_rpc兼容标志。8.3 出站流量拦截egress intercept容器通过 sidecar 被拦截出站流量测试覆盖testSetEgressHttp/testSetEgressHttpWithInternet/testSetEgressHttpNoInternetinterceptOutboundHttp(host, binding)按主机名路由到 workerd 内的TestServiceWorkerEntrypointinterceptAllOutboundHttp则接管所有主机含 IPv4/IPv6/带端口/带路径并验证更新拦截器后新旧连接都生效testSetEgressHttpsinterceptOutboundHttps支持精确主机、通配符*.cloudflare.com:443与*兜底容器侧通过NODE_EXTRA_CA_CERTS信任 sidecar 的 CA 证书testSetEgressTcpinterceptOutboundTcp把容器内裸 TCP 连接11.0.0.1:7777转发给TestService.connect(socket)后者从socket.readable读数据、向socket.writable回写形成 tcp binding: 500 got: ping 的闭环testInterceptWebSocket/testInterceptWebSocketHttpsWebSocket 升级请求101经容器/ws代理到被拦截地址再由TestService.fetch以WebSocketPair回显 Binding 42: …。配套的容器端实现位于 images/container-client-test/app.js它提供了/interceptHTTP 出站、/intercept-httpsHTTPS 出站、/intercept-tcp裸 TCP与/wsWebSocket 代理/回显等探测端点是测试与容器交互的探针。8.4 快照snapshot目录快照snapshotDirectory({dir, name})后destroy()再以directorySnapshots: [{snapshot, mountPoint}]恢复覆盖命名快照、多目录快照、自定义挂载点原路径 404、新路径 200、重叠挂载顺序无关性子挂载遮蔽父挂载、重复恢复路径拒绝、根目录/相对路径恢复拒绝、对停止的容器快照报错、不存在的快照 ID 报错等边界整容器快照snapshotContainer({name})捕获整个可写层containerSnapshot恢复验证快照排除已挂载的目录快照、恢复时又可重新分层testContainerSnapshotRelayerWithDirectoryMountstestSnapshotCrossDoRestore/testContainerSnapshotCrossDoRestore一个 DO 创建快照传给另一个 DO 恢复验证快照的跨对象可移植性testImageOverridestart({image})覆盖默认镜像利用:overridetag并断言image与containerSnapshot互斥。8.5 标签labels与 inspecttestLabels自定义标签含my.label/key、kv: abc、emoji 等经inspect()完整往返testLabelValidation/testSetLabelsValidation标签名不能为空、最多 10 个、名称 ≤16 字节、值 ≤64 字节、不允许控制字符testSetLabels/testSetLabelsReplaces/testSetLabelsClearsAllsetLabels()是整体替换而非合并未启动/已销毁容器调用会报错并发调用由服务端 RpcTurn 机制 FIFO 串行化最后一次调用生效testSetLabelsSerializedtestInspectBeforeStart/testInspectEmptyLabels/testInspectAfterDestroy未启动与销毁后inspect()返回null。8.6 其余运行时语义testSetInactivityTimeoutsetInactivityTimeout(0)抛TypeError设置后 DOabort()退出容器仍保持运行testAlarmDO 定时器与容器标志恢复验证use_containers标志在 DO 被驱逐后经setAlarm正确重建testPidNamespace通过/pid-namespace端点读取/proc/1/cmdline断言 PID 1 是容器 entrypoint 而非宿主机 init如 systemdtestImageValidation/testInstanceTypeValidation镜像引用必须为非空可打印 ASCII 且 ≤4096 字节实例资源vcpu/memoryMib/diskMb必须为大于 0 的有限整数且不超Number.MAX_SAFE_INTEGER。九、常见问题与排查思路结合 README 步骤与仓库实现整理典型的失败场景docker ps报错或权限不足Docker daemon 未运行或当前用户不在 docker 组先修复 Docker 环境再回到第三步的清理步骤。bazel run //images:load_all失败该目标仅支持 Linuxjs_image_layer与oci_image均有target_compatible_with限制非 Linux 平台需自行评估另外需保证 Bazel 能拉取node_25_slim与proxy_everything等外部依赖。测试报 Container is not listening to port 8080 重试耗尽通常是镜像未加载成功或容器应用启动失败。验证docker images | grep container-client-test是否存在latest与override两个 tag以及cloudflare/proxy-everything:main是否存在缺少 sidecar 会导致出站拦截类测试全部失败。残留容器/镜像干扰务必在重跑前执行 README 中的清理命令否则旧的cloudflare/workerd/container-client-test容器或镜像可能被复用导致状态断言失败。测试超时该测试size enormous单次运行耗时较长stream-test已通过--nocache_test_results强制重跑耐心等待即可必要时可提高 Bazel 的测试超时时间。十、小结container-client 测试是验证 workerd 容器编排能力的核心端到端套件。它的运行链路非常清晰Docker 就绪 → 清理残留 →bazel run //images:load_all构建并加载镜像 →just stream-test执行 wd-test。而 container-client.wd-test 中的containerEngine、DO 命名空间与 binding 声明加上 test.js 中六大类测试组共同构成了对容器生命周期、进程执行、流量拦截、快照迁移、标签管理等能力的系统性验证。如果你想深入了解容器 API 的某一方面直接阅读test.js中对应的测试组和 images/container-client-test/app.js 的探针端点是成本最低的入门路径。【免费下载链接】workerdThe JavaScript / Wasm runtime that powers Cloudflare Workers项目地址: https://gitcode.com/GitHub_Trending/wo/workerd创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表