真正健壮的服务健康检查需三重验证:优先调用健康接口(如curl -f --max-time 3),失败则查带唯一标识的进程(pgrep -f "app.name=myapi"),最后才查端口;重启前须graceful shutdown、资源清理及权限适配,并动态配置检查频率与退避策略。

用 curl + pgrep 三重校验判断容器是否真挂了
只查 docker ps 或 pgrep 进程名,大概率误判——进程在,但 HTTP 接口卡死、DB 连接超时、线程死锁,服务已不可用。必须分层验证:
- 第一优先级:调用业务健康端点,如
curl -f --max-time 3 http://localhost:8080/health,非 2xx 返回直接判失败 - 第二 fallback:查带唯一标识的进程,启动时加
-Dapp.name=order-svc,再用pgrep -f "app.name=order-svc"精准匹配 - 第三兜底:仅当前两者都不可用时,才用
nc -z localhost 8080查端口;避免用netstat(已被弃用)或无超时的telnet
注意:pgrep -f 容易匹配到日志行或临时脚本,务必在启动命令里注入可识别的字符串,而不是依赖 jar 包名这种模糊字段。
重启前必须做 graceful shutdown 和资源清理
直接 docker kill -s SIGKILL 或 kill -9 容器主进程,会导致连接未释放、临时文件残留、端口被占、PID 文件未更新,下次启动必失败。
- 先发
SIGTERM:docker kill -s SIGTERM order-svc,等 5 秒 - 用
docker inspect order-svc | jq -r '.State.Status'或kill -0 $(cat /var/run/order-svc.pid) 2>/dev/null预检进程是否已退出 - 若仍存活,再发
SIGKILL;之后手动清理:rm -f /var/run/order-svc.pid /tmp/order-svc.lock - 确认宿主机端口未被残留进程占用:
ss -tnlp | grep :8080,有则kill -9对应 PID
如果容器内应用不响应 SIGTERM(比如某些 Python 脚本没捕获信号),得改用 exec python app.py 启动,让 Python 进程成为 PID 1,否则信号根本收不到。
防雪崩:加锁 + 冷却时间 + 最大重试限制
多个检查脚本并发执行,或网络抖动触发连续失败,会引发“重启风暴”——刚拉起又杀掉,循环往复。
- 用
flock -n /tmp/health.lock包裹整个检查逻辑,确保同一时间只有一个实例在操作 - 每次重启后强制冷却:
sleep $((5 * $RESTART_COUNT)),第 1 次等 5 秒,第 2 次等 10 秒,指数退避 - 记录重启次数到文件:
/var/log/order-svc/restart_count,连续 3 次失败就停机告警,不再自动拉起 - 检查前校验基础资源:
df -h / | awk '$5 > 95 {exit 1}'、free -m | awk '/Mem:/ {if ($7 ,资源不足直接跳过重启
别省略冷却时间——哪怕只是 sleep 3,也能拦住 80% 的瞬时抖动误触发。
日志必须独立落盘,不能依赖 stdout
后台运行的脚本(如 nohup ./health.sh &)一旦 stdout/stderr 重定向丢失,所有检查记录、错误堆栈、重启动作全丢,等于没监控。
- 每条关键操作都写入结构化日志:
echo "$(date '+%F %T') CHECK order-svc: $(curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/health)" >> /var/log/order-svc/health.log - 重启动作单独记入
/var/log/order-svc/restart.log,含 PID、退出码、前后docker inspect输出片段 - 错误时追加诊断信息:
docker logs --tail 20 order-svc 2>&1 | tail -n 5 >> /var/log/order-svc/diagnose.log
最常被忽略的是日志轮转——没配 logrotate 的话,health.log 几天就能涨到几个 GB,最后填满根分区。


















