第 5 章:Docker Compose
学习目标
- 用 docker-compose.yml 编排多容器
- 掌握 up / down / logs 等命令
- 配置网络、卷、环境变量
- 避开依赖启动顺序和重启策略坑
一、docker-compose.yml 最小例
yaml
# docker-compose.yml
version: "3.9"
services:
web:
image: nginx:1.25-alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
restart: unless-stopped
api:
build: ./api # 用当前目录 api/ 的 Dockerfile 构建
ports:
- "3000:3000"
environment:
NODE_ENV: production
DB_HOST: db # 引用服务名
depends_on:
- db
restart: unless-stopped
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpw
MYSQL_DATABASE: appdb
volumes:
- dbdata:/var/lib/mysql
restart: unless-stopped
volumes:
dbdata: # 命名卷二、核心命令
bash
# 在 docker-compose.yml 同目录
docker compose up -d # -d 后台
docker compose up --build # 重新构建再起
docker compose ps # 看状态
docker compose logs -f api # 实时日志(后面接服务名)
docker compose logs --tail 50 # 最近 50 行
docker compose exec api bash # 进容器
docker compose restart web # 重启单个
docker compose down # 停掉并删容器、网络
docker compose down -v # 连卷一起删(危险,慎用)
docker compose config # 校验 yml + 看解析后的最终配置⚠️ 坑 1:
docker compose(新版,空格)是 Docker Desktop / 新版 Docker CLI 自带。docker-compose(连字符)是独立的 Python 工具,已停更。统一用docker compose。
三、depends_on 不等于就绪
yaml
services:
api:
depends_on:
db:
condition: service_healthy # 等 db 健康才起
db:
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 10⚠️ 坑 2:
depends_on只控制启动顺序,不等待服务就绪。MySQL 容器启动了但还要几秒初始化,api 起来连不上。不加condition: service_healthy的依赖,经常见到明明写了 depends_on 还是连不上数据库。
四、环境变量与 .env 文件
yaml
services:
api:
image: myapi
environment:
DB_HOST: ${DB_HOST:-db} # ${VAR:-default},带默认值
DB_PORT: ${DB_PORT:-3306}
DEBUG: ${DEBUG} # 来自 .env
env_file:
- .env.production # 整个文件载入.env(同目录,docker compose 自动加载):
DEBUG=false
DB_HOST=mysql.prod.internal
DB_PORT=3306⚠️ 坑 3:
.env文件只支持KEY=VALUE格式,不支持 export。写成export FOO=bar会报错或被忽略。
五、网络与卷细节
yaml
services:
api:
networks:
- backend
web:
networks:
- frontend
- backend # 网关角色跨两个网
networks:
backend:
driver: bridge
frontend:
driver: bridge
volumes:
dbdata:
driver: local
shared: # 多服务共享⚠️ 坑 4:跨 compose 文件用
external网络,否则容器之间互相找不到:yamlnetworks: shared-net: external: true name: my-shared-network
六、重启策略
yaml
services:
web:
restart: no # 默认,不自动重启
api:
restart: always # 永远重启
worker:
restart: on-failure # 退出码非 0 才重启
deploy:
resources:
limits:
memory: 512M⚠️ 坑 5:开发环境别用
restart: always,否则改了代码忘重启就一直跑老版本。生产用always,开发用no或干脆不用 compose。
七、profiles(分组启动)
yaml
services:
web:
image: nginx
profiles: [frontend] # 只在 frontend 组启动
mysql:
image: mysql
profiles: [backend]
redis:
image: redis
profiles: [backend] # backend 组包含两个服务
adminer:
image: adminer
profiles: [tools] # 调试工具单独组bash
docker compose --profile frontend up # 只起 web
docker compose --profile backend up # mysql + redis
docker compose --profile backend --profile tools up⚠️ 坑 6:带
profiles的服务docker compose up默认不起,必须显式--profile。忘加参数起不来,不在服务列表里看清楚就懵。
八、实战:完整 Spring Boot + DB
yaml
services:
app:
build: .
image: myapp:1.0
container_name: myapp
ports:
- "8080:8080"
environment:
SPRING_PROFILES_ACTIVE: prod
DB_HOST: mysql
DB_PORT: "3306"
DB_NAME: appdb
DB_USER: appuser
DB_PWD: apppass
depends_on:
mysql:
condition: service_healthy
restart: unless-stopped
networks:
- appnet
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpw
MYSQL_DATABASE: appdb
MYSQL_USER: appuser
MYSQL_PASSWORD: apppass
volumes:
- mysql-data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-prootpw"]
interval: 5s
timeout: 3s
retries: 20
networks:
- appnet
volumes:
mysql-data:
networks:
appnet:bash
docker compose up -d --build # 第一次构建 + 跑
docker compose logs -f app # 看 app 启动日志九、本章小结
| 命令 | 作用 |
|---|---|
up -d | 后台启动 |
down -v | 停 + 删卷 |
logs -f | 实时日志 |
exec | 进容器 |
--profile | 分组启动 |
depends_on + healthcheck | 控制真正启动顺序 |
动手练习
- 用 compose 拉起 nginx + 一个静态页面,浏览器访问成功
- 加 MySQL + 健康检查,Spring Boot 应用通过服务名连 MySQL
- 用 profiles 分前端 / 后端两套环境
docker compose down -v后再up -d,数据是否还在?(命名卷持久)
下一章:第 6 章:Kubernetes 入门 →