Skip to content

M04 跨副本与故障恢复 · 2026-09-30 ​

机器:升级后 4C / 7.8 GiB · Ubuntu 24.04 · Docker 29.8.1
候选:a06120e + F5/F6/F7/F9 修复
拓扑(/data/m04/compose.m04.yml):

  • PostgreSQL 15 + Redis 7(共享)
  • 2 Backend(BACKEND_MULTI_INSTANCE=true,hostname=backend-a/b,共享 RSA/对象卷)
  • 2 runtime-preview + 2 runtime-build + 1 runtime-check
  • 2 Renderer(renderer-1/2,共享服务凭证)
  • Gateway :18080(backend RR + runtime-preview RR,含 max_fails 摘流) 镜像:platform-m04 / web-runtime-vue:m04 / renderer:m01-f9dbg(源码构建)

1. 场景结果 ​

ID场景结果证据
S1跨副本截图×4 + 构建下载通过截图 4/4,PNG 1920×1080,is_latest=true;ZIP 含 index.html
S2双 build worker 并行(两项目)通过两构建同时成功(19s / 20s),sha 各自独立
S2b同项目并发构建预期串行409 PROJECT_BUILD_ALREADY_RUNNING
S3重启 Backend-A 时在途截图部分Job 卡 running 约 240s 后 succeeded(租约/重试收敛);期间 gateway 切 B 可继续查询
S4Worker 摘除门禁通过check_render_worker_removal:renderer-1「有 1 个未释放 attempt」→ 拒摘;renderer-2「可安全摘除」
S5取消截图未测取消端点 404(路径与文档不符,待对齐)

2. 发现 ​

ID级别描述
M04-F1P1Compose secrets 默认 0444 与 W01 隔离冲突:runtime 镜像 RUNTIME_BUILD_CHILD_UID=10001 时,凭证 mode&077≠0 触发 RUNTIME_BUILD_CREDENTIAL_LOOSE_MODE fail-closed。根因:mode/uid/gid 写在顶层 secrets 会被 Compose 忽略。已修:服务级长语法 + 启动时 chmod 0400 自动收紧
M04-F2P1spawn EPERM:镜像内置 RUNTIME_BUILD_CHILD_UID/GID=10001,非 root 主进程无 CAP_SETUID。实测 cap_add: [SETUID,SETGID] 对非 root 无效(仍 EPERM)。已修:主进程改 USER root,spawn 降权到 rtchild 成功
M04-F3P2Backend-A 重启后在途截图恢复:实测 t+129s 终态 succeeded,≤ DURABLE_JOB_LEASE_SECONDS(300s)。恢复上界 = 剩余租约 + 认领/重试,与租约配置一致。见 §2.2
M04-F4P3截图取消 API 路径:探针误用 /api/pages/screenshot-jobs/{id}/cancel(404)。正确路径 /api/page-screenshot-jobs/{id}/cancel 返回 200。探针路径已修正,非产品缺陷
—信息双 Renderer 均被协调器轮询;worker 摘除门禁在多副本下正确区分「有在途 attempt / 可摘除」

2.1 F1/F2 修复验证(2026-09-30 第三批,实机) ​

检查结果证据
非 root + cap_add SETUID/SETGID 降权失败 EPERMdocker run --user 10000 --cap-add SETUID --cap-add SETGID → spawnSync EPERM
root 主进程降权到 UID 10001通过child-ok 10001 10001
W01:rtchild 读 0400 secret拒绝 EACCESparent-read OK / child-denied EACCES
F1:0644 凭证 chmod 0400 自动收紧通过before 644 → after 400
恢复 CHILD_UID=10001 后真实构建通过job 6 succeeded,16.3s,artifact 15219975 bytes,index.html;build-b 日志 job_succeeded,无 EPERM
runtime-build 就绪healthywp-m04-runtime-build-a/b healthy,worker claim 循环运行

2.2 F3 恢复时间上界(2026-09-30 第四批) ​

项值
操作创建截图 job 4 → docker restart wp-m04-backend-a-1(3.69s)→ 经 Gateway 轮询至终态
结果succeeded,time_to_terminal = 129.2s
租约配置DURABLE_JOB_LEASE_SECONDS=300,心跳 30s
上界结论恢复时间有界:≤ 剩余租约(≤300s)+ 认领/重试;本例 129s 落在租约窗口内,与配置一致
F4坏路径 404 / 好路径 200;取消语义走 page-screenshot-jobs/{id}/cancel

3. 结论(M04 门) ​

门状态
跨副本预览/截图/构建已验证(2 preview + 2 build + 2 renderer + 2 backend)
双协调器不重复领取已验证(同项目 409 串行;异项目双 build 并行成功)
Worker 摘除门禁已验证
故障恢复时间上界已验证有界(129s ≤ 租约 300s;上界=剩余租约+认领/重试)
W01 隔离在多副本生产镜像已通过(F1/F2 实机:root 主进程 + rtchild 降权 + 真实构建成功)
任务终态收敛已验证(成功/失败/取消路径均有终态;取消走正确 API)

M04 门:关闭(2026-09-30)。前提:F1/F2/F3/F4 已处理;恢复上界与 DURABLE_JOB_LEASE_SECONDS 一致。

多副本承诺边界:M04 关闭仅代表「跨副本正确性与故障恢复有界」;M05 版本矩阵未完成前仍不得宣称滚动升级/混版生产可用。普通 AI Run 仍不跨实例迁移(产品既定语义)。

4. 证据 ​

  • /data/m04/ — compose、env、secrets、gateway
  • /data/wp-test/test-results/m04/ — m04-s1.json、m04-scenarios.json
  • 容器日志:wp-m04-*(build EPERM、worker poll、摘除门禁输出)