# 运行时镜像预装 Chrome 与 lark-cli 二进制:可行性评估 > 状态:评估完成;落地方案已定并已上线(2026-10-31):**宿主机 overlay 定制层**——仅本部署生效、不改仓库、不影响开源用户(文件与操作见 §4.5)。镜像已含 Chrome 156 / lark-cli 1.0.98 / 中文字体 / playwright-cli 0.1.22 / Java 17 + Maven;容器 HOME 与 Java `user.home` 对齐(0.0.392,见 §4.5)随部署 + 容器重建生效 > 日期:2026-10-31 > 关联:[2026-10-30-runtime-image-dependency-alignment-technical-design.md](2026-10-30-runtime-image-dependency-alignment-technical-design.md)(镜像依赖对齐 D1–D7;本方案是其「镜像内容扩充」的具体落点)、[2026-10-14-user-runtime-container-isolation-design.md](2026-10-14-user-runtime-container-isolation-design.md) > 一句话:Chrome(无头浏览器)与 lark-cli 官方二进制都不该由每个会话现下,应固化进 `mao-runtime` 镜像;实测可行,增量约 650 MB,技能侧零改动。落地形态选 overlay:上游镜像定义升级可自动继承,无需合并 diff。 --- ## 1. 问题 docker 模式下用户容器以 `mao-runtime:latest` 启动,容器内 `/usr/local` 全部来自镜像层。容器一旦重建(宿主机重启、`docker run` 重建、运维回收后重建),容器内临时安装的东西全部丢失: - playwright 依赖的系统 Chrome(`/opt/google/chrome/chrome`)消失 → 浏览器自动化不可用; - lark-cli 官方二进制(`/usr/local/lib/lark-cli/lark-cli`)消失 → PATH 上的 shim 报 `BIN_MISSING: spawn /usr/local/lib/lark-cli/lark-cli ENOENT`。 **2026-10-31 实测复现**:本会话容器在 05:23 重建(hostname `9d4f1519f2d2` → `49599ff5d1f6`),上述两项均丢失;而用户 `deps` 持久目录里的 lark-cli shim 仍在(`/opt/mao-data/deps/{userId}/npm/bin/lark-cli`),说明「shim 持久、官方二进制易失」的错配就是根因。 每次新会话首用都要现下:Chrome deb 139 MB、lark-cli tar 14.7 MB,叠加外网依赖与等待。 --- ## 2. 结论 | 方案 | 结论 | |---|---| | **A. 镜像预装** | **推荐**。实测可构建、可运行、技能零改动。落地形态二选一:**A1(本部署已定)宿主机 overlay 定制层**——不改仓库、不影响开源用户,见 §4.5;**A2 改仓库 `Dockerfile.runtime`**——惠及所有部署者,见 §4.1–4.4。 | | B. 容器启动脚本幂等安装 | **否决**。与容器启动超时约束冲突,异步则首用竞态;每容器重复下载 140 MB+,依赖运行时外网。 | | C. 持久目录(`deps/{userId}`) | **仅作兜底**。lark-cli 可放持久目录(shim 支持 `LARK_CLI_BIN` 覆盖);Chrome 因系统库依赖无法干净持久化。 | --- ## 3. 实测数据(2026-10-31,在 `node:22-bookworm-slim` 派生容器内实测) ### 3.1 Chrome | 项 | 实测值 | |---|---| | deb 下载 | `google-chrome-stable_current_amd64.deb` 139 MB,约 3 s(`dl.google.com` HTTP 200) | | 依赖规模 | `--no-install-recommends` 仍需 **103 个包**(deb 的 Depends 含 `xdg-utils`→x11/perl 一族、`libgtk-3-0`→gtk 体系、`wget`、`systemd` 等) | | 磁盘增量 | **+600 MB**(其中 `/opt/google/chrome` 447 MB,其余为依赖库) | | 安装后版本 | `Google Chrome 156.0.8078.79` | | 运行冒烟 | `--headless=new --no-sandbox --dump-dom about:blank` 正常输出 DOM(仅 dbus 告警,无影响) | | 固定版本 URL | `https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_156.0.8078.79-1_amd64.deb` → HTTP 200,可按版本钉死 | > 首次运行前无需再装依赖:`dist` 里的 playwright 技能配置为 `channel: "chrome"`(`skills/playwright/scripts/cli.config.json`),直接命中 `/opt/google/chrome/chrome`。 ### 3.2 lark-cli | 项 | 实测值 | |---|---| | 镜像源 | `registry.npmmirror.com/-/binary/lark-cli/v{版本}/` 完整镜像(v1.0.0 → **v1.0.98** 最新) | | 资产 | `lark-cli-1.0.98-linux-amd64.tar.gz` 14.7 MB + 同目录 `checksums.txt`(SHA-256 校验通过) | | 安装体积 | 解压后二进制 49 MB,`/usr/local/lib/lark-cli` 目录 **47 MB** | | 运行 | `/usr/local/lib/lark-cli/lark-cli --version` → `lark-cli version 1.0.98` | | **shim 链路** | 预装后 `lark-cli --version`(走 deps 持久目录的 shim → ECP→UAT 注入 → 镜像内真实二进制)→ **`lark-cli version 1.0.98`,零改动打通** | | 技能安装脚本 | `skills/lark-cli-skill/scripts/install-lark-cli.sh` 已有「目标路径已存在且版本一致 → 跳过」逻辑,镜像预装后第 1 步自动短路 | ### 3.3 可选:中文字体 `fonts-noto-cjk`:deb 56.5 MB / 安装后 **91 MB**。不加则 playwright 截图/PDF 中文全是豆腐块(当前镜像 `fc-list` 为空、`/usr/share/fonts` 无内容)。 ### 3.4 体积与耗时汇总 | 项 | 增量 | |---|---| | Chrome(含依赖) | ~600 MB | | lark-cli 二进制 | ~47 MB | | fonts-noto-cjk(可选) | ~91 MB | | 构建耗时 | 预估 +1–2 分钟(一次性) | | 每容器额外占用 | **0**(镜像层由所有容器共享,不按容器复制) | --- ## 4. 方案 A:具体做法(A2 改仓库形态) > 本部署不采用本节形态(改用 §4.5 overlay 定制层);本节保留给「希望所有部署者都带上」以及上游正式升级的场景。 ### 4.1 `Dockerfile.runtime` 改动 ```dockerfile # ── 新增 build args ── ARG CHROME_DEB_URL=https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_156.0.8078.79-1_amd64.deb ARG LARK_CLI_VERSION=1.0.98 ARG INSTALL_CJK_FONTS=true # ── Chrome(playwright channel:chrome 直接命中 /opt/google/chrome/chrome)── RUN curl -fsSL -o /tmp/google-chrome.deb "${CHROME_DEB_URL}" \ && apt-get install -y --no-install-recommends /tmp/google-chrome.deb \ && rm -f /tmp/google-chrome.deb \ && rm -rf /var/lib/apt/lists/* # ── 中文字体(可选,截图/PDF 中文渲染)── RUN if [ "${INSTALL_CJK_FONTS}" = "true" ]; then \ apt-get update \ && apt-get install -y --no-install-recommends fonts-noto-cjk \ && rm -rf /var/lib/apt/lists/*; \ fi # ── lark-cli 官方二进制(sha256 校验;PATH 入口由技能 shim 提供)── RUN mkdir -p /tmp/lark && cd /tmp/lark \ && ARCHIVE="lark-cli-${LARK_CLI_VERSION}-linux-amd64.tar.gz" \ && BASE="https://registry.npmmirror.com/-/binary/lark-cli/v${LARK_CLI_VERSION}" \ && curl -fsSL -o "${ARCHIVE}" "${BASE}/${ARCHIVE}" \ && curl -fsSL -o checksums.txt "${BASE}/checksums.txt" \ && grep -F "${ARCHIVE}" checksums.txt | sha256sum -c \ && tar -xzf "${ARCHIVE}" \ && mkdir -p /usr/local/lib/lark-cli \ && install -m 0755 lark-cli /usr/local/lib/lark-cli/lark-cli \ && rm -rf /tmp/lark \ && /usr/local/lib/lark-cli/lark-cli --version ``` 要点: 1. **Chrome 用固定版本 deb URL**(可复现;升级时改 ARG 或 build-runtime-image.sh 传参),不用 `_current` 滚动包。 2. **`--no-install-recommends`**:默认安装会额外拉 `zutty`、`x11-utils`、perl LWP 等,必须显式关闭。 3. **`sha256sum -c` 依赖文件名匹配**:下载时保留原始 `ARCHIVE` 名,不要重命名。 4. **不写 `/usr/local/bin/lark-cli` wrapper**:PATH 入口继续由技能 shim 负责(现有安装流程 `npm install . -g` 装到 deps 持久目录);镜像只提供「易失的官方二进制」,避免两套入口打架。 5. Dockerfile 顶部注释的「依赖清单」说明同步更新,明确镜像现在还包含 Chrome 与 lark-cli。 ### 4.2 `scripts/build-runtime-image.sh` 改动 - 新增可覆盖环境变量:`CHROME_DEB_URL`、`LARK_CLI_VERSION`、`INSTALL_CJK_FONTS`,透传 `--build-arg`; - 构建日志打印这三个值(便于回溯「某次镜像里是什么版本」); - 构建成功后提示:「镜像已更新,**已存在容器需重建才会生效**(容器管理 → 重建全部)」。 ### 4.3 生效链路(关键) ``` 改 Dockerfile → bash scripts/build-runtime-image.sh → 重建用户容器 → 生效 ``` **镜像更新不会自动应用到已运行容器**:`docker start` 复用旧容器(旧镜像层)。当前 `reapExpired()` 只做 stop/start,不做镜像比对。可选增强见 §7。 ### 4.4 技能侧影响:零改动(已验证) | 技能 | 依赖点 | 预装后 | |---|---|---| | playwright | `cli.config.json` → `channel: "chrome"` | 直接命中 `/opt/google/chrome/chrome` | | lark-cli | shim → `LARK_CLI_BIN \|\| /usr/local/lib/lark-cli/lark-cli` | 直接命中,无需 `npm install`(二进制层) | ### 4.5 本部署落地形态(A1,2026-10-31 定):宿主机 overlay 定制层 > 决策:**不改仓库**(开源用户构建出的镜像保持官方原样),在宿主机 `/data/runtime-image-ext/` 维护 overlay 定制层 + 一键重建脚本。文件不进 git;上游对 `Dockerfile.runtime` 的任何升级在「重建基础镜像」一步自动继承,overlay **不需要合并任何 diff**。 ``` 官方基础层:bash /opt/mao/scripts/build-runtime-image.sh → mao-runtime:latest 定制层 :docker build -f /data/runtime-image-ext/Dockerfile.runtime.ext → mao-runtime:latest(覆盖 tag) 生效 :管理后台「容器管理 → 重建全部」 ``` #### 文件 1:`/data/runtime-image-ext/Dockerfile.runtime.ext` ```dockerfile # Mao 运行时镜像 · 本部署定制层(overlay) # # 基于官方构建产物 mao-runtime:latest 叠加两类「易失二进制」: # 1) Google Chrome —— playwright 技能 channel:chrome 依赖(/opt/google/chrome/chrome) # 2) lark-cli 官方二进制 —— lark-cli 技能 shim 依赖(/usr/local/lib/lark-cli/lark-cli) # 两者原先装在容器内 /usr/local,容器重建即丢;本层把它们固化到镜像,随容器恢复。 # # 由 /data/runtime-image-ext/rebuild-runtime-image.sh 调用;本文件不进入 git 仓库(仅本部署生效)。 # 评估与操作说明:docs/plan/2026-10-31-runtime-image-preinstall-chrome-lark-cli.md FROM mao-runtime:latest # Chrome 固定版本 deb(升级:改默认值,或构建时传 CHROME_DEB_URL) ARG CHROME_DEB_URL=https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_156.0.8078.79-1_amd64.deb # lark-cli 版本(镜像源 registry.npmmirror.com,sha256 校验) ARG LARK_CLI_VERSION=1.0.98 # 中文字体(playwright 截图/PDF 中文渲染;false 可跳过) ARG INSTALL_CJK_FONTS=true LABEL org.mao.runtime.ext="chrome+lark-cli" # ── 1. Chrome(--no-install-recommends 必加:默认会多拉 zutty/x11-utils 等)── RUN curl -fsSL -o /tmp/google-chrome.deb "${CHROME_DEB_URL}" \ && apt-get update \ && apt-get install -y --no-install-recommends /tmp/google-chrome.deb \ && rm -f /tmp/google-chrome.deb \ && rm -rf /var/lib/apt/lists/* # ── 2. 中文字体(可选)── RUN if [ "${INSTALL_CJK_FONTS}" = "true" ]; then \ apt-get update \ && apt-get install -y --no-install-recommends fonts-noto-cjk \ && rm -rf /var/lib/apt/lists/*; \ fi # ── 3. lark-cli 官方二进制(sha256 校验;PATH 入口由技能 shim 提供,此处不写 wrapper)── RUN mkdir -p /tmp/lark && cd /tmp/lark \ && ARCHIVE="lark-cli-${LARK_CLI_VERSION}-linux-amd64.tar.gz" \ && BASE="https://registry.npmmirror.com/-/binary/lark-cli/v${LARK_CLI_VERSION}" \ && curl -fsSL -o "${ARCHIVE}" "${BASE}/${ARCHIVE}" \ && curl -fsSL -o checksums.txt "${BASE}/checksums.txt" \ && grep -F "${ARCHIVE}" checksums.txt | sha256sum -c - \ && tar -xzf "${ARCHIVE}" \ && mkdir -p /usr/local/lib/lark-cli \ && install -m 0755 lark-cli /usr/local/lib/lark-cli/lark-cli \ && rm -rf /tmp/lark \ && /usr/local/lib/lark-cli/lark-cli --version # ── 4. playwright-cli(全局安装:技能 wrapper 优先使用它,避免容器重建后 npx 现拉)── # 系统 Chrome 已装,不需要 playwright 自带浏览器:跳过 postinstall 的浏览器下载。 # 装包走 npmmirror(官方源实测慢 6 倍);实测 2s / 19MB。 ARG PLAYWRIGHT_CLI_VERSION=0.1.22 RUN PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install -g --no-audit --no-fund --registry=https://registry.npmmirror.com "@playwright/cli@${PLAYWRIGHT_CLI_VERSION}" \ && npm cache clean --force \ && playwright-cli --version # ── 5. pip 国内源(默认 pypi.org:docker 网络实测 ReadTimeout 52s;aliyun 源 0-1s)── RUN printf '[global]\nindex-url = https://mirrors.aliyun.com/pypi/simple/\ntrusted-host = mirrors.aliyun.com\n' > /etc/pip.conf # ── 6. npm 国内源(默认 registry.npmjs.org 装小包 12s,npmmirror 1s)── # 写 npm 的 builtin npmrc(npm 安装目录内):穿透 NPM_CONFIG_PREFIX(deps 持久化把 globalconfig # 挪走后 user/global 两处都不生效)与 HOME 对齐;用户自己的 .npmrc / 项目配置仍优先。 RUN printf 'registry=https://registry.npmmirror.com\n' > /usr/local/lib/node_modules/npm/npmrc \ && npm config get registry \ && HOME=/tmp/npm-probe-home NPM_CONFIG_PREFIX=/tmp/npm-probe npm config get registry # ── 7. Java 17 + Maven(容器内 Java 项目构建;.m2 配置与本地仓库在用户目录,走容器 HOME 对齐)── RUN apt-get update \ && apt-get install -y --no-install-recommends openjdk-17-jdk-headless maven \ && rm -rf /var/lib/apt/lists/* \ && java -version \ && mvn -version ``` #### 文件 2:`/data/runtime-image-ext/rebuild-runtime-image.sh` ```bash #!/usr/bin/env bash # 重建 mao-runtime 镜像 = 官方基础层 + 本部署定制层(Chrome / lark-cli / 中文字体) # # 用法(宿主机执行): # bash /data/runtime-image-ext/rebuild-runtime-image.sh # CHROME_DEB_URL=<新版本 deb URL> bash .../rebuild-runtime-image.sh # 升级 Chrome # LARK_CLI_VERSION=1.0.99 bash .../rebuild-runtime-image.sh # 升级 lark-cli # PLAYWRIGHT_CLI_VERSION=0.1.23 bash .../rebuild-runtime-image.sh # 升级 playwright-cli # INSTALL_CJK_FONTS=false bash .../rebuild-runtime-image.sh # 跳过中文字体 # APT_MIRROR=deb.debian.org bash .../rebuild-runtime-image.sh # 需要时覆盖(默认 mirrors.aliyun.com:本机实测 deb.debian.org 过慢) # # 完整链路:代码升级(deploy-mao.sh)→ 本脚本(重建镜像)→ 管理后台「容器管理 → 重建全部」 # 失败语义:定制层构建失败时 mao-runtime:latest 停在新基础版(无定制),失败显性、不静默;修好后重跑即可。 set -euo pipefail MAO_DIR="${MAO_DIR:-/opt/mao}" EXT_DIR="${EXT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}" EXT_DOCKERFILE="${EXT_DIR}/Dockerfile.runtime.ext" # 本部署构建机实测 deb.debian.org 过慢(apt 阶段超过 15 分钟),默认走阿里云源; # 需要时以 APT_MIRROR=... 覆盖(透传给官方 build-runtime-image.sh)。 export APT_MIRROR="${APT_MIRROR:-mirrors.aliyun.com}" [ -f "${EXT_DOCKERFILE}" ] || { echo "缺少 ${EXT_DOCKERFILE}" >&2; exit 1; } [ -d "${MAO_DIR}/.git" ] || { echo "${MAO_DIR} 不是 git 检出,请检查 MAO_DIR" >&2; exit 1; } echo "==> [1/3] 官方基础层:重建 mao-runtime:latest(继承仓库最新镜像定义)" (cd "${MAO_DIR}" && bash scripts/build-runtime-image.sh) echo "==> [2/3] 定制层:叠加 Chrome + lark-cli + 中文字体" build_args=() if [ -n "${CHROME_DEB_URL:-}" ]; then build_args+=(--build-arg "CHROME_DEB_URL=${CHROME_DEB_URL}"); fi if [ -n "${LARK_CLI_VERSION:-}" ]; then build_args+=(--build-arg "LARK_CLI_VERSION=${LARK_CLI_VERSION}"); fi if [ -n "${INSTALL_CJK_FONTS:-}" ]; then build_args+=(--build-arg "INSTALL_CJK_FONTS=${INSTALL_CJK_FONTS}"); fi if [ -n "${PLAYWRIGHT_CLI_VERSION:-}" ]; then build_args+=(--build-arg "PLAYWRIGHT_CLI_VERSION=${PLAYWRIGHT_CLI_VERSION}"); fi docker build "${build_args[@]}" -f "${EXT_DOCKERFILE}" -t mao-runtime:latest "${EXT_DIR}" docker tag mao-runtime:latest "mao-runtime:ext-$(date +%Y%m%d-%H%M%S)" echo "==> [3/3] 冒烟:验证定制层内容" docker run --rm mao-runtime:latest sh -c '/opt/google/chrome/chrome --version && /usr/local/lib/lark-cli/lark-cli --version' echo echo "完成。存量容器不会自动换用新镜像,请到管理后台「容器管理 → 重建全部」。" echo "(查看在册用户容器:docker ps --filter name=mao-user- --format '{{.Names}}')" ``` #### 首次部署(宿主机) ```bash # 两个文件就位后(本会话工作区 docs/runtime-image-ext/ 有副本,源路径见 §8) bash /data/runtime-image-ext/rebuild-runtime-image.sh # 然后:管理后台 → 容器管理 → 重建全部(存量容器不会自动换镜像) ``` #### 升级与日常维护 | 场景 | 操作 | |---|---| | 上游升级镜像定义(新依赖 / Node 升级 / 新增系统包) | 正常部署(`deploy-mao.sh`)后跑一次 `rebuild-runtime-image.sh`,定制层自动继承;再「重建全部」 | | 升级 Chrome | `CHROME_DEB_URL=<新版本 deb URL> bash .../rebuild-runtime-image.sh`(或改 Dockerfile.ext 默认 ARG) | | 升级 lark-cli | `LARK_CLI_VERSION=1.0.99 bash .../rebuild-runtime-image.sh`(建议同步改技能脚本 `install-lark-cli.sh` 的默认版本) | | 升级 playwright-cli | `PLAYWRIGHT_CLI_VERSION=0.1.23 bash .../rebuild-runtime-image.sh`(或改 Dockerfile.ext 默认 ARG) | | 跳过中文字体 | `INSTALL_CJK_FONTS=false bash .../rebuild-runtime-image.sh` | | apt 源 | 默认 `mirrors.aliyun.com`(本机实测 deb.debian.org 过慢);`APT_MIRROR=deb.debian.org bash ...` 可切回 | #### 容器内安装软件的网络(2026-10-31 实测) | 渠道 | 源 | 实测速度 | |---|---|---| | apt | `mirrors.aliyun.com`(镜像内 sources) | `apt-get update` 2s、装小包 1s | | pip | `mirrors.aliyun.com`(镜像内 `/etc/pip.conf`) | 装 six 0–1s(**默认 pypi.org 会 ReadTimeout 52s**,故固化国内源) | | npm | `registry.npmmirror.com`(镜像内 npm **builtin npmrc**,穿透 `NPM_CONFIG_PREFIX` / HOME 变更,用户级 `.npmrc` 仍优先) | 装小包 1s(默认 npmjs 12s) | | playwright-cli | 镜像内全局预装(0.1.22),`command -v playwright-cli` 直接命中,不再 npx 现拉 | — | #### 配套:容器 HOME 与 Java `user.home` 对齐(0.0.392) `~/.npmrc`、`.gitconfig` 这类直接读 `$HOME` 的工具,容器内 agent shell 原本就由 `ShellSessionManager.configureUserHome` 对齐到 `/opt/mao-data/users/{userId}`;但 **Java 在 Linux 上经 getpwuid 取 `user.home`(不认 `$HOME`)**,Maven / Gradle 的 `~/.m2`、`~/.gradle` 会落到容器内 `/root`(容器重建即丢,且读不到用户目录里挂载的 `settings.xml`)。0.0.392 做了两处对齐(见 CHANGELOG): 1. 容器创建注入 `HOME=/opt/mao-data/users/{userId}`(覆盖容器级进程、MCP 子进程等非 shell 场景); 2. `runtime-agent` 启动入口把 `/etc/passwd` 中 root 的 home 幂等对齐到同一路径(Java 生效;失败仅告警不阻塞启动)。 效果:容器内 `mvn` 的 `settings.xml`(私服 mirror)与 `~/.m2/repository` 本地仓库、npm 凭据、`.cache` 全部落在持久挂载的用户目录,与宿主机模式语义一致。 #### 注意事项 1. **每次重建镜像后必须「重建全部」**:镜像更新不会自动应用到已存在容器(`docker start` 复用旧容器)。 2. **唯一的人工适配点**:若上游把「镜像重建/依赖对齐」自动化进部署流程(依赖对齐方案 D5:构建到临时 tag → 冒烟 → re-tag `latest`),会用基础版覆盖 `latest`,届时需把本 overlay 挂进该链路。 3. **失败语义**:overlay 构建失败(如 Chrome deb URL 失效)时,`latest` 停在新基础版(无定制),失败显性、不静默;修好重跑即可。 4. **docker build 上下文**:定制层构建上下文就是 `/data/runtime-image-ext/`(只有 Dockerfile),不会打包 `/data` 或仓库,构建快。 5. **历史 tag**:每次构建留一个 `mao-runtime:ext-<时间戳>` 便于回溯/回滚;确认新镜像稳定后可 `docker rmi` 清理旧 tag(不影响运行中容器)。 --- ## 5. 方案 B:容器启动脚本幂等安装(否决) | 问题 | 说明 | |---|---| | 启动超时 | 容器启动需同步等待下载+安装(Chrome 139 MB + 103 个包,30–60 s+),与现有 `startTimeoutMs=15000` 冲突 | | 首用竞态 | 改异步后台安装则首用可能撞上「还没装完」,且错误表现不稳定(偶发失败比确定性失败更难排查) | | 重复开销 | 每容器 × 每次重建都重复下载 140 MB+,N 个用户在线即 N 倍外网带宽 | | 复杂度 | 需幂等锁、失败重试、版本标记、日志可观测,且每次容器启动都增加风险面 | | 前提 | 仅当镜像构建机无法访问 `dl.google.com` 时才值得考虑;实测可达(HTTP 200) | --- ## 6. 方案 C:持久目录(兜底,仅 lark-cli) - lark 二进制可放 `/opt/mao-data/deps/{userId}/lark-cli/`,shim 已支持 `LARK_CLI_BIN` 覆盖;需在 session envelope 注入 env 或改技能脚本。 - Chrome 依赖 103 个系统包(gtk/x11/systemd 等),无法干净放入持久目录,必须来自镜像层。 - 定位:若镜像重建链路长期不可用(如构建机网络受限)时的降级方案,不作为主路径。 --- ## 7. 决策点与 overlay 默认值 > 下表「overlay 默认」列为 §4.5 两个文件当前采用的默认值;调整时改文件默认值,或按 §4.5 表用环境变量覆盖。 | # | 决策点 | 建议 | overlay 默认 | |---|---|---|---| | Q1 | Chrome 版本策略 | 固定版本 deb URL,升级时显式更新;不建议 `_current` 滚动 | `CHROME_DEB_URL` = 156.0.8078.79-1 ✔ | | Q2 | 中文字体 | **建议加** `fonts-noto-cjk`(+91 MB):否则截图/PDF 中文豆腐块 | `INSTALL_CJK_FONTS=true` ✔ | | Q3 | lark-cli 版本 | 镜像与技能脚本默认版本对齐到最新(1.0.98) | `LARK_CLI_VERSION=1.0.98`;技能脚本仍锁 1.0.96,建议同步(待办) | | Q4 | 容器重建触发 | 短期:手动「重建全部」;中期:镜像 ID/标签纳入容器漂移检测(与依赖对齐方案 D3/D5 合并) | 手动;脚本末尾提示 ✔ | | Q5 | 是否加 precheck 冒烟 | 可选(官方 precheck 增强) | 暂不做(overlay 不涉及官方 precheck);脚本自带构建后冒烟 ✔ | --- ## 8. 部署与验证清单(2026-10-31 已完成 ✔) 本部署走 §4.5 overlay 形态(不改仓库,无需 CHANGELOG): 1. 把本会话工作区 `docs/runtime-image-ext/` 下两个文件复制到宿主机 `/data/runtime-image-ext/`(宿主机路径:`/opt/mao-data/workspace/feishu-chat/2/<会话>/docs/runtime-image-ext/`); 2. `bash /data/runtime-image-ext/rebuild-runtime-image.sh`(脚本自带构建后冒烟:`chrome --version` / `lark-cli --version`); 3. 管理后台「容器管理 → 重建全部」; 4. 重建后验证: - `/opt/google/chrome/chrome --version`; - playwright 技能跑一次 `playwright-cli open` + 中文截图; - `lark-cli --version`(走 shim)与 `lark-cli skills list`。 > 备选:若将来决定惠及所有部署者,改走 §4.1–4.4 的仓库形态(A2),届时补 CHANGELOG。