跳转至
RAG验证与交付Docker 交付深化与排障

第 20 章:Docker 交付深化与排障

上一章:LangSmith 观测、Trace 与生产化部署

本章目标

  • 复盘 Docker 在企业级 RAG 项目中的作用,并把第 1 章的环境底座提升为交付能力
  • 能从交付视角解释镜像、容器、Compose、网络、卷挂载、健康检查和环境变量
  • 读懂本项目的 Dockerfiledocker-compose.yml.env.compose.example
  • 掌握 Windows / Linux 都适用的 V1 部署、入库、验收和排障命令
  • 能解释为什么本项目把 MySQL、Milvus、MinIO、etcd 和 API 都纳入 Compose 管理

本章边界

Docker 基础概念和最小启动命令已经在第 1 章前置学习。本章聚焦 V1 的 Docker 交付闭环:如何构建镜像、启动依赖、挂载模型、初始化知识库、验证服务和排查常见问题。更复杂的 Kubernetes、滚动发布、多副本自动扩缩容和灾备演练不放在 V1 主线中。


第一部分:本项目 Docker 架构

第 01 章已经完成 Docker 最小启动。本章不重复镜像、容器和 Compose 的通用概念,直接解释项目的 Dockerfile、Compose 配置、完整交付流程和排障方法。

1.1 服务拓扑

flowchart TD
    Browser["浏览器<br/>localhost:8000 或 服务器IP:8000"] --> API["api 容器<br/>FastAPI + WebSocket"]

    subgraph Compose["Docker Compose 网络"]
        API --> MySQL["mysql<br/>版本/历史/反馈/缓存namespace/Manifest"]
        API --> Redis["redis<br/>检索缓存 / query embedding 缓存"]
        API --> Milvus["milvus<br/>FAQ/Doc Hybrid Search"]
        Milvus --> Etcd["etcd<br/>Milvus 元数据协调"]
        Milvus --> MinIO["minio<br/>Milvus 对象存储"]
        API --> Models["./models<br/>挂载到 /app/models"]
        API --> Static["./static / ./site<br/>静态页面与笔记"]
        API --> Reports["./reports / ./logs<br/>报告与日志"]
    end

1.2 六个服务分别做什么

服务 镜像 端口 作用
mysql mysql:8.4 3306 保存控制面数据:聊天、反馈、版本、缓存 namespace、Manifest、质量报告索引
redis redis:7-alpine 6379 保存 FAQ/Doc 检索缓存和 query embedding 缓存
etcd quay.io/coreos/etcd:v3.5.18 容器内访问 Milvus 元数据协调
minio minio/minio 9000 / 9001 Milvus 对象存储与控制台
milvus milvusdb/milvus:v2.5.15 19530 / 9091 向量检索、BM25 Sparse、Hybrid Search
api 本项目 Dockerfile 构建 8000 FastAPI、WebSocket、页面、诊断接口、入库脚本运行环境

这里的 api 是唯一由项目源码构建的镜像;其他服务使用官方镜像。Redis 缓存不保存普通 LLM 自由生成答案,只保存 query embedding 和带版本/权限边界的检索候选结果。


第二部分:Dockerfile 如何构建 API 镜像

项目把 API 镜像拆成两层:

  • Dockerfile.base:安装系统库、PyTorch、LangChain、Milvus、Docling、PaddleOCR、BERT 推理等重型依赖。
  • Dockerfile:基于基础镜像补齐依赖并复制当前应用代码。

日常开发改 Python 代码时,只重新构建 Dockerfile 这一层,避免每次都重新安装 4GB 级依赖。

Dockerfile.base 核心逻辑如下:

FROM python:3.12-slim

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PYTHONIOENCODING=utf-8
ENV TOKENIZERS_PARALLELISM=false
ENV HF_HUB_DISABLE_TELEMETRY=1

WORKDIR /app

ARG DEBIAN_MIRROR=https://deb.debian.org/debian
ARG DEBIAN_SECURITY_MIRROR=https://deb.debian.org/debian-security

RUN set -eux; \
    if [ -f /etc/apt/sources.list.d/debian.sources ]; then \
        sed -i \
            -e "s|https\?://deb.debian.org/debian-security|${DEBIAN_SECURITY_MIRROR}|g" \
            -e "s|https\?://deb.debian.org/debian|${DEBIAN_MIRROR}|g" \
            /etc/apt/sources.list.d/debian.sources; \
    else \
        sed -i \
            -e "s|https\?://deb.debian.org/debian-security|${DEBIAN_SECURITY_MIRROR}|g" \
            -e "s|https\?://deb.debian.org/debian|${DEBIAN_MIRROR}|g" \
            /etc/apt/sources.list; \
    fi; \
    apt-get -o APT::Update::Error-Mode=any -o Acquire::Retries=5 -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 update \
    && apt-get install -y --no-install-recommends \
        build-essential curl \
        libgl1 libglib2.0-0t64 libsm6 libxext6 libxrender1 libgomp1 \
    && rm -rf /var/lib/apt/lists/*

COPY requirements.txt ./
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install --upgrade pip \
    && pip install --extra-index-url https://download.pytorch.org/whl/cpu -r requirements.txt \
    && pip check

默认 Debian 源使用 HTTPS。Linux 服务器若无法访问默认源,可在构建时传入企业镜像:

docker build -f Dockerfile.base \
  --build-arg DEBIAN_MIRROR=https://your-debian-mirror/debian \
  --build-arg DEBIAN_SECURITY_MIRROR=https://your-debian-mirror/debian-security \
  --build-arg PIP_INDEX_URL=https://your-pypi-mirror/simple \
  --build-arg PYTORCH_INDEX_URL=https://download.pytorch.org/whl/cpu \
  -t localhost/knowforge-rag-platform-base:py312 .

DEBIAN_MIRRORDEBIAN_SECURITY_MIRROR 只影响基础镜像构建阶段的 APT 下载,PIP_INDEX_URLPYTORCH_INDEX_URL 只影响 Python 依赖下载,不会改变运行时容器访问地址。API 层也会读取同名 Compose build args:

docker compose --env-file .env.compose build \
  --build-arg PIP_INDEX_URL=https://your-pypi-mirror/simple \
  --build-arg PYTORCH_INDEX_URL=https://download.pytorch.org/whl/cpu api

Dockerfile 核心逻辑如下:

ARG APP_BASE_IMAGE=localhost/knowforge-rag-platform-base:py312
FROM ${APP_BASE_IMAGE}

WORKDIR /app

COPY requirements.txt ./
RUN --mount=type=cache,target=/root/.cache/pip \
    python -m pip install --extra-index-url https://download.pytorch.org/whl/cpu -r requirements.txt \
    && python -m pip check \
    && python -c "import fastapi, uvicorn, langchain, langchain_milvus, pymilvus, sentence_transformers, transformers, safetensors, torch, pandas, fitz, docx, pptx, docling, ragas, markdown, mkdocs, redis; print('runtime dependencies ok')"

COPY . .

CMD ["python", "-m", "uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

逐行看它做了几件事:

代码 作用
FROM python:3.12-slim 基础镜像固定 Python 运行时版本
FROM ${APP_BASE_IMAGE} API 镜像复用基础依赖镜像
ENV PYTHONUNBUFFERED=1 日志实时输出,便于 docker compose logs 查看
WORKDIR /app 容器内项目目录固定为 /app
apt-get install ... 安装文档解析、向量模型、OpenCV 相关依赖
COPY requirements.txt ./ 先复制依赖文件,利用 Docker 缓存
pip install ... -r requirements.txt 基础镜像完整安装;API 镜像补齐新增或版本变化的 Python 依赖
python -c "import ..." API 镜像构建时校验运行期依赖完整性,避免漏装依赖后才在运行期报错
COPY . . 复制项目源码
CMD uvicorn app:app 容器启动后运行 FastAPI

常见误区:

误区 正确理解
改了 Python 代码后只重启容器就行 API 镜像内代码来自构建时的 COPY . .,需要重新 build api
改了 .env.compose 要重新 build 环境变量是容器启动时注入,通常只需要 up -d --force-recreate api
模型文件会打进镜像 不会。模型通过 ./models:/app/models 挂载,避免镜像巨大

第三部分:docker-compose.yml 项目配置

3.1 API 服务的关键配置

api:
  build:
    context: .
  container_name: knowforge-api
  env_file:
    - ${ENV_FILE:-.env.compose}
  ports:
    - "${API_PORT:-8000}:8000"
  volumes:
    - ./models:/app/models
    - ./scenarios:/app/scenarios
    - ./reports:/app/reports
    - ./logs:/app/logs
    - ./site:/app/site:ro
    - ./static:/app/static:ro
  depends_on:
    mysql:
      condition: service_healthy
    milvus:
      condition: service_healthy

这里有几个关键点:

配置 作用
build.context: . 从当前项目目录构建 API 镜像
env_file .env.compose 注入容器环境变量
8000:8000 宿主机通过 8000 访问容器内 API
./models:/app/models Windows / Linux 都使用项目内模型目录挂载
./scenarios:/app/scenarios 场景资料可以在宿主机修改后被容器看到
./reports:/app/reports 入库质量报告、评测报告输出到宿主机
./logs:/app/logs 日志输出到宿主机
./site:/app/site:ro 笔记站点只读挂载
depends_on.condition MySQL 和 Milvus 健康后再启动 API

3.2 为什么模型挂载用 ./models

项目以前容易遇到一个部署问题:Windows 能用 D:/models,Linux 服务器没有这个路径。V1 当前统一使用:

- ./models:/app/models

这样 Windows 和 Linux 的部署方式一致:

项目根目录/
  models/
    bge-m3/
    bge-reranker-large/

容器内配置仍然使用:

EMBEDDING_MODEL_PATH=/app/models/bge-m3
RERANKER_MODEL_PATH=/app/models/bge-reranker-large
INTENT_MODEL_PATH=/app/models/bert_intent_classifier_v1

宿主机本地 Python 调试时使用:

EMBEDDING_MODEL_PATH=./models/bge-m3
RERANKER_MODEL_PATH=./models/bge-reranker-large
INTENT_MODEL_PATH=./models/bert_intent_classifier_v1

判断规则很简单:

API 运行位置 模型路径怎么写
API 在 Docker 容器里 /app/models/...
API 在宿主机 Python 里 ./models/...

3.3 数据卷和挂载目录

本项目同时使用两类卷。

第一类是 Docker named volume,由 Docker 管理:

Volume 保存内容
mysql_data MySQL 数据库
etcd_data etcd 数据
minio_data MinIO 对象数据
milvus_data Milvus 本地数据

第二类是 bind mount,从项目目录挂进容器:

宿主机目录 容器目录 用途
./models /app/models 本地 Embedding / Reranker 模型
./scenarios /app/scenarios 8 个业务场景资料
./reports /app/reports 入库、评测、验收报告
./logs /app/logs 应用日志
./site /app/site MkDocs 构建后的笔记站点
./static /app/static 前端静态页面

不要随意执行:

docker compose down -v

-v 会删除 named volume,MySQL 和 Milvus 数据会一起被清掉。普通停止服务使用:

docker compose --env-file .env.compose down

第四部分:环境变量和运行模式

项目保留两个模板:

文件 用途
.env.compose.example API 在 Docker 容器内运行
.env.local.example API 在宿主机 Python 里运行

二者最大的区别是网络视角:

配置项 Docker Compose 模式 本机 Python 模式
MYSQL_HOST mysql localhost
REDIS_HOST redis localhost
MILVUS_URI http://milvus:19530 http://localhost:19530
EMBEDDING_MODEL_PATH /app/models/bge-m3 ./models/bge-m3
RERANKER_MODEL_PATH /app/models/bge-reranker-large ./models/bge-reranker-large
INTENT_MODEL_PATH /app/models/bert_intent_classifier_v1 ./models/bert_intent_classifier_v1

.env.compose 最小关键配置如下:

APP_ENV=dev
ACTIVE_SCENARIO_ID=enterprise_knowledge
API_PORT=8000
ENV_FILE=.env.compose

MYSQL_HOST=mysql
MYSQL_PORT=3306
MYSQL_PUBLISHED_PORT=13307
MYSQL_USER=root
MYSQL_PASSWORD=请替换为强密码
MYSQL_DATABASE=subjects_kg

CACHE_ENABLED=true
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PUBLISHED_PORT=6379
REDIS_DB=0

MILVUS_URI=http://milvus:19530
MILVUS_PUBLISHED_PORT=19530
MILVUS_HEALTH_PUBLISHED_PORT=9091

MINIO_API_PUBLISHED_PORT=9000
MINIO_CONSOLE_PUBLISHED_PORT=9001

DASHSCOPE_API_KEY=请替换为真实可用的模型服务 Key
ADMIN_API_TOKEN=请替换为随机长令牌

EMBEDDING_MODEL_PATH=/app/models/bge-m3
RERANKER_MODEL_PATH=/app/models/bge-reranker-large
INTENT_MODEL_PATH=/app/models/bert_intent_classifier_v1

.env.compose 不提交到 Git,因为里面会包含真实 Key。


第五部分:V1 Docker 部署流程

5.1 首次准备

Windows PowerShell:

if (!(Test-Path .env.compose)) { Copy-Item .env.compose.example .env.compose }
notepad .env.compose

New-Item -ItemType Directory -Force models, logs, reports | Out-Null
Test-Path .\models\bge-m3
Test-Path .\models\bge-reranker-large

Linux Shell:

cp -n .env.compose.example .env.compose
nano .env.compose

mkdir -p models logs reports
test -d models/bge-m3
test -d models/bge-reranker-large

确认两个模型目录存在后再启动服务。

5.2 构建并启动基础设施

docker compose --env-file .env.compose up -d mysql redis etcd minio milvus
# 新机器首次部署且本地没有基础镜像时先执行:
# docker build -f Dockerfile.base -t localhost/knowforge-rag-platform-base:py312 .
docker compose --env-file .env.compose build api

查看状态:

docker compose --env-file .env.compose ps

5.3 初始化 8 个业务场景

首次部署建议重建全部场景:

docker compose --env-file .env.compose run --rm api python scripts/rebuild_scenarios.py --reset-collections --description "docker init all scenarios"

这条命令会在 API 容器里执行入库脚本,使用容器内的:

/app/scenarios
/app/models
/app/reports

5.4 启动 API

docker compose --env-file .env.compose up -d api
docker compose --env-file .env.compose logs --tail 80 api

浏览器访问:

环境 地址
Windows 本机 http://127.0.0.1:8000
Linux 局域网服务器 http://服务器IP:8000

5.5 单场景更新

只更新一个场景时,不需要重建全部 8 个场景:

docker compose --env-file .env.compose run --rm api python scripts/rebuild_kb_version.py --scenario enterprise_knowledge --new-version --force --quality-gate --activate

如果只是日常资料变更,推荐使用引用式增量版本:

docker compose --env-file .env.compose run --rm api python scripts/rebuild_kb_version.py --scenario enterprise_knowledge --new-version --incremental-from active --quality-gate --activate

只有在 Milvus collection schema 不兼容、BM25 Function 字段变化或需要清空重建时,才使用:

docker compose --env-file .env.compose run --rm api python scripts/rebuild_kb_version.py --scenario enterprise_knowledge --new-version --force --reset-collections --quality-gate --activate

第六部分:部署脚本做了什么

项目提供两个部署验收入口:

脚本 适用场景 作用
scripts/deploy/deploy_docker.ps1 Windows / PowerShell 环境 部署便捷入口,按标准顺序启动依赖、构建镜像、初始化知识库并启动 API
scripts/deploy/verify_fresh_docker_deploy.py Windows / Linux 通用 新环境一键验收入口,在部署后继续执行发布门禁、接口冒烟和缓存冒烟

PowerShell 部署入口:

.\scripts\deploy\deploy_docker.ps1

它的执行顺序可以理解为:

1. 检查 .env.compose 是否存在
2. 启动 mysql / redis / etcd / minio / milvus
3. 构建 api 镜像
4. 在 api 容器中重建并激活知识库
5. 启动 api
6. 输出 docker compose ps 状态

只初始化当前 active 场景:

.\scripts\deploy\deploy_docker.ps1 -ActiveScenarioOnly

脚本适合标准部署;排查问题时建议拆成手工命令逐步执行,这样能更快定位是构建失败、依赖未健康、入库失败还是 API 启动失败。

新环境从部署到验收一次跑完:

python scripts/deploy/verify_fresh_docker_deploy.py --evaluation-limit 3 --performance-limit 3

这条命令会按顺序执行:

1. docker compose config --quiet
2. 启动 mysql / redis / etcd / minio / milvus
3. 检查或构建基础镜像
4. 构建 api 镜像
5. 初始化全部 8 个业务场景
6. 启动 api
7. 执行 V1 发布验收:评测门禁 + 性能门禁 + Docker 集成检查
8. 执行 API 冒烟和缓存冒烟
9. 写入 reports/verification/v1_fresh_docker_acceptance.json

Linux 服务器也使用同一个 Python 脚本。区别只在浏览器访问地址:

python scripts/deploy/verify_fresh_docker_deploy.py --base-url http://192.168.88.100:8000 --evaluation-limit 3 --performance-limit 3

第七部分:Docker 部署检查

7.1 Compose 配置检查

docker compose --env-file .env.compose config --quiet

这条命令只检查 Compose 文件和环境变量能否正确解析,不启动容器。

7.2 V1 发布自检

python scripts/verify_v1_release.py
python scripts/verify_v1_release.py --include-evaluation --include-docker
python scripts/verify_v1_release.py --include-performance --include-docker
python scripts/verify_v1_release.py --include-evaluation --include-performance --include-docker

它会检查:

检查项 含义
Python 编译 主项目和测试文件语法正确
笔记站点构建 mkdocs build --strict 通过
章节实践代码对齐 codealong 与主项目关键文件一致
项目守护规则 关键边界、依赖、冻结场景符合约束
Compose config Docker Compose 配置可解析
主链路评测 --include-evaluation --include-docker 会在 api 容器里生成评测报告、执行评测门禁并导出 Bad Case 候选
性能门禁 --include-performance --include-docker 会在 api 容器里采集首 token、总耗时、阶段耗时并执行性能门禁
新环境验收 verify_fresh_docker_deploy.py 会串起构建、初始化、发布门禁、API 冒烟和缓存验收

7.3 API 冒烟检查

API 启动后执行:

docker compose --env-file .env.compose exec api python scripts/api_e2e_smoke.py --base-url http://127.0.0.1:8000
docker compose --env-file .env.compose exec api python scripts/intent/demo_intent_model.py --eval-only --output latest
docker compose --env-file .env.compose exec api python scripts/quality/cache_acceptance_smoke.py --base-url http://127.0.0.1:8000
docker compose --env-file .env.compose exec api python scripts/acceptance_smoke.py --base-url http://127.0.0.1:8000

如果在宿主机执行,也可以直接访问:

python scripts/acceptance_smoke.py --base-url http://127.0.0.1:8000
python scripts/quality/cache_acceptance_smoke.py --base-url http://127.0.0.1:8000

第八部分:常见问题排查

8.1 ModuleNotFoundError: No module named 'pptx'

含义:当前运行的 API 镜像不是最新依赖构建出来的。

检查镜像内 requirements:

docker run --rm knowforge-rag-platform-api:latest sh -lc "grep -n 'python-pptx' /app/requirements.txt"

重建镜像:

docker compose --env-file .env.compose build --no-cache api
docker compose --env-file .env.compose up -d --force-recreate api

8.2 模型目录不存在

现象:preflight 报 Embedding 模型目录Reranker 模型目录 不存在。

宿主机检查:

Test-Path .\models\bge-m3
Test-Path .\models\bge-reranker-large

容器内检查:

docker compose --env-file .env.compose run --rm --no-deps api sh -lc "ls -la /app/models && test -d /app/models/bge-m3 && test -d /app/models/bge-reranker-large"

8.3 API 容器连不上 MySQL 或 Milvus

先确认 .env.compose 使用的是容器服务名:

MYSQL_HOST=mysql
MILVUS_URI=http://milvus:19530

再看服务状态:

docker compose --env-file .env.compose ps
docker compose --env-file .env.compose logs --tail 80 mysql
docker compose --env-file .env.compose logs --tail 80 milvus

8.4 PermissionError: reports/...

通常是 Linux 上容器用 root 写入了 reports/logs/,宿主机当前用户没有权限。

Linux 处理:

sudo chown -R "$USER":"$USER" reports logs

Windows Docker Desktop 一般不会遇到同样的 ownership 问题。

8.5 collection not found

如果日志出现在 --reset-collections 或首次初始化阶段,通常表示系统尝试清理一个尚不存在的旧集合,后续继续入库即可。需要关注的是最终质量门禁是否通过、active 版本是否激活。

确认 active 版本:

docker compose --env-file .env.compose run --rm api python -c "from qa_core.scenarios.registry import resolve_scenario; from qa_core.governance.kb_versions import get_kb_version_store; sc=resolve_scenario('enterprise_knowledge'); print(get_kb_version_store(sc.scenario_id).resolve_active_version())"

8.6 修改代码后页面还是旧效果

分两种情况:

修改内容 处理
Python 代码 docker compose --env-file .env.compose build api && docker compose --env-file .env.compose up -d api
Python 依赖 docker compose --env-file .env.compose build api && docker compose --env-file .env.compose up -d api
系统依赖或基础镜像缺失 docker build -f Dockerfile.base -t localhost/knowforge-rag-platform-base:py312 .,再 docker compose --env-file .env.compose build api && docker compose --env-file .env.compose up -d api
static/ 页面 目录是 bind mount,刷新浏览器即可;必要时强制刷新缓存
docs/ 笔记 python -m mkdocs build --strict,再刷新 /docs
.env.compose docker compose --env-file .env.compose up -d --force-recreate api

第九部分:交付检查清单

V1 Docker 交付前按下面顺序检查:

检查项 命令或判断
模型目录存在 models/bge-m3models/bge-reranker-largemodels/bert_intent_classifier_v1
基础依赖镜像存在 docker build -f Dockerfile.base -t localhost/knowforge-rag-platform-base:py312 .
环境变量已填写 .env.compose 中 Key 和 token 不是占位符
Compose 可解析 docker compose --env-file .env.compose config --quiet
API 镜像已构建 docker compose --env-file .env.compose build api
基础服务健康 docker compose --env-file .env.compose ps
知识库已初始化 rebuild_scenarios.py --reset-collections 成功
active 版本存在 页面状态或脚本能查到 active kb version
API 可访问 http://127.0.0.1:8000 或服务器 IP
验收脚本通过 acceptance_smoke.py 通过
意图模型治理通过 demo_intent_model.py --eval-only --output latest 生成 reports/intent_model/intent_model_latest.json
缓存验收通过 cache_acceptance_smoke.py 连续两次同问,首次 miss、二次 hit
笔记可访问 /docs 页面可打开

学习总结:到这里,V1 已经形成从 RAG 主链路、Web 服务、知识库治理、质量评测、可观测性到 Docker 部署交付的完整闭环。

返回笔记开头 ↑