原 883 行单体 README 信息密度过高且重复(配置差异表出现 2 次、缓存说明 散落多处)。按主题拆分: 主页 README.md (150行): - 一句话简介 + 功能特性(精简) + 架构(目录树+数据流) + 快速开始 - 文档索引表(链接到 6 个子文档,每行一句话说明) - 入口地址表 + 依赖(精简) docs/ 子文档(原样搬运,不重写): - DEPLOYMENT.md (170行) CPU/GPU 部署、模型选型、CPU↔GPU 切换 - CONFIG.md (187行) 配置差异表、完整字段表、配置示例 - DOCKER.md (185行) 构建/重建/缓存分层/until根因/Volume - API.md (70行) HTTP接口表、分片上传协议、示例 - ARCHITECTURE.md(140行) 断句算法、显存策略、GPU优化、缓存清理 - FAQ.md (42行) 6 条常见问题 每个子文档顶部加「← 返回主页」链接,相关处加交叉引用 (如 DEPLOYMENT 提到缓存时链接 DOCKER.md)。无内容丢失。
7.6 KiB
← 返回主页
Docker 说明
一份 Dockerfile 出 CPU / GPU 两个镜像,依赖层缓存复用,改代码秒级重建。本文档覆盖 构建、重建、缓存管理与 Volume 挂载。部署流程见 部署指南。
一份 Dockerfile,两个镜像
ARG VARIANT=cpu|gpu 控制基础镜像与 torch 轮子:
| VARIANT | 基础镜像 | torch |
|---|---|---|
cpu(默认) |
python:3.12-slim |
CPU 版(--index-url .../whl/cpu) |
gpu |
nvidia/cuda:12.1.0-runtime-ubuntu22.04 |
CUDA 版 |
两个镜像的 Python 依赖列表(requirements.txt)完全一致,仅 torch 不同。镜像内 apt 装
ffmpeg + patchelf。
安全约束:PyTorch CPU wheel 与 GPU(CUDA) wheel 是两个不兼容二进制包,CPU 版
torch.cuda.is_available()=False,GPU 版 =True。torch 必须按 VARIANT 分叉装不同 wheel,
绝不能跨 variant 共享依赖层。deps 阶段用 FROM base-${VARIANT},CPU/GPU 是两条独立
构建链,各自装对应 torch。
新建 / 重建容器
项目提供 setup.sh / start.sh / stop.sh 包装脚本,也可直接用 docker / docker compose。
首次新建(新机器 / 全新拉取代码后)
# 1. 构建镜像 + 生成 config.yaml(CPU 默认)
./setup.sh
# GPU:AUDIO2TEXT_VARIANT=gpu ./setup.sh
# 2. 启动容器
./start.sh
# GPU:start.sh 检测到 audio2text:gpu 镜像 + nvidia-smi 自动加 --gpus all
setup.sh 做三件事:检查 docker → 构建 audio2text:{variant} 镜像 → 把
config.{variant}.yaml 复制为 config.yaml(运行时实际读取的文件)。
重建镜像(改了 app 代码或 requirements 后)
依赖层(apt + pip + torch)由 BuildKit 缓存挂载复用,只有 COPY app 层重建,通常
30 秒内完成。重建不会动运行时数据(./data / ./models 是挂载的 volume)。
# CPU:直接重跑 setup.sh(幂等,会复用缓存层)
./setup.sh
# 或显式构建:
docker build --build-arg VARIANT=cpu -t audio2text:cpu .
# GPU:
AUDIO2TEXT_VARIANT=gpu ./setup.sh
# 或:
docker build --build-arg VARIANT=gpu -t audio2text:gpu .
# 重建后重启容器(替换运行中的旧镜像):
./stop.sh && ./start.sh
改配置(不重建镜像)
config.yaml 是只读挂载,改完重启容器即生效,无需重建镜像:
cp config.gpu.yaml config.yaml # 切换配置(或直接编辑 config.yaml)
./stop.sh && ./start.sh
改依赖(requirements.txt / torch 版本)
会触发 deps 层重建,耗时较长(重装 torch + 全部依赖,CPU ~3 分钟,GPU ~5 分钟)。
BuildKit 的 pip 缓存挂载(/root/.cache/pip)跨构建复用已下载的 wheel,二次构建会快
很多。
# 编辑 requirements.txt 后
./setup.sh # 或 docker build --build-arg VARIANT=gpu -t audio2text:gpu .
./stop.sh && ./start.sh
docker compose(替代脚本)
docker compose --profile dev up -d --build # 开发:源码挂载 + uvicorn reload,改代码零重建
docker compose --profile cpu up -d --build # CPU 生产
docker compose --profile gpu up -d --build # GPU 生产(需 nvidia runtime)
缓存分层与删除边界
这套构建涉及三类缓存,删除策略截然不同,乱删会导致全量重建:
| 缓存类型 | 位置 | 存什么 | 能删吗 | 删了会怎样 |
|---|---|---|---|---|
| BuildKit 构建缓存 | Docker 内部(docker builder 管理) |
Dockerfile 各层(base / deps / final)的构建产物 | ⚠️ 谨慎,见下方 | 命中失效 → 该层及下游全量重建 |
| pip wheel 缓存 | BuildKit cache mount /root/.cache/pip |
下载过的 .whl 文件 |
✅ 可删 | 下次构建重新下载 wheel(不重编译) |
| 模型缓存 | ./models volume(容器内 /models) |
Whisper / NLLB 权重(HF + ctranslate2) | ✅ 可删 | 下次启动重新下载模型(~5.5GB GPU) |
| 运行时数据 | ./data volume(容器内 /data) |
上传视频 / 中间音频 / 输出字幕 / SQLite | ⚠️ 视情况 | 删了任务历史和产物全没 |
⚠️ 不要用 docker builder prune --filter until
这是踩过的坑。BuildKit 的 --filter "until=30m"(或任意时长)会清除"最近 N 分钟未
访问"的缓存层。问题在于:稳定的基础层(如 base-gpu 的 apt 装 python3.12)只在
首次构建时执行一次,之后每次构建都直接 CACHED 跳过——它的"最后访问时间"一直停在首次
构建那一刻,永远不会更新。于是 --filter "until=..." 会把这些仍然在用的稳定层当成
"很久没访问"清掉,导致下一次构建从 base 层开始全量重来(GPU 镜像 ~10 分钟 + 重新下载
torch ~2.5GB)。
正确做法:
# ✅ 想清理磁盘、释放 BuildKit 缓存:用不带 filter 的 prune(清全部未引用缓存)
docker builder prune -f
# 或只清 dangling(悬挂的、无引用的中间层)
docker builder prune -f --filter "type=regular"
# ✅ 清旧镜像(不影响构建缓存)
docker image prune -a # 删所有未被容器使用的镜像
docker image prune # 只删 dangling 镜像
# ✅ 清 pip wheel 缓存(BuildKit cache mount,安全)
docker builder prune -f --filter "type=exec.cachemount"
# ❌ 永远不要这样用——会清掉仍在用的稳定 base 层
docker builder prune -f --filter "until=30m"
docker builder prune -f --filter "until=24h"
根因:BuildKit 的
untilfilter 按"最后访问时间"判定,而非"是否仍在被引用"。CACHED 跳过的层不会刷新访问时间,于是被误判为可回收。这是 BuildKit 的已知行为,不是 bug, 但对"稳定 base + 频繁改代码"的构建模式特别致命。详见 moby/buildkit#2414。
什么时候需要主动清缓存
- 磁盘紧张:
docker builder prune -f+docker image prune释放空间 - 依赖换了 torch / CUDA 大版本:BuildKit 可能复用了不兼容的旧 wheel,清 pip 缓存
mount 强制重下:
docker builder prune -f --filter "type=exec.cachemount" - 换 VARIANT(cpu↔gpu):不需要清——两条构建链独立,缓存互不干扰
- 想从零验证构建:
docker builder prune -af清全部,模拟新机器首次构建
模型缓存(./models)
模型权重在 ./models volume(容器内 HF_HOME=/models/huggingface、
CT2_CACHE=/models/ctranslate2),跨容器复用。首次启动下载,之后秒起。
# 查看模型缓存大小
du -sh ./models
# 删了强制重下(GPU 大模型 ~5.5GB,建议用 prefetch 脚本提前下好)
rm -rf ./models
./scripts/prefetch_models.sh config.gpu.yaml
Volume 挂载
| 容器路径 | 宿主路径 | 用途 | 删除影响 |
|---|---|---|---|
/data |
./data(CPU)/ ./data-gpu(GPU) |
上传视频、中间音频、输出字幕、SQLite | 任务历史和产物全没 |
/models |
./models |
模型缓存(HF + ctranslate2),跨容器复用 | 下次启动重下模型 |
/app/config.yaml |
./config.yaml(只读) |
配置文件 | 改配置需重启容器 |
镜像本身无状态、无敏感数据。
ctranslate2 可执行栈修复
ctranslate2 的 .so(在 ctranslate2.libs/ 隐藏目录)带 PT_GNU_STACK 可执行栈标志,
在某些内核 + Docker 组合下会报 cannot enable executable stack as shared object requires。
Dockerfile 在构建时用 patchelf --clear-execstack 清掉该标志,无需放宽容器安全策略。
构建末尾有 python -c "import ctranslate2" 验证。