一、为什么需要健康检查

容器化应用就像住在公寓里的租客,虽然水电齐全,但房东(运维人员)得定期确认租客是否还活着。没有健康检查的容器,就像从不回消息的室友——你不知道它是在正常工作还是已经崩溃。

举个例子,你的电商网站用Docker部署了商品服务,但某个容器因为内存泄漏悄悄挂了。如果没有健康检查,流量还会继续往这个"僵尸容器"发送,用户看到的可能就是404错误页面。

二、健康检查的三种姿势

1. 命令检查(CMD)

最直接的方式就是让容器执行一条命令,返回0表示健康,非0则异常。比如用curl检查Web服务:

# Dockerfile示例(技术栈:Nginx)
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost/ || exit 1

参数说明

  • --interval:每30秒检查一次
  • --timeout:超过3秒没响应就算失败
  • curl -f:静默模式,失败时返回非0状态码

2. HTTP端点检查

现代应用通常会有专门的健康检查API端点:

# 适用于Spring Boot应用(技术栈:Java)
HEALTHCHECK --start-period=40s \
  CMD curl -s http://localhost:8080/actuator/health | grep -q '"status":"UP"'

技巧

  • /actuator/health是Spring Boot Actuator的标准端点
  • start-period给应用40秒启动时间,避免误判

3. TCP端口探测

适合那些没有HTTP接口的服务,比如数据库:

# Redis健康检查示例(技术栈:Redis)
HEALTHCHECK --retries=3 \
  CMD nc -z localhost 6379 || exit 1

注意

  • nc -z只测试TCP连接,不验证实际功能
  • retries连续失败3次才标记为不健康

三、进阶玩法:组合检查

有时候简单的命令检查不够用,比如你的微服务需要同时满足:

  1. 能响应HTTP请求
  2. 能连接Redis
  3. 磁盘空间充足

这时候可以写个脚本:

#!/bin/bash
# 复合检查脚本(技术栈:Node.js)
curl -f http://localhost:3000/health || exit 1
redis-cli ping | grep -q PONG || exit 1
[ $(df / | awk 'NR==2{print $4}') -gt 1000000 ] || exit 1

然后在Dockerfile中引用:

COPY healthcheck.sh /usr/local/bin/
HEALTHCHECK CMD /usr/local/bin/healthcheck.sh

四、避坑指南

1. 检查频率要合理

  • 太频繁(如1秒1次):浪费资源
  • 间隔太长(如5分钟):故障发现延迟
  • 推荐值:生产环境通常30秒-1分钟

2. 超时时间要设置

特别是网络检查,默认超时时间可能太长:

# 错误示范(超时未设置)
HEALTHCHECK CMD curl http://slow-service/

# 正确做法
HEALTHCHECK --timeout=5s CMD curl -f http://slow-service/

3. 注意启动缓冲期

刚启动的容器可能需要加载数据、初始化连接:

# 给Elasticsearch 2分钟启动时间(技术栈:Elasticsearch)
HEALTHCHECK --start-period=120s \
  CMD curl -s http://localhost:9200/_cluster/health | grep -q '"status":"green"'

4. 不要过度依赖健康检查

以下情况健康检查会失效:

  • 容器进程僵死(PID 1还在,但服务不响应)
  • 内核级故障(如磁盘损坏)
  • 网络隔离

五、实战案例:Kubernetes集成

在K8s中健康检查更关键,因为它是服务自愈的基础。看个Deployment配置片段:

# k8s健康检查示例(技术栈:Golang)
livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 15
  periodSeconds: 20

readinessProbe:
  exec:
    command:
    - /bin/check-db-connection.sh
  failureThreshold: 3

关键区别

  • livenessProbe:失败会重启容器
  • readinessProbe:失败只是从负载均衡摘除

六、监控与告警

健康状态应该被监控系统采集,比如Prometheus可以通过container_health_status指标收集数据。配合Grafana可以做出这样的看板:

UP状态的容器比例 = sum(container_health_status{state="healthy"}) 
                / sum(container_health_status)

当比例低于95%时触发告警,比等用户投诉更主动。

七、总结

健康的健康检查方案应该:

  1. 覆盖关键功能点,但不过度检查
  2. 设置合理的超时和间隔
  3. 区分"存活检查"和"就绪检查"
  4. 与编排系统(如K8s)深度集成
  5. 纳入统一监控体系

就像定期体检不能包治百病,但没有健康检查的容器化系统,就像蒙着眼睛走钢丝——掉下去是迟早的事。