fix(docker): 数据改用 Docker 命名卷,修容器打不开数据库的问题
## 现象
容器跑着跑着页面全部 500,应用日志:
sqlite3.OperationalError: unable to open database file
(db.py:33, conn.execute("PRAGMA journal_mode=WAL"))
## 根因(已最小复现)
数据原本用绑定挂载(./data:/app/data)。Windows + Docker Desktop 的绑定挂载走 9p
(mount 里是 type 9p, aname=drvfs;path=C:\)。9p 本身支持 WAL(新建库能开 WAL),
但**宿主的 Windows 进程打开过这个 WAL 库之后**,容器侧缓存的 -shm 映射就失效,
下一次连接无法重建共享内存文件 → 打不开数据库,且**不会自愈**。
复现:
docker compose -f docker-compose.yml -f docker-compose.hostdir.yml up -d
docker compose exec portal python -c "..." # OK, 1665 条
python manage.py stats # 宿主侧纯读一次
docker compose exec portal python -c "..." # ERR unable to open database file
# 只有 docker compose restart portal 才恢复
## 处理
- docker-compose.yml 改用命名卷 wb_data / wb_logs(容器独占 /app/data)
- 新增 docker-compose.hostdir.yml 叠加层:需要宿主目录时用
docker compose -f docker-compose.yml -f docker-compose.hostdir.yml up -d
(注明**只建议 Linux**;Linux 的 bind mount 与容器同一文件系统,无此问题)
- 数据迁移:docker run --rm -v workbuddy-portal_wb_data:/to -v "$PWD/data":/from:ro \
alpine:3.20 sh -c 'cp -a /from/. /to/'
- 文档同步:DEPLOYMENT 2.4/5.4/第六节全部改为命名卷 + 备份恢复用 docker run;
新增第九节「Windows 绑定挂载的坑」(含复现步骤);FAQ、USER-GUIDE、README、CHANGELOG 同步
## 验证
- 宿主跑 manage.py stats 与 smoke.py 之后,容器侧仍能正常读写(此前会立刻失效)
- 容器实例 check_live 56/56;离线 smoke 99/99;hostdir 叠加层 config 校验通过
这个提交包含在:
+11
-1
@@ -15,7 +15,8 @@
|
||||
### 新增
|
||||
|
||||
- **Docker 化**:多阶段 `Dockerfile`(依赖层与运行层分离,改业务代码不触发重装依赖)、
|
||||
`docker-compose.yml`(单服务、数据绑定挂载、健康检查、日志轮转)、
|
||||
`docker-compose.yml`(单服务、**数据放 Docker 命名卷**、健康检查、日志轮转)、
|
||||
`docker-compose.hostdir.yml`(可选叠加层:把数据/日志放到宿主机目录,仅建议 Linux)、
|
||||
`docker/entrypoint.sh`(幂等初始化 → exec 交接)、`docker/healthcheck.py`(纯标准库)、
|
||||
`.dockerignore`、`.env.example`
|
||||
- **容器环境变量**:`WB_HOST` `WB_PORT` `WB_DATA_DIR` `WB_LOG_DIR` `WB_DB`
|
||||
@@ -58,6 +59,15 @@
|
||||
> 真实 HTTP 只看状态码),是加上 Playwright 截图后才发现的。
|
||||
> 据此给 `tools/smoke.py` 补了「页面所有 `src`/`href` 资源引用逐个断言 200」一节。
|
||||
|
||||
另外在容器实测中发现并修掉一个**部署期才暴露**的问题:
|
||||
|
||||
| 症状 | 根因 | 处理 |
|
||||
|---|---|---|
|
||||
| 容器跑着跑着页面全 500,日志里 `sqlite3.OperationalError: unable to open database file` | 数据原本用**绑定挂载**;Windows + Docker Desktop 走 **9p**,宿主的 Windows 进程只要访问过这个 WAL 库(**纯读也会触发**),容器侧下一次连接就重建不了 `-shm`,且**不会自愈** | 改为 **Docker 命名卷**(容器独占数据目录);需要宿主目录时叠加 `docker-compose.hostdir.yml`(仅建议 Linux) |
|
||||
|
||||
最小复现:容器正常 → 宿主跑一次 `manage.py stats` → 容器立刻打不开库、且重启前不再恢复。
|
||||
已写进 [DEPLOYMENT.md 第九节](DEPLOYMENT.md#windows-绑定挂载的坑容器打不开数据库)。
|
||||
|
||||
### 安全
|
||||
|
||||
- 新增 `security.safe_next()`:登录跳转的 `next` 拒绝 `//evil.com`(协议相对 URL)、
|
||||
|
||||
在新工单中引用
屏蔽一个用户