Docker Compose 生产环境避坑指南:我不说你永远不知道的 7 个坑
Docker Compose 是开发利器,但直接搬到生产环境会炸。
我亲眼见过一个团队把 docker-compose.yml 从本地直接推到服务器,结果第二天早上发现数据库被删了。原因是容器重启时 volume 没挂对。
下面是我在生产环境踩过的 7 个坑。
坑 1:restart 策略不当
YAML
# 错误示范 - 永远不会重启
services:
api:
image: myapp:latest
# 没有 restart 策略!
# 正确配置
services:
api:
image: myapp:latest
restart: unless-stopped
# always: 无论什么原因退出都重启
# unless-stopped: 手动停止才不重启
# on-failure: 仅非零退出码时重启坑 2:数据库密码硬编码
YAML
# 危险!
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: mypassword123 # 硬编码密码
# 安全做法
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt坑 3:资源限制缺失
一个容器内存泄漏就能拖垮整台机器:
YAML
services:
api:
image: myapp:latest
deploy:
resources:
limits:
cpus: '2'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M坑 4:健康检查缺失
YAML
services:
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s # 给数据库启动留时间
api:
image: myapp:latest
depends_on:
db:
condition: service_healthy # 等数据库健康后才启动
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3坑 5:日志无限增长
YAML
services:
api:
image: myapp:latest
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"坑 6:网络配置不当
YAML
# 好的网络隔离
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # 不暴露到外网
services:
nginx:
networks:
- frontend
api:
networks:
- frontend
- backend
db:
networks:
- backend # 数据库只在内网可访问坑 7:数据卷不持久化
YAML
services:
db:
image: postgres:16
volumes:
- postgres_data:/var/lib/postgresql/data
# 不是 ./data:/var/lib/postgresql/data(相对路径坑)
volumes:
postgres_data:
driver: local
# 显式命名 volume,容器删除后数据仍在生产级 docker-compose.yml 模板
YAML
version: '3.8'
x-common: &common
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
services:
nginx:
<<: *common
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- certbot_data:/etc/letsencrypt
networks:
- frontend
healthcheck:
test: ["CMD", "nginx", "-t"]
interval: 30s
timeout: 10s
retries: 3
api:
<<: *common
image: myapp:${APP_VERSION:-latest}
environment:
DATABASE_URL: postgresql://postgres:${DB_PASSWORD}@db:5432/myapp
env_file:
- .env.production
deploy:
resources:
limits:
memory: 512M
networks:
- frontend
- backend
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
db:
<<: *common
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
volumes:
postgres_data:
certbot_data:
secrets:
db_password:
file: ./secrets/db_password.txt记住:开发环境用 Compose 是方便,生产环境用 Compose 是责任。
读者评论 3