Kubernetes 探针(Probe)详解与生产环境配置实践

Kubernetes 探针(Probe)详解与生产环境配置实践

记录时间:2026-08-20
环境:K8s 1.28(信创 ARM64);Helm Charts:jar / tomcat / tongweb / vue 四类业务 chart(内部 chart 仓库);测试环境 argocd-apps / 生产环境 argocd-prod(ArgoCD 管理)

一、背景

最近看了一篇掘金上讲探针的文章,把 livenessProbe / readinessProbe / startupProbe 三大探针从头到尾捋了一遍,写得挺细的。光看文章不过瘾,于是顺手把测试环境和生产环境的 ArgoCD 配置都拉出来对比了一下,发现我们环境里其实已经有很完整的探针实践,但也有十几个服务的探针在测试环境是关着的。整理成一篇笔记,把文章要点和我们自己的配置串起来。

二、三大探针分别管什么

K8s 的探针本质是”服务健康状态的三层校验机制”,三种探针职责不同、失败后果也不同:

探针 管什么 失败后果 典型场景
livenessProbe(存活探针) 容器是否活着 restartPolicy 重启容器 死锁、OOM、进程假死
readinessProbe(就绪探针) 实例是否能用 从 Service Endpoints 摘除,不再收流量 依赖未就绪、初始化未完成
startupProbe(启动探针) 慢启动保护 重启容器 Java/Tomcat 启动 3 分钟以上的服务

一句话区分:liveness 管”生死”,readiness 管”流量”,startup 管”启动保护”。liveness 和 readiness 的失败后果不同——一个重启、一个摘流量,这俩别搞混。

startupProbe 比较特殊:它探测成功之前,liveness 和 readiness 都不会生效,所以它是专门给慢启动服务兜底的。

三、三种探测方式

方式 原理 适用场景 局限
exec 容器内执行命令,退出码 0 为正常 复杂校验(依赖检查、文件存在性) 依赖容器内装好命令(如 curl)
httpGet 请求 HTTP/HTTPS 端点,状态码 200-399 为正常 Web 服务,能校验业务端口可用性 仅支持 HTTP(S) 协议
tcpSocket 建 TCP 连接,连得上即正常 数据库、Redis 等非 HTTP 服务 只能判断端口存活,无法校验内部状态

选择原则:Web 服务优先 httpGet;端口监听类服务用 tcpSocket;需要复杂逻辑校验用 exec

四、探针参数与黄金法则

所有探针共用同一套参数:

参数 含义 推荐值
initialDelaySeconds 容器启动后延迟多久开始探测 略短于实际启动时间(如 Java 实际 40s 设 30-35s)
periodSeconds 探测间隔 liveness 10-15s,readiness 5-8s
timeoutSeconds 单次探测超时 httpGet/tcpSocket 1-3s,exec 2-5s
successThreshold 连续成功多少次算恢复 默认 1(readiness 滚动更新时也建议 1)
failureThreshold 连续失败多少次算故障 liveness 3-5 次,readiness 2-3 次
terminationGracePeriodSeconds 失败后的优雅终止时间 30-60s

startupProbe 的启动窗口 = failureThreshold × periodSeconds,必须覆盖服务最慢一次的启动时间。

五、我们环境的探针配置

5.1 Chart 模板怎么渲染的

我们的 chart(jar / tomcat / tongweb / vue 四类)在 deployment 模板里统一渲染探针,probe.enabled 是总开关,各探针还有独立的 enabled 子开关:

# jar chart 的 deployment.yaml 模板(节选)
{{- if and .Values.probe .Values.probe.enabled }}
  {{- if .Values.probe.startupProbe.enabled | default false }}
  startupProbe:
    {{- omit .Values.probe.startupProbe "enabled" | toYaml | nindent 12 }}
  {{- end }}
  {{- if .Values.probe.livenessProbe.enabled | default false }}
  livenessProbe:
    {{- omit .Values.probe.livenessProbe "enabled" | toYaml | nindent 12 }}
  {{- end }}
  {{- if .Values.probe.readinessProbe.enabled | default false }}
  readinessProbe:
    {{- omit .Values.probe.readinessProbe "enabled" | toYaml | nindent 12 }}
  {{- end }}
{{- end }}

vue chart 的模板更聪明一点:httpGet 配了但 path 为空时,会用 probePath 这个 include 自动补上路径(SPA 首页路径),values 里不用手写:

{{- if and $probe.httpGet (not $probe.httpGet.path) }}
{{- $probe = mergeOverwrite $probe (dict "httpGet" (mergeOverwrite $probe.httpGet (dict "path" (include "probePath" $)))) }}
{{- end }}

注意:模板里探针是 omit "enabled" 后整体透传的,所以 values.yaml 里怎么写,最终 pod 里就是什么。

5.2 Java / Tongweb 服务标配:tcpSocket 三探针

测试和生产的大多数 Java 服务(如 szzx 的 qjksh、infostat、wmyc)配置完全一致,参数带注释的版本如下(测试环境写注释、生产环境没写,参数一个不差):

# argocd-apps/application/szzx/qjksh/values.yaml(测试,生产同参数)
probe:
  enabled: true
  startupProbe:
    enabled: true
    httpGet: null
    tcpSocket:
      port: http              # 使用容器端口名称,自动引用 appConfig.port
    initialDelaySeconds: 60    # 容器启动后 60s 开始检查
    periodSeconds: 10          # 每 10s 检查一次
    failureThreshold: 30       # 最多允许失败 30 次,总计 60s + (10s * 30) = 360s
  # 存活探针:启动探针成功后接手,判断应用是否由于死锁等原因挂掉
  livenessProbe:
    enabled: true
    tcpSocket:
      port: http              # 使用容器端口名称
    initialDelaySeconds: 10    # 启动探针成功后,只需 10s 即可开始
    periodSeconds: 30
    timeoutSeconds: 10
    failureThreshold: 3
  # 就绪探针:启动探针成功后接手,判断应用是否可对外提供服务
  readinessProbe:
    enabled: true
    tcpSocket:
      port: http              # 使用容器端口名称
    initialDelaySeconds: 10    # 启动探针成功后,只需 10s 即可开始
    periodSeconds: 20          # 就绪检查稍快一些,以便及时摘除故障节点
    timeoutSeconds: 10
    successThreshold: 1
    failureThreshold: 3

这套配置的思路:

  • startupProbe 给足 360 秒窗口(60s + 10s×30):东方通/Tomcat 启动慢,最坏情况要 6 分钟,保证启动期间不被误杀。
  • liveness 用 tcpSocket 而非 httpGet:Java 服务端口起来就算活,避免业务层偶发 5xx 触发重启(这点文章里也强调了,liveness 探测条件要”轻量无副作用”)。
  • readiness 周期比 liveness 短(20s vs 30s):故障时能更快把实例从 Service 摘掉,滚动发布时新 pod 就绪判定也更及时。
  • port: http 用的是容器端口名称,由模板自动引用 appConfig.port,不用写死端口号,改端口不用动探针。

5.3 Vue 前端服务:httpGet 探针

前端服务(nginx 容器)用 httpGet,因为 nginx 是纯 HTTP 服务,直接请求页面最实在:

# argocd-apps/application/sccg/market-vue/values.yaml(节选)
probe:
  enabled: true
  readinessProbe:
    enabled: true
    httpGet:
      path: ''                # path 留空,模板自动补 SPA 首页路径
      port: http
    initialDelaySeconds: 5
    periodSeconds: 10
    successThreshold: 1
    failureThreshold: 3
    timeoutSeconds: 3
  livenessProbe:
    enabled: true
    tcpSocket:
      port: http
    initialDelaySeconds: 15
    periodSeconds: 20
    failureThreshold: 3
    timeoutSeconds: 3
  startupProbe:
    enabled: true
    httpGet:
      path: ''
      port: http
    failureThreshold: 30
    periodSeconds: 10
    timeoutSeconds: 3

注意这里 liveness 反而是 tcpSocket——nginx 挂不挂看端口就行,httpGet 留给 readiness 判断页面能不能访问,两边分工明确。生产环境的 gzsw-ui 等 vue 服务配置和测试完全一致。

5.4 测试与生产对比:kjds 口岸平台测试环境探针关闭

把两个环境的 values.yaml 全部扫了一遍(测试 82 个、生产 56 个 values.yaml 启用了至少一个探针),发现一个值得记录的差异:

kjds 的 bordertradeplatform 一组 14 个服务,测试环境 13 个服务探针被显式关闭——values.yaml 里都是 probe.enabled: false;生产环境 14 个服务全部配置了三探针(tcpSocket 版)。

# argocd-apps/application/kjds/bordertradeplatform/base-service/values.yaml(测试,探针关闭)
probe:
  enabled: false

# argocd-prod/application/kjds/bordertradeplatform/base-service/values.yaml(生产,探针齐全)
probe:
  enabled: true
  startupProbe:
    enabled: true
    tcpSocket:
      port: http
    initialDelaySeconds: 60
    periodSeconds: 10
    failureThreshold: 30
  livenessProbe:
    enabled: true
    tcpSocket:
      port: http
    initialDelaySeconds: 10
    periodSeconds: 30
    timeoutSeconds: 10
    failureThreshold: 3
  readinessProbe:
    enabled: true
    tcpSocket:
      port: http
    initialDelaySeconds: 10
    periodSeconds: 20
    timeoutSeconds: 10
    successThreshold: 1
    failureThreshold: 3

这一组里还有个特例:bordertradeplatform-web(前端服务)测试环境是开着的,但没配 startupProbe,而是我把 liveness 的 initialDelaySeconds 拉大到 240s(readiness 120s)来兜住慢启动——startup 窗口的另一种等价写法。生产环境我统一改成了标准的 startupProbe(120s + 10s×30)写法。

测试环境 13 个服务探针全关,是我当时拉长探针参数时一起关掉的,测试环境一直保持这个状态。测试环境没探针意味着:pod 挂了不重启、流量照常打到坏实例,排障时看不到探针事件,和生产的故障表现会不一样。

六、组合场景与 5 个坑

文章总结的典型组合:

场景 组合 关键参数
Spring Boot 微服务 startup + liveness + readiness startup 24×10s=240s;liveness initialDelay 60s;readiness period 5s
MySQL liveness + readiness liveness tcpSocket 3306;readiness exec mysqladmin ping
Nginx liveness + readiness liveness exec pgrep nginx;readiness httpGet /
依赖 MQ 的服务 readiness + liveness readiness 校验内部健康端点(含 MQ 连接),failureThreshold 2

5 个坑(对应到我们环境):

  1. 用 liveness 代替 readiness → 启动中的实例被塞流量,直接 5xx。我们的配置里两探针职责分明,没这个毛病。
  2. 慢启动服务不配 startupProbe → 启动慢的 Java 服务被 liveness 反复重启,起不来的同时疯狂消耗资源。我们全配了,startup 窗口 360s。
  3. 探测方式不当(tcpSocket 校验 Web 服务)→ 端口通但页面 500 也照样”健康”。我们 Java 用 tcpSocket 是刻意为之(liveness 轻量优先),但 readiness 其实可以更严格,后续可以评估给核心服务上 httpGet 健康端点。
  4. 参数过严(如 periodSeconds=2s)→ 探测本身占资源,还可能把抖动当故障。我们的参数都按黄金法则来。
  5. readiness 依赖外部不稳定服务(跨集群 DB)→ 上游一抖,实例被反复摘除,流量雪崩。这条要警惕:readiness 探针里的依赖校验别连跨集群的外部服务。

七、监控告警与排障

文章给出的探针监控指标(kube-state-metrics 提供):

指标 告警建议
kube_pod_probe_success{probe="liveness"} 5 分钟成功率 <90% 告警
kube_pod_probe_success{probe="readiness"} 5 分钟成功率 <80% 告警
kube_pod_probe_success{probe="startup"} 启动后 3 分钟未成功告警
kube_pod_container_status_waiting_reason(ProbeFailed) 持续 5 分钟以上告警
kube_pod_status_ready(0) 持续 10 分钟以上告警

Grafana 可以直接导入官方 Dashboard “Kubernetes Pod Health”(ID 13105)。

排障流程(遇到探针失败时):

# 1. 看 Events,确认失败原因
kubectl describe pod <pod-name> -n <namespace>
#    关注 "Liveness probe failed: ..." / "Readiness probe failed: ..." 事件

# 2. 看日志,排查崩溃或依赖连接问题
kubectl logs --tail=100 <pod-name> -n <namespace>

# 3. 手动执行探测命令,复现问题
kubectl exec -it <pod-name> -n <namespace> -- curl -v http://127.0.0.1:8080/actuator/health

# 4. 确认是参数问题还是业务问题,再决定调整探针还是修业务

八、注意事项

  • probe.enabled 是总开关:values.yaml 里三个探针都配了但总开关是 false,一样不生效(测试 kjds base-service 就是这个状态,光看 probe 段容易误判)。
  • httpGet.path 留空:vue chart 模板会自动补 SPA 路径;其他 chart 透传,path 留空会请求 /
  • tcpSocket 探针的边界:端口通不代表业务健康,核心服务建议上 httpGet + 业务健康端点(如 Spring Boot Actuator),端点必须轻量、耗时 <1s。
  • 探针不是越多越好:核心是”精准校验”,在探测准确性和资源消耗之间取平衡。
  • 测试环境探针状态和生产不一致:kjds 测试环境 13 个服务探针全关是我拉长探针参数时一起处理的,这类差异会掩盖测试环境的问题表现——测试环境没探针意味着 pod 挂了不重启、流量照常打到坏实例,排障时看不到探针事件。

九、参考资料

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇