Docker Compose 完整编排指南
概述
Docker Compose 是 Docker 官方推出的容器编排工具,通过 YAML 文件定义和运行多容器 Docker 应用。本文档详细介绍 Compose 文件的结构、配置项及生产实践。
一、Compose 文件结构
1.1 顶层元素
一个标准的 docker-compose.yml 文件包含以下顶层元素:
yaml
version: "3.9" # Compose 文件格式版本
services: # 定义服务(容器)
service_name:
image: ...
build: ...
# ...
networks: # 定义网络
network_name:
driver: bridge
# ...
volumes: # 定义数据卷
volume_name:
driver: local
# ...
configs: # 定义配置(Docker Swarm 模式)
config_name:
file: ...
secrets: # 定义密钥(Docker Swarm 模式)
secret_name:
file: ...1.2 版本说明
| 版本 | 发布日期 | Docker Engine 最低版本 | 主要特性 |
|---|---|---|---|
| 3.9 | 2022-09 | 20.10.x | 最新稳定版,支持所有特性 |
| 3.8 | 2022-02 | 19.03.x | 支持服务运行时参数 |
| 3.7 | 2019-06 | 18.06.x | 支持扩展字段 |
| 3.0 | 2017-06 | 17.06.x | 支持 Swarm 模式 |
| 2.4 | 2019-06 | 17.12.x | 支持健康检查扩展 |
建议:新项目统一使用
version: "3.9"。
二、服务配置详解
2.1 基本服务定义
yaml
services:
web-app:
# --- 镜像相关 ---
image: myapp:latest # 使用现有镜像
build: # 从 Dockerfile 构建
context: ./app
dockerfile: Dockerfile
args:
BUILD_ENV: production
labels:
org.label-schema.name: "myapp"
pull_policy: always # 镜像拉取策略:always | missing | never | build
# --- 依赖管理 ---
depends_on: # 启动顺序依赖
- mysql
- redis
condition: service_healthy # 等待条件:service_started | service_healthy | service_completed_successfully
# --- 环境配置 ---
environment: # 环境变量(推荐写法)
SPRING_PROFILES_ACTIVE: prod
DB_URL: jdbc:mysql://mysql:3306/db
env_file: # 从文件加载环境变量
- ./env/common.env
- ./env/prod.env
# --- 端口映射 ---
ports:
- "8080:8080" # host:container
- "443:8443"
# --- 卷挂载 ---
volumes:
- named_vol:/data # 命名卷
- ./logs:/var/log/app # 绑定挂载
- type: bind # 扩展语法
source: ./config
target: /app/config
read_only: true
# --- 健康检查 ---
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# --- 重启策略 ---
restart: unless-stopped # no | always | on-failure | unless-stopped
# --- 资源限制 ---
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.5"
memory: 256M
# --- 网络配置 ---
networks:
- frontend
- backend
# --- 日志配置 ---
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# --- 其他 ---
container_name: myapp # 容器名称
hostname: myapp # 容器主机名
user: "1000:1000" # 运行用户
working_dir: /app # 工作目录
command: ["java", "-jar", "app.jar"] # 覆盖默认命令
entrypoint: ["/bin/sh", "-c"] # 覆盖入口点
labels:
com.example.description: "My Application"
expose:
- "8080" # 仅暴露给链接的服务
stdin_open: true # 等价于 -i
tty: true # 等价于 -t
dns:
- 8.8.8.8
- 114.114.114.114
extra_hosts:
- "host.docker.internal:host-gateway"
cap_add:
- SYS_NICE
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
sysctls:
net.core.somaxconn: "1024"
ulimits:
nofile:
soft: 65535
hard: 655352.2 配置项详解
image
yaml
image: postgres:15-alpine # 指定镜像及标签
image: registry.example.com/myapp:1.0.0 # 私有仓库镜像build
yaml
build:
context: ./dir # Dockerfile 所在目录
dockerfile: Dockerfile.prod # 指定 Dockerfile
args: # 构建参数
VERSION: 1.0
BUILD_DATE: "2024-01-01"
labels: # 为镜像添加标签
version: "1.0"
target: builder # 多阶段构建的目标阶段
network: host # 构建时的网络模式
cache_from: # 缓存来源
- myapp:latest
- type=registry,ref=myapp:cachedepends_on
yaml
depends_on:
mysql:
condition: service_healthy # 等待健康检查通过
redis:
condition: service_started # 进程启动即视为就绪
migration:
condition: service_completed_successfully # 等待任务完成ports
yaml
ports:
- "8080:8080" # 简写:host:container
- "8080" # 随机 host 端口
- "127.0.0.1:8000:8000" # 绑定特定 IP
- "443:8443/tcp" # 指定协议
- target: 8080 # 扩展语法
published: 8080
protocol: tcp
mode: hostvolumes
yaml
volumes:
# 简写语法
- db_data:/var/lib/mysql # 命名卷
- ./data:/app/data # 绑定挂载
- /host/path:/container/path # 绝对路径绑定挂载
# 扩展语法(推荐 production 使用)
- type: volume # volume | bind | tmpfs | npipe
source: db_data
target: /var/lib/mysql
volume:
nocopy: true # 创建卷时不复制容器数据
- type: bind
source: ./config
target: /app/config
read_only: true # 只读挂载
bind:
propagation: rshared
- type: tmpfs # 临时文件系统
target: /tmp
tmpfs:
size: 100Mhealthcheck
yaml
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"] # 检查命令
interval: 30s # 检查间隔
timeout: 10s # 超时时间
retries: 3 # 重试次数
start_period: 40s # 启动宽限期
disable: false # 禁用健康检查restart
yaml
restart: always # 策略说明:
# no - 不自动重启(默认)
# always - 无论退出状态,总是重启
# on-failure - 仅非正常退出时重启
# unless-stopped - 除非手动停止,否则总是重启deploy(仅 Swarm 模式完整生效,Compose 也支持资源限制)
yaml
deploy:
mode: replicated # replicated | global
replicas: 3 # 副本数(replicated 模式)
resources:
limits: # 硬限制
cpus: "1.5"
memory: 1024M
reservations: # 预留资源
cpus: "0.5"
memory: 256M
restart_policy:
condition: on-failure # none | on-failure | any
delay: 5s
max_attempts: 3
window: 120s
placement:
constraints: # 节点约束
- node.role == worker
- node.labels.env == prod
update_config:
parallelism: 2 # 并行更新数
delay: 10s
failure_action: rollback # continue | pause | rollback
monitor: 10s
order: start-first # start-first | stop-first
rollback_config:
parallelism: 1
order: stop-first三、网络配置
3.1 网络模式
Compose 自动为每个应用创建一个默认网络,所有服务默认加入该网络并通过服务名相互访问。
yaml
networks:
# 桥接网络(默认)
frontend:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
gateway: 172.20.0.1
# 无网络(loopback only)
none_net:
driver: none
# 主机网络(共享宿主机网络栈)
host_net:
driver: host
# 叠加网络(Swarm 跨节点)
overlay_net:
driver: overlay
attachable: true # 非 Swarm 服务可接入
# 外部网络(使用已有网络)
external_net:
external: true # 声明为外部网络,不创建
name: production-network # 外部网络的实际名称3.2 服务接入网络
yaml
services:
nginx:
networks:
- frontend
- backend
- network_alias: monitoring # 网络别名
aliases:
- web-gateway
app:
networks:
frontend:
ipv4_address: 172.20.1.10 # 指定固定 IP
aliases:
- app-backend
networks:
frontend:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/163.3 网络配置完整示例
yaml
version: "3.9"
services:
web:
image: nginx:alpine
networks:
- public
- internal
api:
image: myapi:latest
networks:
internal:
aliases:
- api-service
dmz:
ipv4_address: 10.0.2.10
db:
image: postgres:15
networks:
- internal
networks:
public:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/24
internal:
driver: bridge
internal: true # 禁止外部访问
ipam:
config:
- subnet: 172.21.0.0/24
dmz:
external: true
name: company-dmz四、数据持久化
4.1 命名卷
由 Docker 管理,存储在 /var/lib/docker/volumes/ 下,适合持久化数据库等关键数据。
yaml
version: "3.9"
services:
mysql:
image: mysql:8.0
volumes:
- mysql_data:/var/lib/mysql # 使用命名卷
- mysql_config:/etc/mysql/conf.d
volumes:
mysql_data: # 默认 local 驱动
driver: local
driver_opts:
type: none
o: bind
device: /data/mysql
labels:
app: mysql
env: production
mysql_config:
driver: local4.2 绑定挂载
将宿主机目录或文件挂载到容器中,适合开发场景和配置文件注入。
yaml
services:
app:
volumes:
# 目录绑定挂载
- type: bind
source: ./src
target: /app/src
read_only: true
# 单个文件挂载(配置文件)
- type: bind
source: ./application-prod.yml
target: /app/config/application-prod.yml
# 挂载宿主机 socket(Docker in Docker)
- type: bind
source: /var/run/docker.sock
target: /var/run/docker.sock4.3 数据卷容器模式
通过专门的数据容器共享数据卷。
yaml
services:
data-container:
image: busybox
volumes:
- shared_data:/data
command: ["sleep", "infinity"]
app1:
image: myapp:latest
volumes_from:
- data-container:ro # 只读挂载数据卷容器的卷
app2:
image: myapp:latest
volumes_from:
- data-container:rw # 读写挂载
volumes:
shared_data:
driver: local4.4 数据持久化最佳实践
| 类型 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 命名卷 | 数据库、缓存等有状态服务 | 完全由 Docker 管理,跨主机迁移方便 | 宿主机上路径不易定位 |
| 绑定挂载 | 开发热重载、配置文件注入 | 直接修改即可生效,路径明确 | 权限问题,跨平台兼容性差 |
| tmpfs | 敏感信息、临时缓存 | 数据不会持久化,安全性高 | 占用内存,重启数据丢失 |
五、完整微服务编排示例
以下为一个完整的 Spring Boot 微服务编排示例,包含应用服务、MySQL 数据库、Redis 缓存、Nginx 反向代理及健康监控。
5.1 docker-compose.yml
yaml
version: "3.9"
# ============================================================================
# 全局配置 - 锚点复用
# ============================================================================
x-common-config: &common-config
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
- backend
x-healthcheck-spring: &healthcheck-spring
healthcheck:
test:
[
"CMD-SHELL",
"curl -sf http://localhost:8080/actuator/health || exit 1",
]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
x-resources-service: &resources-service
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.25"
memory: 256M
# ============================================================================
# 服务定义
# ============================================================================
services:
# ---------- MySQL 数据库 ----------
mysql:
image: mysql:8.0.35
container_name: micro-mysql
hostname: mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root123}
MYSQL_DATABASE: ${MYSQL_DATABASE:-micro_service}
MYSQL_USER: ${MYSQL_USER:-appuser}
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-apppass123}
TZ: Asia/Shanghai
ports:
- "${MYSQL_PORT:-3306}:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./init-db:/docker-entrypoint-initdb.d # 初始化脚本
- ./conf/mysql/my.cnf:/etc/mysql/conf.d/my.cnf:ro
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD:-root123}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
networks:
- backend
deploy:
resources:
limits:
cpus: "1.5"
memory: 1G
reservations:
cpus: "0.5"
memory: 512M
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "3"
# ---------- Redis 缓存 ----------
redis:
image: redis:7.2-alpine
container_name: micro-redis
hostname: redis
restart: unless-stopped
command:
- redis-server
- --appendonly yes
- --requirepass ${REDIS_PASSWORD:-redispass123}
- --maxmemory 256mb
- --maxmemory-policy allkeys-lru
ports:
- "${REDIS_PORT:-6379}:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
networks:
- backend
deploy:
resources:
limits:
cpus: "0.5"
memory: 512M
reservations:
cpus: "0.1"
memory: 128M
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# ---------- 用户服务 ----------
user-service:
build:
context: ./services/user-service
dockerfile: Dockerfile
args:
JAR_FILE: target/user-service.jar
image: micro/user-service:latest
container_name: micro-user-service
<<: *common-config
<<: *healthcheck-spring
<<: *resources-service
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
environment:
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod}
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/${MYSQL_DATABASE:-micro_service}?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true
SPRING_DATASOURCE_USERNAME: ${MYSQL_USER:-appuser}
SPRING_DATASOURCE_PASSWORD: ${MYSQL_PASSWORD:-apppass123}
SPRING_REDIS_HOST: redis
SPRING_REDIS_PORT: 6379
SPRING_REDIS_PASSWORD: ${REDIS_PASSWORD:-redispass123}
SERVER_PORT: 8080
ports:
- "8081:8080"
# ---------- 订单服务 ----------
order-service:
build:
context: ./services/order-service
dockerfile: Dockerfile
image: micro/order-service:latest
container_name: micro-order-service
<<: *common-config
<<: *healthcheck-spring
<<: *resources-service
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
environment:
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod}
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/${MYSQL_DATABASE:-micro_service}?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true
SPRING_DATASOURCE_USERNAME: ${MYSQL_USER:-appuser}
SPRING_DATASOURCE_PASSWORD: ${MYSQL_PASSWORD:-apppass123}
SPRING_REDIS_HOST: redis
SPRING_REDIS_PASSWORD: ${REDIS_PASSWORD:-redispass123}
USER_SERVICE_URL: http://user-service:8080
SERVER_PORT: 8080
ports:
- "8082:8080"
# ---------- Nginx 反向代理 ----------
nginx:
image: nginx:1.25-alpine
container_name: micro-nginx
hostname: nginx
restart: unless-stopped
ports:
- "${NGINX_HTTP_PORT:-80}:80"
- "${NGINX_HTTPS_PORT:-443}:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- ./nginx/logs:/var/log/nginx
depends_on:
user-service:
condition: service_healthy
order-service:
condition: service_healthy
healthcheck:
test: ["CMD", "nginx", "-t"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
networks:
- frontend
- backend
deploy:
resources:
limits:
cpus: "0.5"
memory: 128M
reservations:
cpus: "0.1"
memory: 64M
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "5"
# ============================================================================
# 网络定义
# ============================================================================
networks:
frontend:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/24
labels:
app: micro-platform
tier: frontend
backend:
driver: bridge
internal: false # 根据安全需求设为 true 可隔离后端网络
ipam:
config:
- subnet: 172.20.1.0/24
labels:
app: micro-platform
tier: backend
# ============================================================================
# 数据卷定义
# ============================================================================
volumes:
mysql_data:
driver: local
driver_opts:
type: none
o: bind
device: ${DATA_ROOT:-./data}/mysql
labels:
app: micro-platform
service: mysql
type: data
redis_data:
driver: local
driver_opts:
type: none
o: bind
device: ${DATA_ROOT:-./data}/redis
labels:
app: micro-platform
service: redis
type: data5.2 Nginx 配置(nginx/conf.d/default.conf)
nginx
upstream user-service {
server user-service:8080;
}
upstream order-service {
server order-service:8080;
}
server {
listen 80;
server_name localhost;
# API 路由
location /api/user/ {
proxy_pass http://user-service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 超时配置
proxy_connect_timeout 30s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
# 缓冲配置
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
location /api/order/ {
proxy_pass http://order-service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 健康检查端点
location /health {
proxy_pass http://user-service/actuator/health;
access_log off;
}
# 静态资源
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
}六、环境管理
6.1 .env 文件
Compose 自动读取项目根目录下的 .env 文件,用于注入变量。
# .env 文件
# 数据库配置
MYSQL_ROOT_PASSWORD=root123
MYSQL_DATABASE=micro_service
MYSQL_USER=appuser
MYSQL_PASSWORD=apppass123
MYSQL_PORT=3306
# Redis 配置
REDIS_PASSWORD=redispass123
REDIS_PORT=6379
# Nginx 配置
NGINX_HTTP_PORT=80
NGINX_HTTPS_PORT=443
# 应用配置
SPRING_PROFILES_ACTIVE=prod
DATA_ROOT=./data
TZ=Asia/Shanghai
# 镜像仓库
REGISTRY_URL=registry.example.com
IMAGE_TAG=latest6.2 多 Compose 文件策略
使用 -f 参数合并多个 Compose 文件,实现环境和场景的差异化配置。
# 基础配置(所有环境共享)
docker-compose.yml
# 开发环境覆盖
docker-compose.override.yml
# 生产环境覆盖
docker-compose.prod.yml
# 测试环境
docker-compose.test.yml
# 监控栈
docker-compose.monitor.yml文件结构示例
docker-compose.yml(通用配置)
yaml
version: "3.9"
services:
app:
image: myapp:${IMAGE_TAG:-latest}
networks:
- backend
networks:
backend:
driver: bridgedocker-compose.override.yml(开发配置,自动加载)
yaml
version: "3.9"
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- ./src:/app/src # 源码热重载
- ./dev-config:/app/config
ports:
- "8080:8080"
- "5005:5005" # 远程调试端口
environment:
SPRING_PROFILES_ACTIVE: dev
JAVA_OPTS: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"docker-compose.prod.yml(生产配置,需显式指定)
yaml
version: "3.9"
services:
app:
image: ${REGISTRY_URL}/myapp:${IMAGE_TAG}
deploy:
replicas: 3
resources:
limits:
cpus: "2"
memory: 1G
restart: always
logging:
driver: "syslog"
options:
syslog-address: "tcp://logs.example.com:514"docker-compose.monitor.yml(监控栈)
yaml
version: "3.9"
services:
prometheus:
image: prom/prometheus:latest
volumes:
- ./monitor/prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
networks:
- backend
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
networks:
- backend
depends_on:
- prometheus
networks:
backend:
external: true启动命令
bash
# 开发(docker-compose.override.yml 自动生效)
docker compose up -d
# 生产
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 生产 + 监控
docker compose \
-f docker-compose.yml \
-f docker-compose.prod.yml \
-f docker-compose.monitor.yml \
up -d
# 测试
docker compose -f docker-compose.yml -f docker-compose.test.yml run tests6.3 Profile 配置
使用 --profile 按需启动服务子集。
yaml
version: "3.9"
services:
app:
image: myapp:latest
profiles:
- core # 默认 profile
redis:
image: redis:7-alpine
profiles:
- core
monitoring:
image: grafana/grafana:latest
profiles:
- monitoring # 仅在启用 monitoring profile 时启动
debug-tools:
image: busybox
profiles:
- debugbash
# 只启动核心服务
docker compose --profile core up -d
# 启动核心 + 监控
docker compose --profile core --profile monitoring up -d
# 启动所有服务
docker compose --profile "*" up -d七、常用命令
7.1 启动与管理
| 命令 | 说明 |
|---|---|
docker compose up -d | 后台启动所有服务 |
docker compose up -d --build | 构建后启动 |
docker compose up -d service_name | 启动指定服务 |
docker compose down | 停止并删除容器、网络 |
docker compose down -v | 停止并删除容器、网络、卷 |
docker compose down --rmi all | 停止并删除容器、网络、镜像 |
docker compose restart | 重启所有服务 |
docker compose restart service_name | 重启指定服务 |
docker compose start | 启动已存在的容器 |
docker compose stop | 停止服务(不删除) |
docker compose pause | 暂停服务 |
docker compose unpause | 恢复暂停的服务 |
7.2 日志与调试
| 命令 | 说明 |
|---|---|
docker compose logs -f | 跟踪所有服务日志 |
docker compose logs -f service_name | 跟踪指定服务日志 |
docker compose logs --tail=100 service_name | 查看最近 100 行日志 |
docker compose logs --since=5m service_name | 查看最近 5 分钟的日志 |
docker compose ps | 列出服务状态 |
docker compose top | 查看运行的进程 |
docker compose stats | 实时查看资源使用统计 |
7.3 容器操作
| 命令 | 说明 |
|---|---|
docker compose exec service_name bash | 进入容器执行命令 |
docker compose exec -u root service_name bash | 以 root 用户进入 |
docker compose run --rm service_name cmd | 运行一次性命令 |
docker compose run -p 3000:3000 app npm test | 指定端口运行一次性任务 |
7.4 构建与推送
| 命令 | 说明 |
|---|---|
docker compose build | 构建所有服务镜像 |
docker compose build service_name | 构建指定服务镜像 |
docker compose build --no-cache | 不使用缓存构建 |
docker compose build --parallel | 并行构建 |
docker compose push | 推送镜像到仓库 |
7.5 资源配置
| 命令 | 说明 |
|---|---|
docker compose config | 查看合并后的 Compose 配置 |
docker compose config --services | 列出所有服务名 |
docker compose config --volumes | 列出所有数据卷 |
docker compose images | 列出使用的镜像 |
docker compose version | 查看 Compose 版本 |
7.6 命令速查示例
bash
# 启动完整环境
docker compose up -d
# 仅重建并启动 user-service
docker compose up -d --build user-service
# 查看服务状态
docker compose ps
# 查看实时资源使用
docker compose stats
# 查看 user-service 日志
docker compose logs -f user-service
# 进入容器调试
docker compose exec user-service bash
# 查看运行进程
docker compose top
# 查看合并后的完整配置
docker compose config
# 验证配置语法
docker compose config --quiet
# 安全下线(先检查连接数,再关机)
docker compose exec nginx nginx -s quit
docker compose down
# 清理所有资源(含匿名卷和镜像)
docker compose down --volumes --rmi all八、生产优化
8.1 健康检查最佳实践
yaml
services:
# Spring Boot 应用 - 使用 Actuator
app:
healthcheck:
test:
[
"CMD-SHELL",
"curl -sf http://localhost:8080/actuator/health/liveness || exit 1",
]
interval: 15s # 生产建议 15-30s
timeout: 5s # 超时不宜过长
retries: 3 # 连续失败次数
start_period: 60s # 启动宽限期,不计入 retries
# 数据库 - 使用原生检查工具
mysql:
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# Redis
redis:
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
# Nginx
nginx:
healthcheck:
test: ["CMD", "nginx", "-t"]
interval: 30s
timeout: 5s
retries: 38.2 资源限制配置
yaml
services:
app:
deploy:
resources:
limits: # 硬限制 - 超限则 OOM Kill
cpus: "2.0"
memory: 1G
reservations: # 软限制 - 调度保证
cpus: "0.5"
memory: 512M
mysql:
deploy:
resources:
limits:
cpus: "2.0"
memory: 2G
reservations:
cpus: "1.0"
memory: 1G
redis:
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.25"
memory: 256M8.3 重启策略
yaml
services:
# 关键服务 - 总是重启
critical-service:
restart: always
# 有状态服务 - 非正常退出才重启
stateful-service:
restart: on-failure
deploy:
restart_policy:
condition: on-failure
delay: 10s # 重启延迟
max_attempts: 5 # 最大重试次数
window: 60s # 判定窗口
# 一次性任务 - 不重启
batch-job:
restart: "no"
deploy:
restart_policy:
condition: none8.4 日志限制
yaml
services:
app:
logging:
driver: "json-file" # 默认日志驱动
options:
max-size: "10m" # 单个日志文件最大大小(生产推荐 50-100m)
max-file: "3" # 最大保留文件数(生产推荐 5-10)
compress: "true" # 压缩历史日志
# 外部日志系统(生产推荐)
app-syslog:
logging:
driver: "syslog"
options:
syslog-address: "tcp://logs.example.com:514"
syslog-facility: "local0"
tag: "{{.Name}}/{{.ID}}"
# AWS CloudWatch(AWS 环境)
app-cloudwatch:
logging:
driver: "awslogs"
options:
awslogs-region: "us-east-1"
awslogs-group: "myapp"
awslogs-stream: "{{.Name}}"
# 禁用日志(仅测试用)
no-log:
logging:
driver: "none"8.5 安全加固
yaml
services:
app:
# 禁止特权提升
security_opt:
- no-new-privileges:true
# 移除所有非必需能力
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE # 仅允许绑定低端口
# 以非 root 用户运行
user: "1000:1000"
# 只读根文件系统
read_only: true
tmpfs:
- /tmp
- /var/run
# 内核参数调优
sysctls:
net.core.somaxconn: "1024"
net.ipv4.tcp_syncookies: "1"
# DNS 配置
dns:
- 8.8.8.8
- 114.114.114.114
# 额外 hosts
extra_hosts:
- "host.docker.internal:host-gateway"8.6 完整生产示例
yaml
version: "3.9"
x-prod-defaults: &prod-defaults
restart: always
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "5"
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
services:
app:
<<: *prod-defaults
image: ${REGISTRY_URL}/myapp:${IMAGE_TAG:-latest}
ports:
- "127.0.0.1:8080:8080" # 仅监听本地,前面由 Nginx 反向代理
environment:
SPRING_PROFILES_ACTIVE: prod
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:8080/actuator/health || exit 1"]
interval: 15s
timeout: 5s
retries: 3
start_period: 60s
deploy:
mode: replicated
replicas: 3
resources:
limits:
cpus: "2.0"
memory: 1G
reservations:
cpus: "0.5"
memory: 512M
update_config:
parallelism: 1
delay: 30s
order: start-first
failure_action: rollback
restart_policy:
condition: on-failure
delay: 10s
max_attempts: 5
window: 120s
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
mysql:
<<: *prod-defaults
image: mysql:8.0.35
volumes:
- mysql_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
deploy:
resources:
limits:
cpus: "2.0"
memory: 2G
reservations:
cpus: "1.0"
memory: 1G
redis:
<<: *prod-defaults
image: redis:7.2-alpine
command: ["redis-server", "--appendonly", "yes", "--requirepass", "${REDIS_PASSWORD}"]
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.25"
memory: 256M
volumes:
mysql_data:
driver: local
redis_data:
driver: local8.7 性能监控与调优建议
| 维度 | 建议 |
|---|---|
| CPU 限制 | 为每个容器设置 CPU 上限,防止"吵闹邻居"效应 |
| 内存限制 | 设置 memory + 预留 reservations,避免 OOM |
| 磁盘 I/O | 使用 --device-write-bps、--device-read-bps 限流 |
| 网络 | 合理划分网络,减少跨网络通信 |
| 日志 | 限制日志大小,避免磁盘写满 |
| 健康检查 | start_period 设置合理,避免启动期间被误杀 |
| 镜像 | 使用 Alpine 变体,定期清理无用镜像 |
九、常见问题与排查
9.1 启动顺序问题
depends_on 仅控制启动顺序,不等待服务就绪。务必配合 condition: service_healthy 使用。
yaml
# 错误:应用可能在 MySQL 未就绪时启动
depends_on:
- mysql
# 正确:等待健康检查通过
depends_on:
mysql:
condition: service_healthy9.2 端口冲突
# 错误日志
Error: starting userland proxy: listen tcp4 0.0.0.0:3306: bind: address already in use解决方案:
- 使用
.env文件管理端口变量,避免冲突 - 使用
lsof -i :3306查找占用端口的进程 - 考虑移除非必要端口暴露(仅暴露 Nginx 等网关服务)
9.3 权限问题
# 绑定挂载的配置或数据文件权限不足
# 解决方案:在 Dockerfile 中创建用户并设置权限
RUN groupadd -r appuser -g 1000 && \
useradd -r -g appuser -u 1000 -s /bin/bash appuser && \
mkdir -p /app/data && \
chown -R appuser:appuser /app
USER appuser9.4 容器健康检查失败
bash
# 排查步骤
# 1. 查看健康检查状态
docker compose ps
# 2. 查看容器内健康检查命令是否可执行
docker compose exec app curl -f http://localhost:8080/actuator/health
# 3. 检查容器内是否有 curl(Alpine 镜像默认没有)
# 4. 检查应用是否绑定了正确的地址(必须是 0.0.0.0 不是 127.0.0.1)十、最佳实践总结
- 版本锁定:使用精确版本标签而非
latest,确保环境可重复 - 命名规范:服务名、容器名、网络名、卷名遵循统一命名规范
- 环境变量:敏感信息使用
.env文件,禁止硬编码 - 健康检查:所有关键服务必须配置健康检查
- 资源限制:始终设置 CPU 和内存限制,防止资源争抢
- 日志管理:限制日志大小和文件数,或使用外部日志系统
- 安全加固:以非 root 用户运行,移除不必要的能力
- 网络隔离:合理划分网络,最小化服务间暴露
- 配置复用:使用 YAML 锚点 (
&) 和引用 (<<: *) 减少重复 - 多文件管理:利用多 Compose 文件策略区分环境
- 定期维护:清理未使用的镜像、卷、构建缓存
- 版本控制:将 Compose 文件纳入 Git 管理,
.env文件添加.gitignore