Docker Compose
Docker Compose 是 Docker 官方提供的多容器应用编排工具。
如果说 docker run 是手动驾驶一辆车(一次启动一个容器),那么 docker-compose 就是指挥整个车队(一次性启动、配置和管理多个相互关联的容器)。
它通过一个 YAML 格式的配置文件 (通常命名为 docker-compose.yml),以声明式的方式定义了整个应用架构中的所有服务、网络和数据卷
| 操作方式 | 命令示例 | 适用场景 |
|---|---|---|
| Docker CLI | docker run ... (执行多次) |
单个容器,临时测试 |
| Docker Compose | docker compose up (执行一次) |
微服务、完整应用栈、开发/生产环境 |
核心功能
Docker Compose 主要解决"多容器协作"的复杂性:
- 一键启停整个应用栈 :
不再需要依次手动启动数据库、缓存、后端、前端。一条命令docker compose up -d全部搞定。- 服务依赖管理 :
可以定义"先启动数据库,等数据库就绪了,再启动后端服务"。- 内部网络自动互通 :
自动创建一个专属网络,容器之间可以通过服务名直接互相访问(DNS 解析),无需暴露端口给宿主机。- 数据持久化统一配置 :
统一管理数据卷(Volumes),确保数据库重启后数据不丢失。- 环境隔离与复用 :
开发、测试、生产环境可以使用不同的配置文件(如docker-compose.dev.yml,docker-compose.prod.yml)进行叠加,保证环境一致性。
正面影响
- 🚀 开发效率提升 80% :新员工入职,只需
git clone然后运行docker compose up,5 分钟内即可拥有完整的本地开发环境,彻底告别"在我机器上是好的"这种环境问题。 - 🔗 微服务编排标准化 :对于由 Spring Boot + MySQL + Redis + Nginx 组成的微服务系统,Compose 文件成为了基础设施即代码 (IaC) 的一部分,清晰记录了服务间的依赖关系。
- 🔄 CI/CD 集成简化:在 Jenkins/GitLab CI 中,可以直接使用 Compose 文件来搭建临时的测试环境,运行自动化测试后再一键销毁,干净利落。
- 🛡️ 配置集中管理 :所有环境变量、端口映射、挂载路径都集中在一个 YAML 文件中,修改配置无需记忆长长的
docker run参数。
局限性
单机限制 :Docker Compose 主要用于单节点 (单台服务器)编排。如果需要跨多台服务器(集群)部署,通常需要结合 Docker Swarm 或 Kubernetes (K8s)。
工作原理
- 解析 YAML :Docker Compose 读取
docker-compose.yml文件,解析其中定义的服务(Services)、网络(Networks)和数据卷(Volumes)。 - 调用 Docker API:它本质上是一个 Python/Go 编写的客户端,通过 Docker Daemon 的 API 发送指令。
- 资源创建顺序 :
- 首先创建定义的 Networks(默认创建一个桥接网络)。
- 其次创建定义的 Volumes(用于数据持久化)。
- 最后按照依赖关系(
depends_on)依次创建并启动 Containers。
- 服务发现 :在创建的网络中,Docker 内置的 DNS 服务器会将服务名 解析为对应的容器 IP。例如,服务名为
db,其他容器 pingdb就能通。
实现方案架构
一个典型的 Compose 架构包含三个层级:
- 定义层 (YAML):用户编写声明式配置。
- 编排层 (Compose Engine):处理依赖逻辑、生命周期管理、日志聚合。
- 执行层 (Docker Daemon):真正创建 Linux 命名空间、Cgroup、网络桥接和文件系统挂载。
常用命令速查表
| 命令 | 说明 |
|---|---|
docker compose up -d |
后台启动所有服务 |
docker compose up -d --build |
重新构建镜像并启动 (当 Dockerfile 变更时) |
docker compose ps |
查看当前运行的服务状态 |
docker compose logs -f [service_name] |
实时查看指定服务的日志 |
docker compose exec [service_name] bash |
进入指定容器内部 (如 exec wordpress bash) |
docker compose stop |
暂停服务 (不删除容器) |
docker compose start |
启动已暂停的服务 |
docker compose down |
停止并删除容器、网络 (保留数据卷) |
docker compose down -v |
停止并删除容器、网络、数据卷 (数据清空) |
docker compose top |
查看容器内的进程资源占用 |
实例:构建一个 "博客系统"
假设我们要部署一个简单的博客系统,包含三个部分:
- WordPress (前端 + 后端)
- MySQL (数据库)
- phpMyAdmin (数据库可视化管理工具,可选)
第一步:创建目录结构
mkdir my-blog
cd my-blog
第二步:编写 docker-compose.yml
新建文件 docker-compose.yml,内容如下
bash
services:
# 1. 数据库服务
db:
image: mysql:8.0 # 指定镜像及标签,如本地有dockerfile 可使用build
#build: . # 使用当前目录下的 Dockerfile
container_name: blog-db # 容器名称
restart: always # 重启策略
environment: # 环境变量配置
MYSQL_ROOT_PASSWORD: rootpassword123
MYSQL_DATABASE: wordpress_db
MYSQL_USER: wp_user
MYSQL_PASSWORD: wp_password123
volumes: # 数据卷挂载
- db_data:/var/lib/mysql # 数据持久化:即使删除容器,数据也在
networks: # 网络
- blog-net
# 健康检查:确保数据库完全启动后再让 WordPress 连接
healthcheck: # 健康检查
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
# 2. WordPress 服务
wordpress:
image: wordpress:latest
container_name: blog-web
restart: always
ports:
- "8080:80" # 映射宿主机 8080 到容器 80
environment:
WORDPRESS_DB_HOST: db:3306 # 关键:直接使用服务名 'db' 作为主机名
WORDPRESS_DB_USER: wp_user
WORDPRESS_DB_PASSWORD: wp_password123
WORDPRESS_DB_NAME: wordpress_db
depends_on: # 依赖控制
db:
condition: service_healthy # 等待 db 健康检查通过后才启动
networks:
- blog-net
volumes:
- wp_content:/var/www/html # 挂载主题和插件目录
# 3. phpMyAdmin (可选,方便管理数据库)
phpmyadmin:
image: phpmyadmin:latest
container_name: blog-pma
restart: always
ports:
- "8081:80"
environment:
PMA_HOST: db
PMA_PORT: 3306
depends_on:
- db
networks:
- blog-net
# 定义网络
networks:
blog-net:
driver: bridge
# 定义数据卷 (持久化存储)
volumes:
db_data:
wp_content:
第三步:启动项目
docker compose up -d # 启动所有服务 (后台运行 -d)
docker compose ps # 查看运行状态
docker compose logs -f # 查看实时日志 (排查报错必备
第四步:验证与使用
- 访问博客 :打开浏览器访问
http://localhost:8080,开始 WordPress 安装流程。 - 管理数据库 :访问
http://localhost:8081,输入 root 密码即可管理数据。 - 停止项目: docker compose down
深度参数解析
1. YAML 文件的最外层定义了三大资源池:
services: 最核心部分 。定义应用包含的所有容器(服务)。每个子项(如db,wordpress)就是一个服务的名称。networks: 定义自定义网络。如果不写,Docker 会默认创建一个名为default的网络。volumes: 定义命名数据卷。用于声明持久化存储的空间,防止数据随容器删除而丢失。
2. 服务级参数 services
image :镜像来源
container_name: 容器名称
restart: 重启策略
常用值:
no : 不自动重启(默认)
always : 无论任何原因退出,都自动重启如果容器被手动停止 (
docker stop),重启守护进程也会尝试拉起它。生产环境推荐。on-failure : 只有当容器非正常退出(退出码不为 0)时才重启
unless-stopped : 类似
always,但如果容器被手动停止过,则不再自动重启。
environment: 环境变量
向容器内部传递环境变量,通常用于配置软件
格式:
environment: KEY: VALUE # 方式一:键值对 - KEY=VALUE # 方式二:列表格式 (等价)可以使用
.env文件引用,如PASSWORD: ${DB_PASS},避免将敏感信息硬编码在 YAML 中。
volumes : 数据卷挂载
- 格式 :
- 源路径:目标路径- 示例 :
- db_data:/var/lib/mysql
db_data: 这是在顶层volumes中定义的命名卷 (Named Volume)。Docker 会管理它的实际存储位置(通常在/var/lib/docker/volumes/...)。/var/lib/mysql: 容器内 MySQL 存放数据的目录。- 作用 : 即使你执行
docker compose down删除了容器,db_data里的数据依然存在。下次启动新容器挂载该卷,数据还在。
networks: 网络归属
networks:
- blog-net
- 加入
blog-net后,该服务可以与同在该网络下的其他服务通过服务名互相访问。- 如果不指定,所有服务默认加入一个名为
default的网络。- 如果一个服务加入了多个网络,它可以充当不同网络间的网关。
跨 Compose 项目通信 :如果你有两个独立的 Compose 项目(例如 project-A 和 project-B),想让 A 里的容器访问 B 里的数据库。你需要手动创建一个外部网络,然后在两个 YAML 中都引用这个外部网络。
bash
networks:
shared-net:
external: true # 告诉 Compose:这个网络已经存在了,别创建,直接连
ports: 端口映射
- 含义: 将容器的端口暴露给宿主机,让外部(浏览器、其他机器)能访问。
- 格式 :
- "宿主机端口:容器端口" - 示例 :
- "8080:80"- 访问
http://localhost:8080-> 转发到 -> 容器内部的80端口。
- 访问
- 注意 :
- 如果只写容器端口(如
- "80"),宿主机端口会随机分配。 - 如果不需要外部访问(如纯内部数据库),不要写 ports,这样更安全。
- 如果只写容器端口(如
healthcheck : 健康检查
- 含义: 定期运行一个命令来检测容器内的服务是否真正"可用"(不仅仅是进程在跑)。
- 参数详解 :
test: 执行的命令。["CMD", ...]是直接执行,["CMD-SHELL", ...]是通过 shell 执行。
- 例:
mysqladmin ping如果成功返回 0,失败返回非 0。interval: 检查间隔 (如10s)。timeout: 命令超时时间 (如5s)。retries: 连续失败多少次算作 "unhealthy"。start_period: (可选) 容器启动后给予多少秒的缓冲期,期间失败不计入重试次数。- 状态 : 容器状态会显示
healthy,starting, 或unhealthy。
depends_on: 依赖控制
-
含义: 定义服务启动的先后顺序。
depends_on:
- db
只是控制启动顺序 :先启动 db 容器,再启动 wordpress 容器。但不保证 db 已经准备好接受连接(MySQL 启动可能需要几秒到几十秒)
高级用法 (配合 healthcheck):
depends_on:
db:
condition: service_healthy
强烈推荐 。这意味着:启动 db -> 等待 db 通过健康检查 (变为 healthy) -> 才启动 wordpress。这能有效解决 "App 启动时报错连不上数据库" 的问题。
3. 网络级参数 networks
bashnetworks: blog-net: driver: bridge
blog-net: 网络名称,需与services中引用的名称一致。driver: 网络驱动类型。
bridge: (默认) 桥接模式,单机容器通信的标准方式。host: 移除网络隔离,容器直接使用宿主机网络 (Linux 特有,性能高但端口易冲突)。none: 无网络。overlay: 用于 Swarm 集群跨主机通信 (Compose 单机模式下较少用)。
4. 数据卷级参数 volumes
bashvolumes: db_data: wp_content:
- 含义: 声明命名的数据卷。
- 作用 :
- 这里只需要写出名字(如
db_data),不需要写具体路径。Docker 会自动在默认位置创建它。- 如果在
services中使用了该名字,Docker 就会关联起来。- 优势 : 相比直接绑定宿主机路径(如
/home/user/data),命名卷由 Docker 管理,权限问题更少,迁移更方便。
Dockerfile 与 Compose 的关联
假如项目环境是这样的,通过Dockerfile 与Compose 的关联 运行容器时

使用 build 参数替代 image 参数。
dockerfile
bash
# 基础镜像
FROM python:3.11-slim
# 设置工作目录
WORKDIR /app
# 复制依赖文件并安装 (利用缓存层加速构建)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制源代码
COPY . .
# 暴露端口
EXPOSE 8000
# 启动命令
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
docker-compose.yml 中的关联写法
bash
services:
api-server:
# ❌ 不使用 image: python:3.11
# ✅ 使用 build 指向 Dockerfile 所在目录
build:
context: ./backend # 构建上下文路径 (Dockerfile 在这里)
dockerfile: Dockerfile # 指定文件名 (默认就是 Dockerfile,可省略)
args: # (可选) 传递构建参数 (ARG)
BUILD_VERSION: "v1.2.0"
container_name: eco-api
restart: always
# 其他配置...
- 当你运行
docker compose up --build时,Compose 会先读取./backend/Dockerfile,执行构建步骤生成一个新的镜像(例如my-ecommerce-api-server),然后再用这个新镜像启动容器。
完整实战代码
docker-compose.yml
bash
version: '3.8' # v2 中可省略,但写上也没错
services:
# 1. 数据库
db:
image: postgres:15-alpine
container_name: eco-db
restart: always
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASS}
POSTGRES_DB: ${DB_NAME}
volumes:
- pg_data:/var/lib/postgresql/data
- ./data/db-init:/docker-entrypoint-initdb.d # 自动执行初始化脚本
networks:
- eco-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
interval: 5s
timeout: 5s
retries: 5
# 2. 后端 (自定义构建 + 多卷挂载)
api:
build:
context: ./backend
dockerfile: Dockerfile
args:
ENVIRONMENT: production
container_name: eco-api
restart: always
depends_on:
db:
condition: service_healthy
environment:
DATABASE_URL: postgresql://${DB_USER}:${DB_PASS}@db:5432/${DB_NAME}
REDIS_URL: redis://cache:6379
volumes:
# 开发时:挂载代码实现热重载 (生产环境建议不打此包,直接构建进镜像)
- ./backend:/app
# 配置文件
- ./config/settings.yaml:/app/config.yaml:ro
# 日志
- ./data/logs:/app/logs
# 用户文件持久化
- app_uploads:/app/uploads
networks:
- eco-net
ports:
- "8000:8000"
# 3. 缓存
cache:
image: redis:7-alpine
container_name: eco-redis
restart: always
networks:
- eco-net
volumes:
- redis_data:/data
# 4. 反向代理 (Nginx)
proxy:
image: nginx:alpine
container_name: eco-proxy
ports:
- "80:80"
volumes:
- ./frontend/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./frontend/dist:/usr/share/nginx/html:ro # 挂载构建好的前端静态文件
depends_on:
- api
networks:
- eco-net
networks:
eco-net:
driver: bridge
volumes:
pg_data:
redis_data:
app_uploads:
.env文件
bash
DB_USER=admin
DB_PASS=secure_password_123
DB_NAME=ecommerce_db
总结
关于 Build:
- 开发环境:可以使用
volumes挂载代码,配合--build频繁更新。- 生产环境:建议不要挂载代码目录,而是通过 CI/CD 流水线构建好包含最新代码的镜像,推送到仓库,Compose 只负责拉取镜像运行。这样更干净、安全。
关于 Network:
- 放心地在 YAML 里定义,不用提前建。
- 如果需要跨项目,记得加
external: true。关于 Volumes:
- 数据 (DB, Redis, 用户上传) -> 必须用 Named Volumes (
pg_data)。- 配置 -> 推荐 Bind Mount + :ro (
./config:/conf:ro)。- 代码 -> 仅开发环境用 Bind Mount,生产环境打入镜像。
- 日志 -> 推荐 Bind Mount 到宿主机的统一日志目录,方便
filebeat等工具采集。关于 Secrets:
- 千万不要把密码硬编码在
docker-compose.yml里!- 一定要用
.env文件,并将.env加入.gitignore。- 在 K8s 或 Swarm 高级场景中,使用 Docker Secrets 机制。