一键指挥千军万马 —— Docker Compose多容器编排实战
一个容器是独奏,多个容器是交响乐。Docker Compose就是那个站在台上的指挥家,让所有乐手按照总谱协调演奏。
📋 开篇自测:你已经知道多少?
- 你知道docker-compose.yml文件的基本结构吗?
docker compose up和docker compose up -d有什么区别?- 如何让Compose中的某个服务等待另一个服务启动后再运行?
一、手动编排的痛苦
回顾一下上一章的实战例子:我们启动了三个容器(Nginx + Node.js + Redis),需要敲一堆命令:
docker network create webapp-net
docker volume create pgdata
docker run -d --name redis --network webapp-net redis:7.2
docker run -d --name nodeapp --network webapp-net -e REDIS_HOST=redis my-node-app
docker run -d --name nginx --network webapp-net -p 80:80 -v ./nginx.conf:/etc/nginx/conf.d/default.conf:ro nginx
这才三个容器就已经这么长了。如果你的项目有十几个微服务,加上数据库、缓存、消息队列、监控系统…手动管理简直是噩梦。而且每次要重启整套环境,还得记住正确的启动顺序。
更糟糕的是,这些命令散落在你的脑子里(或者某个笔记里),新同事接手项目时完全不知道怎么把环境跑起来。
Docker Compose就是为了终结这种混乱而生的。它让你把所有容器的配置写在一个YAML文件里,然后一条命令就能启动、停止、重建整套环境。
二、docker-compose.yml:你的编排剧本
让我们把刚才手动敲的那堆命令,翻译成一个 docker-compose.yml 文件:
# docker-compose.yml
services:
# Nginx反向代理
nginx:
image: nginx:1.28-alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- nodeapp
networks:
- webapp-net
# Node.js应用
nodeapp:
build: ./app
environment:
- REDIS_HOST=redis
- NODE_ENV=production
depends_on:
- redis
networks:
- webapp-net
# Redis缓存
redis:
image: redis:7.2-alpine
volumes:
- redis-data:/data
networks:
- webapp-net
networks:
webapp-net:
driver: bridge
volumes:
redis-data:
然后只需一条命令:
docker compose up -d
所有容器按照依赖关系自动创建网络、创建Volume、拉取/构建镜像、启动容器。想要停掉整套环境?也是一条命令:
docker compose down
从一堆零散的docker run命令,到一个结构化的配置文件加一条命令——这就是Docker Compose带来的改变。
YAML文件结构剖析
一个docker-compose.yml文件主要包含以下几个顶级配置块:
services:最核心的部分,定义了所有需要运行的容器。每个服务(service)对应一个容器。
networks:定义网络。如果不写,Compose会自动创建一个默认网络,所有服务都连在上面。
volumes:定义命名Volume。
让我们逐个看看 services 里的常用配置项。
🤔 想一想 把所有配置集中到一个YAML文件里,除了操作方便之外,还有什么好处?(提示:想想版本控制、文档化、团队协作)
三、服务配置详解
image vs build
services:
# 直接使用现成镜像
redis:
image: redis:7.2-alpine
# 从Dockerfile构建镜像
webapp:
build: ./webapp
# 或者更详细的构建配置
# build:
# context: ./webapp
# dockerfile: Dockerfile.prod
# args:
# NODE_ENV: production
image 指定使用哪个已有镜像,build 指定从本地Dockerfile构建。两者也可以同时使用——先构建,然后给构建出来的镜像打上指定的名称标签。
端口映射
services:
web:
image: nginx
ports:
- "80:80" # 宿主机80 → 容器80
- "443:443" # 宿主机443 → 容器443
- "127.0.0.1:8080:80" # 只绑定本地
环境变量
services:
app:
image: my-app
environment:
- NODE_ENV=production
- DB_HOST=postgres
- DB_PORT=5432
# 或者用键值对写法
# environment:
# NODE_ENV: production
# DB_HOST: postgres
# 从文件加载环境变量
app-from-file:
image: my-app
env_file:
- .env
- .env.production
使用 env_file 加载环境变量文件是更好的做法,特别是当你有很多环境变量或者不想把敏感信息写在YAML文件里时。.env 文件应该被添加到 .gitignore 中。
数据卷
services:
postgres:
image: postgres:16
volumes:
# 命名Volume(持久化数据库数据)
- pgdata:/var/lib/postgresql/data
# Bind Mount(挂载初始化脚本)
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
volumes:
pgdata:
依赖关系
services:
webapp:
build: ./app
depends_on:
- postgres
- redis
postgres:
image: postgres:16
redis:
image: redis:7.2
depends_on 控制启动顺序:先启动postgres和redis,再启动webapp。但要注意一个坑:depends_on只保证容器启动的顺序,不保证服务就绪。也就是说,postgres容器可能已经启动了,但PostgreSQL服务还没准备好接受连接。
更可靠的做法是使用健康检查:
services:
webapp:
build: ./app
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
这样webapp会等到postgres通过健康检查后才启动。
重启策略
services:
app:
image: my-app
restart: unless-stopped
# 可选值:
# "no" - 默认,不自动重启
# always - 总是重启
# on-failure - 非正常退出时重启
# unless-stopped - 除非手动停止,否则一直重启
生产环境推荐使用 unless-stopped 或 always,确保服务在崩溃后自动恢复。
资源限制
services:
app:
image: my-app
deploy:
resources:
limits:
cpus: "0.5" # 最多使用0.5个CPU核心
memory: 512M # 最多使用512MB内存
reservations:
cpus: "0.25" # 预留0.25个CPU核心
memory: 256M # 预留256MB内存
四、Compose常用命令
# 启动所有服务(前台运行,可看日志)
docker compose up
# 后台启动
docker compose up -d
# 只启动特定服务
docker compose up -d postgres redis
# 停止并删除容器、网络
docker compose down
# 停止并删除容器、网络、Volume(慎用!会删除数据)
docker compose down -v
# 查看服务状态
docker compose ps
# 查看日志
docker compose logs
# 实时追踪特定服务的日志
docker compose logs -f webapp
# 在服务容器中执行命令
docker compose exec postgres psql -U postgres
# 重新构建镜像
docker compose build
# 重新构建并启动(代码改了之后常用)
docker compose up -d --build
# 扩展服务实例数
docker compose up -d --scale webapp=3
# 停止服务(不删除容器)
docker compose stop
# 启动已停止的服务
docker compose start
# 重启服务
docker compose restart
⚠️ 常见误区
- 误区一:“docker compose down会删除数据”。默认不会。
down只删除容器和网络。只有加上-v参数才会删除Volume。但确实要小心,别手滑加了-v。- 误区二:“depends_on能保证服务完全就绪”。不能。它只控制启动顺序。要等服务就绪,需要配合healthcheck。
- 误区三:“修改了docker-compose.yml后需要down再up”。不需要。直接
docker compose up -d就行,Compose会智能地只重建有变化的服务。
📌 阶段小结:到这里,你已经掌握了Docker Compose的核心知识——YAML文件结构、常用服务配置项(image/build、ports、volumes、environment、depends_on等)以及日常操作命令。接下来我们进入实战环节,用Compose搭建一套真实的博客系统。
五、实战:搭建一个完整的博客系统
让我们用Docker Compose搭建一个包含WordPress、MySQL和Nginx的完整博客系统:
# docker-compose.yml
services:
nginx:
image: nginx:1.28-alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- wordpress
restart: unless-stopped
networks:
- frontend
wordpress:
image: wordpress:6-fpm-alpine
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: wppass123
WORDPRESS_DB_NAME: wordpress
volumes:
- wp-content:/var/www/html
depends_on:
mysql:
condition: service_healthy
restart: unless-stopped
networks:
- frontend
- backend
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpass123
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppass123
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- backend
networks:
frontend:
backend:
volumes:
wp-content:
mysql-data:
注意这个架构的网络设计:
- Nginx和WordPress在frontend网络中
- WordPress和MySQL在backend网络中
- Nginx不能直接访问MySQL——提供了额外的安全层
对应的Nginx配置文件 nginx.conf:
server {
listen 80;
server_name localhost;
root /var/www/html;
index index.php;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass wordpress:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
一条命令启动整套博客系统:
docker compose up -d
等几十秒钟让所有服务就绪后,打开浏览器访问 http://localhost,你应该能看到WordPress的安装向导。
整个博客系统——Web服务器、应用、数据库——从无到有,就是一个YAML文件加一条命令的事。
六、多环境配置管理
实际项目中,开发环境和生产环境的配置往往不同。Docker Compose支持使用多个配置文件来管理这种差异:
# docker-compose.yml(基础配置)
services:
webapp:
build: ./app
environment:
- DB_HOST=postgres
postgres:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
# docker-compose.override.yml(开发环境,自动加载)
services:
webapp:
build:
context: ./app
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- ./app/src:/app/src # 热重载
environment:
- NODE_ENV=development
- DEBUG=true
postgres:
ports:
- "5432:5432" # 开发时暴露数据库端口,方便用GUI工具连接
environment:
- POSTGRES_PASSWORD=devpass
# docker-compose.prod.yml(生产环境,需要手动指定)
services:
webapp:
build:
context: ./app
dockerfile: Dockerfile.prod
environment:
- NODE_ENV=production
restart: unless-stopped
deploy:
resources:
limits:
memory: 512M
postgres:
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD} # 从环境变量读取
restart: unless-stopped
# 开发环境(自动合并 docker-compose.yml + docker-compose.override.yml)
docker compose up -d
# 生产环境(手动指定配置文件)
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
这种分层配置的方式既保持了公共配置的统一,又允许不同环境有各自的差异。
🤔 想一想 你的项目有开发、测试、预发布、生产四个环境。你会怎么组织docker-compose配置文件?是每个环境一个完整的文件,还是一个基础文件加多个覆盖文件?
七、Compose实用技巧
查看合并后的完整配置
# 查看最终生效的配置(合并了所有配置文件后的结果)
docker compose config
只重建有变化的服务
# 智能重建:只有相关的Dockerfile或依赖变了的服务才会重建
docker compose up -d --build
一次性任务
# 使用run执行一次性命令(不影响正在运行的服务)
docker compose run --rm webapp npm run migrate
docker compose run --rm webapp python manage.py createsuperuser
查看服务的资源使用
docker compose top # 查看各服务的进程
docker compose stats # 查看资源使用情况(CPU、内存等)
.env文件与变量替换
在 docker-compose.yml 同级目录下创建 .env 文件:
# .env
POSTGRES_VERSION=16
APP_PORT=3000
DB_PASSWORD=supersecret
# docker-compose.yml中可以引用这些变量
services:
postgres:
image: postgres:${POSTGRES_VERSION}
webapp:
ports:
- "${APP_PORT}:3000"
📝 掌握度自测
-
写出一个最简单的docker-compose.yml,包含一个Nginx服务和一个Redis服务,它们在同一个自定义网络中。
-
docker compose down和docker compose down -v的区别是什么?什么时候用前者,什么时候用后者? -
depends_on的局限性是什么?如何确保一个服务在另一个服务”真正就绪”后才启动? -
如何在不停掉整套环境的情况下,只重建和重启其中一个服务?
-
解释docker-compose.yml中
volumes顶级配置块和service内volumes配置的区别。
💡 自我评估
- 全部答对:你已经可以用Docker Compose管理复杂的多容器应用了。后面的实战篇会进一步提升你的技能。
- 答对3-4题:很好的基础。建议把本章的WordPress博客系统实际搭建一遍,感受Compose的便利。
- 答对1-2题:Docker Compose的配置项确实比较多。先记住最核心的几个:services、image/build、ports、volumes、environment、depends_on。其他的用到再查文档。
购买课程解锁全部内容
告别「在我电脑上能跑」:Docker 容器化实战
¥29.90