时间:2026-09-20 20:00 → 2026-09-21 00:30 主机:REDACTED_HOSTNAME(QNAP TS-451D · QTS 5.2.10 · Docker 27.1.2-qnap8) 起因:Farmer 提问「web-calibre 容器新版本为什么不用?升级后服务起不来,也导致本地 Calibre 客户端不能升 9.x」
一、结论先行
| 问题 | 结论 |
|---|---|
| 新版本为什么不能用 | Calibre 9 删除了 books.isbn/lccn/flags,而 Calibre-Web ≤0.6.25 在 ORM 里硬编码映射了这些列 → 页面 500 |
| 是"服务起不来"吗 | 不是。容器正常启动,是页面报错 |
| 修好了吗 | ✅ 已升到 0.6.27-ls402,实测全绿 |
| 阻塞 Calibre 桌面 9.x 吗 | ✅ 已解锁,实测 Calibre 9.15 转换格式成功 |
| 自定义列要处理吗 | ❌ 不用(是 composite,在 Calibre-Web 忽略名单里) |
二、技术根因(源码级)
Calibre 9 做了什么
kovidgoyal/calibre → src/calibre/db/schema_upgrades.py → upgrade_version_26:
for x in {'flags', 'isbn', 'lccn'} & columns:
statements.append(f'ALTER TABLE books DROP COLUMN {x};')
同时新建 books_pages_link、重建 meta 视图、PRAGMA application_id = 0x63616c69、user_version 26 → 27。
关键特性:Calibre 打开书库时在 BEGIN EXCLUSIVE TRANSACTION 里原地升级,不可逆。
Calibre-Web 为什么会崩
- ≤0.6.25:
cps/db.py的Books模型有isbn = Column(...)/flags = Column(...)→ SQLAlchemy 生成SELECT ... books.isbn ...→no such column: books.isbn→ HTTP 500 - 0.6.26 起:commit
7f7fc884c2(2026-01-31)把这两列注释掉 → 不再查询 → 修复 - ⚠️ Calibre-Web 从不读
PRAGMA user_version(源码 5 处文件零命中)→ 无版本判断,只在查询时炸
为什么极难自查
错误只写进
/config/calibre-web.log(应用日志),不进docker logs(容器 stdout)。 所以看容器日志只会看到"一切正常启动",真正原因藏在 config 目录的日志文件里。
三、自定义列排查(虚惊一场)
custom_columns 表(1 行)
label = formats
name = 格式
datatype = composite ← 关键
display = {"composite_template": "{:'re(approximate_formats(), ',', ', ')'}", ...}
custom_column_1 表:0 行 ← composite 是计算列,不存数据
Calibre-Web cps/db.py:
cc_exceptions = ['composite', 'series']
if row.datatype not in cc_exceptions: # ← composite 直接跳过
→ composite 落在忽略名单内,不会触发 books_custom_column_N_link 查询
→ issue #3643(Calibre 9 + 自定义列 → 书详情页/OPDS 失败)对本库不适用
注:
cc_exceptions里之所以有composite,正是源于 2016 年的 cbw #20 —— 那位报告人的列也叫 formats、也是 composite,janeczku 据此把它加进了忽略名单。
四、决定性复现实验
在库副本上施加 upgrade_version_26 的等效迁移(删三列 + 重建 meta 视图 + application_id/user_version),然后同一份库分别跑新旧两个 Calibre-Web:
| URL | 0.6.20(旧) | 0.6.26(新) |
|---|---|---|
/ 首页 | 500 | 200 |
/login | 200 | 200 |
/opds | 200 | 200 |
/opds/books/letter/00 | 500 | 200 |
→ 症状精确复现,修复确认。(/login 正常是因为它不查书)
五、升级执行(三步走,每步都带回滚)
① 备份 一致快照(SQLite backup API,非裸拷贝)
→ REDACTED_BACKUP_DIR/
② 沙箱验证 库副本 + config 副本 + 独立端口(8093/8094/8095)
不碰生产容器/配置/书库
③ 生产切换 docker rename 旧容器保留为回滚锚点 → 同规格起新容器
→ 等待 → 验证 → 失败自动回滚
升级链:0.6.20-ls225 → 0.6.26-ls388 → 0.6.27-ls402(当日发布,含 2026-09 安全批次)
最终验证(生产 8083):
/ 200 /login 200 /opds 200
/book/1 200 ← 旧版必崩的页面
/opds/books/letter/00 200
restarts=0 stdout 错误签名=0 app.db 21 表 / 3 用户完整保留
终局实测(最有说服力的一条)
Farmer 升 Calibre 桌面 9.15 并成功转换一本书的格式 → 书库已升到 schema 27:
user_version : 27
application_id : 0x63616c69
books_pages_link : PRESENT(1065 行)
books.isbn/lccn/flags : 全部已删除
books 行数 : 1065(一本书没丢)
而 Calibre-Web 0.6.27 完全扛住:/book/1 与 /opds/* 全 200、零错误签名。
→ 如果当初没升级,此刻这两个页面必定 500。整条链路验证通过。
六、镜像加速的真相(重要可复用结论)
registry-mirrors 只对 Docker Hub(docker.io)生效。
✅ linuxserver/calibre-web:0.6.27-ls402 → 命中加速,实测 5 秒
❌ lscr.io/linuxserver/calibre-web:0.6.27-ls402 → 绕过加速,直连境外 → 卡死
- 本机 daemon 配置:
/share/CACHEDEV1_DATA/.qpkg/container-station/etc/docker.json"registry-mirrors": ["https://docker.1ms.run", "https://docker.xuanyuan.me"] - 实测
docker pull hello-world5 秒完成 → 加速本身完全正常 - 规则:拉 LinuxServer 镜像一律用 Docker Hub 路径,不要用
lscr.io(其镜像同时发布在 Hub 上) - 不需要改
daemon.json—— 改它要重启 dockerd,会导致所有容器重启,代价大而无必要 - ⚠️ 容器 Station UI 走的是 Hub + 加速,所以 UI 能拉、命令行用
lscr.io不能拉 —— 差异就在仓库路径
