<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>滚动 · XB 现场笔记</title><link>https://www.xbcatm.com/zh/tags/%E6%BB%9A%E5%8A%A8/</link><description>基于 HTTP、DNS、TLS、Caddy、SSH、发布与恢复的可复现运维现场笔记。</description><generator>Hugo</generator><language>zh-CN</language><atom:link href="https://www.xbcatm.com/zh/tags/%E6%BB%9A%E5%8A%A8/index.xml" rel="self" type="application/rss+xml"/><item><title>Kubernetes 滚动更新逐步替换 Pod</title><link>https://www.xbcatm.com/zh/posts/k8s-rolling-update-is-a-strategy/</link><pubDate>Sat, 27 Nov 2027 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/zh/posts/k8s-rolling-update-is-a-strategy/</guid><description>根据版本兼容性、资源所有权与经过演练的恢复方案选择 RollingUpdate 或 Recreate；副本预算本身不能证明服务不中断。</description><content:encoded>&lt;p&gt;先选择发布的形状，再调整速度。新旧版本能否同时处理请求？能否安全共享存储和任务所有权？如果新版本起不来，如何恢复？这些问题决定 Deployment 应该用 &lt;code&gt;RollingUpdate&lt;/code&gt; 重叠运行，还是用 &lt;code&gt;Recreate&lt;/code&gt; 先停旧版、再起新版。姊妹篇讨论滚动的节奏和现场观察；本篇讨论兼容性、停机与恢复边界。&lt;/p&gt;
&lt;h2 id="逐步替换还是先停后起"&gt;逐步替换，还是先停后起&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;RollingUpdate&lt;/code&gt; 是 Deployment 的默认策略。surge 和 unavailable 两个预算控制替换过程，允许新旧 ReplicaSet 同时存在。就绪状态参与可用副本的计算，但不代表业务逻辑正确，更不代表每个请求都成功。不能因为看到 Ready，就推断订单、支付或后台任务已经通过发布验收。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Recreate&lt;/code&gt; 在 Deployment 升级时等待旧 Pod 终止，再创建新版本，因此应计划一段没有可用服务副本的时间。选择这个策略时，要移除 &lt;code&gt;rollingUpdate&lt;/code&gt; 配置块；其中的字段只属于滚动更新。停机窗口要通过测量得到，不能顺手把三十秒当成保证。旧进程退出慢、新镜像下载慢、存储挂载慢，都可能拉长用户实际感受到的中断。&lt;/p&gt;
&lt;h2 id="兼容性才是选择依据"&gt;兼容性才是选择依据&lt;/h2&gt;
&lt;p&gt;允许版本重叠，意味着接口、持久化数据以及后台工作都能兼容。前端健康检查可能正常，旧 worker 却读不懂新 worker 刚写入的记录。调整副本数之前，应测试混合版本的读写、任务领取和重试行为。副本预算能安排进退顺序，不能修复不兼容的数据库结构变化。&lt;/p&gt;
&lt;p&gt;有状态并不自动意味着必须选择 &lt;code&gt;Recreate&lt;/code&gt;：协调正确的副本式应用，也可能支持新旧版本同时运行。反过来，&lt;code&gt;Recreate&lt;/code&gt; 不是分布式锁。它的先停后起约束适用于 Deployment 升级；如果人工删除一个 Pod，ReplicaSet 可能在旧 Pod 仍处于终止状态时创建替代者。真正的单写者需要应用层协调，必要时还要有隔离旧所有者的机制，确保失去资格的进程不能继续写入。不能把发布策略当成这种机制的替代品。&lt;/p&gt;
&lt;p&gt;存储术语也不能混淆。&lt;code&gt;ReadWriteOnce&lt;/code&gt; 限制的是一个节点以读写方式挂载，并不等于只能有一个 Pod；同一节点上的多个 Pod 仍可能使用同一卷。对于支持的 CSI 卷，&lt;code&gt;ReadWriteOncePod&lt;/code&gt; 才提供单 Pod 访问约束。应检查实际驱动、卷的访问模式与工作负载需求，而不是看到一个存储标签，就认定已经解决了所有权问题。&lt;/p&gt;
&lt;h2 id="回滚也是一次变更"&gt;回滚也是一次变更&lt;/h2&gt;
&lt;p&gt;保留可以重新启动的旧镜像和修订历史。&lt;code&gt;kubectl rollout undo&lt;/code&gt; 恢复记录过的 Pod 模板；只把镜像地址改回去，可能漏掉已经变化的启动参数或环境引用。这两种操作都不会撤销数据库迁移，也不会恢复独立修改过的 ConfigMap 内容。旧模板继续引用同一个 ConfigMap 名称时，重新启动的旧程序仍可能读到新数据。&lt;/p&gt;
&lt;p&gt;发布前要记录镜像、配置和数据结构之间的兼容关系。如果需要混合运行或回退，应优先安排向后兼容的迁移顺序。恢复流程必须演练，包括谁执行、按什么顺序做、需要多久，以及如何判断旧版已经恢复业务。只留着旧 ReplicaSet，不能证明恢复是安全的。使用 &lt;code&gt;Recreate&lt;/code&gt; 时，回到旧版也可能再经历一次停机和启动；恢复时间不能只按正常升级的一半估算。&lt;/p&gt;
&lt;h2 id="坏掉的样子"&gt;坏掉的样子&lt;/h2&gt;
&lt;p&gt;没有正确协调的 worker，可能在所有 Pod 都 Ready 时重复执行任务或破坏数据。此时需要检查任务所有权与版本兼容性，而不只是看容器是否健康。另一类故障是 &lt;code&gt;Recreate&lt;/code&gt; 的冷启动超出预算：镜像拉取或卷挂载很慢，应用真正开始启动之前，停机时间就已经被耗尽。因此演练要包含真正的冷启动和旧版退出耗时，不能只测缓存齐全时的进程启动。&lt;/p&gt;
&lt;p&gt;四个副本设置 &lt;code&gt;maxUnavailable: 50%&lt;/code&gt;，预算允许减少到两个可用副本，不会自动变成一个。新替代 Pod 崩溃可能让进度停滞；另外发生的节点故障等独立事件，才可能把现有容量继续压低。决定采用这个预算之前，先验证两个实例能否承担预期流量，而不是把「更新仍在允许范围内」等同于业务容量足够。&lt;/p&gt;
&lt;p&gt;零 unavailable 也无法防止无关故障。另一个极端是允许全部副本不可用：这样的配置可以合法存在，只是通常不符合服务目标。若团队要禁止它，应明确这是自己的发布政策，而不是 Kubernetes API 的校验规则。真正不能同时为零的是 surge 和 unavailable，否则替换过程没有可推进的空间。&lt;/p&gt;
&lt;h2 id="快速验证"&gt;快速验证&lt;/h2&gt;
&lt;p&gt;把下面保存为 &lt;code&gt;strategy_window.py&lt;/code&gt;，运行 &lt;code&gt;python3 strategy_window.py&lt;/code&gt;。这是只使用标准库的本地模型，仅接受正整数副本数与非负整数预算。它计算规划量，不模拟控制器，也不是完整的 API 校验器；百分比换算、自动扩缩容和副本数为零的情况不在这个模型内。Kubernetes 允许把 Deployment 缩容到零；程序拒绝它，只因为本练习讨论的是有副本需要替换的场景。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Integer rollout-budget arithmetic; no cluster or traffic is exercised.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rolling_budget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;replicas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;surge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unavailable&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;replicas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;surge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unavailable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;this model accepts integers only&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;replicas&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;surge&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;unavailable&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;positive replicas and nonnegative budgets required&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;surge&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;unavailable&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;surge and unavailable cannot both be zero&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;planned_floor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;replicas&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;unavailable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;target_ceiling&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;replicas&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;surge&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;planned_floor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_ceiling&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;rolling_budget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;rolling_budget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;rolling_budget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;rolling_budget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;invalid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;AssertionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;invalid model input accepted: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;invalid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rolling: planned availability floor=5, replica target ceiling=7&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;full-unavailability budget: planned floor=0 (permitted)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;recreate: expect a zero-serving interval during replacement&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;PASS: budget arithmetic only; runtime availability untested&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;预期输出四行，最后一行明确写着只通过预算算术、未测试运行时可用性。六副本、一 surge、一 unavailable 的结果是五和七；允许四个副本全部不可用的例子，故意得到零，而不是抛出「非法配置」。两项预算同时为零、负预算、零副本等不属于本模型有效输入的情况，则通过断言检查拒绝路径。&lt;/p&gt;
&lt;p&gt;正在终止的 Pod、独立故障、调度以及真实请求处理都没有进入模型。因此，五不是实际观测到的服务能力下限，七也不是所有运行进程的绝对上限。程序只帮助检查算术和策略假设，不能给线上可用性出具证明。把数字换成自己的副本配置之前，也要先处理百分比和实际需求变化，而不是直接复制结论。&lt;/p&gt;
&lt;p&gt;本次修订在 Linux 的 Python 3.12.14 中执行了断言，没有执行 Kubernetes 集成实验。采用某个策略前，应在测试环境演练升级和回滚，同时持续发请求、检查存储使用，并运行混合版本业务测试。把这些现场结果与本地算术结果分开记录，才知道哪些结论有真实环境的证据支持，哪些还只是规划假设。&lt;/p&gt;
&lt;h2 id="延伸阅读"&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.xbcatm.com/zh/posts/kubernetes-rolling-update-waves/"&gt;滚动更新分批换上新版本&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.xbcatm.com/zh/posts/blue-green-deployments/"&gt;蓝绿部署让两套环境随时待命&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.xbcatm.com/zh/notes/a-release-checklist-for-a-static-site/"&gt;静态网站的发布检查清单&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/"&gt;Kubernetes Deployment&lt;/a&gt;，升级顺序与修订回退，已核验 2026-10-15。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/reference/kubernetes-api/apps/deployment-v1/"&gt;Deployment API&lt;/a&gt;，策略字段与更新预算，已核验 2026-10-15。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes"&gt;Kubernetes 持久卷&lt;/a&gt;，节点级与 Pod 级访问模式，已核验 2026-10-15。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/configuration/configmap/"&gt;Kubernetes ConfigMap&lt;/a&gt;，配置引用与传播，已核验 2026-10-15。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/"&gt;Kubernetes StatefulSet 删除注意事项&lt;/a&gt;，身份与重复进程风险，已核验 2026-10-15。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>