实战指南:实例选择、更新顺序与 maxSurge/maxUnavailable 策略配置)
kOps 滚动更新Rolling Update实战指南实例选择、更新顺序与 maxSurge/maxUnavailable 策略配置【免费下载链接】kopsKubernetes Operations (kOps) - Production Grade k8s Installation, Upgrades and Management项目地址: https://gitcode.com/gh_mirrors/kop/kopskOpsKubernetes Operations在对集群进行升级或配置变更时通常需要替换底层云实例。为避免服务中断kOps 通过**滚动更新Rolling Update**机制增量式地替换实例一次只替换一小部分并在替换间隙持续验证集群健康。本文以 docs/operations/rolling-update.md 为主线结合kops rolling-update cluster命令的完整参数与 pkg/instancegroups 包的真实实现系统讲解实例选择规则、分组更新顺序、单实例的 cordon/drain/terminate 全流程以及maxUnavailable、maxSurge、drainAndTerminate等可配置策略的语义与默认值帮助你安全、可控地完成集群滚动更新。什么是滚动更新为什么需要它升级 Kubernetes 版本、调整实例规格、修改 kubelet 参数等操作都会导致集群的期望规格发生变化。为了让真实运行的云实例与 kOps 生成的规格保持一致通常需要替换replace实例——直接全部销毁再重建会带来明显的服务中断而逐个替换则可以维持集群整体可用。kOps 的做法是在 pkg/instancegroups/rollingupdate.go 中实现RollingUpdateCluster.RollingUpdate方法将实例分组后串行/受控地逐组、逐个替换并在每个实例替换前后执行集群验证确保替换出的新实例工作正常后才继续下一个。所有滚动更新操作统一通过kops rolling-update cluster命令触发该命令的详细参数说明见 docs/cli/kops_rolling-update_cluster.md。一个关键的前提执行滚动更新之前必须先用kops update cluster --yes将云资源AutoScaling 组、启动模板等更新到最新规格。滚动更新只负责换掉旧实例让新实例以新规格创建出来如果云资源本身还是旧规格替换出来的实例依然不满足要求。如果集群状态存储中已经有kops.k8s.io/needs-update注解标记的节点也可以直接触发滚动更新。实例选择哪些实例会被更新滚动更新不会盲目替换所有实例。从源码看实例是否进入待更新集合由 pkg/cloudinstances 中的NeedUpdate判定满足以下任一条件即会被选中替换规格过期实例是用比最近一次kops update cluster生成的规格更旧的规范创建的。这是最常见的触发场景——只要kops update cluster --yes改变了实例组的规格相关实例就会被标记为需要更新。上一次滚动更新遗留的 surge 分离实例如果此前一次滚动更新因失败或中断而中止被临时 detach分离出来做 surge 的实例仍然处于待终结状态会被继续处理。节点带有kops.k8s.io/needs-update注解可以手动给节点打上该注解强制其进入待更新队列。在 pkg/instancegroups/rollingupdate_test.go 的测试中可以看到通过igm.Node.Annotations[kops.k8s.io/needs-update] somevalue构造这类场景的写法。显式指定--force标志即使集群报告无需更新也会强制把全部实例纳入更新范围。对应实现位于 pkg/instancegroups/instancegroups.goif c.Force { update append(update, group.Ready...) }即把当前就绪的实例也追加进待更新集合。更新顺序Bastion → Master控制面→ APIServer → Node滚动更新一次只处理一个实例组且组与组之间有严格顺序。在 pkg/instancegroups/rollingupdate.go 中实例组按角色被划分到四个桶Bastion 组堡垒机最先更新。源码注释解释了原因——Upgrade bastions first; if these go down we cant see anything堡垒机最先升级如果它们挂了我们就什么都看不到了。堡垒机组在 pkg/instancegroups/rollingupdate.go 中并行更新多个堡垒机组同时进行但任一堡垒机组更新失败会立即中止整个滚动更新bastion not healthy after update, stopping rolling-update。Master控制面组接下来更新且严格串行——即使控制面节点分布在多个实例组也一次只更新一个组见 pkg/instancegroups/rollingupdate.go注释引用了 issue #284不能同时滚动所有控制面节点。任一控制面组失败同样会中止更新。APIServer 组随后更新逐个组处理失败仅记录日志只有可退出错误ValidationTimeoutError或DeregisterError见 pkg/instancegroups/rollingupdate.go才会中断。Node 组最后更新。源码注释解释了串行的原因如果并行滚动多个节点组可能同时驱逐同一个 StatefulSet 的多个 Pod破坏有状态应用。在每个角色内部实例组按名称字母序sortGroups见 pkg/instancegroups/rollingupdate.go依次处理。限制更新范围--instance-group 与 --instance-group-roles默认情况下滚动更新会涉及所有实例组。需要收窄范围时可以使用--instance-group 名称只更新指定的一个或多个实例组可多次传递例如--instance-group nodes-1a。命令入口的--instance-group标志支持 shell 补全见 cmd/kops/rolling-update_cluster.go。--instance-group-roles 角色列表只更新特定角色的实例组。可取值包括control-plane,apiserver,node,bastion,etcd,scheduler,kubecontrollermanager多个角色用逗号分隔。注意虽然命令帮助里列出了 etcd、scheduler 等角色但实际的滚动更新主体逻辑pkg/instancegroups/rollingupdate.go只按Node / APIServer / ControlPlane / Bastion四种角色归类其他角色会返回unknown group type错误。更新一个实例组的完整流程选定实例组后pkg/instancegroups/instancegroups.go 的rollingUpdateInstanceGroup负责具体的组内替换流程分三步。第一步集群验证进入实例组更新前先执行与kops validate cluster相同逻辑的集群验证见 docs/cli/kops_validate_cluster.md。如果验证失败整个滚动更新立即报错停止——因为在一个已经不健康的集群上继续换实例只会让情况更糟。以下两种情况跳过此验证实例组角色为 Bastion堡垒机不承载工作负载使用了--cloudonly标志见下文故障场景。第二步给待更新节点打 PreferNoSchedule 软污点验证通过后滚动更新会给被选中要更新的节点打上PreferNoSchedule软污点。这能阻止新 Pod包括被驱逐 Pod 的替代者调度到旧节点上除非集群中没有其他可调度位置。这是一个尽量约束而非硬性拒绝目的是在避免新流量涌入旧节点的同时不破坏集群的调度弹性。taintAllNeedUpdate的具体实现见 pkg/instancegroups 包。与验证类似Bastion 组和--cloudonly模式不执行打污点操作。第三步按策略替换实例最后滚动更新会按照该实例组的rollingUpdate策略maxSurge/maxUnavailable并发替换被选中的节点。这里有几个实现细节值得注意Warm Pool 实例会被直接删除而不经历 cordon/drain见 pkg/instancegroups/instancegroups.goKarpenter 管理的实例组无法 surgegroup.Spec.Manager api.InstanceManagerKarpenter时maxSurge被强制归零见 pkg/instancegroups/instancegroups.go控制面节点无法 surge源码注释解释了原因——控制面节点依赖本地 apiserver 完成自身注册而该 apiserver 依赖本地 etcdetcd 又依赖已经加入集群的节点形成循环依赖因此 Master 不支持先建后删见 pkg/instancegroups/instancegroups.go--interactive模式下并发度被强制降为 1每个实例更新后等待用户确认见 pkg/instancegroups/instancegroups.go。更新单个实例cordon、drain 与 terminate组内每个实例的替换按以下顺序进行Cordon封锁将节点标记为不可调度阻止任何新 Pod 被调度到该节点同时部分云厂商的负载均衡器会把该节点从可用后端集合中摘除。Drain排空驱逐节点上所有非 DaemonSet 管理的 Pod。驱逐过程会尊重 PodDisruptionBudgetPDB——如果驱逐会违反 PDBdrain 会等待。等待连接关闭所有 Pod 驱逐完成后等待5 秒让已建立的 TCP 连接自然关闭。等待时长可用--post-drain-delay调整。终止实例调用云厂商 API 终止该实例除非该实例此前已被 detach 用于 surge。如果实例未被 detach云厂商会自动按当前规格创建新实例补齐数量。等待 APIserver 感知终止后等待15 秒让 Kubernetes APIserver 感知到节点下线。时长由--bastion-interval、--control-plane-interval、--node-interval三个标志分别控制按角色区分默认均为 15s。集群验证除非使用--cloudonly滚动更新会等待集群验证成功默认最多等--validation-timeout15 分钟且需连续验证成功--validate-count次默认 2 次确认替换出的新实例工作正常后才继续下一个实例。以下实例不会被执行 cordon/drainBastion 实例未注册为 Kubernetes 节点not registered as nodes的实例--cloudonly模式下所有实例。这些默认值与标志的定义可以在 cmd/kops/rolling-update_cluster.go 的InitDefaults中找到PostDrainDelay 5s、ValidationTimeout 15m、ValidateCount 2、DrainTimeout 15m、三种Interval均为15s。可配置的滚动更新策略rollingUpdate 字段滚动更新在实例组内的行为可以通过rollingUpdate字段配置。它有两个配置层级实例组级InstanceGroupSpec.rollingUpdate只影响该实例组集群级ClusterSpec.rollingUpdate作为所有实例组的默认值。两者的合并逻辑在 pkg/instancegroups/settings.go 的resolveSettings中实现实例组级配置优先未设置的字段回落到集群级默认集群级也未设置时再落到内置默认值。对应的 API 结构定义在 pkg/apis/kops/cluster.goRollingUpdate结构体字段drainAndTerminate、maxUnavailable、maxSurge。maxUnavailable允许同时不可用的最大节点数maxUnavailable指定滚动更新过程中最多允许多少个节点同时处于不可用状态。调大该值可提升并行度、加快更新速度但会降低集群冗余度。取值可以是绝对数值如5或组内节点的百分比如10%百分比向下取整rounding down取整逻辑见 pkg/apis/kops/cluster.go 的字段注释默认值规则若maxSurge为0则默认maxUnavailable 1否则默认0见 pkg/instancegroups/settings.go。示例允许两个实例并行更新spec: rollingUpdate: maxUnavailable: 2一个重要的保护机制如果组内没有任何实例是用当前规格创建的即全部都是旧规格滚动更新会先从更新单个实例开始等该实例验证成功后再放开到完整并发度。这样可以在新规格本身有问题时把损害限制在一个节点内。该逻辑在 pkg/instancegroups/instancegroups.go 中由noneReady : len(group.Ready) 0触发Ready表示按当前规格创建的实例。maxSurge滚动更新期间的额外实例数先建后删Surging浪涌是指滚动更新期间临时增加实例组的实例数量。与先 drainterminate 旧实例、再创建新实例不同surge 模式先创建新实例再 drain 并终止旧实例服务容量在整个过程中不降反升。实现方式surge 通过detach分离实例完成——被 detach 的实例不再计入实例组的期望数量从而促使云厂商创建新实例来补齐期望数量被 detach 的旧实例最后才被 drain 和 terminate且终止时云厂商不会再创建替代实例因为其 detach 状态使组内数量已满足期望值。detach 相关调用见 pkg/instancegroups/instancegroups.go 的c.detachInstance。maxSurge是更新期间允许创建的额外实例数调大可提升并行度滚动更新创建的新实例数不会超过待更新实例数取值可以是绝对数值如5或百分比如10%百分比向上取整rounding up见 pkg/apis/kops/cluster.go默认值AWS 上默认1其他云平台默认0见 pkg/instancegroups/settings.go且 Karpenter 管理、启用 Spotinst 功能时除外Master 不能 surge集群级默认值对 Master 角色无效如果在 Master 实例组上显式设置maxSurge会产生 API 校验错误。源码层面由 pkg/instancegroups/instancegroups.go 强制清零兜底。示例允许滚动更新期间最多增加两个额外实例即允许两个并行更新spec: rollingUpdate: maxSurge: 2与maxUnavailable相同的保护机制若组内没有按当前规格创建的实例surge 会先只创建 1 个新实例验证成功后再创建剩余的 surge 实例见 pkg/instancegroups/instancegroups.go 中if numSurge maxSurge || noneReady的等待与验证逻辑。drainAndTerminate部分禁用滚动更新将drainAndTerminate设为false可以部分禁用实例组的滚动更新spec: rollingUpdate: drainAndTerminate: false此时行为如下需要更新的节点仍然会被打上污点阻止新 Pod 调度到旧节点若maxSurge非零仍然会创建最多maxSurge个额外新节点但不会执行 drain 与 terminate旧节点保持运行。在实现上pkg/instancegroups/instancegroups.go 会在 surge 环节结束后检查*settings.DrainAndTerminate为false时直接返回跳过整个 drain/terminate 阶段。这个配置适合只想补新节点、不打算回收旧节点的场景。命令行完整参考kops rolling-update cluster的完整参数来自 docs/cli/kops_rolling-update_cluster.md参数默认值说明--yes/-yfalse立即执行滚动更新不加则执行 dry-run预览不实际变更--forcefalse强制滚动更新即使报告无需更新--cloudonlyfalse跳过集群验证可能造成停机用于集群已损坏无法验证的场景--instance-group全部指定要更新的实例组可多次指定--instance-group-roles全部指定要更新的角色control-plane,apiserver,node,bastion,etcd,scheduler,kubecontrollermanager--post-drain-delay5s每个节点 drain 后等待的时间--drain-timeout15m0s单个节点 drain 的最大等待时间--fail-on-drain-errortruedrain 节点失败时是否报错--fail-on-validate-errortrue集群验证失败时是否报错--validation-timeout15m0s集群验证的最大等待时间--validate-count2单个节点更新后集群需要连续验证成功的次数--bastion-interval15s重启堡垒机之间的等待时间--control-plane-interval15s重启控制面节点之间的等待时间--node-interval15s重启工作节点之间的等待时间--interactive/-ifalse每个实例更新后提示继续--admin18h0m0s集群管理员凭证的有效期--api-server—覆盖通信使用的 API server 地址--use-kubeconfigfalse使用本地 kubeconfig 中的 server 端点而非从集群名推断常用命令示例来源docs/cli/kops_rolling-update_cluster.md# 1. 预览滚动更新dry-run不会实际变更 kops rolling-update cluster # 2. 用默认策略执行滚动更新节点会被 drain替换间隙会验证集群 kops rolling-update cluster --yes # 3. 更新指定集群验证失败时不报错退出 kops rolling-update cluster k8s-cluster.example.com --yes \ --fail-on-validate-errorfalse # 4. 集群已损坏、无法验证时强制整体更新跳过验证 kops rolling-update cluster k8s-cluster.example.com --yes \ --cloudonly \ --force # 5. 只更新 nodes-1a 这一个实例组 kops rolling-update cluster k8s-cluster.example.com --yes \ --instance-group nodes-1a故障场景与注意事项集群无法验证导致卡死滚动更新默认在每轮替换后等待集群验证通过。若集群处于损坏状态、永远无法验证命令会一直卡住直至--validation-timeout默认 15 分钟超时并报错。此时可以用--cloudonly跳过验证继续推进注意可能造成停机或配合--fail-on-validate-errorfalse让验证失败不阻断流程。Terraform 用户如果使用 Terraform 管理云资源必须从同一目录依次执行kops update cluster --targetterraform、terraform plan、terraform apply之后才能运行kops rolling-update cluster见 docs/terraform.md。验证的定义滚动更新所依赖的集群验证通过指所有必需节点都在运行、所有 critical priority 的 Pod 都处于可用状态。这与kops validate cluster的判定口径一致。dry-run 的价值不加--yes时命令只做预览可以安全地查看哪些实例会被替换、按什么顺序替换建议在变更前先跑一次确认影响范围。通过理解实例选择规则、组间顺序、单实例处理流程以及rollingUpdate策略的默认值与语义你可以在升级集群时精准控制替换节奏与并发度在服务可用性和更新速度之间找到最合适的平衡点。【免费下载链接】kopsKubernetes Operations (kOps) - Production Grade k8s Installation, Upgrades and Management项目地址: https://gitcode.com/gh_mirrors/kop/kops创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考