Docker Compose 生产环境部署:健康检查与资源限制
Docker Compose 通常被当作本地开发工具,但官方明确支持将其用于生产:一台或多台单机服务器上,Compose 足以跑起一套可维护的应用栈。关键在于把"开发配置"与"生产配置"分开,并补齐健康检查、资源限制与重启策略这些生产必备项。
一、为什么 Compose 可以用于生产
Compose 的定位是单机多容器编排。对于日活在数千到数万、不需要横向扩容到多节点的项目,它比 Kubernetes 轻量得多:没有控制平面、不需要学习大量概念,一条 docker compose up -d 即可上线。如果需要集群化调度,再考虑升级到 Kubernetes。
什么时候该升级到 Kubernetes?出现下面任一信号时再考虑:需要跨多台机器调度、要按流量自动伸缩副本数、需要比滚动更新更复杂的发布策略、团队已经开始为配置漂移头痛。在这些信号出现之前,Compose 加一台机器加一套监控,往往是更划算的选择。
二、生产配置的关键改动
官方建议:生产环境不是直接复用 compose.yaml,而是把改动放到独立的 compose.production.yaml 中,用 -f 叠加。
docker compose -f compose.yaml -f compose.production.yaml up -d
典型的生产改动包括:
- 移除应用代码的 bind mount:生产环境应让代码"留在镜像里",不允许从宿主机直接改文件。
- 修改端口绑定:不要把数据库、Redis 等端口暴露到公网。
- 降低日志冗长度:设置环境变量减少 debug 输出。
- 设置重启策略:如
restart: always,避免单点故障后服务不拉起。 - 增加日志聚合:如 JSON 日志 + 采集器。
举一个实际拆分的例子:开发用的 compose.yaml 里 web 服务用 bind mount 挂源码、端口 3000 暴露给本地;compose.production.yaml 只写覆盖项:
# compose.production.yaml
services:
web:
image: myapp:1.0.0 # 用 image 覆盖 build
ports:
- "127.0.0.1:8080:8080" # 只绑本机,前面再放 Nginx
environment:
- NODE_ENV=production
restart: always
logging:
driver: json-file
options: { max-size: "10m", max-file: "3" }
这样在服务器上执行 docker compose -f compose.yaml -f compose.production.yaml up -d 就以生产配置启动,而开发机依旧用原来的文件,互不干扰。
三、健康检查与启动顺序
depends_on 的短语法只保证"依赖服务先启动",并不保证依赖已经"可用"。生产环境应使用长语法 + healthcheck,让 Web 服务真正等到数据库就绪:
services:
web:
image: myapp:1.0.0
depends_on:
db:
condition: service_healthy
restart: always
db:
image: postgres:16
healthcheck:
test: ["CMD", "pg_isready", "-U", "appuser"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
start_period 很关键:它给了容器"预热"时间,避免启动较慢的服务被误判为 unhealthy 而反复重启。
四、资源限制
不设限制的容器可能吃光整台机器的内存。生产环境应为每个服务声明 CPU 与内存上限:
services:
web:
image: myapp:1.0.0
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.25"
memory: 128M
pids_limit: 512
read_only: true
tmpfs:
- /tmp
read_only + tmpfs 的组合能让文件系统只读,显著降低被入侵后写入恶意文件的可能;pids_limit 则能防住 fork 炸弹类攻击。
资源限制怎么定?没有标准答案,但可以按经验起步:先跑起来看 docker stats 观察峰值,再按峰值的 1.5 倍设上限、按基线的 80% 设预留。一台 4GB 的机器上要同时塞数据库、应用和 Redis 三个容器时,记得把 MySQL 的 innodb_buffer_pool_size、Redis 的 maxmemory 一起算进去,否则容器限制和进程内存会互相打架。
五、部署更新的正确姿势
docker compose build web
docker compose up --no-deps -d web
先 build 再 up --no-deps,避免连带重建依赖服务。更新后记得验证健康状态:
docker compose ps
docker compose logs --tail=100 web
up --no-deps 的关键是只重建 web 一个服务。想更接近零停机,可以先用新镜像标签启动一个暂存实例验证再切换,或预演 --rollback 回滚。数据库 schema 变更不要顺手在 docker compose up 里做,先单独执行迁移命令并做好备份,再更新应用代码。
六、安全与密钥
生产环境不要把密码写死在 Compose 文件里。优先用 .env 文件配合环境变量注入,敏感程度更高的密钥用 Docker Secrets:
services:
web:
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
更多容器安全实践见 容器安全最佳实践,配置与密钥管理见环境变量与配置管理。
七、监控与运维
上线后要接监控:用 监控告警 覆盖容器重启次数与资源占用,备份策略可参考备份恢复手册。服务器初始化可参考服务器初始化脚本。
常见坑
- 忘给数据卷做备份:容器可以重建,数据卷不行。备份命名卷用
docker run --rm -v myvolume:/data -v $PWD:/backup alpine tar czf /backup/vol.tgz -C /data .。 - 把 .env 提交进仓库:
.env里往往有密码,务必加进.gitignore,密钥走 Docker Secrets 或独立密钥系统。 - 端口全开:数据库、Redis 端口绑到
0.0.0.0,等于把内部服务暴露到公网,是扫描器的首选目标。 - 忽视日志轮转:不设
max-size,日志文件一直涨,撑爆磁盘是常见线上事故。
16IDC 观察
Compose 生产化是一个“把开发便利转换为运维纪律”的过程:健康检查让部署可验证,资源限制让一台机器能安全承载多个应用,重启策略让故障自动恢复。对中小项目而言,这是从“能跑”走向“稳定跑”的关键一步,也是日后迁移到 Kubernetes 前的良好过渡。
参考:Docker Compose 生产部署指南 https://docs.docker.com/compose/how-tos/production/ ;Compose 文件参考 https://docs.docker.com/reference/compose-file/services/ ;备份数据卷 https://docs.docker.com/storage/volumes/#back-up-restore-or-migrate-data-volumes
原文来源:https://docs.docker.com/compose/how-tos/production/ 、https://docs.docker.com/reference/compose-file/services/