
1. 升级不只是点升级按钮先理清K8s里的两套更新逻辑我最早接触Kubernetes的时候以为升级更新就是把集群版本从1.24换到1.25这么简单后来才知道这个概念在K8s里其实藏着两个完全不同的更新场景搞混了会出大事。第一个场景是集群本身的版本升级——比如把control plane和node从v1.26升到v1.28涉及kube-apiserver、kube-controller-manager、kubelet、etcd这些核心组件的协同替换。第二个场景是业务应用的滚动更新——你有一个Deployment跑了三个副本改了镜像tag从v1到v2K8s如何在不中断服务的情况下把旧Pod换成新Pod。这两个场景的操作思路、风险点、排查方式完全不一样但很多文章把两者混在一起讲导致读者上手时一头雾水。这篇博客就把这两套逻辑拆开讲清楚先说应用层面的滚动更新因为这才是你每天发布版本都会遇到的再说集群层面的版本升级因为它是你半年到一年才做一次、但做错一次就够你折腾半天的重活。最后我还会把我在生产环境里踩过的一些坑整理出来包括maxSurge和maxUnavailable怎么配才合理、金丝雀发布怎么在原生K8s里落地、集群升级时drain节点为什么必须看PodDisruptionBudget这些细节官方文档未必说得够透但实际运维的时候每一个都能救命。不管你是刚接触K8s的运维新人还是已经跑过一段时间集群、想把手上的发布流程打磨得更稳的开发者这篇文章都值得你花十几分钟读完至少能帮你少踩几个我当年踩过的坑。2. 滚动更新的底层逻辑ReplicaSet如何悄悄完成新旧交接2.1 从Deployment到ReplicaSet一次更新牵动了什么我们日常接触最多的更新入口就是Deployment。你执行kubectl set image deployment/order-service order-serviceregistry.example.com/order-service:v2或者直接kubectl apply一个改过镜像tag的YAMLK8s立刻开始行动。但真正干活的不是Deployment本身而是它底下的ReplicaSet。Deployment是一个声明式的控制器它只负责维护我想要的最终状态长什么样。具体到每一次更新Deployment会创建一个新的ReplicaSet新的ReplicaSet负责把新版本的Pod拉起来旧的ReplicaSet负责把旧版本的Pod缩下去。这里有一个关键认知每次镜像或配置变更不是修改现有的Pod而是创建全新的ReplicaSet。这也是为什么kubectl get rs能看到一长串历史版本记录比如order-service-7d8f9b6c5d和order-service-5c6f7a8b9c并存。它们分别对应不同的模板hash代表不同的版本状态。理解了这层关系你就能理解为什么Deployment更新天然支持回滚——旧版本的ReplicaSet根本没被删除只是副本数缩到了0随时可以重新拉起来。2.2 三个关键字段strategy、maxSurge、maxUnavailableDeployment的更新策略定义在spec.strategy里。默认是RollingUpdate另一种是Recreate先全部杀掉旧Pod再创建新Pod会导致短暂停机。生产环境几乎不会用Recreate我们重点说RollingUpdate。滚动更新有两个核心参数决定了一次更新的节奏和安全性maxSurge更新期间允许超出期望副本数的最大Pod数量可以是绝对数值也可以是百分比。默认值是25%。比如副本数4个maxSurge25%那么更新期间最多同时存在5个Pod。maxUnavailable更新期间允许不可用的最大Pod数量同样支持绝对值和百分比。默认值也是25%。比如副本数4个maxUnavailable25%意味着任意时刻至少要有3个Pod是可用的。这两个参数需要配合起来看。我举个例子副本数10个maxSurge25%maxUnavailable25%。翻译成人话就是新的Pod最多同时启动2.5个向上取整3个旧Pod最多同时停掉2.5个向上取整3个整个更新过程中可用Pod数量始终保持在7到13之间。K8s的调度逻辑是先启动新的Pod等新Pod进入Ready状态再杀掉对应数量的旧Pod如此往复直到全部替换完成。参数配置不当的后果我后面专门写一节这里先记住一个核心原则偏重可用性的服务maxUnavailable应该设置得很低甚至为0偏重快速发布的场景可以把maxSurge调高来加快替换速度但前提是你的集群有足够的资源余量。下面我们看一个典型的滚动更新YAML片段apiVersion: apps/v1 kind: Deployment metadata: name: order-service spec: replicas: 4 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 selector: matchLabels: app: order-service template: metadata: labels: app: order-service spec: containers: - name: order-service image: registry.example.com/order-service:v2 ports: - containerPort: 8080这套配置的意思是每次最多多出1个新Pod但旧的不能停保证4个副本中间每一时刻都有4个旧Pod在服务。适合那种流量敏感、一点都不能缩容的核心服务。2.3 滚动更新节奏分析三步到底是怎么循环的很多人只是知道滚动两个字但不清楚内部的具体节奏。我拆解一下上面的例子假设当前4个Pod都是v1:K8s创建新的ReplicaSet先创建1个v2 Pod此时集群里有4个v1加1个v2超出期望副本数1个符合maxSurge1。v2 Pod成功通过readinessProbe探针检测进入Ready状态。此时可用Pod数是5超出了期望的4所以K8s从旧ReplicaSet里删除1个v1 Pod集群回到4个Pod总数但v1剩3个、v2有1个。接着又创建1个v2 Pod总数又变成5个v2 Ready后再删1个v1……如此循环直到4个Pod全部变成v2。注意第二步里有个容易被忽略的细节新Pod必须通过就绪探针readinessProbe才会被认为可用。如果你没有配readinessProbePod只要容器进程启动了就会被标记为Ready流量立刻打进来。这时候如果你的新版本启动耗时比较长、或者启动后头几秒无法正常处理请求就有请求失败的风险。所以滚动更新配合readinessProbe不是推荐而是必须。更细的节奏控制可以用kubectl rollout status deployment/order-service实时观察更新完成后会输出类似deployment order-service successfully rolled out的信息。3. 生产环境真正在用的三种发布策略如何用原生K8s实现金丝雀和蓝绿搞懂了Deployment自带滚动更新只是起步。真实的生产发布远比全部替换复杂你希望先让5%的流量跑新版本观察一段时间再逐步放量或者你希望两个版本共存一套环境一键切流量。这就要用到更高级的发布模型了。3.1 金丝雀发布靠多个Deployment手工控制权重原生K8s里没有内置金丝雀发布的一键功能但我们完全可以用多个Deployment加Service来实现。思路是这样的维护两个Deployment一个跑稳定版一个跑金丝雀版通过它们的副本数比例来控制流量权重。举一个可操作的例子。假设稳定版Deployment叫order-service-stable副本数9个跑v1镜像金丝雀版Deployment叫order-service-canary副本数1个跑v2镜像。它们共享同一个Service选择器比如app: order-serviceService的selector匹配两个Deployment里Pod的共同label。由于Service做负载均衡时按Pod数量大致均分流量9:1的比例就能实现大约90%流量打到v1、10%打到v2的效果。观察一段时间后如果金丝雀指标正常把金丝雀的副本数扩到3个、稳定版缩到7个流量比例变成70:30。继续观察再逐渐调整到50:50、100:0。全部确认没问题后把稳定版Deployment的镜像也更新到v2然后删掉金丝雀Deployment。这种做法的优点是完全基于K8s原生对象不依赖任何额外组件缺点是权重控制不够精细只能按副本数粗粒度调整。如果你的团队已经在用Istio或Linkerd这类服务网格可以用VirtualService里的weight字段做1%、5%、10%这种精确的流量比例控制效果会好很多但代价是引入额外的控制面和数据面组件。小团队从原生方案起步完全足够。3.2 蓝绿发布一套环境两套部署切换只在Service层面蓝绿发布的核心思路是同时准备一套蓝色旧版本环境和一套绿色新版本环境发布时先完整部署绿色环境验证通过后把Service的选择器从蓝色切到绿色流量瞬间切换有问题再切回来。用K8s实现非常简单。两个Deploymentapp: order-service-blue和app: order-service-green它们的Pod都带一个版本标签比如version: blue和version: green。Service最初这样写apiVersion: v1 kind: Service metadata: name: order-service spec: selector: app: order-service version: blue ports: - port: 80 targetPort: 8080全部流量打到蓝色环境。新版本上线时先完整部署绿色Deployment跑通接口测试和健康检查然后修改Service的selector把version: blue改成version: green执行kubectl apply流量一次性切换。如果发现问题把selector改回蓝色即可。蓝绿发布的最大优势是切换速度极快秒级完成回滚也极其简单。最大的代价是资源开销翻倍你要同时跑两套完整环境。所以它特别适合那种资源充裕、对切换速度要求极高、但又希望发布风险可控的核心应用。3.3 策略对比什么时候该用哪种我把三种方式放在一起做个直观对比策略切换速度回滚速度资源占用流量控制精度依赖组件Deployment滚动更新最慢逐步替换快自动回滚低只需少量额外副本粗按批次无K8s原生金丝雀原生方案手动控制分钟级快删掉金丝雀低到中与金丝雀副本数相关较粗按副本比例无K8s原生蓝绿秒级切换极快改selector高两套完整环境只有0或100%无K8s原生金丝雀Istio秒级调整权重快改VirtualService额外控制面开销精确到百分比Istio我的个人建议业务初期、发布频率高、团队规模小直接用Deployment滚动更新就够了当你的服务对稳定性要求上升到发布当天不能出现任何异常的时候再把金丝雀做起来从5%开始放量蓝绿适合那种回滚需求特别苛刻的情况比如你改了数据库结构导致新旧版本不兼容只能瞬间切流量不能慢慢滚动。4. 回滚不是玄学rollout undo的底层机制与参数陷阱4.1 更新失败了Deployment会怎么做滚动更新过程中如果出了问题比如新版本Pod一直CrashLoopBackOff或者readinessProbe迟迟不通过Deployment会按照你设置的maxUnavailable对应的可用性下限继续推进还是停下来实际行为是这样的如果新Pod一直无法进入Ready状态Deployment会卡住既不会继续推进也不会自动回滚。你执行kubectl get pods会看到新版本的Pod处于CrashLoopBackOff或ContainerCreating状态而旧版本Pod正常运行。此时有几种选择手动执行kubectl rollout undo deployment/order-service一键回滚到上一个版本。用kubectl rollout undo deployment/order-service --to-revision2回滚到指定历史版本。直接修改镜像tag重新apply触发新一轮滚动更新。第一种最常用。背后发生的事情是K8s把当前Deployment的template重新设置成上一个ReplicaSet对应的模板随即触发一次新的滚动更新。因为旧版本的ReplicaSet和它的Pod模板都还完整保留着所以回滚本质上就是一次反向的滚动更新。有一点要特别注意Deployment默认只保留最近的10个ReplicaSet历史版本由spec.revisionHistoryLimit控制。超过10个之后最早的ReplicaSet会被清理掉对应的历史版本就再也无法通过--to-revision回滚了。如果你有回滚到很久以前的某个版本的需求建议把revisionHistoryLimit调大一些代价是etcd里多存一些ReplicaSet对象一般服务保留20到30个足够了。4.2 查看历史版本revision、rollout history、annotation要回滚到指定版本首先得知道有哪些版本可选。两个常用命令kubectl rollout history deployment/order-service kubectl rollout history deployment/order-service --revision3第一条列出所有历史ReplicaSet对应的revision号、更新时间、写入的change-cause注解第二条查看指定revision的模板细节确认这个版本跑的是什么镜像、什么环境变量。这里有个小技巧如果你希望历史记录里能一眼看出每个版本改了什么记得在Deployment的template里加上annotationstemplate: metadata: annotations: kubernetes.io/change-cause: update image to v2, fix timeout bug ...然后用kubectl rollout history查看时每个revision就会带上这行注释这在团队协作时非常管用。我一度靠git log回忆每次发布改了什么后来统一要求开发在Deployment的change-cause里写一行说明回滚时再也不用翻commit记录了。4.3 滚动更新卡死时的排查路径回滚最常见的前置场景是滚动更新卡住了。我总结一套完整的排查链路照着走基本能定位问题先看事件kubectl describe deployment order-service关注Events尾部是否有FailedCreate、Unhealthy等异常事件。再看Pod状态kubectl get pods -l apporder-service找出处于Pending、ImagePullBackOff、CrashLoopBackOff等非Running状态的Pod。看Pod详情kubectl describe pod pod-name关注Events里是调度失败资源不足、节点亲和性不满足、拉镜像失败镜像tag不存在、仓库鉴权失败、还是探针失败Liveness/Readiness返回非200。看容器日志kubectl logs pod-name --previous注意加--previous看上一个容器的日志很多CrashLoop场景下新日志还没打出来老容器就退出了。确认新旧ReplicaSet状态kubectl get rs -l apporder-service看DESIRED和CURRENT是否一致新RS的DESIRED是否卡住不动。这套动作做完问题大概率就浮出水面了。如果最终判定新版本有Bug不用犹豫直接rollout undo。5. 集群版本升级从kubeadm到节点池的完整操作链应用更新的问题讲透了接下来处理集群本身升级的场景。这是一件低频但高危的操作我从几个层面拆解。5.1 到底该不该升版本支持的节奏和你的升级窗口K8s的版本演进有自己的生命周期。每个大版本发布后官方提供大约14个月的支持窗口包括3个多月的主版本更新和接下来大约9个月的补丁支持。版本太老会面临两个问题一是漏洞修复停止安全风险累积二是社区生态里的新组件、新功能往往只兼容较新版本你的集群会逐渐变成一座孤岛。但升级又确实有风险K8s的API有严格的兼容性承诺但很多第三方组件比如ingress controller、监控agent未必跟上节奏。所以我的建议是不要追最新也不要落后超过两个大版本。比如当前社区主流版本在v1.28到v1.30那么v1.26以下的集群就属于需要认真规划升级的范围了。升级策略上kubeadm工具链支持两种方式kubeadm upgrade plan查看可升级版本和依赖关系kubeadm upgrade apply执行升级。生产环境建议跳版本升级时先看官方文档里的Version Skew Policy表确认哪些版本可以跨版本升级避免从太老的版本一步跳到最新比如v1.22直接升v1.28大概率不被支持需要分步走。5.2 实操步骤先用cordon和drain安全腾空节点集群升级最危险的操作不是升级apiserver而是替换kubelet和节点组件。因为kubelet在节点上控制着所有Pod的运行升级kubelet的时候如果节点上还跑着业务Pod这些Pod会面临中断风险。所以在升级每个节点前必须执行两件事kubectl cordon node-01 kubectl drain node-01 --ignore-daemonsets --delete-emptydir-data --forcecordon把节点标记为不可调度新的Pod不会调度上去。drain把节点上已有的Pod驱逐到其他节点。这里有几个参数值得解释--ignore-daemonsetsDaemonSet的Pod比如fluentd、kube-proxy、监控agent不会被驱逐因为每个节点必须有一个驱逐了也没用升级后它们会重新起来。如果不加这个参数drain会因为无法驱逐DaemonSet而卡住。--delete-emptydir-data空目录的数据请自行确认是否可丢。如果Pod用的emptyDir里存了重要临时数据建议先摘流量再处理不要轻易加这个参数。--force有些Pod不在Deployment/StatefulSet管理下或者没有控制器管理默认不允许驱逐。force会跳过这些限制但你要确认这些Pod真的能被安全删除最好先处理掉这些裸Pod再用force。drain成功之后节点上的Pod已经清空此时升级kubelet和kubeadm再重启节点相关服务。5.3 从单控制平面到高可用集群控制面的升级顺序控制面的升级是整个过程中最核心的一环顺序错了会直接导致集群短暂不可用。标准流程先备份etcd。前提是你有etcd快照工具或定期备份任务。升级前必须手动触发一次完整快照以防升级过程中出现不可恢复的数据损坏。升级第一个控制平面节点上的kubeadm执行kubeadm upgrade plan确认目标版本再执行kubeadm upgrade apply v1.28.7。升级其余控制平面节点上的kubeadm和kubelet。所有控制平面完成升级后再开始动worker节点。高可用集群三台master还要注意kubeadm upgrade apply只需要在第一个控制平面节点上执行一次后续节点用kubeadm upgrade node即可不要重复执行apply。如果集群用的是外部etcd而非内置etcd还要单独升级etcd集群顺序是先在非leader节点上升级再升级leader始终保持至少两个etcd成员可用。5.4 节点池和托管集群云厂商环境下的另一种姿势如果你用的是云厂商提供的托管Kubernetes服务集群升级通常不会这么费劲。以常见托管集群为例控制面由云厂商负责升级你只需要关注节点组Node Group的滚动升级。一般操作是创建一个新的节点组新kubelet版本确认Pod成功迁移再删除旧节点组。这本质上就是利用滚动替换的思路完成节点版本升级。但即便在托管环境drain的概念依然适用。云厂商的节点组升级会主动做cordon和drain操作但你要提前确认PodDisruptionBudget设置合理否则drain可能会因PDB阻止而挂起。关于PDB我下面要专门讲。6. 升级更新策略里的隐形坑PDB、镜像拉取、探针一个都不能少6.1 PodDisruptionBudget集群升级时保护可用性的关键也是最常被忽略的配置很多人部署应用的时候不关心PodDisruptionBudget我在很长一段时间里也没概念直到一次集群节点维护时一个核心服务的4个副本被drain操作全部驱逐下线前端直接报警。真相是drain节点时会逐个驱逐Pod如果没有PDB限制kubelet/控制器会尽可能把所有Pod同时挪走导致服务可用副本数变为0。PDB的作用是给自愿中断包括集群升级、节点维护导致的驱逐、主动缩容设定一个可用副本数的底线。举个例子apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: order-service-pdb spec: minAvailable: 3 selector: matchLabels: app: order-service这条PDB的意思是任何自愿中断操作包括drain都不能让app: order-service的可用Pod少于3个。你有4个副本那么一次最多只允许驱逐1个Pod必须等新的Pod在其他节点上Ready之后才能继续下一次驱逐。这极大地降低了升级节点的风险。这里有一个需要注意的坑minAvailable: 3表示最多允许1个副本被同时中断但如果你把minAvailable写死为3副本数调整时PDB不会感知新的副本数可能造成副本扩到10个但一次仍然只允许1个离线的过度保守情况。建议用百分比写法比如minAvailable: 75%在副本数变化时自动换算更灵活。另外StatefulSet的Pod也需要PDB保护尤其是像etcd、Kafka这类有状态组件。升级节点时如果它们的Pod被驱逐单个节点故障倒是还好但多个Pod被同时重新调度到新节点可能造成集群内的网络分区或数据同步延迟。6.2 imagePullPolicy明明改错了镜像tagPod却还在跑旧代码这是我在微服务发布时踩过的一个非常隐蔽的坑。场景是这样的我们手动尝试一个紧急修复改了Deployment的镜像tag从v2改成v2.1发现README上写错了tag名于是立刻改回来v2apply之后Pod被替换成新Pod但新Pod启动时拉到的镜像还是旧的。原因很简单imagePullPolicy默认值跟镜像tag有关——如果tag是latest默认策略是Always每次都会尝试拉最新镜像如果tag是具体版本号默认策略是IfNotPresent节点上如果已经存在相同tag的镜像就不会重新拉取。改回v2时节点上已经有v2的镜像缓存所以新Pod直接复用了旧镜像。正确的做法有两种发布流程中要求每次构建都生成新的、唯一的镜像tag比如带commit短哈希或构建时间戳彻底杜绝同名tag覆盖带来的缓存问题。如果团队习惯用固定tag比如v2这种必须在Deployment里显式声明imagePullPolicy: Always确保每次Pod重建都从仓库拉一次。我个人的建议是第一种方案优先配合imagePullPolicy: IfNotPresent反而能加快正常发布的Pod启动速度。对于紧急回滚场景手动给kubectl rollout undo或者指定--to-revision时如果镜像tag完全相同也要注意缓存问题。6.3 readinessProbe与滚动更新速度的关系探针间隔决定了发布一次要多久很多人的readinessProbe参数都是从网上复制来的而且从来不看它跟滚动更新节奏之间的关系。实际上readinessProbe的间隔和超时时间直接决定了新Pod从创建到就绪需要多久也就决定了滚动更新一个批次需要多久。举一个实际计算例子。假设探针配置如下readinessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 2 successThreshold: 1Pod创建后至少10秒才开始第一次探测每次探测周期5秒假设第一次探测就成功Pod从创建到Ready大约需要10到15秒。如果你的服务启动本身就要20秒第一次探测大概率失败要等下一个5秒周期再试加上failureThreshold的容忍次数新Pod标记为Ready可能要30到40秒。如果maxSurge1、replicas10那么每一轮替换都要等新Pod完全Ready再删旧Pod。按单Pod 40秒就绪计算10个副本的完整滚动更新大约需要7分钟。如果探针配置不合理比如initialDelaySeconds太小导致探测过早失败、failureThreshold设得太大导致判断周期过长一轮发布可能拖到十几分钟甚至更久看起来像滚动更新卡死其实是探针配置拖了后腿。所以当发布速度不符合预期时别急着调maxSurge先检查readinessProbe的参数是否和你服务真实的启动时间匹配。一个合理的参考值是initialDelaySeconds比启动时间多几秒periodSeconds设在5到10秒之间failureThreshold设置在2到3次这样既不会误判也不会拖慢节奏。6.4 资源request与limit在滚动更新里的影响还有一个容易被忽略的点新Pod调度时如果集群当前可用资源不足新Pod会一直Pending滚动更新卡住。很多人以为maxSurge设成1就意味着必须有一个Pod的空余配额但如果你的节点资源已经跑到95%以上新增的这1个Pod很可能调度不上去——尤其当其他服务也在进行滚动更新时集群资源碎片化会导致更明显的调度困难。这里建议两件事一是核心服务更新前用kubectl top nodes看一下节点剩余资源二是给Deployment配上合理的resources.requests不要只设limit不设requests——调度器根据requests判断节点是否满足要求如果你不写requestsPod会因为按理说不占资源而被调度到已严重过载的节点上运行质量完全没有保障。7. 把升级策略固化到流程里版本规划、演练和监控7.1 制定一份可执行的升级Checklist内容讲了这么多最后落到日常执行层面。我建议每个团队都维护一份自己的升级Checklist至少包含这些项确认版本兼容性查官方Upgrade Notes确认目标版本有没有breaking change比如某个API被移除、某个内置组件行为变化。备份关键数据至少包括etcd快照、应用层关键数据库备份、Deployment/ConfigMap/Secret定义文件可以用kubectl get all -o yaml导出。检查第三方组件兼容性ingress controller、CI系统、监控采集器、日志采集器在你目标K8s版本下是否正常运行。评估资源余量滚动更新和集群升级都需要额外的资源来承接新Pod确认集群和节点有buffer。设置PDB所有核心工作负载都配上minAvailable/percentage的PDB。准备回滚方案应用层面关注的是rollout undo集群层面关注的是是否有历史etcd快照和旧版本kubeadm二进制。选择一个流量低谷窗口尽量不要在业务高峰期做集群升级。我个人的另一个建议是把升级当作一次发布来对待而不是当作一次维护操作来随手做掉。你为应用发布准备的一切灰度、回滚、监控、通知升级集群时全部都应该有对应版本。7.2 实测下来最顺手的验证顺序如果你做一次完整的K8s滚动更新能力演练我建议按这个顺序验证先在一个测试Deployment上执行镜像更新观察rolling update节奏记录从第一个新Pod创建到全部就绪的时间。故意把新镜像tag改成不存在观察ImagePullBackOff状态和Deployment是否自动暂停。故意让新版本readinessProbe不通过观察滚动更新是否按预期卡住。执行rollout undo确认回滚后Pod状态恢复正常期间观察Service的endpoints变化确认流量没有中断。验证PDB生效在副本数为3、minAvailable2的情况下执行drain确认两个Pod被驱逐后drain会阻塞等待新Pod Ready。这套验证做一遍你对滚动更新的底层行为会有一个特别直观的体感比看多少文档都管用。演练过程中配合kubectl get events -w和kubectl get pods -w实时观察状态变化效果更好。7.3 最后分享一个我很早就该知道的小技巧很多人排查滚动更新问题时喜欢盯Pod列表但Pod名字变动太快看久了容易眼花。我后来养成了一个习惯先看ReplicaSet再看Pod。kubectl get rs -l apporder-service的输出里DESIRED、CURRENT、READY三列直接告诉你新旧版本各自的推进情况。如果新RS的DESIRED一直不增长说明控制器判定当前状态不能继续推进如果新RS的READY数小于CURRENT说明有Pod没通过就绪检查。一眼就能定位问题出在调度、镜像、还是探针不用去Pod列表里瞎猜。说到底K8s的升级更新策略并不高深它本质上是一套如何在不影响用户体验的前提下优雅地替换运行中的组件的工程方法论。把Deployment滚动更新、金丝雀放量、蓝绿切换、集群节点升级串起来看你会发现核心都是同一件事控制变更的节奏保证任何时刻都有最小可用集合存在。把这个底层逻辑想明白了不管是面对新功能发布还是集群大版本升级你都不会慌。