Files
audio2text/docs/DOCKER.md
audio2text dev 1e355e6138 docs: 新增离线部署导入镜像说明 + 更新功能特性
README:
- 新增「离线部署(导入预构建镜像)」章节:docker load + 启动命令
- 功能特性更新:设置页、任务删除、进度细分、离线运行、/docs 公开

docs/DOCKER.md:
- 新增「导入预构建镜像(离线部署)」完整章节:
  前置要求、导出步骤、需拷贝文件清单、导入启动命令、离线说明、后续更新代码

.gitignore:
- 排除 *.tar(导出的镜像文件不入库)
2026-07-11 11:37:52 +08:00

274 lines
10 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

← [返回主页](../README.md)
# Docker 说明
一份 Dockerfile 出 CPU / GPU 两个镜像,依赖层缓存复用,改代码秒级重建。本文档覆盖
构建、重建、缓存管理与 Volume 挂载。部署流程见 [部署指南](./DEPLOYMENT.md)。
---
## 一份 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`
### 首次新建(新机器 / 全新拉取代码后)
```bash
# 1. 构建镜像 + 生成 config.yamlCPU 默认)
./setup.sh
# GPUAUDIO2TEXT_VARIANT=gpu ./setup.sh
# 2. 启动容器
./start.sh
# GPUstart.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
```bash
# 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` 是只读挂载,改完重启容器即生效,**无需重建镜像**
```bash
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二次构建会快
很多。
```bash
# 编辑 requirements.txt 后
./setup.sh # 或 docker build --build-arg VARIANT=gpu -t audio2text:gpu .
./stop.sh && ./start.sh
```
### docker compose替代脚本
```bash
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
```
### 导入预构建镜像(离线部署)
当目标机器无法访问 Docker Hub或构建太慢可在已构建好镜像的机器上导出 tar
拷到新机器导入,跳过整个构建过程。
#### 前置要求(新机器)
- **NVIDIA GPU 驱动**(宿主机)
- **nvidia container runtime**`nvidia-container-toolkit`
- Docker
- `config.gpu.yaml` 配置文件(从项目仓库取,或自行编写)
- 模型缓存 `./models` 目录(约 5.5GB,从源机器拷贝或联网预拉)
验证 GPU 可用:
```bash
nvidia-smi # 宿主能看到 GPU
docker run --rm --gpus all nvidia/cuda:12.1.0-runtime-ubuntu22.04 nvidia-smi
```
#### 步骤 1源机器导出镜像
```bash
# 在已构建好 audio2text:gpu 镜像的机器上
docker save -o audio2text-gpu.tar audio2text:gpu
ls -lh audio2text-gpu.tar # ~4.3GB
```
#### 步骤 2拷贝到新机器
需要拷贝的文件:
| 文件/目录 | 大小 | 说明 |
|---|---|---|
| `audio2text-gpu.tar` | ~4.3GB | Docker 镜像(含 ffmpeg + torch + faster-whisper + transformers + app 代码) |
| `config.gpu.yaml` | <1KB | GPU 配置文件 |
| `models/` | ~5.5GB | 模型缓存Whisper large-v3-turbo + NLLB distilled-1.3B 权重 |
> `models/` 可不拷贝,新机器联网时用 `prefetch_models.sh` 预拉。但离线部署必须拷贝。
```bash
# 用 scp / rsync / U盘 等方式拷贝
scp audio2text-gpu.tar config.gpu.yaml user@newhost:~/audio2text/
rsync -avP models/ user@newhost:~/audio2text/models/
```
#### 步骤 3新机器导入并启动
```bash
cd ~/audio2text
# 1. 导入镜像
docker load -i audio2text-gpu.tar
# 输出Loaded image: audio2text:gpu
# 2. 准备数据目录
mkdir -p data-gpu/uploads data-gpu/.work data-gpu/outputs
# 3. 启动容器
docker run -d --name audio2text-gpu \
--gpus all \
-p 8001:8000 \
-v "$(pwd)/data-gpu:/data" \
-v "$(pwd)/models:/models" \
-v "$(pwd)/config.gpu.yaml:/app/config.yaml:ro" \
--restart unless-stopped \
audio2text:gpu
# 4. 验证
curl -s http://127.0.0.1:8001/health | python -m json.tool
# 应见 cuda_available=true, gpu="NVIDIA GeForce RTX 3090"
```
打开 `http://127.0.0.1:8001/` 即可使用
#### 离线运行说明
镜像内置 `HF_HUB_OFFLINE=1` + `TRANSFORMERS_OFFLINE=1` 环境变量模型缓存就位后
**完全离线运行**不会尝试访问 HuggingFace这避免了离线环境下 transformers
pipeline 因网络请求超时导致的翻译失败
#### 后续更新代码
导入的镜像包含导出时的 app 代码如需更新代码有两个选择
1. **重新构建**把项目代码拷到新机器`docker build --build-arg VARIANT=gpu -t audio2text:gpu .`
2. **挂载源码**临时调试启动时加 `-v "$(pwd)/app:/app/app"` 覆盖镜像内代码
---
## 缓存分层与删除边界
这套构建涉及三类缓存**删除策略截然不同**乱删会导致全量重建
| 缓存类型 | 位置 | 存什么 | 能删吗 | 删了会怎样 |
|---|---|---|---|---|
| **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)。
正确做法
```bash
# ✅ 想清理磁盘、释放 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 的 `until` filter 按"最后访问时间"判定,而非"是否仍在被引用"。CACHED
> 跳过的层不会刷新访问时间,于是被误判为可回收。这是 BuildKit 的已知行为,不是 bug
> 但对"稳定 base + 频繁改代码"的构建模式特别致命。详见
> [moby/buildkit#2414](https://github.com/moby/buildkit/issues/2414)。
### 什么时候需要主动清缓存
- **磁盘紧张**`docker builder prune -f` + `docker image prune` 释放空间
- **依赖换了 torch / CUDA 大版本**BuildKit 可能复用了不兼容的旧 wheel pip 缓存
mount 强制重下`docker builder prune -f --filter "type=exec.cachemount"`
- ** VARIANTcpugpu**不需要清——两条构建链独立缓存互不干扰
- **想从零验证构建**`docker builder prune -af` 清全部模拟新机器首次构建
### 模型缓存(`./models`
模型权重在 `./models` volume容器内 `HF_HOME=/models/huggingface`
`CT2_CACHE=/models/ctranslate2`跨容器复用首次启动下载之后秒起
```bash
# 查看模型缓存大小
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"` 验证