探针(Probe) 是 kubelet 对容器执行的周期性体检,分三种:startup 探针守护慢启动、liveness 探针决定要不要重启容器、readiness 探针决定要不要给容器分发流量。探针由你在 Pod 声明中配置,kubelet 执行,裁判规则一经声明即刻生效。
容器跑起来了,旅程还差最后一道工序:集群如何知道它能接活?没有这道工序,一个启动到一半、连不上数据库的容器会立刻被当成健康副本接进流量,用户请求成批超时。本节讲清三种探针的职权划分、参数调法与误杀事故的防范——这是声明从"能跑"到"可控"的分界线。
三种探针用的是同一套检查手段(HTTP 请求、TCP 连接、执行命令),区别全在判负后的处置:
| 探针 | 回答的问题 | 判负后的动作 | 典型误配后果 |
|---|---|---|---|
| startup | 启动完成了吗 | 通过前暂停另外两种探针 | 慢启动应用被 liveness 误杀 |
| liveness | 进程还活着吗 | 重启容器 | 检查项含外部依赖时引发重启风暴 |
| readiness | 现在能接活吗 | 摘出负载均衡,不重启 | 无探针时半死副本照样收流量 |
处置逻辑的差异用一张流程图固化下来,三条分支对应三种探针:
readiness 的摘除动作值得展开:Service 的流量分发名单(端点列表)以 READY 为准入条件,探针失败的副本会被移出名单、恢复后再加回。这给了集群一个精细的流量闸门——升级时新副本先过体检再接客,正是第4章滚动更新不中断服务的技术底座。
给 1.2 节的容器声明加上完整的体检条款:
containers: - name: orders-api image: orders-api:1.4.2 startupProbe: # 先问"起来了吗":最多等 60 秒 httpGet: path: /healthz # 轻量存活端点,只查进程不查依赖 port: 8080 periodSeconds: 2 # 每 2 秒查一次 failureThreshold: 30 # 连续失败 30 次才算启动失败(2x30=60 秒) livenessProbe: # 再问"还活着吗":判负即重启 httpGet: path: /healthz port: 8080 periodSeconds: 5 failureThreshold: 3 # 连续 3 次失败(约 15 秒)触发重启 readinessProbe: # 最后问"能接活吗":判负摘流量 httpGet: path: /ready # 就绪端点,可查关键依赖是否就位 port: 8080 initialDelaySeconds: 5 # 首查延迟,给应用初始化留时间 periodSeconds: 5
三个参数的乘积决定裁判的严厉程度:failureThreshold 乘 periodSeconds 是容忍窗口,窗口越短反应越快也越容易误杀。经验起点:liveness 容忍 15 秒左右,readiness 10 秒左右,startup 按应用最慢启动时间的两倍设置。检查端点的设计同样有讲究——下一小节的反例会看到,端点查错对象,参数再准也白搭。
判负的现场证据在事件流里,格式固定:
kubectl describe pod orders-api-6d9f7c8b5-2vxk7 -n production | grep -E "Liveness|Readiness" | tail -3 # Warning Unhealthy 4m kubelet Liveness probe failed: # HTTP probe failed with statuscode: 503 # Warning Unhealthy 4m kubelet Readiness probe failed: # dial tcp 10.244.1.5:8080: connect: connection refused
背景:同事把 liveness 探针的检查端点配成了 /deepcheck——这个端点会连数据库、连缓存、连下游风控服务,任何一家抖动都返回 503。某天风控服务慢了两分钟,orders-api 全体副本被 liveness 判死重启,重启后又因启动期依赖未就绪继续被判死,雪崩成形。
操作:事故中的关键观察与修复步骤——
# 第一步:确认现象:三副本齐刷刷重启 kubectl get pods -n production -l app=orders-api # NAME READY STATUS RESTARTS AGE # orders-api-6d9f7c8b5-2vxk7 1/1 Running 5 18m # orders-api-6d9f7c8b5-9mjq4 1/1 CrashLoopBackOff 6 18m # orders-api-6d9f7c8b5-x3p2q 1/1 Running 5 18m # 第二步:改声明——liveness 只查进程自身,依赖健康交给 readiness # /healthz 只做进程内存自检,/ready 才去 ping 数据库与风控 # 第三步:提交修复后的声明 kubectl apply -f orders-deploy.yaml # deployment.apps/orders-api configured
结果:滚动替换完成后风暴停止。风控服务恢复前,副本因 readiness 失败被摘出流量名单,用户请求由其余就绪副本承接;进程本身不再被重启。
解读:这次事故的教训是一条铁律——liveness 只回答"进程是否需要重启",因此只查进程自身;一切外部依赖的健康问题属于"暂时不能接活",归 readiness 管。重启解决不了外部依赖的故障,只会把局部抖动放大成全局重启风暴。
变式:慢启动的老旧单体应用(启动要 90 秒)不配 startup 探针就会被 liveness 在 30 秒处误杀,正确做法是补 startup 探针并把容忍窗口设到 180 秒;TCP 型探针适合没有 HTTP 端口的中间件容器;exec 型探针能执行自定义脚本,但开销最大,留给前两者覆盖不了的场合。
⚠️ 常见坑:给所有容器照抄同一套探针参数。不同应用的启动速度、依赖数量、恢复能力差异很大,探针参数应当按应用画像定制,抄来的参数迟早变成误杀清单。
💡 关键直觉:readiness 是流量闸门,liveness 是复活按钮。拿闸门当按钮用(或反之),是生产环境探针事故的几乎全部来源。
至此,orders-api 的 3 个副本全部通过体检、进入就绪名单,声明完成了从纸面到运行的全程。但集群不会静止:节点会宕、版本要升、副本数要调。下一章看控制器如何守住这份声明——调和循环、自愈与滚动更新的机制全景。