第1章 Docker 容器化与 Compose 编排¶
学习目标¶
- 理解 Docker 容器化的核心概念与运行机制
- 掌握 Dockerfile 编写最佳实践,能编写高效、可复用的镜像构建脚本
- 学会使用多阶段构建优化镜像体积与安全性
- 掌握 Docker Compose 编排多容器应用的方法
- 能够将一个完整的 AI Agent 应用容器化部署
问题导入¶
某团队开发了一个 AI 客服 Agent,本地运行一切正常,交付给运维上线时却问题不断:①开发用 Python 3.11,生产服务器是 3.8,依赖的 pydantic 行为不一致导致报错;②requirements.txt 锁定不全,线上装了一个新版本 SDK,流式接口签名变了;③本地连的是 Redis 测试库,线上配置不一致,会话状态丢失;④每次发版都要运维手动装依赖、改配置,耗时半天还容易出错。
这些"在我机器上能跑"的经典问题的根因是:运行环境没有被打包成不可变制品。Docker 通过容器技术将应用代码、依赖、运行时、配置整体打包成镜像,确保从开发到生产环境完全一致。本章将系统讲解如何用 Docker 与 Docker Compose 将 AI Agent 应用容器化,实现一次构建、处处运行。
核心概念¶
1. Docker 基础¶
Docker 是一种轻量级容器化技术,核心三要素:
- 镜像(Image):只读模板,包含运行应用所需的一切(代码、依赖、操作系统库)
- 容器(Container):镜像运行的实例,相互隔离、资源受限
- 仓库(Registry):镜像存储与分发中心,如 Docker Hub、私有 Harbor
与传统虚拟机相比,容器共享宿主内核,启动秒级、资源占用低,非常适合微服务与 AI 应用的快速弹性伸缩。
2. Dockerfile 编写最佳实践¶
Dockerfile 是构建镜像的脚本,编写质量直接影响镜像体积、构建速度与安全性。核心原则:
- 选择精简基础镜像:Python 应用优先用
python:3.11-slim而非完整版 - 合理排序指令:将变化频率低的指令放前面,最大化利用缓存
- 依赖单独一层:先拷贝
requirements.txt安装依赖,再拷贝源码 - 不要把密钥写进镜像:API Key 等敏感信息用环境变量或 secrets 注入
- 使用 .dockerignore:排除
__pycache__、.git、venv等无用文件
3. 多阶段构建¶
多阶段构建(Multi-stage Build)在一个 Dockerfile 中定义多个 FROM,最终镜像只保留最后阶段的内容。对 AI 应用的价值:
- 构建阶段:包含编译工具、git 等,用于安装依赖、构建资源
- 运行阶段:仅拷贝产物与最小运行时,镜像体积可缩减 60% 以上
- 安全性提升:生产镜像不含编译工具,攻击面更小
4. Docker Compose 编排¶
单个 AI Agent 服务通常依赖多个组件:API 服务、Redis(会话缓存)、PostgreSQL(持久化)、向量数据库(RAG)。Docker Compose 用一个 YAML 文件定义并启动全部容器:
- 服务(services):定义每个容器
- 网络(networks):容器间通信
- 数据卷(volumes):持久化数据
- 依赖关系(depends_on):控制启动顺序
- 环境变量:通过
.env文件注入配置
5. 镜像优化¶
AI Agent 镜像常因模型依赖、向量库客户端等膨胀,优化手段:
- 合并
RUN指令减少层数 - 安装后清理 apt 缓存、pip 缓存
- 使用
.dockerignore排除大文件 - 基础镜像用
slim或alpine(注意 alpine 的 musl 库兼容性) - 大模型文件不入镜像,用对象存储或挂载卷
原理图¶
flowchart TB
subgraph 构建["构建阶段"]
SRC[源代码 + requirements.txt]
DF[Dockerfile]
IMG[镜像 Image]
SRC --> DF
DF --> IMG
end
subgraph 仓库["镜像仓库"]
REG[(Docker Registry<br/>Hub/Harbor)]
end
subgraph 运行["运行阶段"]
subgraph 主机["生产服务器"
C1[Agent API 容器]
C2[Redis 容器]
C3[Postgres 容器]
C4[向量库 容器]
C1 <--> C2
C1 <--> C3
C1 <--> C4
end
end
IMG -->|docker push| REG
REG -->|docker pull| C1
subgraph Compose["Docker Compose 编排"]
YAML[docker-compose.yml]
YAML -.->|定义| C1
YAML -.->|定义| C2
YAML -.->|定义| C3
YAML -.->|定义| C4
end
图:Docker 容器化架构——Dockerfile 构建镜像推送至仓库,生产服务器拉取镜像后由 Compose 编排多容器协同运行
示例代码¶
1. AI Agent 应用的 Dockerfile(多阶段构建)¶
# ---------- 构建阶段 ----------
FROM python:3.11-slim AS builder
WORKDIR /build
# 系统依赖(编译某些 Python 包需要)
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential gcc && \
rm -rf /var/lib/apt/lists/*
# 仅拷贝依赖文件,利用缓存层
COPY requirements.txt .
# 安装依赖到指定目录,便于后续拷贝
RUN pip install --user --no-cache-dir -r requirements.txt
# ---------- 运行阶段 ----------
FROM python:3.11-slim
WORKDIR /app
# 从构建阶段拷贝已安装的 Python 包
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
# 拷贝应用源码
COPY ./app ./app
# 非 root 用户运行,提升安全性
RUN useradd -m appuser && chown -R appuser /app
USER appuser
EXPOSE 8000
# 健康检查
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
2. docker-compose.yml 编排多服务¶
version: "3.9"
services:
agent-api:
build: .
image: mycorp/agent-api:1.2.0
container_name: agent-api
ports:
- "8000:8000"
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
- REDIS_URL=redis://redis:6379/0
- POSTGRES_URL=postgresql://agent:agent@postgres:5432/agent
- VECTOR_DB_URL=http://qdrant:6333
depends_on:
redis:
condition: service_healthy
postgres:
condition: service_healthy
restart: unless-stopped
networks:
- agent-net
redis:
image: redis:7-alpine
container_name: agent-redis
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
networks:
- agent-net
postgres:
image: postgres:16-alpine
container_name: agent-postgres
environment:
- POSTGRES_USER=agent
- POSTGRES_PASSWORD=agent
- POSTGRES_DB=agent
volumes:
- pg-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U agent"]
interval: 10s
timeout: 3s
retries: 5
networks:
- agent-net
qdrant:
image: qdrant/qdrant:v1.9.0
container_name: agent-qdrant
volumes:
- qdrant-data:/qdrant/storage
ports:
- "6333:6333"
networks:
- agent-net
networks:
agent-net:
driver: bridge
volumes:
redis-data:
pg-data:
qdrant-data:
3. .dockerignore 文件¶
__pycache__
*.pyc
.git
.gitignore
venv
.venv
.env
*.md
tests/
.pytest_cache
.mypy_cache
4. .env 文件示例¶
# 切勿提交到 git,仅本地与部署环境使用
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxx
LOG_LEVEL=INFO
企业案例¶
案例 1:客服 Agent 服务容器化部署¶
某电商 AI 客服团队将 Agent 服务从裸机部署迁移到 Docker 容器化。改造前每次发版需运维 SSH 登录、拉代码、装依赖、重启服务,平均 40 分钟且常出错;改造后流程变为:开发提交代码 → CI 构建镜像 → 推送 Harbor → 生产 docker compose pull && docker compose up -d,发版缩短到 5 分钟。
关键经验:①用多阶段构建把镜像从 1.8GB 降到 480MB,拉取速度提升 3 倍;②所有配置走 .env 与环境变量,镜像与配置解耦;③HEALTHCHECK 配合编排实现自动重启不健康容器;④数据卷独立挂载,升级镜像不丢数据。容器化后,团队后续接入 K8s 也变得顺理成章。
案例 2:RAG 知识库应用的 Compose 编排¶
某企业 RAG 系统包含 API 服务、Qdrant 向量库、PostgreSQL(元数据)、Redis(查询缓存)。团队用单个 docker-compose.yml 把 4 个服务一键拉起,开发与测试环境完全一致。新人入职只需 docker compose up 即可获得完整可运行环境,环境搭建时间从半天降到 3 分钟。
常见问题¶
Q1:为什么镜像体积这么大,怎么瘦身?
A:常见原因:用了完整版基础镜像(如 python:3.11)、未清理 apt/pip 缓存、把模型文件打进镜像。优化手段:换 slim 基础镜像、多阶段构建、合并 RUN 并清理缓存、大文件用对象存储或挂载卷。
Q2:容器里访问不到宿主机的服务怎么办?
A:①用 host.docker.internal(开发环境)访问宿主机服务;②把宿主服务也容器化加入同一 Compose 网络;③生产环境应把所有依赖容器化,避免依赖宿主机裸服务。
Q3:环境变量和 .env 文件会不会泄露密钥?
A:.env 文件必须加入 .gitignore 不提交代码库;生产环境优先用 Docker secrets 或外部的密钥管理服务(如 Vault、云厂商 KMS);镜像构建时绝不能把密钥 COPY 进去,否则即使后续删除也会留在历史层中。
Q4:Compose 能用于生产吗?
A:中小规模可以,Docker Compose 适合单机多容器编排。但生产环境若需多机集群、自动伸缩、滚动更新,应迁移到 Kubernetes。Compose 的价值是开发测试环境一致性与中小项目快速上线。
Q5:容器时区不对,日志时间错乱怎么解决?
A:在 Dockerfile 中设置 ENV TZ=Asia/Shanghai 并安装 tzdata,或在 docker-compose.yml 中挂载 /etc/localtime:/etc/localtime:ro。
面试题¶
- 简答题:Docker 容器与虚拟机有什么本质区别?
参考要点:容器共享宿主内核,启动快、资源占用低;虚拟机每个实例运行完整客户机内核,隔离更强但开销大。容器适合微服务弹性伸缩,虚拟机适合强隔离场景。
- 实践题:解释多阶段构建的原理与优势。
参考要点:一个 Dockerfile 多个 FROM,每个 FROM 开始新阶段;最终镜像只包含最后阶段内容;优势是镜像体积小、不含编译工具更安全、构建缓存可复用。
- 设计题:一个 Agent 服务依赖 Redis 与 Postgres,如何用 Compose 保证启动顺序?
参考要点:用
depends_on+condition: service_healthy;为依赖服务配置healthcheck;Compose 会等待依赖健康后再启动主服务。
- 原理题:为什么说"把密钥 COPY 进 Dockerfile 再删除"仍不安全?
参考要点:Docker 镜像由层组成,删除只在新层移除文件,历史层仍存在;任何人
docker history或拉取镜像都能从中间层提取密钥。正确做法是用构建参数或运行时环境变量注入。
- 优化题:如何把一个 2GB 的 Python 镜像优化到 500MB 以内?
参考要点:换 slim 基础镜像;多阶段构建只拷贝依赖;合并 RUN 清理缓存;用 .dockerignore 排除大文件;大模型文件移出镜像改用挂载或对象存储。
练习题¶
-
基础实现:为本章示例的 Agent 应用编写完整 Dockerfile,要求镜像体积小于 600MB,并能通过
docker build成功构建。 -
Compose 实战:编写一个
docker-compose.yml,包含 Agent API、Redis、Postgres 三个服务,配置健康检查与依赖顺序,并用docker compose up验证启动。 -
镜像优化:选一个现有镜像(如自己之前构建的),用多阶段构建 + .dockerignore 优化,对比优化前后的体积差异,记录每步优化效果。
小结¶
Docker 容器化是现代应用部署的基石。本章核心:Dockerfile 通过分层指令把应用打包成不可变镜像,解决"环境不一致"顽疾;多阶段构建显著缩减镜像体积与攻击面;Docker Compose 用一个 YAML 编排多容器应用,实现开发测试生产环境统一;镜像优化与密钥安全是生产上线的必修课。容器化只是部署的第一步——服务跑起来后,如何用 Linux 与 Nginx 对外提供稳定访问,是第 2 章的主题。