fix(deploy): update.sh 默认源改腾讯避开阿里滞后(卡 litellm>=1.83);build 跳过改 --skip-build;进度可见
阿里 PyPI 镜像同步滞后(只到 litellm 1.82.6),撞 requirements 的 >=1.83.0。 - 默认 APT/PIP/NPM 源阿里 -> 腾讯(已到 1.88);step 2 host pip 显式 --index-url(sudo -u 会洗掉 PIP_INDEX_URL env) - ZCBOT_SKIP_SANDBOX_BUILD=1 env -> --skip-build CLI flag(开发期不留兼容) - step 2 pip 去 -q + step 4 docker build --progress=plain,部署逐行流式可见 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b6faeda83d
commit
4eacfe1bd9
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。
|
||||
|
||||
最后更新:2026-06-01(加一键部署脚本 deploy/update.sh:pull/pip/migration/build/restart/healthz,build 必先于 restart)
|
||||
最后更新:2026-06-01(update.sh 默认源改腾讯/避开阿里滞后卡 litellm;build 跳过改 --skip-build;pip 不静默 + build --progress=plain 进度可见)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
### 2026-06-01
|
||||
|
||||
- **`deploy/update.sh` 默认源改腾讯 + build 跳过改 `--skip-build` + 进度可见**:部署 build 报 `Could not find a version that satisfies the requirement litellm>=1.83.0`。**根因 = 阿里 PyPI 镜像同步滞后(只到 litellm 1.82.6),而 `requirements.txt` 钉 `>=1.83.0`(zai/GLM provider 要)**;腾讯 / 清华源已到 1.88。三处改:① 默认镜像源(APT/PIP/NPM)由阿里改腾讯(`mirrors.cloud.tencent.com`),并修 step 2 host venv pip —— 经 `sudo -u` 后 `PIP_INDEX_URL` env 被洗掉,改为脚本显式拼 `--index-url`,不再靠 host pip.conf(否则 host 仍撞阿里缺版本);② 跳过 sandbox build 从 env `ZCBOT_SKIP_SANDBOX_BUILD=1` 改为 CLI flag `--skip-build`(开发期不留 env 别名兼容),顶部 `while/case` 解析参数;③ 进度可见 —— step 2 pip 去掉 `-q`、step 4 `docker build --progress=plain`(默认 BuildKit TTY UI 多层并行折叠刷新,看着像非串行且管道下缓冲),部署时能逐行看实时输出。**纯运维脚本,DESIGN 不动**;`RUN.md` §部署 SOP 三条要点改写 + 故障表加 litellm 版本找不到一行 + 最后更新日期刷新。
|
||||
- **修 MP host 工具的全量下载(IP 被封根因)**:`mp_search_summary` 之前不给 `summary.search` 传分页参数 → mp-api 默认 `chunk_size=1000` 且 `list(docs)` 自动翻完所有页,`limit` 只在客户端切片,等于每次搜索都整库级下载 → MP 判 abusive traffic 封 host IP/ASN(403 "blocked")。改为 `search(num_chunks=1, chunk_size=limit, ...)`,服务端单页限量。`mp_get_entries` 的 `limit` 同样是"只裁剪保存、不减网络流量"的假参数,但 `get_entries_in_chemsys` 天然全量(相图用途),改不了,只在 description 里点明"拉整个 chemsys、元素越多越重、别反复调"。测试加断言锁定 `num_chunks/chunk_size` 已传。**注:宿主 IP `49.232.14.174` 当前仍被 MP 临时封(无公开时限、不确认自动解除),需发邮件 support@materialsproject.org 人工解封后才能联网复测。**
|
||||
- **加一键部署脚本 `deploy/update.sh`(Ubuntu / systemd)**:把日常部署固化成一把梭 —— `git pull --ff-only` → `pip install -r` → `db upgrade head` → `docker build` sandbox 镜像 → `systemctl restart zcbot` → `curl /healthz` 验活。**两处必须钉死的顺序 / 步骤**:① migration 不能漏(`db/migrations/env.py` 直读 `os.environ['ZCBOT_DB_URL']` 不读 .env,脚本从 .env 抠出来 `env ZCBOT_DB_URL=...` 喂进去);② **build 必须在 restart 之前** —— sandbox 容器 per-user 长驻复用、`tools/` 是 build 进镜像(非 mount),restart 时 `pool.shutdown_all` 清旧容器、下次 `ensure()` 才用新 `zcbot-sandbox:latest` 重建,顺序反了新 tools/ 要等下次重启才生效。**sandbox 每次都 build 无所谓**:重活(pip ~1G / chromium / 字体 / mermaid)都在 Dockerfile `COPY tools/` 之上,layer cache 让改代码部署秒过、只有 `requirements.txt` 变了才整体重建(~5-10min)。镜像源默认阿里(`${VAR-default}` 不带冒号,显式置空可回落官方源)。前置守卫:非 root / 非 git 仓库 / 工作区脏(已跟踪文件)/ 缺 .env 中止;healthz 15s 不 ok → dump journalctl 非零退出。`ZCBOT_SKIP_SANDBOX_BUILD=1` 跳过 build(host backend 机)。一次性 bootstrap(useradd / 写 unit / enable)不进脚本,留 RUN.md。git 可执行位已置(100755)。**纯运维脚本,DESIGN 不动**;`RUN.md` §部署 SOP 重写为指向脚本 + 手动逐条 fallback。
|
||||
- **sandbox 镜像加中文字体,修 matplotlib / mermaid 出图中文方块**:用户报绘图(mermaid + matplotlib)出的 PNG 里中文全是豆腐块 □。**根因 = `deploy/sandbox/Dockerfile` 从 `python:3.12-slim` 起一个 CJK 字体都没装**:matplotlib `skills/plot_pub/style.py::_find_chinese_font()` 扫候选无果退回 Arial/DejaVu;mermaid 经 mmdc→chromium 渲染,chromium 经 fontconfig 也找不到中文字形;`skills/ppt/scripts/render_icon.py` 引用的 `wqy-microhei.ttc` / `NotoSansCJK-Regular.ttc` 路径根本不存在。三处同一病根。**改法**:① Dockerfile chromium 块后加一层 `apt-get install fonts-noto-cjk fonts-wqy-microhei fontconfig && fc-cache -f`(Noto 出版级 +~330MB,wqy 兜底 +~5MB 且匹配 style.py 现有候选 / render_icon 引用路径);② `style.py` 候选清单首位加 `"Noto Sans CJK SC"` 让 matplotlib 优先用 Noto。fontconfig 刷缓存供 chromium 选字;matplotlib 走自家 font_manager 扫 `/usr/share/fonts` 运行时首用自动建缓存,无需额外处理。**否决**仅装 wqy(体积小但黑体不如 Noto 精致,出版图略糙)/ 仅装 Noto(render_icon 第一候选 wqy.ttc 落空走 fallback OK 但不够干净)。**纯镜像 + 配置改,DESIGN 不动**(无架构 / 取舍 / schema 变化);`RUN.md` 故障表加一行(中文方块 → 重 build 镜像 + 清旧容器 + `fc-list :lang=zh` 验证)。**生效**:改了 Dockerfile 必须 `docker build` 重建 + `docker rm -f` 清旧容器 + restart web,旧容器仍跑老镜像不会自动更新。
|
||||
|
|
|
|||
8
RUN.md
8
RUN.md
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> 怎么把 zcbot 跑起来。env / 常用命令 / 故障兜底。设计看 `DESIGN.md`,进度看 `PROGRESS.md`。
|
||||
|
||||
最后更新:2026-06-01(加一键部署脚本 deploy/update.sh;§部署 SOP 重写为指向脚本 + 手动 fallback)
|
||||
最后更新:2026-06-01(update.sh 默认源改腾讯/阿里滞后卡 litellm;build 跳过改 --skip-build;pip 不静默 + build --progress=plain)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -262,8 +262,9 @@ sudo bash /opt/zcbot/deploy/update.sh
|
|||
脚本顺序写死:`git pull --ff-only` → `pip install -r` → `db upgrade head` → **`docker build` sandbox 镜像** → **`systemctl restart zcbot`** → `curl /healthz` 验活。要点:
|
||||
|
||||
- **build 必须在 restart 之前**:sandbox 容器 per-user 长驻 + 复用,`tools/` 是 build 进镜像的(非 mount)。restart 时 `shutdown_all` 清旧容器,下次 `ensure()` 才用新 `zcbot-sandbox:latest` 重建 —— 顺序反了新 tools/ 要等下次重启才生效。
|
||||
- **sandbox build 每次都跑没关系**:layer cache 让重活(pip ~1G / chromium / 字体 / mermaid,都在 `COPY tools/` 之上)在改代码部署时秒过;只有 `requirements.txt` 变了才整体重建(~5-10min,正好也是该重建的时候)。host backend 机器不想动 docker:`ZCBOT_SKIP_SANDBOX_BUILD=1 sudo -E bash deploy/update.sh`。
|
||||
- **镜像源默认阿里**(`APT_MIRROR=mirrors.aliyun.com` / `PIP_INDEX_URL=mirrors.aliyun.com/pypi/simple/` / `NPM_REGISTRY=registry.npmmirror.com`,境内快)。要命中 docker cache 就别两组源来回换(换源从 pip 层炸开全重跑)。想用官方源:`PIP_INDEX_URL= sudo -E bash deploy/update.sh`(置空即回落 Dockerfile 官方默认)。
|
||||
- **sandbox build 每次都跑没关系**:layer cache 让重活(pip ~1G / chromium / 字体 / mermaid,都在 `COPY tools/` 之上)在改代码部署时秒过;只有 `requirements.txt` 变了才整体重建(~5-10min,正好也是该重建的时候)。host backend 机器 / 临时不想动 docker:`sudo bash deploy/update.sh --skip-build`。
|
||||
- **镜像源默认腾讯**(`APT_MIRROR=mirrors.cloud.tencent.com` / `PIP_INDEX_URL=mirrors.cloud.tencent.com/pypi/simple/` / `NPM_REGISTRY=mirrors.cloud.tencent.com/npm/`,境内快且新包同步及时)。**阿里 PyPI 镜像同步滞后**(litellm 一度只到 1.82.6,卡死 `requirements.txt` 的 `>=1.83.0`),故默认避开阿里。要命中 docker cache 就别两组源来回换(换源从 pip 层炸开全重跑)。想用官方源:`PIP_INDEX_URL= sudo -E bash deploy/update.sh`(置空即回落 Dockerfile 官方默认)。host venv 的 step 2 pip 也吃这个源(脚本显式 `--index-url`,不靠 host pip.conf)。
|
||||
- **进度可见**:step 2 pip 不带 `-q`、step 4 `docker build --progress=plain`,部署时能逐行看到装包 / 各 build 层的实时输出(默认 BuildKit TTY UI 会折叠刷新、看着像非串行)。
|
||||
- **migration 取 DB URL**:`db/migrations/env.py` 直接读 `os.environ['ZCBOT_DB_URL']`(不读 .env),脚本从 `.env` 抠出来显式 `env ZCBOT_DB_URL=... ` 喂进 `main.py db upgrade`。
|
||||
- **前置守卫**:非 root / 不是 git 仓库 / 工作区脏(已跟踪文件有未提交改动)/ 缺 .env → 直接中止。`/healthz` 15s 内不返回 `ok` → dump `journalctl -n 50` 并以非零退出。
|
||||
|
||||
|
|
@ -598,6 +599,7 @@ sudo xfs_quota -x -c "limit -p bhard=10g zcbot_<user_uuid>" /opt
|
|||
| `[startup] [warn] fs quota: <fstype> on ...` | workspace 所在 fs 没启 OS 层 quota。dogfood 阶段忽略;外部用户开放前必须升级 xfs prjquota / ext4 project / zfs(详 RUN.md「配额硬化」段) |
|
||||
| `docker run zcbot-sandbox:latest` 报 `Unable to find image` | 镜像没 build。`sudo -u zcbot docker build -f deploy/sandbox/Dockerfile --build-arg HOST_UID=$(id -u zcbot) --build-arg HOST_GID=$(id -g zcbot) -t zcbot-sandbox:latest .` |
|
||||
| 镜像 build pip 报 `ReadTimeoutError: HTTPSConnectionPool(host='files.pythonhosted.org', ...)` | 境内访问 PyPI 抖动。加 `--build-arg PIP_INDEX_URL=https://mirrors.cloud.tencent.com/pypi/simple/`(腾讯云内网)或阿里云 / 清华源,详 RUN.md「镜像构建」段。Dockerfile 已把 pip timeout 拉到 60s,主因仍是源不通而非超时 |
|
||||
| pip 报 `Could not find a version that satisfies the requirement litellm>=1.83.0`(伴随一串 `Ignored ... yanked versions: 0.1.xxxx`) | 用的镜像源同步滞后,没有该新版本。**阿里 PyPI 一度只到 litellm 1.82.6** —— update.sh 默认已改腾讯源(到 1.88)。若手动 build 撞到:换 `PIP_INDEX_URL=https://mirrors.cloud.tencent.com/pypi/simple/` 或清华源。那串 `0.1.xxxx` 是 litellm 远古版本,纯干扰信息 |
|
||||
| 镜像 build npm 装 mermaid-cli 慢 / fail | npm 源境内慢。加 `--build-arg NPM_REGISTRY=https://mirrors.cloud.tencent.com/npm/`(腾讯云)或 `https://registry.npmmirror.com/`(阿里) |
|
||||
| 镜像 build apt 报 `OpenSSL error: ... unexpected eof while reading` | 腾讯云 / 阿里 mirror HTTPS 端偶发 close_notify 缺失,OpenSSL 3 严格 fail。改用 http 形式:`--build-arg APT_MIRROR=http://mirrors.cloud.tencent.com`(apt 包 GPG 签名校验,无 HTTPS 安全收益)。Dockerfile 已配 apt retry=5 + 关 pipeline,重 build 一般直接过 |
|
||||
| 容器内 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)` |
|
||||
|
|
|
|||
|
|
@ -12,17 +12,29 @@
|
|||
# 6) curl /healthz 验活
|
||||
#
|
||||
# 怎么跑:在部署机 `sudo deploy/update.sh`(需 root 跑 systemctl;文件操作降到 $APP_USER)。
|
||||
# --skip-build 跳过第 4 步 sandbox 镜像重建(host backend 机器 / 临时不想动 docker)
|
||||
# 一次性 bootstrap(useradd / 写 systemd unit / enable)不在这里 —— 见 RUN.md §部署 一次性。
|
||||
#
|
||||
# 可调 env(不传则用默认 / Dockerfile 默认):
|
||||
# APP_DIR(默认=脚本上级目录,即仓库根) APP_USER(默认=部署目录属主) SERVICE(zcbot) PORT(8765)
|
||||
# APT_MIRROR / PIP_INDEX_URL / PIP_TRUSTED_HOST / NPM_REGISTRY 镜像源,
|
||||
# 默认阿里源(境内快);要稳定命中 docker cache 就别在两组源之间来回换
|
||||
# (换源会从 pip 层炸开全重跑)。想用官方源:PIP_INDEX_URL= sudo -E bash ...(置空)
|
||||
# ZCBOT_SKIP_SANDBOX_BUILD=1 跳过第 4 步(host backend 机器 / 临时不想动 docker)
|
||||
# 默认腾讯源(境内快,litellm 等新包同步及时;阿里源滞后过——曾卡死在没有的版本上)。
|
||||
# 要稳定命中 docker cache 就别在两组源之间来回换(换源会从 pip 层炸开全重跑)。
|
||||
# 想用官方源:PIP_INDEX_URL= sudo -E bash ...(置空)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── 参数 ──────────────────────────────────────────────────────
|
||||
# 默认 build;--skip-build 显式跳过(不为旧用法留 env 别名,开发期不留兼容)。
|
||||
SKIP_BUILD=0
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--skip-build) SKIP_BUILD=1; shift ;;
|
||||
-h|--help) echo "用法:sudo $0 [--skip-build]"; exit 0 ;;
|
||||
*) echo "[deploy] ERROR: 未知参数:$1(只支持 --skip-build)" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 部署目录 = 本脚本所在目录(deploy/)的上一级,不写死路径;仍可 env 覆盖。
|
||||
# readlink -f 解析软链 + 相对路径,兼容 `sudo ./update.sh` / `sudo bash /abs/deploy/update.sh`
|
||||
# / 经软链调用(Ubuntu coreutils 有 readlink -f)。
|
||||
|
|
@ -37,11 +49,12 @@ IMAGE="zcbot-sandbox:latest"
|
|||
PY="$APP_DIR/.venv/bin/python"
|
||||
HEALTH_URL="http://127.0.0.1:${PORT}/healthz"
|
||||
|
||||
# 镜像源默认阿里(境内快);Dockerfile sed 只替 host 前缀,mirror 站目录结构与官方一致。
|
||||
# 镜像源默认腾讯(境内快 + 新包同步及时;Dockerfile sed 只替 host 前缀,目录结构同官方)。
|
||||
# 阿里 PyPI 镜像同步滞后(litellm 曾只到 1.82.6,卡死 requirements 的 >=1.83.0),故默认改腾讯。
|
||||
# 置空(如 PIP_INDEX_URL= sudo -E ...)则该项不传 build-arg,回落 Dockerfile 官方源默认。
|
||||
APT_MIRROR="${APT_MIRROR-https://mirrors.aliyun.com}"
|
||||
PIP_INDEX_URL="${PIP_INDEX_URL-https://mirrors.aliyun.com/pypi/simple/}"
|
||||
NPM_REGISTRY="${NPM_REGISTRY-https://registry.npmmirror.com/}"
|
||||
APT_MIRROR="${APT_MIRROR-https://mirrors.cloud.tencent.com}"
|
||||
PIP_INDEX_URL="${PIP_INDEX_URL-https://mirrors.cloud.tencent.com/pypi/simple/}"
|
||||
NPM_REGISTRY="${NPM_REGISTRY-https://mirrors.cloud.tencent.com/npm/}"
|
||||
|
||||
log() { echo "[deploy] $*"; }
|
||||
fail() { echo "[deploy] ERROR: $*" >&2; exit 1; }
|
||||
|
|
@ -80,8 +93,14 @@ else
|
|||
fi
|
||||
|
||||
# ── 2) 依赖 ───────────────────────────────────────────────────
|
||||
log "pip install -r requirements.txt ..."
|
||||
asuser "$PY" -m pip install -q -r "$APP_DIR/requirements.txt"
|
||||
# host venv 的 pip 也走同一镜像源 —— 经 sudo -u 后 PIP_INDEX_URL env 会被洗掉,
|
||||
# 不显式 --index-url 的话 host 用自己的 pip.conf(可能仍指阿里,撞 litellm>=1.83 缺版本)。
|
||||
# 不带 -q:依赖装包是耗时步骤,留输出让部署时能看到进度(下载哪个包 / 卡在哪)。
|
||||
pip_args=(-r "$APP_DIR/requirements.txt" --timeout 60)
|
||||
[ -n "${PIP_INDEX_URL:-}" ] && pip_args+=(--index-url "${PIP_INDEX_URL}")
|
||||
[ -n "${PIP_TRUSTED_HOST:-}" ] && pip_args+=(--trusted-host "${PIP_TRUSTED_HOST}")
|
||||
log "pip install -r requirements.txt(源:${PIP_INDEX_URL:-官方默认})..."
|
||||
asuser "$PY" -m pip install "${pip_args[@]}"
|
||||
|
||||
# ── 3) DB migration ──────────────────────────────────────────
|
||||
# env.py 直接读 os.environ['ZCBOT_DB_URL'](不读 .env),这里从 .env 抠出来显式喂进去。
|
||||
|
|
@ -92,8 +111,8 @@ log "db upgrade head ..."
|
|||
asuser env ZCBOT_DB_URL="$DB_URL" "$PY" "$APP_DIR/main.py" db upgrade head
|
||||
|
||||
# ── 4) 重建 sandbox 镜像 ──────────────────────────────────────
|
||||
if [ "${ZCBOT_SKIP_SANDBOX_BUILD:-0}" = "1" ]; then
|
||||
log "ZCBOT_SKIP_SANDBOX_BUILD=1,跳过 sandbox 镜像重建"
|
||||
if [ "$SKIP_BUILD" = "1" ]; then
|
||||
log "--skip-build,跳过 sandbox 镜像重建"
|
||||
else
|
||||
# build-arg 稳定才命中 cache:HOST_UID/GID 跟 host $APP_USER 对齐(bind mount owner),
|
||||
# 镜像源仅在对应 env 非空时才传(传空会把 Dockerfile 默认覆盖成空串,pip 直接挂)。
|
||||
|
|
@ -106,8 +125,11 @@ else
|
|||
[ -n "${PIP_TRUSTED_HOST:-}" ] && build_args+=(--build-arg "PIP_TRUSTED_HOST=${PIP_TRUSTED_HOST}")
|
||||
[ -n "${NPM_REGISTRY:-}" ] && build_args+=(--build-arg "NPM_REGISTRY=${NPM_REGISTRY}")
|
||||
|
||||
# --progress=plain:逐行流式输出(默认 TTY 进度 UI 多层并行折叠刷新,看着像非串行
|
||||
# 且非交互管道下会缓冲);部署时要能持续看到每层在干啥,故强制 plain。
|
||||
log "docker build $IMAGE(改代码秒过 / requirements 变了 ~5-10min)..."
|
||||
asuser docker build \
|
||||
--progress=plain \
|
||||
-f "$APP_DIR/deploy/sandbox/Dockerfile" \
|
||||
"${build_args[@]}" \
|
||||
-t "$IMAGE" \
|
||||
|
|
|
|||
Loading…
Reference in New Issue