docker healthcheck 需用自定义脚本判断业务可服务性,而非仅进程存活;java 应用应检查 actuator 健康端点、关键线程池状态及 http 响应时效性。

在 Docker 中,HEALTHCHECK 是容器健康状态监控的核心机制,但默认的 CMD 方式(如 curl 或 ps)往往无法准确反映业务逻辑是否“挂死”——比如线程阻塞、连接池耗尽、消息积压、GC 长停顿等。此时需借助自定义健康检查脚本,结合业务特征判断真实可用性,并触发容器自动隔离(如停止、重启或被编排系统剔除)。关键在于:脚本要快、准、低侵入,且与 Docker 的健康状态机协同工作。
编写高敏感度业务健康检查脚本
脚本需聚焦“业务可服务性”,而非仅进程存活。例如 Java 应用可检查:
- JVM 是否响应 JMX 或 Actuator 端点(如
/actuator/health返回UP且diskSpace、db子项正常) - 关键线程池是否有活跃线程且队列未持续积压(通过
jstack+ 正则或 Micrometer 暴露的指标) - 本地 HTTP 健康端口能返回 200,且响应时间
- 若应用有内部状态机(如订单服务处于“处理中”超时),可调用其诊断接口
示例脚本 /healthcheck.sh(带超时和错误码规范):
#!/bin/sh set -e timeout 3s curl -f http://localhost:8080/actuator/health || exit 1 # 进一步校验子健康项(可选) if ! curl -s http://localhost:8080/actuator/health | grep -q '"status":"UP"'; then exit 1 fi # 检查本地 TCP 端口连通性(防端口被占但服务无响应) if ! nc -z localhost 8080; then exit 1 fi
在 Dockerfile 中正确配置 HEALTHCHECK
必须指定合理间隔、超时、重试次数,并使用非 root 用户可执行路径:
FROM openjdk:17-jre-slim COPY healthcheck.sh /healthcheck.sh RUN chmod +x /healthcheck.sh HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \ CMD ["/healthcheck.sh"]
说明:
-
--start-period=40s给应用冷启动留足时间,避免初期误判为失败 -
--timeout=5s防止脚本卡死拖垮整个健康检查周期 -
--retries=3连续失败 3 次才标记为unhealthy,容忍瞬时抖动 - 不要用
bash -c "...",Docker 的 HEALTHCHECK 不支持 shell 解析,必须用数组格式调用可执行文件
配合容器运行时与编排系统实现自动隔离
Docker 自身不自动销毁 unhealthy 容器,需依赖上层策略:
-
Docker Compose:设置
restart: on-failure,Docker 会在健康检查失败后按策略重启(注意:不是立即重启,而是等下次健康检查周期结束) -
Kubernetes:将
HEALTHCHECK映射为LivenessProbe,配置failureThreshold和restartPolicy: Always,K8s 会直接 kill 并重建 Pod -
自建巡检脚本:用
docker ps --filter "status=unhealthy" -q定期扫描,调用docker stop或docker rm -f隔离问题容器(适用于无编排环境)
注意:避免健康检查与业务共用线程池或 DB 连接——否则健康检查本身可能加剧挂死。建议健康端点走独立轻量通道(如内嵌 Jetty 的专用端口、或 Unix Socket)。
验证与可观测性增强
上线前务必验证健康状态流转:
- 运行容器后执行
docker inspect <cid> | grep -A 10 Health</cid>,确认状态从starting→healthy→ 手动触发故障后变为unhealthy - 模拟挂死:如
kill -STOP <java-pid></java-pid>,观察健康检查是否在 3×(30s+5s) 内判定失败 - 在脚本中加入日志输出(重定向到
/dev/stderr),并通过docker logs --since 5m <cid></cid>查看失败原因 - 将健康检查结果推送到 Prometheus(如用
blackbox_exporter主动探测),与业务指标关联告警
不复杂但容易忽略:健康脚本必须返回标准 Unix 状态码(0=成功,非0=失败),且不能产生大量 stdout/stderr(Docker 会缓存,长期运行可能撑爆磁盘)。











