尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

【Kubernetes从入门到精通】第34篇:Pod健康检查——Liveness、Readiness和Startup探针

【Kubernetes从入门到精通】第34篇:Pod健康检查——Liveness、Readiness和Startup探针 上一篇【第33篇】Pod生命周期全解——从Pending到Terminating的每一步下一篇【第35篇】Pod的优雅上下线——Graceful Shutdown和PreStop Hook摘要你在K8s上部署了一个Java服务Pod显示Running但客户端全部502——我猜你没配Readiness探针。Pod变成了Running、容器进程还活着跟能正常接客是两码事——Java的JVM可能还在初始化Spring容器Nginx进程活着但配置文件写错了导致无法响应请求。K8s不是神仙它不会自己判断你的应用能不能干活——你得告诉它怎么检查。三种探针就是干这事的Liveness探针问你还活着吗“——连续失败就杀掉重启治的是死锁/内存溢出这类不可恢复的病。Readiness探针问你能接客吗”——失败就从Service的Endpoints里摘掉治的是还没初始化完/依赖暂时不可用这些暂时性问题。Startup探针是慢启动应用的保护伞——专门给那些需要30秒以上才能启动的应用争取时间避免Liveness探针提前介入把人给kill了。这篇文章逐一讲清它们怎么配、什么时候用、参数怎么调——尤其是那个很多人配错的initialDelaySeconds。一、三种探针区别——活着和能干活是两码事1.1 一张图看懂三种探针的职责【三种探针的分工——保安前台门卫】 Liveness Probe保安——还活着吗 ┌─────────────────────────────────────────────────────────┐ │ 检查容器进程是否健康应用有没有死锁/卡死 │ │ 频率持续检查 │ │ 失败后果kubelet 杀掉容器并重启杀人 │ │ 不应该做的事检查外部依赖DB/Redis—— │ │ DB挂了你重启自己有啥用 │ │ 典型场景Java OOM后JVM还在但无法处理请求 │ │ 内存泄漏导致GC频繁、死锁 │ └─────────────────────────────────────────────────────────┘ Readiness Probe前台——能接客吗 ┌─────────────────────────────────────────────────────────┐ │ 检查应用是否准备好接收流量 │ │ 频率持续检查 │ │ 失败后果从Service Endpoints中移除摘流量但留着容器 │ │ 应该做的事检查数据库连接、Redis连接、是否在加载 │ │ 典型场景Spring Boot还在初始化Bean │ │ 缓存预热中暂时不接客 │ └─────────────────────────────────────────────────────────┘ Startup Probe门卫——启动完了吗 ┌─────────────────────────────────────────────────────────┐ │ 检查应用是否完成启动 │ │ 频率只在启动阶段检查成功后就不再执行 │ │ 失败后果和Liveness一样——杀容器重启 │ │ 适用场景慢启动应用Java大型微服务、遗留系统 │ │ 关键作用启动期间停用Liveness和Readiness │ │ 启动成功后才启用 │ └─────────────────────────────────────────────────────────┘1.2 三种探针的时序关系【三种探针在Pod生命周期中的关系】 Pod创建 │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ 启动阶段Startup Probe生效Liveness/Readiness被挂起 │ │ │ │ Startup Probe 检查... │ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ Pass │ │ Pass │ │ Pass │ │ Pass │ → 启动成功 │ │ └──────┘ └──────┘ └──────┘ └──────┘ │ │ │ │ 如果失败failureThreshold次→ 杀容器重启 │ └──────────────────────────┬───────────────────────────────────┘ │ Startup Probe 成功后 ▼ ┌──────────────────────────────────────────────────────────────┐ │ 运行阶段 │ │ │ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │ │ Liveness Probe │ │ Readiness Probe │ │ │ │ 持续检查还活着吗 │ │ 持续检查能接客吗 │ │ │ │ │ │ │ │ │ │ 失败 → Kill Restart│ │ 失败 → 摘除流量 │ │ │ └─────────────────────┘ └─────────────────────┘ │ └──────────────────────────────────────────────────────────────┘探针类型检查频率失败后果是否持续运行适用问题类型Startup启动阶段Kill Restart成功后停止启动慢的应用Java/复杂初始化Liveness持续Kill Restart始终运行死锁、内存泄漏、进程僵死Readiness持续摘除流量始终运行未初始化完、依赖不可用、过载要点最经典的误区是把Liveness当成Readiness来配——在Liveness里检查数据库连接结果数据库一抖动所有Pod被Liveness杀掉重启整出重启风暴。记住Liveness只管我自己还活着吗Readiness才管我还能干活吗。外部依赖的问题用Readiness管——摘流量等它恢复别杀人。二、四种检查方式——“怎么知道你还活着”2.1 四种方式对比检查方式原理适用场景侵入性性能开销exec在容器内执行命令exit 0成功通用最灵活低低fork进程httpGet向容器发送HTTP GET2xx/3xx成功Web/API服务低低tcpSocket向容器端口建立TCP连接连上成功数据库、消息队列、gRPC老版本零极低gRPC调用gRPC健康检查协议v1.24gRPC服务低低2.2 exec——“运行这个命令看返回值”apiVersion:v1kind:Podmetadata:name:exec-probe-demospec:containers:-name:appimage:myapp:v1.0ports:-containerPort:8080# exec方式的Liveness——检查进程是否存在livenessProbe:exec:command:-cat-/tmp/healthy# 文件存在 → 健康initialDelaySeconds:5periodSeconds:10# exec方式的Readiness——检查应用是否就绪readinessProbe:exec:command:-sh--c-|# 更复杂的检查逻辑 if [ -f /tmp/ready ] curl -s http://localhost:8080/health; then exit 0 else exit 1 fiinitialDelaySeconds:10periodSeconds:5# 在容器内做健康检查的命令示例# 简单——检查进程command:[pgrep,java]# Java进程还在吗# 中等——检查文件command:[cat,/var/run/app/healthy]# 健康标记文件在吗# 复杂——综合检查command: -sh--c-|curl-sfhttp://localhost:8080/actuator/health/liveness||exit1pgrepjava||exit1[-f/tmp/healthy]||exit12.3 httpGet——“调我这个接口看状态码”apiVersion:v1kind:Podmetadata:name:http-probe-demospec:containers:-name:appimage:spring-boot-app:v1.0ports:-containerPort:8080name:http# Liveness——轻量检查只查进程本身livenessProbe:httpGet:path:/actuator/health/liveness# Spring Boot Actuatorport:8080scheme:HTTPhttpHeaders:# 可以加自定义Header-name:X-Health-Checkvalue:livenessinitialDelaySeconds:30periodSeconds:10timeoutSeconds:5failureThreshold:3# 连续3次失败才kill# Readiness——重检查查所有外部依赖readinessProbe:httpGet:path:/actuator/health/readiness# Spring Boot: DB/Redis都OK吗port:8080scheme:HTTPinitialDelaySeconds:15periodSeconds:5timeoutSeconds:3failureThreshold:3# 连续3次失败摘流量successThreshold:1# 1次成功就恢复2.4 tcpSocket——“端口开着就行”apiVersion:v1kind:Podmetadata:name:tcp-probe-demospec:containers:-name:redisimage:redis:7-alpineports:-containerPort:6379name:redis# TCP检查——能连上6379端口就认为活着livenessProbe:tcpSocket:port:6379initialDelaySeconds:15periodSeconds:20timeoutSeconds:5# Readiness也可以用TCPreadinessProbe:tcpSocket:port:6379initialDelaySeconds:5periodSeconds:102.5 gRPC——“v1.24的原生支持”apiVersion:v1kind:Podmetadata:name:grpc-probe-demospec:containers:-name:grpc-serverimage:grpc-service:v1.0ports:-containerPort:50051name:grpc# gRPC健康检查需要服务实现 gRPC Health Checking ProtocollivenessProbe:grpc:port:50051service:# 空字符串 检查服务器整体健康initialDelaySeconds:10periodSeconds:10readinessProbe:grpc:port:50051service:myapp.v1.OrderService# 检查特定服务initialDelaySeconds:5periodSeconds:5三、探针参数调优——“别乱配initialDelaySeconds”3.1 所有参数一览livenessProbe:# 检查方式四选一exec:...# httpGet: ...# tcpSocket: ...# grpc: ...# 时序参数 initialDelaySeconds:30# 容器启动后等多久才开始第一次检查periodSeconds:10# 每隔多久检查一次频率timeoutSeconds:5# 单次检查的超时时间successThreshold:1# 连续成功几次才算恢复健康Liveness固定为1failureThreshold:3# 连续失败几次才算不健康terminationGracePeriodSeconds:30# 不是探针参数但相关优雅关闭等待时间【探针参数的时间线——以Liveness为例】 容器启动 │ │ ← initialDelaySeconds: 30s等待30秒 │ ▼ ┌──────────┐ │ 第1次检查 │────────┬────────────────────── └──────────┘ │ timeoutSeconds: 5s │ 如果5秒没响应 → 算失败 │ │ ← periodSeconds: 10s等10秒 │ ▼ ┌──────────┐ │ 第2次检查 │────────┬────────────────────── └──────────┘ │ │ │ ▼ │ 失败数≥failureThreshold(3) │ YES → Kill容器 重启 │ NO → 继续检查3.2 参数配置经验法则参数推荐值太小的后果太大的后果initialDelaySeconds应用平均启动时间 × 1.5应用还没起来就检查→失败→重启循环启动很久才检查→挂了很久才发现periodSecondsLiveness: 10-30sReadiness: 5-10s频繁检查→浪费资源检查间隔长→发现问题慢timeoutSeconds1-5s网络抖动就超时→误判检查卡住→长时间不更新状态failureThresholdLiveness: 3-5Readiness: 2-3偶尔一次失败就杀/摘→抖动问题持续很久才发现successThreshold1默认—恢复慢一般不需要调# 不同场景的推荐配置# 场景1轻量级Web服务Go/Node.js——启动快livenessProbe:httpGet:path:/healthzport:8080initialDelaySeconds:5# 启动快periodSeconds:10timeoutSeconds:3failureThreshold:3# 场景2重量级Java服务Spring Boot——启动慢# 强烈推荐加StartupProbestartupProbe:httpGet:path:/actuator/healthport:8080initialDelaySeconds:10periodSeconds:5failureThreshold:30# 5s × 30 最多等150秒livenessProbe:httpGet:path:/actuator/health/livenessport:8080periodSeconds:15timeoutSeconds:5failureThreshold:3# 启动成功后才启用readinessProbe:httpGet:path:/actuator/health/readinessport:8080periodSeconds:5timeoutSeconds:3failureThreshold:3# 场景3数据库/中间件redis/postgres——不需要复杂逻辑livenessProbe:exec:command:-redis-cli-pinginitialDelaySeconds:15periodSeconds:20timeoutSeconds:5failureThreshold:3要点Startup Probe是慢启动应用的最佳伴侣。不用Startup Probe时你得把Liveness的initialDelaySeconds设得巨大——比如Java服务启动要120秒你就设150秒。但这样启动阶段Liveness完全不检查如果启动真的卡死了也要150秒才发现。用了Startup Probe启动阶段有专人盯着启动完成Liveness立刻介入——两全其美。四、常见配置误区——“这些坑我都踩过”4.1 误区1把Liveness当Readiness用——“自毁开关”# ❌ 错误的Liveness——检查外部依赖livenessProbe:httpGet:path:/actuator/health# Spring Boot默认端点——检查了DB/Redisport:8080initialDelaySeconds:30periodSeconds:10failureThreshold:3# 后果# DB暂时不可用 → /actuator/health 返回 DOWN# → Liveness连续3次失败 → kubelet杀掉容器# → 重启后的Pod还是连不上DB → 又被杀# → 重启风暴——所有Pod都在循环重启# ✅ 正确的做法——分离Liveness和ReadinesslivenessProbe:httpGet:path:/actuator/health/liveness# ← 只检查进程本身port:8080periodSeconds:10readinessProbe:httpGet:path:/actuator/health/readiness# ← 检查DB/Redis等外部依赖port:8080periodSeconds:5failureThreshold:3# 效果DB挂了 → Readiness失败 → 摘流量Pod不重启# DB恢复 → Readiness成功 → 流量回来【Liveness vs Readiness——分治的原则】 问题类型 用什么探针 动作 ┌──────────────────┐ ┌──────────┐ ┌──────────┐ │ 死锁/内存溢出 │ │ Liveness │ │ Kill重启 │ │ 进程僵死 │ │ │ │ │ │ 启动失败 │ └──────────┘ └──────────┘ └──────────────────┘ ┌──────────────────┐ ┌──────────┐ ┌──────────┐ │ 数据库暂时不可用 │ │Readiness │ │ 摘流量 │ │ Redis连接池满了 │ │ │ │(不重启!) │ │ 缓存预热中 │ └──────────┘ └──────────┘ │ 过载保护 │ └──────────────────┘4.2 误区2不配Readiness——“开业即接客”# ❌ 没配Readiness——Service立刻开始转发流量apiVersion:v1kind:Podmetadata:name:no-readinesslabels:app:myappspec:containers:-name:appimage:spring-boot-app# 启动需要60秒ports:-containerPort:8080# 没有readinessProbe---apiVersion:v1kind:Servicemetadata:name:myappspec:selector:app:myappports:-port:80targetPort:8080# 后果# Pod变成Running → Service立刻转发流量过去# → 但Spring还在加载Bean → /api/xxx 返回502# → 用户看到的就是错误# ✅ 配Readiness——Service等Pod真正Ready才转发apiVersion:v1kind:Podmetadata:name:with-readinesslabels:app:myappspec:containers:-name:appimage:spring-boot-appports:-containerPort:8080readinessProbe:httpGet:path:/actuator/health/readinessport:8080initialDelaySeconds:15periodSeconds:54.3 误区3initialDelaySeconds设太小——“还没起来就检查”# ❌ initialDelaySeconds太小livenessProbe:httpGet:path:/healthzport:8080initialDelaySeconds:5# 但应用启动需要30秒failureThreshold:3periodSeconds:10# 时间线# T0s: 容器启动# T5s: 第一次Liveness检查 → 失败还没启动完# T15s: 第二次 → 失败# T25s: 第三次 → 失败# T25s: failureThreshold3 → Kill容器 → 重启# T55s: 第二次循环...又一次被杀# → CrashLoopBackOff4.4 误区4Liveness太重——自己把自己查死# ❌ Liveness执行太重的操作livenessProbe:exec:command:-sh--c-|# 这个检查太重了——每次检查都创建一个HTTP请求 数据库查询 curl -s http://localhost:8080/api/heavy-check # ↑ 这个API会查数据库、调外部服务、做复杂计算periodSeconds:5# 后果# 每次Liveness检查都在增加负载# 高负载时Liveness反而成为压死骆驼的最后一根稻草# ✅ Liveness应该尽量轻livenessProbe:httpGet:path:/healthz# 极简检查——return OK就行port:8080periodSeconds:15# 频率也别太高要点Liveness要轻、要快、只查自己。Readiness可以稍重但也不要做复杂的业务逻辑。想象一个场景你的服务正处在高负载中每5秒一次的Liveness还要去调HTTP、查数据库——这相当于在快淹死的人身上又加了一块石头。五、综合实战——给一个真实服务配齐三种探针apiVersion:apps/v1kind:Deploymentmetadata:name:order-servicespec:replicas:3selector:matchLabels:app:order-servicetemplate:metadata:labels:app:order-servicespec:terminationGracePeriodSeconds:60containers:-name:order-serviceimage:order-service:v2.3ports:-containerPort:8080# # Startup Probe——给Java启动留足时间# startupProbe:httpGet:path:/actuator/healthport:8080initialDelaySeconds:10periodSeconds:5failureThreshold:30# 最多等 10 5×30 160秒timeoutSeconds:5# 启动成功前Liveness和Readiness不执行# # Liveness Probe——轻量只看进程本身# livenessProbe:httpGet:path:/actuator/health/livenessport:8080periodSeconds:15# 15秒查一次timeoutSeconds:5failureThreshold:3# 连续3次失败才kill# 这个端点只检查# 1. 进程是否响应# 2. 内部状态是否正常# # Readiness Probe——检查外部依赖# readinessProbe:httpGet:path:/actuator/health/readinessport:8080periodSeconds:5# 5秒查一次快点发现问题timeoutSeconds:3failureThreshold:3# 连续3次失败摘流量successThreshold:1# 1次成功就恢复# 这个端点会检查# 1. 数据库连接池是否可用# 2. Redis是否可达# 3. 消息队列是否连通resources:requests:cpu:500mmemory:1Gilimits:cpu:2000mmemory:2Gi# # Pod层面——反亲和性 PreStop# lifecycle:preStop:exec:command:-sh--c-|# 收到停止信号后标记Readiness为DOWN curl -X POST http://localhost:8080/actuator/health/down sleep 10 # 等kube-proxy更新规则# 部署后验证探针状态kubectl describe pod order-service-xxx# 重点看 Conditions:# Conditions:# Type Status# Initialized True ← Init容器完成了# Ready True ← Readiness Probe通过了# ContainersReady True ← 所有容器就绪# PodScheduled True ← 调度成功# 如果 ReadyTrueService已经开始转发流量# 检查探针事件kubectl describe pod order-service-xxx|grep-A5Events:# 如果Liveness失败会看到# Warning Unhealthy Liveness probe failed: HTTP probe failed with statuscode: 500# Normal Killing Container order-service failed liveness probe, will be restarted# 测试Readiness摘流量效果# 1. 进入Pod手动让readiness端点返回500kubectlexecorder-service-xxx --curl-XPOST http://localhost:8080/mock/readiness-fail# 2. 观察Pod状态——READY变成 0/1kubectl get pod order-service-xxx-w# NAME READY STATUS# order-service-xxx 0/1 Running ← READY变成0/1但没重启# 3. Service不会再转发流量到这个Pod要点如果你用的是Spring BootActuator的三个端点天然支持Liveness/Readiness分治——/actuator/health/liveness只检查进程/actuator/health/readiness检查所有外部依赖。非Spring Boot应用可以自己实现——Liveness端点返回简单的OK字符串Readiness端点做完整的依赖检查。本篇小结健康检查是K8s运维中最容易被忽视但影响最大的配置之一三种探针各司其职Liveness管活着没挂了重启Readiness管能干活没不行摘流量Startup管启动完没慢启动的保护伞四种检查方式exec最灵活、httpGetWeb服务首选、tcpSocket中间件首选、gRPCgRPC服务专用Liveness要轻、要快、只查自己——别在里面查数据库那会让DB故障变成Pod重启风暴Readiness可以稍重——查DB/Redis/消息队列连通性失败只摘流量不重启Startup Probe是慢启动应用的救星——不用把Liveness的initialDelaySeconds设得巨大启动阶段有专人盯着别不配Readiness——Service会在Pod一变成Running就转发流量而你的应用可能还在初始化健康检查配好了Pod上线就稳了。但Pod下线呢下一篇咱们聊Graceful Shutdown——怎么让你的Pod优雅地告别不丢一个请求。上一篇【第33篇】Pod生命周期全解——从Pending到Terminating的每一步下一篇【第35篇】Pod的优雅上下线——Graceful Shutdown和PreStop Hook
返回列表