量一量发布是不是真的不停机
本实验在 Pod 会真实启动和终止的集群中进行
VM 中运行着真正的 k3s。滚动更新会真正逐个替换实例,失败的发布会真正停止,kubectl drain 也会真正被 PodDisruptionBudget 阻止。
CKAD 课程中其他发布实验使用的模拟集群不会实际运行 Pod,因此无法衡量“是否实现了零停机”。
环境首次启动大约需要 2 分钟。
预计用时 70 分钟。会话默认持续 60 分钟,请在到期前通过+时间延长(最长 180 分钟)。会话结束后 VM 和文件都会被回收,请另行保存所需结果。
目标
在部署替换前、替换期间和替换后持续发送请求,实际统计 HTTP 失败,并观察失败的发布如何停止,以及回滚究竟会恢复什么。
为什么重要
“零停机部署”并不是选择一个策略名称,而是多项设置相互配合后才能实现的结果。缺少任何一项,都可能悄无声息地失效。
- 没有
readinessProbe时,新 Pod 一启动就会接收流量,即使它还没有准备好。 maxUnavailable过大时,多个实例会同时下线,导致容量不足。- 终止宽限期(
terminationGracePeriodSeconds)过短时,正在处理的请求会被中断。
而且,是否发生中断必须实际测量才知道。等部署结束后再检查,看起来总是正常的。
步骤
所有资源都创建在 crol 命名空间中。
- 创建
webDeployment(副本数 3、nginx、包含 readinessProbe),并显式设置maxSurge和maxUnavailable。将内容保存到/root/crol/rolling.txt。 - 为 web 配置 replicas=3、maxSurge=1、maxUnavailable=0 和 readinessProbe,然后使用
python3 /opt/fixtures/ckad_rollout_observer.py capture观测两次真实发布。最初的 Pod 不得包含 preStop。在/root/crol/nodowntime.txt中以 key=value 形式写入total、failed、total_before_prestop、failed_before_prestop,并同时写入scope=single-vm-rollout-sample、hook_not_retroactive=yes。失败数必须按观测结果记录。 - 更新为不存在的镜像,确认发布会停止,并把期间旧 Pod 仍然存活这一事实一同保存到
/root/crol/stuck.txt。 - 使用
rollout undo回滚,并将结果保存到/root/crol/undo.txt。同时显式设置revisionHistoryLimit。 - 使用
rollout pause和resume制造只替换了部分实例的状态,并将其保存到/root/crol/pause.txt。 - 创建
web-pdbPodDisruptionBudget,并将当前最多可以中断多少个实例,以及 PDB 无法阻止什么,保存到/root/crol/pdb.txt。 - 创建
legacyDeployment,并使用Recreate策略;将需要这种策略的原因保存到/root/crol/recreate.txt。 - 在
/root/crol/report.md中写入downtime_requests_failed=、recreate_causes_downtime=、pdb_min_available=三行及其说明。
参考
- 第 2 步的辅助程序会在使用同一镜像的情况下修改模板注解,从而触发真实发布。顺序为:无钩子的发布 → 完成钩子安装 → 对带钩子的 Pod 进行单独发布。不得解释为新模板中的钩子会追溯应用到旧 Pod。
rollout-observation.json会保存真实请求以及替换前后 Pod UID 和钩子标记。HTTP 500、重定向、错误正文和传输错误都计为失败。不要为了让报告数值吻合而修改观测文件。已经完成的 capture 不会重新部署,而会保留原有观测。- 即使两次失败数都为 0 或没有差异,也应按观测结果填写。仅凭该样本无法证明钩子的效果,也无法证明包含外部负载均衡器和长连接在内的整个服务实现零停机。中断的观测不会自动覆盖,应在新会话中重新进行。
- 使用
kubectl -n crol rollout status deploy/web查看发布状态,停止原因位于.status.conditions。答案示例对正常启动使用 60 秒超时,仅对故意制造的镜像失败实验使用 15 秒超时,恢复后再改回 60 秒。这只是为了进行简短教学实验,并非生产服务的建议值。 - 查看答案和步骤准备都不会覆盖已有答案。如果现有答案错误,请根据失败原因自行修正。同时运行两个功能时,请等待先执行的命令完成后再试。
rollout undo可以通过--to-revision选择特定版本,仍保留的版本可通过rollout history查看。- PDB 当前的余量位于
.status.disruptionsAllowed。如果 Pod 数量不足,该值会变为 0,drain 将被阻止。 - 常见错误 1:没有
readinessProbe却期待零停机。新 Pod 一启动便加入 Endpoint,在尚未准备好的状态下接收流量。 - 常见错误 2:认为 PDB 也能阻止节点故障。它只会阻止自愿中断(drain、eviction),无法阻止节点突然宕机。
两个数值决定替换速度
创建 web Deployment(副本数 3、nginx、包含 readinessProbe),并显式设置 maxSurge 和 maxUnavailable。将内容保存到 /root/crol/rolling.txt。
maxSurge 表示最多可以比目标数量多启动几个实例,maxUnavailable 表示最多允许缺少几个实例。
用数字衡量是否零停机
为 web 配置 replicas=3、maxSurge=1、maxUnavailable=0 和 readinessProbe。最初的 Pod 不得包含 preStop。使用 python3 /opt/fixtures/ckad_rollout_observer.py capture 观测两次真实发布,然后在 /root/crol/nodowntime.txt 中以 key=value 形式写入 total、failed、total_before_prestop、failed_before_prestop。同时写入 scope=single-vm-rollout-sample、hook_not_retroactive=yes,并说明结果。不要假设失败数为 0,应按观测结果记录。
rollout-observation.json 中的 trials 顺序为 without_hook、with_hook。应同时检查各 requests 的 HTTP 状态 200、nginx 正文以及是否存在 error。钩子安装发布在测量之外单独完成,请确认第二次测量中的 before Pod 都已包含钩子。
失败的发布会停止
更新为不存在的镜像,确认发布会停止,并把期间旧 Pod 仍然存活这一事实一同保存到 /root/crol/stuck.txt。
更新为不存在的镜像后,新 Pod 无法启动,并会在 progressDeadlineSeconds 后停止。期间请观察旧 Pod。
回滚究竟会恢复什么
使用 rollout undo 回滚,并将结果保存到 /root/crol/undo.txt。同时显式设置 revisionHistoryLimit。
rollout undo 会回到上一个版本。保留多少个版本由 revisionHistoryLimit 决定。
只替换一部分后观察
使用 rollout pause 和 resume 制造只替换了部分实例的状态,并将其保存到 /root/crol/pause.txt。
在 rollout pause 后修改镜像不会立即发生变化。它用于汇总多项修改后一次性发布。
避免同时下线过多实例
创建 web-pdb PodDisruptionBudget,并将当前最多可以中断多少个实例,以及 PDB 无法阻止什么,保存到 /root/crol/pdb.txt。
PDB 只会阻止自愿中断。disruptionsAllowed 表示当前允许中断的最大数量。
主动造成停机的策略
创建 legacy Deployment,并使用 Recreate 策略;将需要这种策略的原因保存到 /root/crol/recreate.txt。
Recreate 会先停止所有旧实例,再启动新实例。它适用于两个版本绝不能同时存在的情况。
总结学到了什么
在 /root/crol/report.md 中写入 downtime_requests_failed=、recreate_causes_downtime=、pdb_min_available= 三行及其说明。
写入 downtime_requests_failed=、recreate_causes_downtime=、pdb_min_available= 三行及其说明。