健康探针、Conditions 与真正的 Ready

“进程还在”与“系统可用”不是同一件事

一个容器进程存在,只能说明它还没有退出。它可能仍在加载数据、已经死锁、依赖的网关不可用,甚至根本无法处理请求。

本项目用了两层健康表达:

  • Pod probes:kubelet 判断单个容器是否启动、存活、能否接流量;
  • SubPool Conditions:Operator 判断整套空号池平台的依赖是否满足。

两层都叫“健康”,但观察范围完全不同。

三种 Probe 分别问什么

startupProbe:你启动完了吗

慢启动应用可能需要初始化文件、打开数据库或加载大量配置。startupProbe 在成功前,会保护应用不被 livenessProbe 过早判死。

本项目 CPA 和 cpa-manager 都允许最多约 150 秒启动:

1
2
failureThreshold: 30
periodSeconds: 5

粗略上限是 30 × 5 = 150 秒,实际还会受到 timeout 和执行时间影响。

readinessProbe:现在可以接流量吗

readinessProbe 失败时,容器不一定被重启,但 Pod 会变成 NotReady,通常也不会作为 Service 的 Ready Endpoint 接收流量。

它适合表达暂时不可服务的状态,例如:

  • 应用还没完成初始化;
  • 本地依赖暂时不可用;
  • 应用正在优雅排空请求。

livenessProbe:你是不是已经坏到需要重启

livenessProbe 持续失败时,kubelet 会重启容器。它应检测无法自行恢复的故障,而不是把所有外部依赖都塞进去。

如果 livenessProbe 强依赖互联网或共享网关,一个短暂外部故障可能让整个集群中的 Pod 同时重启,造成故障放大。

本项目的探针设计

CPA 使用 HTTP GET /,端口 8317;cpa-manager 使用 HTTP GET /health,端口 18317。

Mihomo Gateway 使用 TCP socket 探测 18000 端口。TCP 探针只证明进程正在监听端口,不能证明每个代理节点可用或访问外部网站一定成功。

Operator 自己暴露:

  • /healthz:进程健康;
  • /readyz:controller manager 准备状态。

探针越靠近应用语义,结论越强,但也越可能被外部依赖干扰。设计时要明确“这个探针失败后,Kubernetes 会采取什么动作”。

Probe 失败后谁做什么

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
startupProbe 失败
  → 主容器继续等待或最终被重启
  → readiness/liveness 暂不接管

readinessProbe 失败
  → Pod NotReady
  → EndpointSlice 标记后端不可用
  → 通常不再接收 Service 新流量

livenessProbe 失败
  → kubelet 重启该容器
  → restartCount 增加

在多容器 Pod 中,一个容器重启不一定重建整个 Pod;但任何一个普通容器不 Ready,Pod 整体通常不会 Ready。

SubPool Conditions 是系统级状态

本项目固定维护这些 Conditions:

ConditionTrue 表示什么
InfrastructureReadyStatefulSet 有 Ready replica,两个内部 Service 存在
ProxyGatewayReadyShared Mihomo Service 至少有一个 Ready Endpoint
ExposureReadyInternal 边界成立,或 Gateway Route/Gateway 已就绪
PlatformReady上面三类依赖同时满足
Ready当前 generation 的空号池平台已就绪
DeletionBlocked删除正被 Namespace 或 finalizer 阻塞

依赖关系可以画成:

1
2
3
InfrastructureReady ─┐
ProxyGatewayReady ───┼─> PlatformReady ─> Ready
ExposureReady ───────┘

这样比只有一个模糊的 Ready=False 更容易排查。

Condition 的四个核心字段

一个标准 Condition 常包含:

1
2
3
4
type: ProxyGatewayReady
status: "False"
reason: EndpointsUnavailable
message: Shared Mihomo Gateway has no ready Endpoint

阅读顺序:

  1. type:在检查哪个维度;
  2. status:True、False 或 Unknown;
  3. reason:机器可读、相对稳定的原因码;
  4. message:给人看的上下文说明。

自动化应优先依赖 type/status/reason,不要用字符串包含关系解析 message。

observedGeneration 防止读到过期的好消息

每次修改 SubPool spec,metadata.generation 都会递增。Operator 完成本轮状态计算后,把这代数写入:

1
2
status:
  observedGeneration: 3

如果出现:

1
2
metadata.generation = 4
status.observedGeneration = 3

说明 status 仍对应旧 spec。即使 Ready 还是 True,也不能直接当作新配置已生效。

这是异步控制系统的重要常识:状态有时间差,必须知道控制器观察的是哪一代期望。

Internal 模式为什么 ExposureReady 可以为 True

Internal 模式不要求外部 Gateway。它的 ExposureReady 表示设计中的访问边界已经成立:CPA 是 ClusterIP,管理员可通过受控 port-forward 访问。

这不是说 CPA 已经公网可达。Condition 的含义必须结合产品契约,而不能凭名字猜。

Gateway 模式则要求:

  • HTTPRoute 存在;
  • Route 被指定 Gateway listener Accepted;
  • Route 的引用已成功解析;
  • Shared Gateway 的 Programmed=True

所以同一个 Condition type 在两种模式下有不同的检查路径,但都回答“所选择的暴露方式是否准备好”。

Ready=True 明确不检查什么

本项目把 Ready 限定为“空号池平台 Ready”,不承诺:

  • 账号已经导入;
  • OAuth token 有效;
  • 真实模型请求成功;
  • 所有上游代理节点健康;
  • 每个 HTTP 请求切换一次 IP。

这是负责任的状态设计。若控制器没有证据验证某件事,就不应让 Ready 暗示它已经验证。

未来可以新增业务级健康对象或指标,但不能悄悄改变现有 Ready 的语义,否则旧自动化会被误导。

kubectl wait 在等什么

1
2
kubectl wait --for=condition=Ready \
  subpool/demo-pool --timeout=300s

它等待名为 Ready 的 Condition 变为 True。超时不代表 kubectl 出错,只表示在指定时间内条件没有满足。

超时后不要立刻重复 wait,应读取 Conditions:

1
2
kubectl get sp demo-pool \
  -o jsonpath='{range .status.conditions[*]}{.type}={.status}{"\t"}{.reason}{"\t"}{.message}{"\n"}{end}'

然后沿失败维度继续检查。

从 Condition 到对象的排查路径

InfrastructureReady=False

1
2
kubectl -n subpool-demo-pool get statefulset,pod,service,pvc
kubectl -n subpool-demo-pool describe pod runtime-0

ProxyGatewayReady=False

1
2
3
kubectl -n subpool-gateway get deployment,service,pod
kubectl -n subpool-gateway get endpointslice \
  -l kubernetes.io/service-name=mihomo-gateway

ExposureReady=False 且为 Gateway 模式

1
2
kubectl -n subpool-demo-pool get httproute cpa -o yaml
kubectl -n subpool-gateway get gateway subpool-edge -o yaml

DeletionBlocked=True

1
2
kubectl get namespace subpool-demo-pool -o yaml
kubectl -n subpool-demo-pool get pvc -o yaml

常见探针误区

livenessProbe 做得越深越好

错误。它失败会触发重启,检查外部依赖过深容易制造重启风暴。

readinessProbe 失败就一定要重启

不一定。暂时不能接流量与进程不可恢复是两个问题。

TCP probe 成功等于业务正常

不等于。它只证明端口可建立 TCP 连接。

Pod Ready 等于整个平台 Ready

不等于。Pod 可能 Ready,但 Shared Mihomo 或 Gateway 尚未就绪。

Ready=False 都是故障

不一定。新建中的资源、显式 suspend 或等待外部基础设施时,False 可能是诚实的过渡状态。

健康检查的价值不在于把所有东西染成绿色,而在于让每一盏灯都有准确、稳定、可行动的含义。

使用 Hugo 构建
主题 StackJimmy 设计