主题
第 4 章 · 健康检查
生活类比:
被动检查 = 顾客走到柜台才发现收银员发烧了,只好换队——没人来排队时,生病的柜台可能一直「假开着」。
主动检查 = 经理每 5 分钟给每个员工测体温,37.5° 以上立刻安排休息,门口挂牌「暂停服务」——在顾客来之前就知道谁不能上班。
4.1 为什么需要健康检查?
负载均衡器维护一个「后端池」。如果池里有一台 已经宕机但还被分配流量 的机器:
10 个请求 → LB 轮询 → 其中 3~4 个打到死机 → 用户看到 502/超时健康检查(Health Check) 的作用:持续判断后端是否 真的能干活,把不健康的节点 临时摘除,恢复后再 加回。
4.2 两种检查方式
被动健康检查(Passive)
在真实业务流量中 观察失败,失败次数够了就标记不可用。
Nginx 开源版典型配置:
nginx
upstream backend {
server 10.0.0.1 max_fails=3 fail_timeout=30s;
server 10.0.0.2 max_fails=3 fail_timeout=30s;
}| 参数 | 含义 |
|---|---|
max_fails=3 | 在 fail_timeout 窗口内失败 3 次 → 标记 down |
fail_timeout=30s | 标记 down 持续 30 秒,之后再放行试探 |
优点:零额外探针流量,配置简单
缺点:
- 没流量时不会发现 后端已挂(僵尸节点)
- 要靠 真实用户请求失败 才能感知——等于拿用户当探针
- 刚启动的后端可能还没就绪就被打入流量
主动健康检查(Active)
LB 定期主动 发探测请求,不依赖业务流量。
nginx
# Nginx Plus / Tengine / OpenResty 等扩展支持
upstream backend {
server 10.0.0.1;
health_check uri=/healthz interval=5s fails=3 pass=2;
}应用需提供轻量探针接口:
go
// GET /healthz → 200 表示进程活着且依赖 OK
func healthz(w http.ResponseWriter, r *http.Request) {
if db.Ping() != nil {
w.WriteHeader(503)
return
}
w.WriteHeader(200)
w.Write([]byte("ok"))
}优点:提前发现故障、支持预热、可检查依赖(DB、Redis)
缺点:额外探针 QPS、要规范探针接口
对比表
| 被动 | 主动 | |
|---|---|---|
| 发现速度 | 慢(等有流量失败) | 快(定时探) |
| 无流量时 | 发现不了 | 能发现 |
| 实现 | Nginx 开源自带 | 常需 Plus/Tengine/云 LB |
| 生活类比 | 病人来了才量体温 | 上班前统一体检 |
4.3 探针类型
| 类型 | 检查什么 | 典型命令/配置 |
|---|---|---|
| TCP 探针 | 端口能否建立连接 | nc -zv 10.0.0.1 8080 |
| HTTP 探针 | 返回码是否 2xx | GET /healthz 期望 200 |
| HTTPS 探针 | TLS + HTTP | 云 LB 控制台配置 |
| gRPC 健康检查 | grpc.health.v1 | K8s gRPC probe |
| 自定义脚本 | 任意逻辑 | 检查磁盘、队列深度 |
TCP vs HTTP 探针怎么选?
进程活着但死锁 → TCP 探针仍通过 ✗
进程活着且能正确响应 /healthz → HTTP 探针 ✓生产建议:HTTP 探针 + 检查关键依赖,而不是只探端口。
4.4 健康检查参数(通用概念)
| 参数 | 含义 | 设太小 | 设太大 |
|---|---|---|---|
interval | 探测间隔 | 探针风暴 | 发现慢 |
timeout | 单次超时 | 误杀 | 僵死检测慢 |
healthy_threshold | 连续成功几次算健康 | 频繁上下线 | 恢复慢 |
unhealthy_threshold | 连续失败几次算不健康 | 误杀(网络抖动) | 死节点流量多 |
生活例子
员工体温测量:
- 每 5 分钟 测一次(interval)
- 一次测量等 10 秒 出结果(timeout)
- 连续 2 次 正常才返岗(healthy_threshold)
- 连续 3 次 超标才休息(unhealthy_threshold)
4.5 优雅下线(Graceful Shutdown)
健康检查不只「发现挂了」,还要支持 主动维护:
1. 运维对节点 A 执行「下线」
2. LB 立刻把 A 标为 draining(不再分配新请求)
3. 等 A 上已有请求处理完(等待 N 秒)
4. A 进程退出
5. 用户全程无感知K8s 中的体现:
yaml
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15"] # 给 LB 时间摘流量生活例子:收银员下班前,门口牌写「本窗口停止接待新客户」,但 把手上的客户办完 再关窗。
4.6 备用节点(Backup)
nginx
upstream backend {
server 10.0.0.1;
server 10.0.0.2;
server 10.0.0.99 backup; # 仅当 1、2 都不可用时启用
}适合 冷备机、灾备机房,平时不浪费流量。
4.7 常见故障场景
场景 1:假死(进程在但不响应)
- 症状:TCP 能连,HTTP 超时
- 对策:HTTP 探针 + 合理 timeout;应用
/healthz要真检查业务
场景 2:启动未就绪就被打入流量
- 症状:发布瞬间大量 502
- 对策:readiness probe(K8s)/ 启动预热期 /
weight从 0 渐增
场景 3:网络抖动导致误杀
- 症状:节点一会儿 up 一会儿 down(flapping)
- 对策:增大
unhealthy_threshold、用多 AZ 探针
场景 4:探针路径被鉴权拦住
- 症状:业务正常但探针 401,节点被误摘
- 对策:
/healthz放行白名单,不走登录校验
4.8 与熔断、降级的关系
健康检查:这台机器还能不能接客? → 摘除单个节点
熔断: 调用方发现错误率太高 → 暂时不调整个服务
降级: 能力不够时返回兜底数据 → 保证核心可用生活类比:
- 健康检查:某个收银台关了
- 熔断:整条零食货架暂时不补货,避免拖累整个店
- 降级:生鲜没了,先卖常温食品
4.9 实战 Checklist
- [ ] 有独立
/healthz,检查 DB/缓存等关键依赖 - [ ] 探针路径不走重逻辑(别查全表)
- [ ] 主动健康检查(云 LB 或 Tengine/APISIX)
- [ ] 发布时优雅下线 + readiness
- [ ] 监控「后端池健康节点数」告警
4.10 一句话总结
被动检查靠用户踩雷,主动检查靠经理巡检——生产环境请选主动;探针要真探业务,别只探端口。
下一章 → 05 · 会话与粘性