- 云原生
- 虚拟化
- 存储
- 后端
- 高可用
【免费下载链接】harvester
Open source hyperconverged infrastructure (HCI) software
本文基于 Harvester 仓库中的增强提案 20240605-enhanced-maintenance-mode.md 展开,完整介绍该功能的设计目标、四种维护模式策略、标注方式与验证方法,并结合当前仓库源码(维护模式控制器、drain 控制器、VM 标签同步与 Webhook 校验)剖析每条策略在维护流程中的真实执行路径,帮助读者在 HCI 集群中实现“按需迁移、按需停机、按需重启”的节点维护操作。
背景:传统维护模式的行为过于“一刀切”
Harvester 节点进入维护模式时,系统会尝试把该节点上的所有 VM 迁移(live-migration)到集群内的其他节点,或者在无法迁移时将所有 VM 强制关机。这一行为在部分场景下并不理想,因此引入了本增强:让用户能够逐 VM 显式定义其在节点维护事件中的行为。
该增强不改变 API 结构,核心是通过为VirtualMachine资源添加一个标签来声明策略:
metadata: labels: harvesterhci.io/maintain-mode-strategy: ShutdownAndRestartAfterDisable标签常量及其合法取值定义在 pkg/util/constants.go:
LabelMaintainModeStrategy = prefix + "/maintain-mode-strategy" // 即 harvesterhci.io/maintain-mode-strategy AnnotationMaintainModeStrategyNodeName = prefix + "/maintain-mode-strategy-node-name" MaintainModeStrategyMigrate = "Migrate" MaintainModeStrategyShutdownAndRestartAfterEnable = "ShutdownAndRestartAfterEnable" MaintainModeStrategyShutdownAndRestartAfterDisable = "ShutdownAndRestartAfterDisable" MaintainModeStrategyShutdown = "Shutdown" MaintainModeStrategyDefault = MaintainModeStrategyMigrate四种维护模式策略
提案定义了四种策略,语义与源码常量一一对应:
| 策略值 | 行为 | 典型场景 |
|---|---|---|
Migrate(默认) | VM 被实时迁移到集群内其他节点。这是增强前维护模式的既有行为,也是默认行为 | 大多数无状态或支持热迁移的负载 |
ShutdownAndRestartAfterEnable | 维护模式启用时关机,等节点成功进入维护模式后在另一节点上重启 | VM 换节点后需要执行停机/开机脚本做初始化动作 |
ShutdownAndRestartAfterDisable | 维护模式启用时关机,禁用维护模式后在原节点上重启 | VM 必须绑定在原节点上运行 |
Shutdown | 维护模式启用时关机,不再重启,保持关闭状态 | 维护期间明确不需要运行的 VM |
其中Migrate与三个关机类策略的区分逻辑集中在源码中:MaintenanceModeStrategyShutdownValues列表(见 pkg/util/constants.go)收录了三个关机类取值,drain 阶段用这个列表筛选出“必须先关机”的 VM;而Shutdown与另外两个的区别在于是否会被后续流程自动重启——drain 控制器只对ShutdownAndRestartAfterEnable和ShutdownAndRestartAfterDisable两种打“待重启”注解,Shutdown策略的 VM 关机后不会被打注解,也永远不会被自动拉起(对应测试用例 3 中“注解不应存在”的断言)。
典型用户场景(来自提案)
- 只关特定 VM 且不重启:将节点放入维护模式时只希望某些 VM 关机,且维护结束后仍保持关闭 —— 使用
Shutdown; - 关机后在新节点重启:某些 VM 换节点后必须执行停机/启动脚本(通过关机脚本和开机自启保证)—— 使用
ShutdownAndRestartAfterEnable; - 关机后回原节点重启:某些 VM 维护结束后必须回到原节点 —— 使用
ShutdownAndRestartAfterDisable。
Force(强制)复选框的覆盖语义
如果在维护模式对话框中勾选了Force,则所有 VM 已配置的maintain-mode-strategy策略都被覆盖:VM 一律被强制关机且保持关闭状态,无论其策略为何值。对于没有该标签的其他 VM,则沿用原有的强制维护行为。
从源码看,这一语义由 nodedrain 控制器 中的forced分支实现:当请求携带强制标记时,FindNonMigratableVMS找出的所有不可迁移 VM 会并入关机清单,与策略类 VM 一起被findAndStopVM统一关机;而RestartMaintenanceModeVMsOnNode的“待重启”注解只有在非强制流程下才会写入,因此强制关机后的 VM 不会被自动恢复。
标注方式:UI 与 kubectl
用户可以通过 Harvester UI 为任意 VM 添加harvesterhci.io/maintain-mode-strategy标签(Edit YAML后在metadata.labels下添加),也可以使用kubectl通过标签过滤器一次性为多个 VM 打标签:
# 将某命名空间中匹配标签选择器的所有 VM 标记为维护时关机且维护结束后重启 kubectl label -n default virtualmachines kubevirt \ harvesterhci.io/maintain-mode-strategy=ShutdownAndRestartAfterDisable --overwrite # 批量查看已配置策略的 VM kubectl get vms -l harvesterhci.io/maintain-mode-strategy --show-labels注意标签取值大小写必须与常量完全一致。Webhook 会对取值做校验:pkg/webhook/resources/virtualmachine/validator.go 中的checkMaintenanceModeStrategyIsValid会拒绝不在MaintenanceModeStrategyValidValues白名单内的值(返回invalid maintenance mode strategy错误);对未打标签或沿用既有非法值的 VM 则仅记录告警日志,行为按默认Migrate处理,以兼容 Rancher 等集成方可能省略该标签的情况。
实现原理:标签如何驱动维护流程
提案的“Implementation Overview”部分列出了四点设计,以下逐条对应到仓库源码,说明其实际执行链路。维护模式整体由节点上的MaintenanceMode条件驱动一个状态机:Validating → Draining → Evacuating → Completed(失败时进入Error),定义见 pkg/util/maintain.go。
1. 策略标签从 VM 同步到 VMI
标签打在VirtualMachine上,但 drain 过滤器工作于 Pod/VMI 层面。为此,VM 控制器维护了一个“额外标签同步列表”,把harvesterhci.io/maintain-mode-strategy从VirtualMachine同步到VirtualMachineInstance:
// A list of labels that are always automatically synchronized with the // VMI in addition to the instance labels. var syncLabelsToVmi = []string{util.LabelMaintainModeStrategy}见 pkg/controller/master/virtualmachine/vm_controller.go。提案中说明,这个列表是特意设计成可扩展的,未来其他特性可以往里追加需要同步的标签。
2. drain 阶段:筛选、关机与跳过迁移
当用户通过 UI 或 API 请求启用维护模式时,动作处理器(pkg/api/node/formatter.go)会先校验该节点没有进行中的维护请求,然后写入 drain 请求(含Force标记)。随后 nodedrain 控制器接管,流程为:
校验(Validating):
validate()调用DrainPossible做前置检查,并调用FindNonMigratableVMS找出不可迁移的 VM(包括 Longhorn 卷仅剩一个健康副本且副本恰在本节点、VMI 带VirtualMachineInstanceIsMigratable=False条件、以及 nodeAffinity/nodeSelector 无法满足的 VM,见 pkg/controller/master/nodedrain/nodedrain_controller.go)。非强制模式下若存在不可迁移 VM,维护会以前置检查失败告终,提示使用 force drain。关机(Draining):drain() 通过
listVMILabelMaintainModeStrategy(L634-L641)按节点列出所有策略取值为三个关机类之一的 VMI,对每一台:- 归入关机清单
shutdownVMs[MaintainModeStrategyKey],随后统一调用findAndStopVM将对应 VM 的Spec.RunStrategy改为RunStrategyHalted完成优雅关机; - 对
ShutdownAndRestartAfterEnable/ShutdownAndRestartAfterDisable两种策略,额外在VM(不是 VMI)上写入注解harvesterhci.io/maintain-mode-strategy-node-name: <节点名>,作为“该 VM 需要在维护流程后续阶段被重启、且重启位置由该节点决定”的跟踪标记。这正是提案中“Introduce the annotation … automatically added and removed by the maintenance mode code”的落点(nodedrain_controller.go L229-L240)。
- 归入关机清单
跳过迁移:随后执行的
kubectl drain使用自定义 Pod 过滤器 maintainModeStrategyFilter:如果 Pod 带有kubevirt.io/vmName标签,且其harvesterhci.io/maintain-mode-strategy取值属于关机类,则跳过对该 Pod 的驱逐/迁移,并打印提案中所示的日志:time="2024-03-19T12:45:24Z" level=info msg="migration of pod owned by VM test01 is skipped because of the label harvesterhci.io/maintain-mode-strategy" namespace=default pod_name=virt-launcher-test01-gfgjp没有标签的其他 VM 的 virt-launcher Pod 则照常被 drain 触发实时迁移,对应 UI 中显示的
Migrating状态。
3. 两个“重启时机”的执行位置
提案的两种重启语义由两处不同代码触发,都以“策略标签 + 节点跟踪注解”作为双重筛选条件:
- 维护模式启用后重启(ShutdownAndRestartAfterEnable):maintain-node-controller 的
OnNodeChanged在节点处于Evacuating阶段时轮询(每 10 秒)确认该节点上已无任何 VMI,然后调用RestartMaintenanceModeVMsOnNode(..., ShutdownAndRestartAfterEnable)重启这些 VM,最后把条件推进到Completed(即 UI 上的Maintenance状态)。此时节点已被 cordon,KubeVirt 重新调度 VM 自然会落在其他节点——这解释了测试用例 2 中“VM 重启在了别的节点,且明确不是被迁移的”。 - 维护模式禁用后重启(ShutdownAndRestartAfterDisable):用户在 Hosts 页面禁用维护模式时,API 动作处理器在解除 cordon、清理 drain 请求之后,调用 disableMaintenanceMode 中的
RestartMaintenanceModeVMsOnNode(..., ShutdownAndRestartAfterDisable),再移除维护条件。VM 被重新调度回记录在注解中的原节点。
两个路径共用的 RestartMaintenanceModeVMsOnNode 函数行为值得注意:它只重启当前处于Halted运行策略的 VM;重启时优先恢复关机前保存在注解中的原运行策略(AnnotationRunStrategy),否则回退为RunStrategyRerunOnFailure;处理完成后删除maintain-mode-strategy-node-name注解,避免重复触发。
4. 节点删除时的注解清理
提案测试用例 5 覆盖了一个边界场景:VM 关机后节点被直接删除。maintain-node-controller 的OnNodeRemoved会遍历所有 VM,删除那些注解maintain-mode-strategy-node-name仍指向已删除节点的资源,防止“悬空”注解在将来同名节点上引发误重启。
验证方法(Test Plan)
提案给出了 5 个完整的手工测试用例,以下完整保留其操作要点,可作为功能验收与自测清单。前提:两台(或更多)VM 运行在同一节点,一台打了策略标签,另一台不打标签(作为对照组,预期走实时迁移)。
用例 1:ShutdownAndRestartAfterDisable
- 为选定 VM 打标签
harvesterhci.io/maintain-mode-strategy: ShutdownAndRestartAfterDisable(UI 的Edit YAML或 kubectl)。 - 确保同节点上还有一台无标签的 VM 在运行,必要时把它迁移过来。
- 记录两台 VM 所在的节点。
- 在
Hosts页对目标节点选择Enable Maintenance Mode动作。 - 一段时间后该节点应显示
Cordon状态。 - 在
Virtual Machines页,带标签的 VM 应显示Stopping状态徽标,另一台 VM 应显示Migrate徽标。 - 关机后,通过
Edit YAML确认该 VM 的注解中存在harvesterhci.io/maintain-mode-strategy-node-name: <NODENAME>。 - 回到
Hosts页,节点应(过一段时间后)显示Maintenance状态徽标。 - 通过节点动作菜单禁用维护模式,一段时间后
Maintenance Mode徽标消失。 - 在
Virtual Machines页,之前被关机的 VM 应已再次启动。
harvester-xxx-xxxPod 的日志中应能看到类似:
time="2024-03-19T12:45:24Z" level=info msg="attempting to place node in maintenance mode" node_name=harvester-node-2 time="2024-03-19T12:45:24Z" level=info msg="force stopping VM" namespace=default virtualmachine_name=test01 time="2024-03-19T12:45:24Z" level=info msg="migration of pod owned by VM test01 is skipped because of the label harvesterhci.io/maintain-mode-strategy" namespace=default pod_name=virt-launcher-test01-gfgjp time="2024-03-19T12:55:37Z" level=info msg="restarting the VM that was shut down in maintenance mode" namespace=default virtualmachine_name=test01用例 2:ShutdownAndRestartAfterEnable
步骤 1–8 同用例 1,仅标签值改为ShutdownAndRestartAfterEnable;关键差异在第 9–11 步:
- 在
Virtual Machines页,被关机的 VM 应在维护模式启用完成(Maintenance徽标出现)后自动重启,并且运行在与之前不同的节点上——注意它是“关机+重启”而非迁移。 - 在
Hosts页禁用维护模式。 - 两台 VM 应继续运行,且所在节点不再发生变化(重启时机已过,禁用维护模式不应再触发调度)。
日志中应出现restarting the VM that was temporary shut down for maintenance mode一类信息(本仓库当前实现中对应 pkg/util/maintain.go 打印的Restarting VM that was shut down for maintenance mode,字段为namespace/name/runStrategy)。
用例 3:Shutdown
- 打标签
harvesterhci.io/maintain-mode-strategy: Shutdown,其余设置同用例 1。 - 启用维护模式后,带标签 VM 显示
Stopping,对照 VM 显示Migrate。 - 关机后通过
Edit YAML确认注解中不存在harvesterhci.io/maintain-mode-strategy-node-name(因为该策略无需重启跟踪)。 - 节点进入
Maintenance状态后,带标签 VM 保持Off,对照 VM 已被迁移到另一节点。 - 禁用维护模式后,带标签 VM仍然保持关闭。
harvester-xxx-xxxPod 日志中不应出现任何restarting the VM that was ... shut down in maintenance mode记录。
用例 4:Force 覆盖策略
- 打标签
ShutdownAndRestartAfterDisable,并保证同节点有一台无标签 VM。 - 在内置 Longhorn UI 的
Volume页将该 VM 的卷副本数降为 1,并删除所有不在待维护节点上的副本——构造“不可迁移”场景。 - 启用维护模式时勾选
Force复选框(注意它会覆盖所有 VM 的维护策略,这正是本用例要验证的)。 - 节点进入
Cordon后,两台 VM 都应显示Stopping,随后都处于Off。 - 禁用维护模式后两台 VM 都应保持关闭。
- 检查被打过
ShutdownAndRestartAfterDisable标签的 VM,注解中不应存在maintain-mode-strategy-node-name。 - 注意由于 Longhorn 副本被删除导致部分 Pod 无法 drain,该节点实际上无法切换到
Maintenance Mode完成态——这正是强制模式的预期表现。 - 日志中不应出现任何自动重启记录。
用例 5:节点删除后注解清理
- 打标签
ShutdownAndRestartAfterDisable,记录 VM 所在节点。 - 启用维护模式,确认 VM 关机且注解中存在
maintain-mode-strategy-node-name: <NODENAME>,节点进入Maintenance状态。 - 删除该节点(可能需要一些时间)。
- 节点删除完成后检查该关机 VM:注解
maintain-mode-strategy-node-name应已被 OnNodeRemoved 逻辑清除。
小结
Harvester 的增强维护模式用“一个标签 + 一个注解”的轻量设计,把节点维护时 VM 的处置方式从二选一(迁移或强制关机)细化为四种可编排的策略:
- 默认
Migrate保持向后兼容; - 三个关机类策略在 drain 前通过
RunStrategyHalted优雅关机,并由 drain 过滤器跳过其 Pod 的迁移; - 两个“RestartAfter…”策略借助系统自动维护的节点跟踪注解,分别在维护模式启用完成时(maintain-node-controller)与维护模式禁用时(node 动作处理器)精准重启,重启后恢复原运行策略;
Force复选框始终拥有最高优先级,可无条件强制关机;- Webhook 校验保证标签取值合法,节点删除时的注解清理避免悬空状态。
相关源码入口汇总:常量定义 pkg/util/constants.go、drain 主流程 pkg/controller/master/nodedrain/nodedrain_controller.go、Pod 迁移过滤 pkg/util/drainhelper/helper.go、标签同步 pkg/controller/master/virtualmachine/vm_controller.go、重启逻辑 pkg/util/maintain.go、策略校验 pkg/webhook/resources/virtualmachine/validator.go。
- 云原生
- 虚拟化
- 存储
- 后端
- 高可用
【免费下载链接】harvester
Open source hyperconverged infrastructure (HCI) software
相关推荐
如何快速实现Android应用前后台检测:5分钟掌握核心技巧
如何快速实现Android应用前后台检测:5分钟掌握核心技巧 在Android应用开发中,准确判断应用是否处于前台运行状态是许多功能实现的基础需求。无论是为了优
Harvester维护模式:安全升级和节点维护的终极方案
Harvester维护模式:安全升级和节点维护的终极方案 Harvester维护模式是超融合基础设施管理中的革命性功能,它让节点维护、系统升级和硬件更换变得前所
云原生虚拟化存储后端高可用ScyllaDB 维护模式(Maintenance Mode)完全指南:入口参数、行为边界与运维实操
ScyllaDB 维护模式(Maintenance Mode)完全指南:入口参数、行为边界与运维实操 ScyllaDB 的维护模式(Maintenance Mo
数据库分布式数据库后端大数据
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考