From c305daf415310997a11c1bf1083b44e6e5810061 Mon Sep 17 00:00:00 2001 From: ReSerendipity Date: Sun, 20 Sep 2026 08:58:39 +0800 Subject: [PATCH] =?UTF-8?q?fix(docker):=20=E4=BF=AE=E6=AD=A3=E5=AE=B9?= =?UTF-8?q?=E5=99=A8=E5=86=85=E5=8C=85=E5=AF=BC=E5=85=A5=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E4=B8=8E=20ttsuser=20=E7=9A=84=20home=EF=BC=8C=E5=AE=B9?= =?UTF-8?q?=E5=99=A8=E6=89=8D=E8=B5=B7=E5=BE=97=E6=9D=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: ReSerendipity #80 把 Compose file validity 修绿后,Docker Smoke 暴露下一层: `Wait for liveness (ping 200, max 180s)` 失败,容器日志末行是 ModuleNotFoundError: No module named 'integrated_app' 两个叠加缺陷: 1. useradd -d /app 让 ttsuser 的 HOME=/app,于是 Python 的 user-site 是 /app/.local/lib/python3.12/site-packages;而镜像把 `pip install --user` 的产物放在 /home/ttsuser/.local(第 70 行 COPY、第 85 行 PATH 都按这个假设写)。 装上了但永远不在 sys.path 上 —— CMD 的 python -c 因此找不到包。 改为 useradd -m -d /home/ttsuser。 2. 更关键:即使走 site-packages 导入,app_server._PROJECT_ROOT = dirname(dirname(/app_server.py)) 会算成 site-packages 里的路径, 而那是**只读镜像层**(冒烟用 --read-only 启动)—— data/outputs/logs 这些 compose 挂的可写卷全部落空,config.yaml 的 /app 挂载也接不上。 Dockerfile 第 77-79 行注释本来就声明要 "editable-style imports",只是没人 把 /app/app 放进 PYTHONPATH。补 ENV PYTHONPATH=/app/app 后 _PROJECT_ROOT 恰为 /app,与冒烟用例的 -v $PWD/smoke/data:/app/data 对齐。 与 #81 的相互作用(先查过再改):CSRF 密钥写失败从静默降级改成拒绝启动后, 若仍从只读 site-packages 导入就会直接起不来;修好导入路径才是让两者共存的前提。 冒烟侧 -v smoke/data:/app/data 且 chmod -R 777 smoke,可写,不会误触发硬失败。 本地无 docker 守护进程,判据是 Docker Smoke 作业本身(该环境已稳定复现原错误)。 --- Dockerfile | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8271993d..06da9661 100644 --- a/Dockerfile +++ b/Dockerfile @@ -62,11 +62,14 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* \ && python3.12 -m ensurepip --upgrade \ && groupadd -r ttsuser \ - && useradd -r -g ttsuser -d /app -s /sbin/nologin ttsuser + && useradd -r -g ttsuser -m -d /home/ttsuser -s /sbin/nologin ttsuser WORKDIR /app # Copy installed Python packages from the builder stage. +# 建用户时必须给真实 home(-m -d /home/ttsuser):ttsuser 的 home 若是 /app, +# Python 的 user-site 就成了 /app/.local/...,而这里把 pip --user 的产物放到 +# /home/ttsuser/.local —— 包装上了却永远不在 sys.path 上(容器起不来的根因之一)。 COPY --from=builder /root/.local /home/ttsuser/.local # Copy the built wheel and install it so package metadata is available. @@ -83,6 +86,13 @@ RUN chown -R ttsuser:ttsuser /home/ttsuser/.local /app USER ttsuser ENV PATH=/home/ttsuser/.local/bin:$PATH +# 让 `import integrated_app` 命中 /app/app 的源码而非只读 site-packages: +# app_server._PROJECT_ROOT = dirname(dirname(__file__ 所在包)) 只有从 /app/app 导入时 +# 才等于 /app,而 /app/data、/app/outputs、/app/logs 是 compose 与冒烟用例挂的可写卷。 +# 走 site-packages 会把工程根算进只读镜像层 —— CSRF 密钥等运行态文件写不进去 +# (旧代码对此只 warning 后静默关防护,现改为拒绝启动,故必须先修导入路径)。 +# wheel 仍保留安装:提供包元数据(版本号)与 ~/.local/bin 入口。 +ENV PYTHONPATH=/app/app EXPOSE 7869