Docker Compose 生产部署:健康检查、重启策略与日志管理
凌晨三点,服务器告警短信一条接一条地弹到手机上。打开终端一看,磁盘空间 99%——容器日志占了 50GB。
这还不是最糟糕的。去年有个项目,API 容器状态显示 “running”,但数据库连接早就断了,请求全部返回 500。排查了整整三个小时才定位到问题。据 Last9 的研究数据,容器”假死”这种事,平均每次要浪费 3.2 小时排查时间。
说实话,很多团队第一次在生产环境部署 Docker Compose 时,都只是简单配个端口映射和卷挂载,就把容器往线上扔了。健康检查没配置,日志轮转也没加,重启策略更是随便写个 restart: always。结果就是:容器看起来在跑,其实早就挂了;日志文件疯狂增长,最后把磁盘撑爆;崩溃的服务无限重启,把 CPU 和内存都吃光。
这篇文章,我会把生产环境的三个核心配置——健康检查、重启策略、日志管理——掰开揉碎讲清楚。不光有配置示例,还有常见服务的健康检查命令、故障排查步骤,以及一份可以直接复制使用的完整 docker-compose.yml 模板。
健康检查 — 让容器真正活着
容器状态显示 running,不代表应用真的在工作。数据库连不上、端口没监听、进程卡死——这些情况 Docker 可不知道。健康检查就是给容器装个”心跳监测器”,定期检查应用到底是不是还能正常响应。
配置语法
在 docker-compose.yml 里,healthcheck 配置长这样:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s # 每隔 10 秒检查一次
timeout: 5s # 每次检查最多等 5 秒
retries: 5 # 连续失败 5 次才标记为 unhealthy
start_period: 30s # 容器启动后给 30 秒预热时间
这几个参数得搭配好。timeout 不能比 interval 大,不然检查还没结束下一次就开始了。start_period 不能省——数据库这类服务启动慢,如果预热时间太短,健康检查会误判容器挂了。
常见服务的健康检查命令
不同服务,检查方式不一样。这里列几个常用的:
PostgreSQL
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d mydb"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
pg_isready 是 PostgreSQL 自带的检查工具,专门用来判断数据库是否准备好接受连接。
MySQL / MariaDB
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$$MYSQL_ROOT_PASSWORD"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
注意密码用 $$ 转义,不然 YAML 会把 $ 当成变量引用。
Redis
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
interval: 10s
timeout: 3s
retries: 3
Redis 的 ping 命令返回 PONG,用 grep 过滤一下确保结果正确。
Web Server(HTTP 检查)
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
-f 参数让 curl 在 HTTP 状态码非 2xx 时返回非零退出码,触发健康检查失败。
坑点提醒:Alpine 这类精简镜像里可能没有 curl。要么安装它(apk add curl),要么改用 wget:
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1"]
启动顺序控制
数据库还没准备好,API 容器就启动了,结果连接失败、报错、崩溃——这种事我见过太多次。用 depends_on 配合 condition: service_healthy 能解决:
services:
postgres:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
api:
build: ./api
depends_on:
postgres:
condition: service_healthy # 等 postgres 健康检查通过才启动
这样一来,Docker Compose 会等 postgres 的健康检查返回 healthy 后,才启动 api 容器。不会再出现”数据库还没就绪,API 就去连”的尴尬。
重启策略 — 失败后优雅恢复
容器崩溃了怎么办?让它自动重启看起来是个好主意。但问题是:如果崩溃的根本原因没解决,重启只会无限循环,浪费 CPU 和内存,还掩盖了真实故障。
配置语法
重启策略在 deploy 块里配置:
deploy:
restart_policy:
condition: on-failure # 只在失败时重启
delay: 5s # 重启前等 5 秒
max_attempts: 3 # 最多尝试重启 3 次
window: 120s # 120 秒内重启成功才算真正恢复
condition 有三个选项:
none:不重启,容器挂了就挂着on-failure:只有容器非正常退出(退出码非 0)才重启any:不管什么情况都重启
生产环境推荐
生产环境建议用 on-failure,而不是 always。
为啥?restart: always 会让容器不管什么情况都重启。应用代码有 bug 导致崩溃?重启。数据库连不上导致进程退出?重启。配置文件写错了启动失败?还是重启。结果就是崩溃循环,日志疯狂刷屏,CPU 被反复消耗。
on-failure 加上 max_attempts 就不一样了——最多重启 3 次,如果还是失败就停下来。运维人员能看到容器最终挂掉了,去排查真正的问题。
参数调优
delay 是重启间隔。太短的话,容器可能还没完全清理干净就又启动了;太长又会延长恢复时间。一般 5-10 秒比较合适。
window 这个参数容易被忽略。它定义的是:重启后多长时间内不再次失败,才算重启成功。比如设置 window: 120s,容器重启后如果 120 秒内又挂了,max_attempts 计数不会重置。这样能避免”刚重启成功一秒钟又崩溃”的误判。
健康检查与重启策略的配合
健康检查和重启策略不是独立工作的,它们会协同配合:
- 健康检查连续失败
retries次 → 容器被标记为unhealthy - 如果配置了
restart_policy,Docker 会尝试重启容器 - 重启后健康检查重新开始计数
- 如果重启后健康检查通过,容器恢复正常;如果重启后还是失败,继续尝试重启直到
max_attempts用完
这个链路让故障有了”自动恢复”的能力,同时又限制了无限重启的风险。
日志管理 — 防止磁盘被撑爆
开头提到的凌晨三点告警——磁盘 99%,日志占了 50GB——这事儿我经历过不止一次。Docker 默认的日志驱动 json-file 不会自动清理旧日志,文件会无限增长。如果不配置日志轮转,早晚把磁盘撑爆。
日志轮转配置
在 docker-compose.yml 里加上 logging 配置:
logging:
driver: "json-file"
options:
max-size: "10m" # 单个日志文件最大 10MB
max-file: "3" # 最多保留 3 个日志文件
compress: "true" # 压缩旧日志节省空间
这样配置后,容器日志最多占用 30MB(10MB × 3)。超过 10MB 时,Docker 会创建新文件;超过 3 个文件时,最旧的会被删除或压缩。
日志文件存放在 /var/lib/docker/containers/<container-id>/<container-id>-json.log。用 du 命令可以查看实际占用:
du -sh /var/lib/docker/containers/*/*-json.log
驱动选择
Docker 支持多种日志驱动:json-file、syslog、fluentd、journald、local 等。对于大多数场景,json-file 或 local 都够用。
Docker 官方文档提到,local 驱动比 json-file 更高效,自带日志轮转,不需要手动配置 max-size/max-file。如果你日志量很大(比如每天几十 GB),可以考虑用 local:
logging:
driver: "local"
不过 local 驱动有个缺点:不能用 docker logs 直接查看日志内容。需要在配置里加上 mode: "non-blocking" 才能兼容。
集中式日志收集(可选)
单机部署用 json-file 或 local 就行。但如果你有几十台服务器、几百个容器,日志分散在各处就不好管理了。这时候可以考虑集中式日志方案:
- Fluentd:轻量级日志收集,适合小规模集群
- ELK Stack(Elasticsearch + Logstash + Kibana):功能强大,但部署成本高
- Loki + Grafana:云原生方案,与 Prometheus 生态集成好
这些方案的配置比较复杂,不在本文范围内。简单提一下配置 Fluentd 的思路:
logging:
driver: "fluentd"
options:
fluentd-address: "localhost:24224"
tag: "docker.{{.Name}}"
Fluentd 会把日志转发到指定的地址,你可以在另一台服务器上统一收集和分析。
完整配置模板
把健康检查、重启策略、日志管理组合起来,就是一个生产级的 docker-compose.yml。下面是一个完整示例,包含 PostgreSQL 数据库、Redis 缓存、和 API 服务:
version: '3.8'
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myuser -d mydb"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
deploy:
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
compress: "true"
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
interval: 10s
timeout: 3s
retries: 3
start_period: 5s
deploy:
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
api:
build:
context: ./api
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
DATABASE_URL: postgres://myuser:mypassword@postgres:5432/mydb
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
deploy:
restart_policy:
condition: on-failure
delay: 10s
max_attempts: 3
window: 120s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "5"
compress: "true"
volumes:
postgres_data:
配置要点说明
启动顺序:api 容器的 depends_on 同时等待 postgres 和 redis 的健康检查通过。数据库和缓存都准备好了,API 才启动,避免启动时的连接错误。
日志大小差异:postgres 和 redis 的日志量通常不大,10MB × 3 足够;API 服务日志可能更多,设置 50MB × 5。根据实际日志量调整,别一刀切。
重启延迟差异:postgres 启动慢,重启后需要时间恢复,delay 设置 5 秒;API 启动快,delay 设置 10 秒给健康检查更多缓冲。
启动预热时间:postgres start_period: 30s,给数据库足够的初始化时间;redis start_period: 5s,Redis 启动本来就快;API start_period: 10s,应用启动通常几秒就够了。
这个模板可以直接复制使用,只需要把环境变量和镜像换成你自己的。如果你的项目还有其他服务(比如 MongoDB、MinIO),按照同样的模式加上健康检查、重启策略和日志配置就行。
常见陷阱与排查
配置写完了,部署上去,问题可能还是会出现。这里列几个常见坑和排查步骤。
健康检查一直失败
症状:容器状态一直是 unhealthy,但应用看起来能正常工作。
排查步骤:
-
先检查健康检查命令用的工具是否存在:
docker exec <container> which curl docker exec <container> which pg_isreadyAlpine 镜像里经常没有 curl,需要手动安装或者改用 wget。
-
手动运行健康检查命令,看输出:
docker exec <container> curl -f http://localhost:8080/health如果返回错误,可能是健康检查端点本身有问题。
-
查看健康检查的详细状态:
docker inspect --format='{{json .State.Health}}' <container> | jq能看到最近几次检查的结果、失败原因、时间戳。
容器反复重启
症状:容器启动几秒后又挂掉,日志里全是重启记录。
排查步骤:
-
查看容器退出原因:
docker inspect --format='{{.State.ExitCode}}' <container> docker inspect --format='{{.State.Error}}' <container>退出码能告诉你大概是什么问题(1 = 一般错误,137 = 被 OOM 杀掉,139 = 段错误)。
-
查看重启次数:
docker inspect --format='{{.RestartCount}}' <container>如果次数很大,检查
max_attempts是否生效。 -
查看容器日志找具体错误:
docker logs --tail 100 <container>
日志磁盘满
症状:磁盘空间告警,发现 /var/lib/docker/containers 目录占用很大。
排查步骤:
-
找出占用最大的日志文件:
du -sh /var/lib/docker/containers/*/*-json.log | sort -rh | head -5 -
检查日志轮转配置是否生效:
docker inspect --format='{{.HostConfig.LogConfig}}' <container>如果输出显示
Config: {},说明没配置日志轮转。 -
手动清理日志(临时方案):
truncate -s 0 /var/lib/docker/containers/<id>/<id>-json.log这是临时方案,长期还是要加上日志轮转配置。
快速排查命令清单
遇到问题时,这几个命令能帮你快速定位:
# 查看所有容器健康状态
docker ps --format "table {{.Names}}\t{{.Status}}"
# 查看某个容器的健康检查历史
docker inspect --format='{{json .State.Health}}' <container>
# 查看容器退出码和重启次数
docker inspect --format='ExitCode: {{.State.ExitCode}}, RestartCount: {{.RestartCount}}' <container>
# 查看日志文件大小
du -sh /var/lib/docker/containers/*/*-json.log | sort -rh
# 查看容器最近 100 条日志
docker logs --tail 100 <container>
总结
生产环境部署 Docker Compose,这三个配置不是可选的,而是必须的:健康检查让容器不只是”看起来活着”,重启策略让故障有自动恢复的机会同时限制无限循环,日志管理防止磁盘被撑爆。
核心配置清单:
- 健康检查:
test+interval+timeout+retries+start_period - 重启策略:
condition: on-failure+max_attempts: 3 - 日志轮转:
max-size: 10m+max-file: 3+compress: true
三步行动计划:
- 检查你现有的 docker-compose.yml,看看有没有这三个配置。没有的话,至少加上健康检查和日志轮转。
- 用上面的完整模板部署一个测试服务,观察健康检查是否生效、日志是否在轮转。
- 把排查命令记下来。下次凌晨三点收到告警时,能快速定位问题。
别让你的容器裸跑在生产线上。配置好这三个”保护罩”,出问题时至少能自动恢复、能快速排查、不会把磁盘撑爆。
常见问题
健康检查的 interval 和 timeout 怎么设置才合理?
重启策略用 restart: always 还是 on-failure 更好?
日志文件多大、保留几个合适?
容器健康检查一直失败但应用正常运行怎么办?
depends_on 的 condition: service_healthy 有什么用?
如何快速查看容器日志占用了多少磁盘空间?
11 分钟阅读 · 发布于: 2026年4月12日 · 修改于: 2026年4月12日

评论
使用 GitHub 账号登录后即可评论