Docker Compose 生产部署三要素:健康检查、重启策略与资源限制
凌晨三点,服务器告警短信把我从床上炸醒。
打开电脑一看,容器状态显示 running,绿色的小圆点看着挺健康。但访问服务?502 Bad Gateway。
数据库容器还没启动完成,应用容器就已经急吼吼地去连了。连接失败,服务挂掉。容器还在”运行”,但服务早已暴毙。
这就是我第一次把 Docker Compose 部署到生产环境时的真实经历。
跑得起来和跑得稳,完全是两码事。你的 docker-compose up 能一键启动,不代表半夜内存爆炸、进程崩溃时它还能站着。
Docker Compose 生产部署三要素——健康检查、重启策略、资源限制——就是用来填这个坑的。这篇文章分享我在踩坑过程中总结的配置方法,附带可直接复制的 YAML 模板,帮你把容器从”能跑”变成”跑得稳”。
1. 健康检查 (healthcheck):判断容器是否真正可用
docker ps 显示的 running 状态,只是告诉你的容器进程还在。但它能不能正常提供服务?不知道。
健康检查就是让 Docker 定期”体检”你的容器:发个 HTTP 请求、连一下数据库、或者跑个脚本,看服务是不是真活着。
健康检查怎么工作?
Docker 会按你设定的间隔,往容器里发送检查命令。命令返回 0 就是健康,非 0 就是不健康。连续失败几次后,容器会被标记为 unhealthy。
关键在于:健康检查失败不会自动触发重启。它只是把状态亮出来,告诉你”这哥们有问题”。要让它自动恢复,得配合 depends_on 的条件启动和重启策略。
四个参数你得搞清楚
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval: 30s # 每隔多久检查一次
timeout: 10s # 单次检查超时时间
retries: 3 # 连续失败几次标记为 unhealthy
start_period: 60s # 启动宽限期,这段时间失败不计入 retries
start_period 这个参数我曾经忽略过,结果就是:应用启动慢,数据库连接要 40 秒,但健康检查 10 秒就开始了。连续失败 3 次,直接被标记为 unhealthy。加上 start_period: 60s 后,给应用留够了初始化时间。
常用健康检查命令
Web 服务:
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
PostgreSQL:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
Redis:
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
配合 depends_on 实现依赖启动
这个才是健康检查最实用的地方:让等服务依赖真正就绪后再启动。
services:
app:
depends_on:
db:
condition: service_healthy # 等数据库健康检查通过
redis:
condition: service_healthy # 等 Redis 健康检查通过
以前我用 depends_on: [db, redis],结果应用容器启动了,数据库还在初始化。连不上,直接报错退出。改成 condition: service_healthy 后,应用会乖乖等到数据库能响应 pg_isready 才启动。世界清静了。
2. 重启策略 (restart):让容器具备自愈能力
容器挂了,谁来扶它起来?
手动 docker restart?凌晨三点告警的时候你试试。
重启策略就是让 Docker 守护进程帮你干这事儿。容器退出后,Docker 自动判断要不要重启。
四种策略对比
| 策略 | 行为 | 适用场景 |
|---|---|---|
no | 挂了就挂了,不重启 | 临时测试、CI/CD |
always | 不管怎么退出都重启 | 核心服务 |
on-failure | 只有异常退出才重启 | 任务型容器 |
unless-stopped | 始终重启,除非手动停止 | 生产首选 |
生产环境首选:unless-stopped
restart: unless-stopped
为什么推荐 unless-stopped 而不是 always?
区别在于:手动 docker stop 后的行为。
always:手动停止后,系统重启或 Docker 服务重启,容器又会自动启动unless-stopped:手动停止后,就真的停着,不会自己活过来
想象一下:你手动停了一个容器做维护,结果服务器重启后它自己又跑起来了。可能你只想说一句:搞什么鬼。
on-failure 的重试限制
on-failure 可以设置重试次数:
restart: on-failure:5 # 最多重启 5 次
如果容器连续 5 次启动失败,Docker 就放弃了。适合那些可能因为外部原因(数据库连不上、配置错误)导致反复崩溃的场景——防止无限重启循环。
怎么选?简单说
- 核心服务(Web、API、数据库):
unless-stopped - 后台任务、定时脚本:
on-failure - 开发调试、临时运行:
no
一个坑:重启策略只管”容器退出后要不要重启”。它不管”服务是不是真的可用”。要结合起来,还得靠健康检查。
3. 资源限制 (deploy.resources):防止容器失控
你有没有遇到过这种情况:一个容器内存泄漏,吃掉了服务器所有内存,其他容器全被 OOM Killer 干掉。
我遇到过。那感觉,啧。
资源限制就是给每个容器设个”天花板”:超过这个限度,就把它干掉,保护其他服务。
limits vs reservations
deploy:
resources:
limits:
cpus: '1.0' # 最多用 1 个 CPU
memory: 512M # 最多用 512MB 内存
reservations:
cpus: '0.5' # 至少保留 0.5 个 CPU
memory: 256M # 至少保留 256MB 内存
- limits:硬限制,超过就杀进程(OOM)
- reservations:软限制,告诉调度器”这个容器至少需要这么多资源”
说得直白点:limits 是”不能超过”,reservations 是”至少保证”。
CPU 限制怎么设置?
cpus: '1.0' # 最多用满 1 个 CPU 核心
cpus: '0.5' # 最多用 50% 的 CPU
cpus: '2.0' # 最多用 2 个核心
CPU 限制是软限制,容器超过后会被限速,不会被杀。所以宁可设高点。
内存限制怎么设置?
memory: 512M # 512MB
memory: 2G # 2GB
内存限制是硬限制。超过,容器直接被 OOM Killer 干掉,没商量。
我的经验值:
- Node.js 应用:至少
512M,生产环境建议1G - Python 应用:
256M - 512M - PostgreSQL:根据连接数和数据量,
1G - 4G - Redis:
256M - 512M,如果做缓存可能要更大
一个实战配置
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.25'
memory: 256M
这样设置:应用最多用 1 个 CPU 和 1GB 内存,但 Docker 会保证至少给它 0.25 个 CPU 和 256MB 内存。
注意:deploy 配置主要用于 Docker Swarm。单机部署用 docker-compose up 时,资源限制生效但需要 Docker Compose V2 或使用 docker-compose --compatibility 参数。更通用的单机写法是用 mem_limit 和 cpus(已废弃)或直接用 deploy(Compose V2.20+ 支持)。
4. 完整配置模板:可直接复制的生产级 YAML
三要素单独用都有用,但组合起来才是生产级的配置。下面是一个 Web 服务 + PostgreSQL + Redis 的完整示例,可以直接复制改造。
完整示例
version: '3.8'
services:
# Web 应用
app:
image: myapp:latest
restart: unless-stopped
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.25'
memory: 256M
logging:
driver: json-file
options:
max-size: "10m" # 单个日志文件最大 10MB
max-file: "3" # 最多保留 3 个日志文件
# PostgreSQL 数据库
db:
image: postgres:15
restart: unless-stopped
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: apppassword
POSTGRES_DB: appdb
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
volumes:
- pgdata:/var/lib/postgresql/data
# Redis 缓存
redis:
image: redis:7-alpine
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
memory: 128M
volumes:
pgdata:
配置分离技巧
开发环境和生产环境的配置通常不同。用两个文件分开管理:
# compose.yaml - 开发环境
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
restart: "no" # 开发时不想自动重启
# compose.production.yaml - 生产环境覆盖
version: '3.8'
services:
app:
image: myapp:v1.2.3 # 生产用构建好的镜像
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
memory: 1G
启动生产环境:
docker-compose -f compose.yaml -f compose.production.yaml up -d
两个文件会合并,compose.production.yaml 的配置覆盖 compose.yaml。
日志管理:防止磁盘撑爆
Docker 默认的日志驱动是 json-file,日志会无限增长。不加限制的话,几个月后磁盘就被日志吃满了。
logging:
driver: json-file
options:
max-size: "10m" # 单文件最大 10MB
max-file: "3" # 最多 3 个文件,总共最多 30MB
每个容器最多 30MB 日志,自动轮转。这配置我都加到每个服务里了,省得以后还要去手动清理。
总结
说了这么多,三要素的核心逻辑就是:
健康检查发现问题 → 重启策略自动恢复 → 资源限制防止崩溃蔓延
这套组合拳下来,你的 Docker Compose 应用就能:
- 启动时等依赖服务真正就绪,而不是盲目冲上去
- 崩溃后自己爬起来,半夜不用你起床
- 一个容器失控不会拖垮整台服务器
现在去检查一下你的 docker-compose.yml。缺了哪个配置?补上。
如果还没开始用 Docker Compose 部署生产环境,下次部署时把这三个配置带上。你会感谢自己的。
配置 Docker Compose 生产部署三要素
为 Docker Compose 添加健康检查、重启策略和资源限制,构建生产级稳定部署
⏱️ 预计耗时: 15 分钟
- 1
步骤1: 添加健康检查配置
为每个服务添加 healthcheck 配置:
• test: 检查命令(curl、pg_isready、redis-cli ping 等)
• interval: 检查间隔(推荐 10-30s)
• timeout: 超时时间(推荐 5-10s)
• retries: 失败次数(推荐 3-5 次)
• start_period: 启动宽限期(根据应用启动时间设置 30-60s) - 2
步骤2: 配置依赖服务条件启动
使用 depends_on 的 condition 参数:
• 将 depends_on: [db] 改为 depends_on: db: condition: service_healthy
• 确保依赖服务的健康检查已配置
• 应用服务会等待依赖服务真正可用后再启动 - 3
步骤3: 设置重启策略
根据服务类型选择重启策略:
• 核心服务(Web/API/数据库):restart: unless-stopped
• 后台任务/定时脚本:restart: on-failure:5
• 开发调试:restart: "no"
• 避免使用 always(手动停止后会意外重启) - 4
步骤4: 配置资源限制
在 deploy.resources 中设置 limits 和 reservations:
• limits: 硬限制,超过会被 OOM Killer 干掉
• reservations: 软限制,Docker 保证的最小资源
• Node.js 应用:limits.memory 建议至少 512M
• 数据库根据连接数和数据量设置 1G-4G - 5
步骤5: 添加日志轮转配置
防止日志文件撑爆磁盘:
• logging.driver: json-file(默认驱动)
• logging.options.max-size: "10m"(单文件最大 10MB)
• logging.options.max-file: "3"(保留 3 个文件)
• 总日志上限 30MB,自动轮转
常见问题
健康检查失败后容器会自动重启吗?
unless-stopped 和 always 有什么区别?
资源限制中的 limits 和 reservations 有什么区别?
start_period 参数有什么作用?
如何在开发和生产环境使用不同的配置?
• compose.yaml:开发环境配置(build: ., restart: "no")
• compose.production.yaml:生产环境覆盖(image: xxx, restart: unless-stopped)
• 启动命令:docker-compose -f compose.yaml -f compose.production.yaml up -d
• 后者会覆盖前者的配置,实现配置分离
单机部署可以用 deploy.resources 吗?
8 分钟阅读 · 发布于: 2026年4月24日 · 修改于: 2026年4月25日
Docker 实战指南
如果你是从搜索进入这篇文章,建议顺手补上上一篇或继续下一篇,这样更容易把同一主题读完整。
上一篇
Docker Compose 多服务编排:本地开发环境一键启动
用 Docker Compose 编排多服务,一键启动 Web、API、MySQL、Redis 本地开发环境。彻底解决手动安装繁琐、版本冲突、端口占用问题,团队新人 clone 仓库 5 分钟即可开始开发,项目切换秒级完成。
第 6 / 33 篇
下一篇
Dockerfile优化实战:5个技巧让镜像体积缩小80%
Docker镜像动辄几GB?掌握Alpine基础镜像、合并RUN指令、多阶段构建、.dockerignore配置和清理缓存5个技巧,将镜像从1.2GB降到180MB,减少85%。附完整Node.js优化案例和实测数据。
第 8 / 33 篇
相关文章
Dockerfile入门教程:从零构建你的第一个Docker镜像(含实例)
Dockerfile入门教程:从零构建你的第一个Docker镜像(含实例)
Docker vs 虚拟机:5分钟搞懂性能差异与场景选择指南
Docker vs 虚拟机:5分钟搞懂性能差异与场景选择指南
Docker安装避坑指南2025:从permission denied到成功运行的完整解决方案

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