配置与实际动作之间
一句话总结
Probe、Job、QoS、配置注入全部由 kubelet 执行。没有 kubelet,可以练习编写 YAML,却永远看不到它真正做了什么。
为什么必须观察实际行为
CKAD 询问的是“应用如何在集群上生存”,probe、Job、QoS、配置注入讲的都是这个问题。
但前面模块使用的模拟集群没有 kubelet,所以**这些行为一个都不会实际发生。**probe 不执行,Job 不完成,ConfigMap 也不会 mount。
三种 probe 的关系
| probe | 问题 | 失败后 |
|---|---|---|
startupProbe |
启动完成了吗 | 继续推迟另外两个 |
livenessProbe |
还活着吗 | 重新创建容器 |
readinessProbe |
能接收请求吗 | 从 endpoint 移除 |
在 startupProbe 成功前,另外两个根本不会启动。没有它,只剩两个选择。
- 放宽 liveness → 真正死亡后也会长期放置
- 收紧 liveness → 启动中被杀死,永远重启
Job 的两个值
completions 表示需要成功多少次,parallelism 表示同时运行多少个。混淆两者,Job 会永远不结束或消耗不必要的资源。
backoffLimit 的重试间隔会指数增长,默认值 6 意味着需要数分钟才会放弃,其间失败 Pod 会持续堆积。
QoS 不是手动指定的
不能直接写 qosClass 字段。kubelet 根据 requests 与 limits 的组合赋值;节点内存不足时会先驱逐 BestEffort。
配置注入何时生效
修改 ConfigMap 和 Secret 后,Pod 是否立即看到变化取决于注入方式。环境变量在容器启动时复制一次,后续变更绝不会反映,必须重建 Pod。卷挂载的文件则由 kubelet 周期性更新,稍后会变成新内容;但应用如果不重新读取文件,更新也毫无意义。
实务中常用两种办法:把配置内容 hash 放入 Pod template annotation,配置变化时 template 也改变,自动触发 rolling update;或者让应用检测文件变化并重读。前者简单得多,也容易回退,大多数情况下更好。
还要知道引用不存在对象时的差异。环境变量引用不存在的 ConfigMap 时,无法创建容器,显示 CreateContainerConfigError;卷引用时 mount 无法完成,Pod 会停在 ContainerCreating。同一个拼写错误症状不同,记住映射能大幅缩短调查时间。
Probe 值应该依据什么确定
比添加 probe 更难的是填什么值。照搬默认值导致服务杀死自己的事故并不少见,因此要理解每个值改变什么。
| 值 | 含义 | 设置错误后 |
|---|---|---|
periodSeconds |
多久询问一次 | 太短增加应用负担,太长延迟发现故障 |
timeoutSeconds |
等待响应多久 | 默认 1 秒,受载时会把正常应用判为失败 |
failureThreshold |
连续失败多少次后动作 | 设为 1 时短暂延迟也会重启 |
最常引发事故的组合是 liveness timeout 太短。负载使响应变慢,probe 失败并重启容器,负载转移到剩余 Pod,使其也变慢,最终全部反复重启。用重启解决负载导致的延迟,只会自我恶化。因此原则是:liveness 宽松,readiness 敏感。暂时移出流量可以恢复,重启却无法撤销。
检查内容同样重要。liveness 只应检查进程是否处于无法自愈的状态,不能连依赖数据库是否可达也一起检查。数据库短暂波动时若所有应用 Pod 重启,connection 会同时重新涌入,情况更加糟糕。**依赖检查属于 readiness。**失败时只退出流量,数据库恢复后悄然返回。
为启动缓慢的应用使用 startupProbe 时,只需记住 failureThreshold × periodSeconds 就是允许启动时间。每 30 秒检查、最多 20 次,表示最多等待 10 分钟,超过后才开始重启。
实际工作中真正重要的内容
**启动缓慢的应用应使用 startupProbe。**没有它,只有放宽 liveness 让真实死亡长期不被发现,或收紧 liveness 在启动中杀死应用、使其永久重启这两个选项。第三种 probe 正是为消除该困境而生。
**backoffLimit 默认值 6 比想象中更长。**指数增长的重试间隔会让放弃耗时数分钟,失败 Pod 在此期间持续累积。需要快速失败的 Job 应降低该值。
**QoS 不是写出来的,而是计算出来的。**kubelet 根据 requests 与 limits 组合决定,节点内存不足时先驱逐 BestEffort。重要 workload 不写 requests,等于写下“可以先杀死我”。
后续实验将通过实际行为验证全部内容。