fix(sandbox): mermaid 渲染必崩定案:chromium 坏点版本;refresh 旋钮 + build canary 根治(bump 0.58.8)

9dcae061 三轮排查收官。真凶 = 7-08 镜像重建时 apt 烤进的 chromium 点版本
150.0.7871.46 自带启动即崩 bug(int3 自杀、零日志、strace 无失败 syscall;
上游 .100 已修,容器内 --only-upgrade chromium 实测复活出图)。被洗清的:
hardening 六件套 / seccomp / AppArmor / overcommit / ulimit / max_map_count /
QEMU EPYC vCPU / 镜像环境(纯净 trixie 对照 rc=0 反转定位)。

修复:
- Dockerfile ARG CHROMIUM_REFRESH cache-bust 旋钮:chromium 出问题改日期
  重建,只刷 chromium 层,pip 大层不动
- Dockerfile build 末尾渲染 canary:真渲一张中文最小图,chromium/字体链坏
  则 build 当场失败(有它 7-08 就不会把坏版本带上线)
- RUN.md 故障兜底条目改写最终版(两根因 + 判别特征);CHANGELOG 面向用户记

部署:服务器重建镜像(新 ARG 自然打穿 chromium 层 cache)+ 回收存量容器。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-07-11 12:19:31 +08:00
parent 9f78b151c1
commit d7add3926c
5 changed files with 29 additions and 5 deletions

View File

@ -5,6 +5,10 @@
> 所以不是每个版本号都有条目。条目格式 `## <版本> — <日期>`,新条目加在最上面。 > 所以不是每个版本号都有条目。条目格式 `## <版本> — <日期>`,新条目加在最上面。
> 工程口径的完整记录见 `PROGRESS.md` / git log。 > 工程口径的完整记录见 `PROGRESS.md` / git log。
## 0.58.8 — 2026-07-11
- 修复流程图 / 示意图(mermaid)渲染:此前生成流程图、申报书 / 专利配图时渲染持续失败(任务反复重试仍出不了图),根因已定位并修复,重新部署后恢复正常。
## 0.58.1 — 2026-07-10 ## 0.58.1 — 2026-07-10
- Pro 档模型选单再新增 2 个国际模型(一个旗舰档、一个轻量快速档),按任务需要选用。 - Pro 档模型选单再新增 2 个国际模型(一个旗舰档、一个轻量快速档),按任务需要选用。

View File

@ -2,7 +2,7 @@
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9` > 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`
最后更新:2026-07-11(round-3 探针改 pinned 版本直下,绕开被墙的版本解析端点,bump 0.58.7) 最后更新:2026-07-11(mermaid 渲染必崩定案:chromium 坏点版本;refresh 旋钮 + build canary 根治,bump 0.58.8)
--- ---
@ -23,6 +23,7 @@
### 2026-07 ### 2026-07
- **07-11 / 0.58.8**:**mermaid 渲染必崩最终定案 + 根治**(9dcae061 三轮排查收官):真凶 = 7-08 镜像重建时 apt 烤进的 chromium 点版本 `150.0.7871.46` 自带启动即崩 bug(int3 自杀、零日志、strace 无失败 syscall;上游 `.100` 已修,容器内 `--only-upgrade chromium` 实测复活出图)。排查中被逐一洗清的:沙箱 hardening 六件套、seccomp、AppArmor(Ubuntu 24.04 userns 限制在开但无关)、overcommit/ulimit/max_map_count、QEMU EPYC vCPU、镜像环境(纯净 trixie 对照 rc=0 反转定位)。修复两件套:Dockerfile ① `ARG CHROMIUM_REFRESH=<日期>` cache-bust 旋钮(chromium 出问题改日期重建,pip 大层不动);② **build 末尾渲染 canary**(真渲一张中文最小图,chromium/字体链坏则 build 当场失败——有它 7-08 就不会把坏版本带上线)。RUN.md 故障兜底条目改写为最终版(两根因 + 判别特征 + "别再猜 hardening")、CHANGELOG 面向用户记修复。部署:服务器重建镜像(新 ARG 自然打穿 chromium 层 cache)+ 回收存量容器。
- **07-11 / 0.58.7**:round-3 第 3 步改从镜像内 puppeteer-core `revisions.js` 读 pinned 版本号直下(`@stable` tag 需先访问 Google 版本解析端点——被墙,上一轮三源全败的真因;`--base-url` 只管二进制不管解析),错误输出改 grep Error 行;加 `[步骤]` 参数支持只跑第 3 步。round-1/2 结论沉淀:崩溃无 FATAL 行(静默 CHECK)、gdb 栈无符号、CPU 无 pku/shstk/ibt(排除新安全特性交互)。 - **07-11 / 0.58.7**:round-3 第 3 步改从镜像内 puppeteer-core `revisions.js` 读 pinned 版本号直下(`@stable` tag 需先访问 Google 版本解析端点——被墙,上一轮三源全败的真因;`--base-url` 只管二进制不管解析),错误输出改 grep Error 行;加 `[步骤]` 参数支持只跑第 3 步。round-1/2 结论沉淀:崩溃无 FATAL 行(静默 CHECK)、gdb 栈无符号、CPU 无 pku/shstk/ibt(排除新安全特性交互)。
- **07-11 / 0.58.6**:round-3 探针下载源修正——npx 拉 `@puppeteer/browsers` 显式走阿里 npm registry,chrome-for-testing 二进制按 阿里 CDN → 阿里 registry binary → 官方 三级 fallback(服务器实测默认源下不动)。 - **07-11 / 0.58.6**:round-3 探针下载源修正——npx 拉 `@puppeteer/browsers` 显式走阿里 npm registry,chrome-for-testing 二进制按 阿里 CDN → 阿里 registry binary → 官方 三级 fallback(服务器实测默认源下不动)。
- **07-11 / 0.58.5**:**chromium 秒崩 round-3 探针**:bisect 结果全 case FAIL(含 baseline 无 hardening、seccomp=unconfined)→ hardening 全部无罪,宿主 overcommit/ulimit 数据也干净——嫌疑收敛为 trixie chromium 150 构建 × 本宿主组合本身跑不了。新 `deploy/sandbox/probe_chromium_round3.sh`:①抓崩溃 FATAL 行(此前日志被 head 截断)+ gdb backtrace(best-effort);②实测 Google 官方 chrome-headless-shell(npmmirror 下载,puppeteer 生态对着它测试)在本宿主能否跑 + mmdc 接上出图——直接验证「镜像弃发行版 chromium 改 pinned headless shell」修法可行性,PASS 即定案动 Dockerfile。 - **07-11 / 0.58.5**:**chromium 秒崩 round-3 探针**:bisect 结果全 case FAIL(含 baseline 无 hardening、seccomp=unconfined)→ hardening 全部无罪,宿主 overcommit/ulimit 数据也干净——嫌疑收敛为 trixie chromium 150 构建 × 本宿主组合本身跑不了。新 `deploy/sandbox/probe_chromium_round3.sh`:①抓崩溃 FATAL 行(此前日志被 head 截断)+ gdb backtrace(best-effort);②实测 Google 官方 chrome-headless-shell(npmmirror 下载,puppeteer 生态对着它测试)在本宿主能否跑 + mmdc 接上出图——直接验证「镜像弃发行版 chromium 改 pinned headless shell」修法可行性,PASS 即定案动 Dockerfile。

5
RUN.md
View File

@ -516,6 +516,9 @@ sudo -u zcbot docker build \
# 所以容器内**裸调 `mmdc -i x.md -o x.png` 就能出图**,无需 --no-sandbox / 自写配置。 # 所以容器内**裸调 `mmdc -i x.md -o x.png` 就能出图**,无需 --no-sandbox / 自写配置。
# render_diagrams.py 等走 `which mmdc` 的脚本透明受益(原靠 MERMAID_PUPPETEER_CONFIG # render_diagrams.py 等走 `which mmdc` 的脚本透明受益(原靠 MERMAID_PUPPETEER_CONFIG
# env,已删 ── mmdc 本就不读它,改 wrapper 兜底)。host 上跑无 wrapper,行为不变 # env,已删 ── mmdc 本就不读它,改 wrapper 兜底)。host 上跑无 wrapper,行为不变
# build 末尾有**渲染 canary**(真渲一张中文最小图):chromium/字体链坏则 build 当场
# 失败。apt 拿到的 chromium 点版本可能自带崩溃 bug(2026-07 实锤 150.0.7871.46),
# canary 拦下后改 Dockerfile 的 `ARG CHROMIUM_REFRESH=<日期>` 重建刷新 chromium 层
# 3) 创建 sandbox 网络(bridge,dogfood 阶段保留 outbound NAT —— 让模型能 pip/curl 公网; # 3) 创建 sandbox 网络(bridge,dogfood 阶段保留 outbound NAT —— 让模型能 pip/curl 公网;
# iptables 仍挡内网/cloud metadata/PG。--internal 完全禁出站是外部用户开放时才改,见 §7.5 #2) # iptables 仍挡内网/cloud metadata/PG。--internal 完全禁出站是外部用户开放时才改,见 §7.5 #2)
@ -848,7 +851,7 @@ sudo xfs_quota -x -c "limit -p bhard=10g zcbot_<user_uuid>" /opt
| 容器内 shell 写工作目录报 `Permission denied`(but `sandbox check` ⑤ HOST_UID aligned ok) | DockerExecutor 写死了 `--user 1000:1000` 不会自动跟 build 的 HOST_UID 同步(改 `--user zcbot` 后已修)。仍报错检查镜像内 `docker run --rm --entrypoint id zcbot-sandbox:latest zcbot` 输出 uid 是否 = `id -u $(whoami)` | | 容器内 shell 写工作目录报 `Permission denied`(but `sandbox check` ⑤ HOST_UID aligned ok) | DockerExecutor 写死了 `--user 1000:1000` 不会自动跟 build 的 HOST_UID 同步(改 `--user zcbot` 后已修)。仍报错检查镜像内 `docker run --rm --entrypoint id zcbot-sandbox:latest zcbot` 输出 uid 是否 = `id -u $(whoami)` |
| 容器内 mmdc 渲 mermaid 报 `Failed to launch chromium` / `No usable sandbox` | chromium 在 `--cap-drop=ALL` 下自家 setuid sandbox 起不来,要 `--no-sandbox`。镜像已落 `/sandbox/puppeteer-config.json` + 给 mmdc 套了 wrapper 自动 `-p` 注入 ── **裸调 `mmdc -i x -o y` 就该成**。仍跪:`docker exec ... cat /usr/local/bin/mmdc` 看 wrapper 在不在(老镜像没 rebuild 则没有);或显式 `mmdc -p /sandbox/puppeteer-config.json -i x -o y` | | 容器内 mmdc 渲 mermaid 报 `Failed to launch chromium` / `No usable sandbox` | chromium 在 `--cap-drop=ALL` 下自家 setuid sandbox 起不来,要 `--no-sandbox`。镜像已落 `/sandbox/puppeteer-config.json` + 给 mmdc 套了 wrapper 自动 `-p` 注入 ── **裸调 `mmdc -i x -o y` 就该成**。仍跪:`docker exec ... cat /usr/local/bin/mmdc` 看 wrapper 在不在(老镜像没 rebuild 则没有);或显式 `mmdc -p /sandbox/puppeteer-config.json -i x -o y` |
| 容器内 mmdc 渲图卡到 **timeout** 而非报错 | chromium 默认用 `/dev/shm`,docker 不传 `--shm-size` 时只 64MB → 起不来一直挂。已在 `pool.py``docker run``--shm-size`(默 512m,env `ZCBOT_SANDBOX_SHM_SIZE` / yaml `sandbox.shm_size`)。**已 running 的旧容器不会自动生效**,重启 web + 等 idle 回收(或 `docker rm -f zcbot-sandbox-<uid>`)后新起的才带。实测脚本 `deploy/sandbox/probe_mermaid.sh` | | 容器内 mmdc 渲图卡到 **timeout** 而非报错 | chromium 默认用 `/dev/shm`,docker 不传 `--shm-size` 时只 64MB → 起不来一直挂。已在 `pool.py``docker run``--shm-size`(默 512m,env `ZCBOT_SANDBOX_SHM_SIZE` / yaml `sandbox.shm_size`)。**已 running 的旧容器不会自动生效**,重启 web + 等 idle 回收(或 `docker rm -f zcbot-sandbox-<uid>`)后新起的才带。实测脚本 `deploy/sandbox/probe_mermaid.sh` |
| 容器内 mmdc 报 `Failed to launch the browser process: Code: null`(伴满屏 dbus 报错),wrapper / `-p` 配置都给了照崩;或直接跑 chromium `Trace/breakpoint trap (core dumped)` | **`--pids-limit` 打满**(线程也计数):chromium headless 一次启动 ~150-200 线程,加上 shell 超时残留在容器里的 mmdc+chromium 树(超时只杀 host 侧 docker CLI),256 秒穿 → `pthread_create: Resource temporarily unavailable (11)` → chromium CHECK 崩(SIGTRAP)。2026-07-11 起默认已调 1024(yaml `sandbox.pids_limit` / env `ZCBOT_SANDBOX_PIDS_LIMIT`)。**已 running 的旧容器不生效**,重启 web + 等 idle 回收(或 `docker rm -f zcbot-sandbox-<uid>`)。dbus 报错是干扰项(成功时也打);验证跑 `deploy/sandbox/probe_mermaid.sh`。**2026-07-11 实测 1024 后全新容器仍首调秒崩** → 第二根因在 hardening × chromium 150 交互,跑 `deploy/sandbox/probe_chromium_bisect.sh` 逐 flag 定位(脚本尾部带判读表) | | 容器内 mmdc 报 `Failed to launch the browser process: Code: null`(伴满屏 dbus 报错),wrapper / `-p` 配置都给了照崩;或直接跑 chromium `Trace/breakpoint trap (core dumped)` | 两个已实锤根因(2026-07 排查,dbus 报错全程是干扰项——成功时也打):**① `--pids-limit` 打满**(线程也计数,chromium 一次启动 ~150-200 线程 + shell 超时残留的进程树),特征是 stderr 有 `pthread_create: Resource temporarily unavailable (11)`;已默认调 1024(yaml `sandbox.pids_limit`),改后需等旧容器回收。**② apt 烤进镜像的 chromium 点版本自带崩溃 bug**(150.0.7871.46 启动即 int3 自杀、零日志、strace 无失败 syscall,上游 .100 已修),特征是全新容器首调即秒崩、`--only-upgrade chromium` 后就好;修法:改 Dockerfile `ARG CHROMIUM_REFRESH=<日期>` 重建(只刷 chromium 层),build 末尾 canary 从此在 build 时就拦住坏版本。判别工具:`probe_mermaid.sh`(shm/配置类)、`probe_chromium_bisect.sh`(hardening 逐 flag)、`probe_chromium_round3.sh`(FATAL/strace);hardening/AppArmor/seccomp/内存/ulimit 已全部排查无罪,别再从这些方向猜 |
| 模型不渲本地 mmdc、反复试 `mermaid.ink` 等在线渲图服务还失败 | 容器**有外网**(bridge+NAT),但 `mermaid.ink` 等**境外服务易被墙/不稳**,渲图不该依赖出站。docker backend 的 system prompt「运行环境」段(`agent_builder.py` 注入)已写明"渲图走本地 mmdc、别调在线服务";撞到多半是 prompt 没更新 / 跑在 host backend。渲 mermaid 一律 `mmdc -i x.md -o x.png` | | 模型不渲本地 mmdc、反复试 `mermaid.ink` 等在线渲图服务还失败 | 容器**有外网**(bridge+NAT),但 `mermaid.ink` 等**境外服务易被墙/不稳**,渲图不该依赖出站。docker backend 的 system prompt「运行环境」段(`agent_builder.py` 注入)已写明"渲图走本地 mmdc、别调在线服务";撞到多半是 prompt 没更新 / 跑在 host backend。渲 mermaid 一律 `mmdc -i x.md -o x.png` |
| Export 报 "无可导出内容" | task 没 messages(只 system 不算);先发条消息再 export | | Export 报 "无可导出内容" | task 没 messages(只 system 不算);先发条消息再 export |
| `NoSubtaskError: working_dir ... 前缀嵌套` | §7.4 no-subtask:同 user 不允许 working_dir 嵌套(child / parent)。**同项目多对话**用**完全相同**的 working_dir;否则改成 sibling(平级) | | `NoSubtaskError: working_dir ... 前缀嵌套` | §7.4 no-subtask:同 user 不允许 working_dir 嵌套(child / parent)。**同项目多对话**用**完全相同**的 working_dir;否则改成 sibling(平级) |

View File

@ -1,3 +1,3 @@
# zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。
# 改版本只动这一行。 # 改版本只动这一行。
__version__ = "0.58.7" __version__ = "0.58.8"

View File

@ -73,8 +73,15 @@ RUN printf '[global]\nindex-url = %s\ntimeout = 60\n%s\n' \
# Node + mermaid-cli + Chromium ── proposal / patent skill 渲 mermaid 图必备 # Node + mermaid-cli + Chromium ── proposal / patent skill 渲 mermaid 图必备
# 镜像膨胀约 +400MB,接受成本(ASCII fallback 出 docx 没图不能用) # 镜像膨胀约 +400MB,接受成本(ASCII fallback 出 docx 没图不能用)
# Debian bookworm 自带 nodejs 18.x + chromium,够新;不走 NodeSource repo 减一步外网 # Debian 自带 nodejs + chromium,够新;不走 NodeSource repo 减一步外网
RUN apt-get update && apt-get install -y --no-install-recommends \ #
# CHROMIUM_REFRESH:本层 cache-bust 旋钮。apt 拿的是 build 时源里的最新点版本,而
# 点版本可能自带致命 bug(2026-07 实锤:150.0.7871.46 启动即 int3 自杀、零日志,
# .100 已修 ── 完整排查见 RUN.md 故障兜底"chromium 秒崩"条)。chromium 出问题时
# 改这个日期重建,只刷新本层及之后,上面的 pip 大层不动。
ARG CHROMIUM_REFRESH=2026-07-11
RUN echo "chromium refresh: ${CHROMIUM_REFRESH}" \
&& apt-get update && apt-get install -y --no-install-recommends \
chromium nodejs npm \ chromium nodejs npm \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
@ -145,6 +152,15 @@ exec /usr/local/bin/mmdc.real -p /sandbox/puppeteer-config.json "$@"
EOF EOF
RUN chmod +x /usr/local/bin/mmdc RUN chmod +x /usr/local/bin/mmdc
# build 时渲染 canary ── 用整条真实链路(node + mmdc wrapper + puppeteer config +
# chromium + CJK 字体)渲一张最小图,chromium 起不来 / 字体缺失则 **build 当场失败**,
# 而不是上线后静默挂(2026-07 事故:坏点版本 150.0.7871.46 烤进镜像,线上 26 次
# launch fail + 67 次 timeout、烧掉数十万 token 才被发现)。带中文节点顺带验字体链。
RUN printf 'flowchart TB\n A[数据准备]-->B[模型训练]\n' > /tmp/canary.mmd \
&& timeout 120 mmdc -i /tmp/canary.mmd -o /tmp/canary.png \
&& test -s /tmp/canary.png \
&& rm -f /tmp/canary.mmd /tmp/canary.png
# fs 工具进容器(§7.5 #6,2026-05-26 修正)── tool_runner.py 在容器内通过 # fs 工具进容器(§7.5 #6,2026-05-26 修正)── tool_runner.py 在容器内通过
# `python /sandbox/tool_runner.py <name>` 调用 tools/fs.py 的 Tool 子类,read/write/ # `python /sandbox/tool_runner.py <name>` 调用 tools/fs.py 的 Tool 子类,read/write/
# edit/glob/grep 全在容器内执行,物理边界替代代码护栏。tools/ 目录与 host 同步 # edit/glob/grep 全在容器内执行,物理边界替代代码护栏。tools/ 目录与 host 同步