Docker Compose 生产环境配置完全指南

Docker Compose 生产环境配置完全指南

Someone Lv5

前言

Docker Compose 是定义和运行多容器 Docker 应用的重要工具。开发者通常在开发环境中使用 docker-compose.yml 快速启动服务,但将其直接迁移到生产环境会面临诸多挑战——安全、高可用、日志管理、备份恢复、性能调优等。

本文基于 Ubuntu 22.04 LTS + Docker Compose V2(docker compose 子命令,不再是独立二进制),从零搭建一个生产级的 Docker Compose 部署架构,涵盖安全加固、日志轮转、健康检查、备份策略、CI/CD 集成和故障排查。

一、生产环境与开发环境的差异

维度开发环境生产环境
重启策略无或 alwaysunless-stopped + healthcheck
日志默认 json-file 无限制日志轮转 + 集中式日志(Loki/ELK)
网络默认 bridge自定义 overlay/manual 网络 + 网络策略
持久化匿名卷或 bind mount命名卷 + 定时备份
密码硬编码在 yml.env 文件或 Docker Secrets
资源限制无限制明确 CPU/Memory reservations 和 limits
监控资源监控 + 容器探活告警

二、安装最新版 Docker 与 Compose V2

首先确保系统已安装 Docker Engine 24+(推荐 27.x)和 Compose V2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 卸载旧版本
sudo apt remove docker docker-engine docker.io containerd runc

# 安装依赖
sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release

# 添加 Docker 官方 GPG 密钥和源
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 安装 Docker Engine 和 Compose V2
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# 将当前用户加入 docker 组(避免每次 sudo)
sudo usermod -aG docker $USER

# 验证
docker --version # Docker version 27.x
docker compose version # Docker Compose version v2.x

注意:生产环境不建议使用 sudo 运行 Docker,务必通过 docker 组管理权限。同时需严格审查哪些用户属于 docker 组——docker 组的成员拥有相当于 root 的权限。

三、基础安全加固

3.1 Docker Daemon 配置

创建 /etc/docker/daemon.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"live-restore": true,
"userland-proxy": false,
"iptables": true,
"storage-driver": "overlay2",
"exec-opts": ["native.cgroupdriver=systemd"],
"default-ulimits": {
"nofile": { "Name": "nofile", "Hard": 65535, "Soft": 65535 }
}
}

关键参数说明:

  • live-restore: true:Docker 守护进程重启时不停止运行中的容器。生产环境必开。
  • userland-proxy: false:禁用用户态代理,减少攻击面,配合 iptables: true 使用。
  • default-ulimits:为所有容器设置默认文件描述符限制。

重启 Docker 使配置生效:

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker

3.2 限制 Docker Socket 访问

Docker Socket(/var/run/docker.sock)映射到容器内是极其危险的操作。如果确实需要(如 Traefik、Portainer),应严格限制:

  • 使用只读模式挂载 docker.sock
  • 在容器内以非 root 用户运行
  • 使用 Docker Socket Proxy(如 tecnativa/docker-socket-proxy)提供安全访问层
1
2
3
4
5
6
7
8
9
10
11
12
# 安全做法:通过 socket-proxy 代理访问
services:
docker-proxy:
image: tecnativa/docker-socket-proxy:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- CONTAINERS=1
- NETWORKS=1
- SERVICES=1
- TASKS=1
restart: unless-stopped

四、生产级 compose.yml 模板

以下是一个包含 Nginx + PHP-FPM + MariaDB + Redis 的完整生产级 compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# compose.yml
version: "3.9"

services:
nginx:
image: nginx:1.27-alpine
container_name: app-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./app:/var/www/html:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- static-data:/var/www/static
depends_on:
- php
networks:
- frontend
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:80/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
deploy:
resources:
limits:
cpus: "0.5"
memory: "256M"
reservations:
cpus: "0.25"
memory: "128M"
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"

php:
image: php:8.3-fpm-alpine
container_name: app-php
restart: unless-stopped
volumes:
- ./app:/var/www/html
- php-socket:/var/run/php
environment:
- APP_ENV=production
- DB_HOST=mariadb
- REDIS_HOST=redis
env_file:
- .env.production
depends_on:
mariadb:
condition: service_healthy
redis:
condition: service_healthy
networks:
- frontend
- backend
healthcheck:
test: ["CMD", "php-fpm", "-t"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s
deploy:
resources:
limits:
cpus: "1.0"
memory: "512M"
reservations:
cpus: "0.5"
memory: "256M"

mariadb:
image: mariadb:11.4
container_name: app-mariadb
restart: unless-stopped
volumes:
- mariadb-data:/var/lib/mysql
- ./db/init:/docker-entrypoint-initdb.d:ro
environment:
MARIADB_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
env_file:
- .env.production
networks:
- backend
healthcheck:
test: ["CMD", "healthcheck.sh", "--su=mysql", "--connect", "--innodb_initialized"]
interval: 15s
timeout: 10s
retries: 5
start_period: 60s
deploy:
resources:
limits:
cpus: "2.0"
memory: "2G"
reservations:
cpus: "1.0"
memory: "1G"
secrets:
- db_root_password
- db_password

redis:
image: redis:7.4-alpine
container_name: app-redis
restart: unless-stopped
command: >
redis-server --requirepass ${REDIS_PASSWORD}
--appendonly yes
--appendfsync everysec
--save 900 1
--save 300 10
--save 60 10000
volumes:
- redis-data:/data
networks:
- backend
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 15s
timeout: 5s
retries: 3
start_period: 10s
deploy:
resources:
limits:
cpus: "0.5"
memory: "256M"

volumes:
mariadb-data:
driver: local
redis-data:
driver: local
static-data:
driver: local
php-socket:
driver: local

networks:
frontend:
driver: bridge
ipam:
config:
- subnet: "172.20.0.0/24"
backend:
driver: bridge
internal: true # 后端网络无外部访问,提升安全性
ipam:
config:
- subnet: "172.20.1.0/24"

secrets:
db_root_password:
file: ./secrets/db_root_password.txt
db_password:
file: ./secrets/db_password.txt

五、关键配置详解

5.1 健康检查(Healthcheck)

每个服务都应配置 healthcheck,让 Docker 能自动检测服务状态,配合 depends_on.condition: service_healthy 确保依赖服务启动后再启动当前服务。

健康检查参数速查:

参数说明推荐值
test健康检查命令(需返回 0 表示健康)根据服务类型定制
interval检查间隔15-30s
timeout单次检查超时5-10s
retries连续失败次数3-5
start_period启动后的宽限期根据服务启动时间而定

5.2 资源限制

deploy.resources 设置容器的 CPU 和内存上限,防止某个容器耗尽主机资源导致雪崩。

  • limits:硬上限,超过则 OOM kill
  • reservations:软预留,调度时保证至少分配这些资源

5.3 网络隔离

使用 两个网络 实现分层隔离:

  • frontend:Nginx 等对外暴露的服务在此网络
  • backend:内部服务(DB、Redis)使用 internal: true,完全隔离外部访问

只有 php 服务同时加入 frontendbackend 网络,作为应用层的数据通道。

5.4 Docker Secrets

敏感信息(数据库密码、API 密钥等)不应写死在 compose.yml 或环境变量中。使用 Docker Secrets:

  • 创建 ./secrets/db_password.txt 文件(一行明文)
  • 在 compose 中通过 secrets: 声明
  • 在 service 中通过 secrets: 引用
  • 密码读取方式:MARIADB_ROOT_PASSWORD_FILE: /run/secrets/db_root_password

六、日志管理

6.1 容器日志轮转

必须在 Docker Daemon 或每个服务级别设置日志轮转,防止日志撑爆磁盘:

1
2
3
4
5
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"

6.2 集中式日志方案

推荐使用 Loki + Promtail(轻量)或 ELK(功能丰富):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
services:
loki:
image: grafana/loki:3.0
volumes:
- ./loki-config.yml:/etc/loki/local-config.yml:ro
- loki-data:/loki
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yml
restart: unless-stopped

promtail:
image: grafana/promtail:3.0
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail-config.yml:/etc/promtail/config.yml:ro
command: -config.file=/etc/promtail/config.yml
restart: unless-stopped

6.3 日志清理脚本

即便配置了轮转,长期运行的服务器仍可能出现日志残留。建议配合定时任务清理:

1
2
3
4
5
#!/bin/bash
# /usr/local/bin/clean-docker-logs.sh
# 清理 3 天前的容器日志文件
find /var/lib/docker/containers -name "*-json.log" -mtime +3 -delete
journalctl --vacuum-time=7d

添加到 crontab:

1
0 3 * * * /usr/local/bin/clean-docker-logs.sh

七、数据备份与恢复

7.1 数据库自动备份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash
# /usr/local/bin/backup-mariadb.sh
BACKUP_DIR=/data/backups/mariadb
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=30
DB_PASSWORD=$(cat /opt/docker-app/secrets/db_root_password.txt)

mkdir -p $BACKUP_DIR

docker exec app-mariadb mysqldump \
--all-databases \
--single-transaction \
--routines \
--triggers \
--events \
-u root -p"$DB_PASSWORD" \
| gzip > $BACKUP_DIR/full_backup_$TIMESTAMP.sql.gz

# 保留最近 30 天
find $BACKUP_DIR -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete

# 同步到远程备份服务器
rsync -avz --delete $BACKUP_DIR/ backup@remote-server:/data/backups/mariadb/

添加到 crontab:

1
0 2 * * * /usr/local/bin/backup-mariadb.sh

7.2 容器卷备份

1
2
3
4
5
6
#!/bin/bash
# 备份 Docker 命名卷到 tar.gz
docker run --rm \
-v mariadb-data:/data \
-v /data/backups/volumes:/backup \
alpine tar czf /backup/mariadb-data_$(date +%Y%m%d).tar.gz -C /data .

7.3 恢复流程

1
2
3
4
5
6
7
8
# 数据库恢复
gunzip < full_backup_20260612_020000.sql.gz | docker exec -i app-mariadb mysql -u root -p"$PASSWORD"

# 卷恢复
docker run --rm \
-v mariadb-data:/data \
-v /data/backups/volumes:/backup \
alpine tar xzf /backup/mariadb-data_20260612.tar.gz -C /data

八、部署与 CI/CD 集成

8.1 项目目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/opt/docker-app/
├── compose.yml
├── .env.production # 环境变量(不进版本库)
├── secrets/ # Docker Secrets(不进版本库)
│ ├── db_root_password.txt
│ └── db_password.txt
├── nginx/
│ ├── conf.d/
│ └── ssl/
├── app/ # 应用代码
├── db/
│ └── init/ # 初始化 SQL
├── scripts/
│ ├── backup.sh
│ └── deploy.sh
└── monitoring/
├── loki-config.yml
└── promtail-config.yml

8.2 零停机部署脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash
# /opt/docker-app/scripts/deploy.sh
set -e

cd /opt/docker-app

echo "[1/4] 拉取最新代码..."
git pull origin main

echo "[2/4] 构建新镜像..."
docker compose build --pull

echo "[3/4] 滚动更新服务..."
# 先更新无状态服务
docker compose up -d --no-deps --scale nginx=2 nginx
docker compose up -d --no-deps php

# 等待健康检查通过
echo "[4/4] 等待健康检查..."
sleep 15

# 清理旧容器
docker image prune -f

echo "部署完成!"

8.3 GitHub Actions 自动部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# .github/workflows/deploy.yml
name: Deploy to Production

on:
push:
branches: [main]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Copy files via SSH
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_KEY }}
source: "."
target: "/opt/docker-app"

- name: Execute deploy script
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_KEY }}
script: cd /opt/docker-app && bash scripts/deploy.sh

九、监控与告警

9.1 本地资源监控

1
2
3
4
5
6
7
8
# 查看所有容器资源使用
docker stats --no-stream

# 查看特定服务日志
docker compose logs --tail=100 -f nginx

# 磁盘空间预警
df -h | grep -E "overlay|/dev/sda"

9.2 cAdvisor + Prometheus 集成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
services:
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
restart: unless-stopped
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
ports:
- "8080:8080"
privileged: true
networks:
- monitoring

node_exporter:
image: prom/node-exporter:latest
container_name: node-exporter
restart: unless-stopped
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--path.rootfs=/rootfs'
ports:
- "9100:9100"
networks:
- monitoring

networks:
monitoring:
driver: bridge

十、常见问题排查

问题原因解决方案
容器不断重启健康检查失败或配置错误docker compose logs [service] 查看具体错误
端口被占用其他服务占用了映射端口ss -tlnp | grep :80 查看占用进程
磁盘空间满日志或镜像占用docker system prune -af 清理;docker compose logs --tail=0 清空日志
数据库连不上网络配置或认证问题确认网络是否正确;检查 secrets 文件是否一致
容器内时区不对未设置 TZ 环境变量添加 environment: TZ: Asia/Shanghai
PHP 写文件报错权限不一致确认 UID/GID 映射;使用 user: "1000:1000"
Docker 守护进程无法启动daemon.json 语法错误dockerd --validate 检查配置
swap 导致 OOM内存限制 + swap 交互daemon.json 中禁用 swap:设置 "swappiness": 0

十一、安全最佳实践清单

  1. 最小权限原则:容器内使用非 root 用户运行进程(RUN adduser -D appuser && USER appuser
  2. 只读文件系统:不需要写权限的目录使用 :ro 挂载
  3. 镜像签名验证:使用 Docker Content Trust(export DOCKER_CONTENT_TRUST=1
  4. 定期更新基础镜像docker compose build --pull && docker compose up -d
  5. 安全扫描:集成 Trivy 或 Docker Scout 扫描镜像漏洞
  6. 网络策略:后端服务使用 internal: true 网络
  7. 密钥管理:Docker Secrets 替代明文环境变量
  8. 审计日志:启用 Docker Daemon 的 auditd 规则
  9. 资源配额:所有容器设置 CPU/Memory limits
  10. 备份恢复演练:定期测试备份数据能否正常恢复

总结

Docker Compose 并非仅适用于开发环境,通过合理的配置和加固,完全可以在生产环境中安全、稳定地运行。本文从安装、安全加固、生产级配置模板、日志管理、数据备份、CI/CD 集成到监控告警,覆盖了完整的生产化流程。

关键在于记住几个核心原则:安全隔离、资源限制、日志管理、健康检查、定期备份。遵循这些原则,Docker Compose 可以成为中小规模生产环境的高效部署方案。

对于更大规模的集群场景,可考虑升级到 Docker Swarm 或 Kubernetes,但 Docker Compose 的配置思维和最佳实践在这些平台同样适用。


本文由AI辅助生成,内容仅供参考

  • 标题: Docker Compose 生产环境配置完全指南
  • 作者: Someone
  • 创建于 : 2026-06-12 20:11:00
  • 更新于 : 2026-06-18 08:39:57
  • 链接: https://demo-blog.qusite.cn/2026-06-12-docker-compose-production-guide/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。