1. 为什么需要健康检查与优雅关机?
在Kubernetes集群中,Pod是运行应用的最小单元。想象一下,你正在运营一个电商平台,某个商品详情页的Pod突然崩溃但进程还在运行,或者启动时依赖的数据库还没准备好就接收流量,这会导致什么后果?健康检查机制就是为解决这类问题而生的。
LivenessProbe(存活探针)相当于给Pod装了个"心跳检测器"。我们团队曾遇到过Java应用因内存泄漏导致线程阻塞,但进程依然存在的案例。这时LivenessProbe会检测到应用无响应,kubelet会自动重启Pod,就像给病人做心肺复苏。
ReadinessProbe(就绪探针)则像是餐厅的"营业准备检查"。去年我们上线新支付服务时,曾因Pod过早接收流量导致交易失败。后来配置ReadinessProbe后,只有当前Pod完成初始化、连接好所有依赖服务后,才会被加入Service的Endpoint列表。
优雅关机机制则像是飞机的"降落程序"。突然断电式地终止Pod,可能导致:
- 正在处理的请求被强行中断(比如支付回调)
- 数据库事务未提交
- 文件写入不完整
我们某个金融项目就曾因未配置优雅关机,导致日终批处理时丢失了300多笔交易记录。后来通过preStop钩子实现了完整的业务闭环处理。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 探针类型与配置实战
2.1 HTTP探针的深层逻辑
HTTP GET探针最常用,但配置不当反而会成为故障源。我们的最佳实践是:
yaml复制livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: X-Custom-Header
value: Awesome
initialDelaySeconds: 15 # 避免应用启动慢导致的误杀
periodSeconds: 20 # 检查间隔不宜过短
timeoutSeconds: 5 # 超时时间要小于periodSeconds
successThreshold: 1 # 成功阈值
failureThreshold: 3 # 连续失败3次才判定异常
关键经验:
- 健康检查接口要轻量(我们曾因/healthz接口查询Redis导致雪崩)
- 避免使用业务接口做探针(某次订单查询接口超时引发大规模重启)
- HTTP状态码要精确(我们约定:200=健康,503=不健康,其他=待定)
2.2 TCP探针的特殊场景
当应用没有HTTP服务时,TCP探针就派上用场了。比如我们的RabbitMQ集群配置:
yaml复制readinessProbe:
tcpSocket:
port: 5672
initialDelaySeconds: 5
periodSeconds: 10
但要注意:
- 只能检测端口是否监听,无法验证服务真实状态
- 某次故障中MySQL端口可连接但已hang住,导致误判
2.3 Exec探针的风险控制
通过执行命令检查是最灵活也最危险的方式。我们监控Elasticsearch节点的方案:
yaml复制livenessProbe:
exec:
command:
- sh
- -c
- 'curl -s http://localhost:9200/_cluster/health | grep -q "green\|yellow"'
initialDelaySeconds: 60
血泪教训:
- 命令必须幂等(曾经有同事的探针脚本会修改数据)
- 避免复杂逻辑(某次因grep命令不存在导致所有Pod被误杀)
- 资源消耗要监控(有个探针脚本占用了50% CPU)
3. 优雅关机的完整实现方案
3.1 信号处理机制剖析
当Pod被删除时,Kubernetes会先发送SIGTERM信号,等待terminationGracePeriodSeconds(默认30秒)后发送SIGKILL。我们Go服务的典型处理:
go复制func main() {
// 注册信号处理
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGTERM)
// 启动服务
srv := &http.Server{...}
go srv.ListenAndServe()
<-stop // 等待信号
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second)
defer cancel()
srv.Shutdown(ctx) // 优雅关闭
}
关键数字:
- terminationGracePeriodSeconds要大于服务关闭耗时
- 我们生产环境通常设为60秒
- 批处理任务可能需要更长时间
3.2 preStop钩子的高级用法
对于更复杂的关闭逻辑,preStop钩子是终极武器。某交易系统的配置:
yaml复制lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- |
echo "开始优雅关闭" >> /var/log/shutdown.log
/app/scripts/complete_transactions.sh
sleep 10 # 等待剩余请求处理完成
我们总结的最佳实践:
- preStop脚本必须有超时处理
- 重要操作要记录日志(曾因脚本静默失败难以排查)
- 避免与主进程竞争资源(某次因同时写数据库导致死锁)
3.3 多容器Pod的关闭顺序
当Pod包含多个容器时,关闭顺序很重要。比如日志收集场景:
yaml复制containers:
- name: app
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sync; sleep 5"]
- name: log-agent
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "flush_logs.sh"]
我们制定的规则:
- 业务容器先收到SIGTERM
- Sidecar容器延迟5秒处理
- 关键数据要双重确认(曾因日志未刷盘丢失数据)
4. 生产环境疑难问题排查
4.1 探针风暴问题诊断
某次凌晨3点,我们收到告警:集群大量Pod重启。排查过程:
- 查看kubelet日志发现大量探针超时
- 检查节点监控发现CPU使用率100%
- 定位到某服务的/healthz接口执行全表扫描
- 同时200个Pod的探针并发查询导致数据库雪崩
解决方案:
- 为健康检查接口添加缓存
- 调整探针间隔错峰执行
- 实现分级健康检查机制
4.2 优雅关机失效案例
金融系统升级时出现数据不一致,排查步骤:
- 发现部分交易状态为"处理中"但实际已完成
- 检查preStop脚本日志发现超时被强杀
- 复现问题:当数据库响应慢时关闭时间不足
- 最终方案:
- 增加terminationGracePeriodSeconds到120秒
- 实现事务补偿机制
- 添加关闭进度监控
4.3 资源竞争导致的死锁
最棘手的是一次多方资源竞争:
- preStop脚本等待数据库连接释放
- 数据库连接池等待业务请求完成
- 业务请求等待preStop脚本释放锁
- 最终整个Pod卡死直到被强杀
解决方案:
- 为preStop脚本设置独立连接池
- 实现请求排空机制
- 添加死锁检测日志
5. 进阶配置与优化策略
5.1 动态调整探针参数
通过Downward API实现根据负载动态调整:
yaml复制env:
- name: PROBE_INTERVAL
valueFrom:
fieldRef:
fieldPath: metadata.annotations['probeInterval']
livenessProbe:
periodSeconds: $(PROBE_INTERVAL)
我们某AI服务的实际效果:
- 空闲时:interval=60s
- 高负载时:interval=10s
- 通过HPA自动更新annotation实现
5.2 混合探针策略
对关键服务采用多级检查:
yaml复制livenessProbe:
exec:
command: ["check_essential.sh"]
httpGet:
path: /healthz
port: 8080
failureThreshold: 2
只有当两种检查都失败时才重启,避免误判。
5.3 分布式健康检查
对于有状态服务,实现集群感知的健康检查:
yaml复制readinessProbe:
exec:
command: ["/bin/sh", "-c", "curl -s http://127.0.0.1:2379/health | grep -q 'true'"]
我们Etcd集群的特别处理:
- 主节点检查写能力
- 从节点检查复制延迟
- 使用自定义脚本实现
在Kubernetes中实施健康检查就像给系统安装了一套智能监护系统,而优雅关机则是确保业务连续性的安全气囊。经过多个项目的实战检验,我总结出三条黄金法则:
- 探针配置要遵循"最小权限原则":只检查核心功能,避免级联故障
- 关闭流程要留有"安全余量":实际需要的关闭时间总是比预估的长30%
- 任何机制都要有"逃生通道":当优雅关闭失败时,要有补偿机制兜底
最后分享一个真实案例:我们某个全球部署的服务,通过优化健康检查策略,将异常检测时间从5分钟缩短到30秒,同时将误报率降低了90%。关键改动只是调整了failureThreshold从1到3,并添加了抖动因子避免同时检查。这告诉我们:有时候最简单的调整反而能带来最显著的效果。
