上一篇 《Docker 数据持久化与网络》 我们把数据持久化和容器间通信都搞定了。但每次启动一个完整的 AI 系统都要敲一长串 docker run,非常痛苦——这一篇把它装进一份 docker-compose.yml,用声明式管理整个应用环境。
一、为什么需要 Docker Compose
1.1 痛点场景
没有 Docker Compose 时,想启动一个「FastAPI 应用 + PostgreSQL 数据库」:
# 1. 启动 postgres
docker run -d --name my-postgres \
-e POSTGRES_PASSWORD=secret \
-v db_data:/var/lib/postgresql/data \
--network web-network \
postgres:15
# 2. 启动应用
docker run -d --name my-app \
-p 8000:8000 \
--network web-network \
my-python-app
过程里全是坑:
- 必须手动保证数据库先于应用启动。
- 必须手动创建和管理网络。
- 启动参数分散在多个命令行里,难以版本化、复用、交接。
- 横向扩展时命令呈指数级变长。
1.2 Docker Compose 的核心价值
Docker Compose 把这些手动、分散的操作,集中到一份声明式配置文件 docker-compose.yml 里:
- 定义即代码(IaC):把多容器环境以代码形式管理,可追踪、可审查、可复现。
- 一键环境管理:
docker compose up/docker compose down一键启停。 - 简化的服务间通信:自动创建隔离网络,服务之间通过服务名直接通信。
- 易于共享的开发环境:团队成员拿到
docker-compose.yml+ 源码,一键启动完全一致的环境。
二、Compose 快速入门
2.1 环境准备
Docker Compose 现在作为 Docker Desktop 和 Docker Engine 的一部分被直接集成,无需单独安装。验证:
docker compose version
# 输出类似:Docker Compose version v2.40.2-desktop.1
2.2 三大顶级键
docker-compose.yml 文件的核心是三个顶级键:
services:定义应用的各个容器组件。每个服务基于一个 Docker 镜像,配置端口、环境变量、依赖关系等。networks:定义容器之间通信的网络。Compose 默认会创建<project_name>_default网络,所有服务都连上去。volumes:用于持久化容器产生的数据,独立于容器的生命周期。
2.3 一份完整配置
下面基于一个开源的 LLM 代理应用 LiteLLM(统一调用 OpenAI / Claude / Gemini 等 100+ 模型)为例:
# docker-compose.yml
version: "3.8"
services:
# LiteLLM 代理服务
litellm:
image: ghcr.io/berriai/litellm:main-latest
container_name: litellm-proxy
command: ["--config", "/app/config.yaml"]
ports:
- "4000:4000" # 宿主机 4000 -> 容器 4000
volumes:
- ./config.yaml:/app/config.yaml
depends_on:
- postgres-db # postgres-db 先启动
networks:
- litellm-network
environment:
- LITELLM_MASTER_KEY=sk-******
- DATABASE_URL=postgresql://myuser:123456@postgres-db:5432/litellmdb
# PostgreSQL 数据库
postgres-db:
image: postgres
container_name: postgres-db
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: 123456
POSTGRES_DB: litellmdb
volumes:
- postgres-data:/var/lib/postgresql # 数据持久化
ports:
- "5432:5432" # 仅本机需要调试时映射
networks:
- litellm-network
volumes:
postgres-data: # 命名卷
networks:
litellm-network: # 用户定义桥接网络
2.4 启动和管理
注意:下面命令一律需要在与
docker-compose.yml同级目录执行。
# 启动(-d 后台)
docker compose up -d
# 查看服务状态
docker compose ps
# 查看日志
docker compose logs -f # 持续跟踪所有服务
docker compose logs litellm-proxy # 只看某个服务
# 停止并移除容器、网络
docker compose down
# 注意:默认不会删除数据卷!要同时删除请加 -v
docker compose down -v
访问 http://localhost:4000 就能使用 LiteLLM 代理。
三、配置详解
3.1 build vs image
image:当服务直接使用 Docker Hub 或私有仓库的现成镜像时使用(如mysql、redis、nginx)。build:当需要基于本地源码先构建镜像再启动时使用。
项目结构:
my_ai_app/
├── backend/
│ ├── app.py
│ ├── requirements.txt
│ └── Dockerfile
└── docker-compose.yml
docker-compose.yml:
services:
my_ai_app:
build:
context: ./backend # Dockerfile 所在目录
dockerfile: Dockerfile # 可省略(默认就是 Dockerfile)
target: dev # 使用 Dockerfile 多阶段构建中的 dev 阶段
args:
DEBUG_MODE: "true" # 传递给 Dockerfile 的 ARG
ports:
- "8000:8000"
volumes:
- ./backend:/app # 绑定挂载实现热加载
environment:
- APP_ENV=development
3.2 environment:环境变量
services:
db:
image: postgres:15
environment:
- POSTGRES_USER=myuser
- POSTGRES_PASSWORD=mysecretpassword
# 也可以用字典格式:
# POSTGRES_DB: mydatabase
3.3 volumes:数据卷
命名卷(推荐用于生产数据)
volumes:
db_data:
driver: local # 默认就是 local,可省略
services:
db:
image: postgres:15
volumes:
- db_data:/var/lib/postgresql/data
数据卷驱动
driver: local 把数据存在宿主机本地文件系统上(最简单、单机最常用)。其他驱动可以对接:
- 网络文件系统(NFS/SMB/CIFS):多宿主机共享
- 云存储(AWS EBS、Azure Disk、GCP Persistent Disk):云上的高可用块存储
- 企业级 SAN/NAS:高性能生产存储
共享卷
Docker 默认会把卷名前面拼上「项目名_」来避免冲突。如果希望跨项目共享同一个卷:
volumes:
global_mysql_data:
external: true # 声明为外部卷,Compose 不会创建它
绑定挂载(开发环境)
volumes:
- ./backend:/app # 把当前目录的 backend 挂进容器
适合开发环境代码热加载;生产环境慎用(与宿主机强耦合)。
3.4 networks:网络拓扑
services:
frontend:
image: my-frontend
networks:
- frontend_net
backend:
image: my-backend
networks:
- frontend_net # 可以被 frontend 访问
- backend_net # 可以被 db 访问
db:
image: postgres
networks:
- backend_net # 完全与 frontend 隔离
networks:
frontend_net:
driver: bridge
backend_net:
driver: bridge
在这个例子里:frontend 只能访问 backend;backend 能访问 frontend 和 db;db 与 frontend 完全隔离——这是「网络分段」的最简实现。
四、生产级最佳实践
4.1 健康检查(healthcheck)
depends_on 只等待容器启动,不保证服务可用。要等数据库真正可读,再启动应用:
services:
web:
build: .
depends_on:
db:
condition: service_healthy # 关键:等待 db 健康
db:
image: postgres:15
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=db
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 5s
timeout: 5s
retries: 5
web 会一直等待,直到 db 的 healthcheck 成功返回。这极大提高了多服务启动的稳定性。
4.2 环境变量文件:.env
严禁将敏感信息(密码、API Key)硬编码在
docker-compose.yml中并提交到版本库!
在 docker-compose.yml 同级创建 .env 文件(必须在 .gitignore 中忽略):
# .env
POSTGRES_PASSWORD=a_very_secure_password_from_env
OPENAI_API_KEY=sk-xxxxxx
在 Compose 中通过 ${VARIABLE_NAME} 引用:
services:
db:
image: postgres:15
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
4.3 Docker secrets:更安全的方式
secrets 把敏感数据作为文件挂载到容器的 /run/secrets/ 目录下,而不是作为环境变量。这可以防止敏感信息在 docker inspect 或应用日志中意外泄露。
# db_password.txt(必须 .gitignore)
a_very_secure_password_from_file
services:
db:
image: postgres:15
environment:
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./db_password.txt
4.4 水平扩展
# 把 web 扩展到 3 个实例
docker compose up -d --scale web=3
Compose 自动处理端口映射和负载均衡——对 localhost:8000 的请求会被分发到这 3 个 web 容器。
五、超越 Compose:何时选择 Kubernetes?
Docker Compose 是出色的开发和中小型应用部署工具,但它有局限。作为应用架构师,了解何时需要迁移到 Kubernetes 至关重要。
5.1 Compose 的优势
- 简单易学,YAML 配置直观,学习曲线平缓
- 快速启动,适合本地开发、测试、CI/CD
- 单机部署,多容器应用管理非常高效
5.2 何时考虑 Kubernetes
| 需求 | Compose | Kubernetes |
|---|---|---|
| 单机开发测试 | ✅ 完美 | ❌ 过重 |
| 中小型生产部署 | ⚠️ 可用但不推荐 | ✅ 推荐 |
| 跨多主机集群 | ❌ 不支持 | ✅ 原生支持 |
| 自动伸缩(按 CPU/内存) | ❌ 不支持 | ✅ HPA 原生 |
| 服务发现与自愈 | ⚠️ 基础 | ✅ 完善 |
| 滚动更新 / 蓝绿 / 金丝雀 | ❌ 不支持 | ✅ 原生 |
| 精细的网络策略 | ⚠️ 基础 | ✅ NetworkPolicy |
| 监控 / 日志 / 服务网格生态 | ❌ 弱 | ✅ 极丰富 |
5.3 实践路径
在开发和测试阶段使用 Docker Compose,在生产环境中使用 Kubernetes。
像 Kompose 这样的工具甚至可以帮你把 docker-compose.yml 文件转换为 Kubernetes 的资源定义——从 Compose 迁到 K8s 不是推倒重来。
六、小结
这一篇你掌握了:
- Docker Compose 的核心价值:把一组
docker run浓缩成一份声明式 YAML - 三大顶级键:
services/networks/volumes的作用 - 核心配置:
buildvsimage、environment、volumes(命名卷 / 绑定挂载 / 共享卷)、networks - 生产最佳实践:
healthcheck、.env环境变量文件、secrets、水平扩展 - 何时该升级到 Kubernetes:跨主机、自动伸缩、滚动更新、网络策略
到这里,单机的 Docker 工具链你已经完整掌握。但当你面对「线上几十个节点、几百个容器、滚动升级、灰度发布」时——下一篇 《Docker 集群演进史:Swarm → Compose → Kubernetes》 会带你走一遍容器编排 10 年的演化历程,看看 Docker 公司自己做过哪些尝试,Kubernetes 为什么最终胜出。