☰
Harvester VM 迁移网络(vm-migration-network)设置详解:从 Setting 校验到 KubeVirt 配置的落地
2026/10/13 8:28:31 网站建设 项目流程
  • 云原生
  • 虚拟化
  • 存储
  • 后端
  • 高可用

【免费下载链接】harvester

Open source hyperconverged infrastructure (HCI) software

项目地址:https://gitcode.com/gh_mirrors/ha/harvester
点击查看免费下载

Harvester 默认使用管理(mgmt)网络承载 VM 实时迁移数据流,而 mgmt 网络同时是控制平面,迁移带宽会与之竞争。本文围绕设计文档 20250519-vm-migration-network 展开,讲清vm-migration-network设置的设计动机、配置结构、Webhook 校验规则,以及harvester-vm-migration-network-controller如何生成 Network-Attachment-Definition(NAD)并更新 KubeVirt 配置,使迁移流量可以切换到独立的集群网络上。

背景与设计目标

设计文档的 Summary 开宗明义:当前 Harvester 只支持通过 mgmt 网络进行 VM 迁移,而 mgmt 网络承担控制平面职责,VM 迁移本质是数据搬运,理应允许用户将其放到另一张网络上。该增强源于 issue #5848。

文档列出的 Goals(目标):

  • 用户可以为 VM 迁移选择一张集群网络(cluster network);
  • 用户可以为 VM 迁移指定 VLAN ID;
  • 用户可以为 VM 迁移指定 CIDR 地址段;
  • 用户可以在 CIDR 中排除部分 IP。

Non-goals(明确不做的事):

  • 不负责搭建集群网络,用户须先自行配置好集群网络;
  • 不负责检测集群网络配置是否正确、连通性是否良好;
  • 不在安装阶段配置 VM 迁移网络。

两个典型使用故事:

  1. 切换网络:用户建好集群网络后,在设置中填入集群网络名、VLAN ID、CIDR 与排除 IP;控制器据此创建 NAD 并更新到 KubeVirt 配置。
  2. 回滚到 mgmt 网络:用户把设置恢复为默认(空值);控制器会清理 KubeVirt 配置中的迁移网络字段并删除 NAD。

完整的用户体验链路为:

  1. 用户配置带网口的集群网络;
  2. 用户在设置中提供集群网络名、VLAN ID、CIDR 与排除 IP;
  3. 控制器基于设置创建 NAD 并更新 KubeVirt 配置;
  4. KubeVirt 自动重启 virt-controller 与 virt-handler Pod;
  5. 重启期间无法迁移 VM,API server 会拒绝迁移请求;
  6. 待 KubeVirt 的Available条件 reason 为AllComponentsReady后,用户即可在迁移网络上迁移 VM。

对应的 API 层面改动有两处:新增设置vm-migration-network;在 migrate API 中加入 KubeVirt 状态检查。

设置结构:一段 JSON 描述迁移网络

vm-migration-network在 settings.go 中注册,默认值为空字符串——空值即表示"回退到 mgmt 网络"这一 Non-goal 之外的默认行为:

VMMigrationNetwork = NewSetting(VMMigrationNetworkSettingName, "") ... VMMigrationNetworkSettingName = "vm-migration-network"

设置的取值是一段 JSON。从 common.go 中的network.Config结构体(其 JSON tag 即设置字段名)可以确认字段构成:

字段类型含义
clusterNetworkstring已存在的集群网络名,迁移流量将从该网络的网口进出
vlanuint16该集群网络上的 VLAN ID;不填时默认为 1
rangestringIPv4 CIDR,如192.168.200.0/24,作为 Whereabouts IPAM 的分配范围
exclude[]string从range中排除的具体 IP 列表
rangeV6/rangeV6Start/rangeV6Endstring双栈场景下的 IPv6 外层网络与可分配窗口(需集群以双栈方式安装)

因此一个典型设置值如下(字段名与取值形态由Config结构体推导,示例地址仅作演示):

{ "clusterNetwork": "tr-1", "vlan": 200, "range": "192.168.200.0/24", "exclude": ["192.168.200.1"] }

从源码结构看,当前代码在双栈增强(见 20260528-dual-stack-ipv4-first 相关设计)之后还支持 IPv6:配置了rangeV6时必须同时给出 IPv4 的range(IPv4 Family First 约束),且rangeV6Start/rangeV6End必须与rangeV6一同设置。

Webhook:设置写入前的五类校验与额外防线

设计文档要求 Webhook 完成五项校验:集群网络存在于所有节点、VLAN ID 合法、CIDR 合法、排除 IP 合法且落在 CIDR 内、可用 IP 数不小于节点数(每个节点需要一个 IP)。

这些规则在 validator.go 中注册并实现,validateNetworkHelper(validator.go#L1134-L1206)对三个网络类设置(storage-network、vm-migration-network、rwx-network)共用一套公共校验器,再叠加各设置专属校验器:

commonValidators := []func(*networkutil.Config) error{ v.checkNetworkVlanValid, v.checkNetworkRangeValid, v.checkVlanStatusReady, v.checkVCspansAllNodes, } ... settingValidators := map[string][]func(*networkutil.Config) error{ ... settings.VMMigrationNetworkSettingName: { v.checkVMMigrationNetworkRangeValid, }, ... }

逐条对应关系如下:

  • 集群网络在所有节点上存在且就绪:checkVCspansAllNodes检查该 cluster network 覆盖全部节点,checkVlanStatusReady检查 VLAN 状态就绪;
  • VLAN ID 合法:checkNetworkVlanValid;
  • CIDR 合法:checkNetworkRangeValid;
  • 可用 IP 数 ≥ 节点数:专属校验器checkVMMigrationNetworkRangeValid(validator.go#L2127-L2150)。注释写得很直白:"1 IP per virt-handler"。实现上先统计非 witness 节点数(countNonWitnessNodes会剔除带node-role.harvesterhci.io/witness标签的节点),再经GetUsableIPAddressesCount计算扣除排除项后可分配地址数,不足则报allocatable IP address range is < N, allocate sufficient range。

在文档设计之外,当前代码还叠加了三道防线,实际运维时值得注意:

  1. 迁移进行中禁止改设置:validateVMMigrationNetwork(validator.go#L1516-L1560)会列出所有 VirtualMachineInstanceMigration,只要存在未完成且未删除中的迁移,就返回There is a VM Migration in progress, please wait until it is completed before updating the VMMigrationNetwork setting,避免切换网络时打断在途迁移;
  2. 设置不可删除:validateDeleteVMMigrationNetwork直接返回MethodNotAllowed,只能清空值来"回滚",与 Story 2 的用法一致;
  3. 与其他网络地址段互斥:checkNetworkOverlap会检查迁移网络的可用 IP 是否与storage-network、rwx-network的重叠,防止三张逻辑网络互相借用同一批地址。

此外,rangeV6相关字段只在接受过双栈安装的集群上生效,单栈 IPv4 集群会直接拒绝;只提供 IPv6 而不提供 IPv4range的配置也会因 "IPv4 Family First" 被拒绝。

Controller:hash 防抖、NAD 生成与 KubeVirt 更新

设计文档中的控制器逻辑,在 vm_migration_network.go 中完整落地,控制器名与文档一致:

const ( ControllerName = "harvester-vm-migration-network-controller" VMMigrationNetworkPrefix = "vm-migration-network.settings.harvesterhci.io" VMMigrationNetworkHashLabel = VMMigrationNetworkPrefix + "/hash" VMMigrationNetworkHashAnnotation = VMMigrationNetworkHashLabel VMMigrationNetworkNadAnnotation = VMMigrationNetworkPrefix + "/net-attach-def" KubeVirtName = "kubevirt" VMMigrationNetworkNetAttachDefPrefix = "vm-migration-network-" )

OnVMMigrationNetworkChange(vm_migration_network.go#L95-L158)的执行顺序与设计文档一一对应:

  1. hash 防抖:checkIsSameHashValue比较设置值 SHA-1 与 setting 上vm-migration-network.settings.harvesterhci.io/hash注解,相同则直接返回,避免重复 reconcile;
  2. 空值回滚:setting.Value为空时,把 KubeVirt 的spec.configuration.migrationConfiguration.network置 nil 并删除旧 NAD;
  3. 非空配置:findOrCreateNad按 hash 标签在harvester-system命名空间查找既有 NAD,找不到则用vm-migration-network-前缀创建新 NAD,再把 NAD 名写入 KubeVirt 的migrationConfiguration.network字段;
  4. 清理旧 NAD:若 KubeVirt 上指向的旧 NAD 与新 NAD 不同(记录在vm-migration-network.settings.harvesterhci.io/net-attach-def注解),调用removeOldNad删除;
  5. 收尾:setConfiguredCondition更新 hash 与 NAD 注解,并把 setting 的Configuredcondition 置为 True(中途失败则分别记录GetKubeVirtError、CreateNadError、UpdateKubeVirtError、RemoveOldNadError等 reason,便于排障)。

NAD 的内容由CreateBridgeConfig(common.go#L64-L101)从设置值渲染成标准 bridge CNI 配置:

  • cniVersion: 0.3.1,type: bridge,promiscMode: true;
  • 桥名固定为<clusterNetwork>-br(BridgeSuffix = "-br");
  • vlan缺省填DefaultPVID = 1;
  • IPAM 使用whereabouts,IPv4 场景写扁平的range/exclude;双栈场景改用 Whereabouts 0.9.3+ 的ipRanges列表格式,同时写入 IPv4 与 IPv6 条目,IPv6 条目携带range_start/range_end。

KubeVirt 侧的对接点是spec.configuration.migrationConfiguration.network——这正是 KubeVirt 官方文档 "Using a different network for migrations" 描述的能力,设计文档 Note 一节引用的就是该文档。

另外两个相关实现细节:

  • 设置注册入口在 Register 中通过settings.OnChange(ctx, ControllerName, ...)监听 setting 变更;
  • 与kubevirt-migration设置的分工:kubevirt_migration.go 中的syncKubeVirtMigration负责把kubevirt-migration(默认如{"parallelOutboundMigrationsPerNode":2,"parallelMigrationsPerCluster":5,"allowAutoConverge":false,...})同步到 KubeVirt,但它刻意保留network字段不覆盖(注释写明 "The network field is handled by 'vm-migration-network' setting"),两个设置各管一摊、互不踩踏。

API 侧的迁移闸门

设计文档第 5、6 步——KubeVirt 重启期间拒绝迁移、AllComponentsReady后才放行——在 handler.go 的migrate动作中实现(handler.go#L584-L599):

kubevirt, err := h.kubevirtCache.Get(util.HarvesterSystemNamespaceName, util.KubeVirtObjectName) ... for _, condition := range kubevirt.Status.Conditions { if condition.Type == kubevirtv1.KubeVirtConditionAvailable { if condition.Reason == kubevirtutil.ConditionReasonDeploymentReady { isKubeVirtReady = condition.Status == corev1.ConditionTrue } break } } if !isKubeVirtReady { return errors.New("KubeVirt is not ready") }

也就是说,migrate API 在创建VirtualMachineInstanceMigration之前,会检查 KubeVirt 对象的Available条件且 reason 为DeploymentReady(即文档所说的AllComponentsReady)为 True,否则直接返回 "KubeVirt is not ready"。切换迁移网络触发 virt-controller/virt-handler 重启的窗口期内,迁移请求就会被这道闸门挡住。

测试计划与升级策略

设计文档给出的 Test plan(覆盖 webhook 五项校验 + 控制器两个核心动作):

  • webhook 能校验集群网络在所有节点上;
  • webhook 能校验 VLAN ID 合法;
  • webhook 能校验 CIDR 合法;
  • webhook 能校验排除 IP 合法且位于 CIDR 内;
  • webhook 能校验可用 IP 数不小于节点数;
  • 控制器能基于设置创建 NAD 并更新 KubeVirt 配置;
  • 控制器能从 KubeVirt 配置中移除vm-migration-network并删除 NAD。

仓库中对应的主要校验与设置注册代码可分别参考 validator.go 及其同目录测试文件 validator_test.go,以及设置注册表 register.go;安装器侧把vm-migration-network纳入系统设置清单(见 config.go)。

文档中 Upgrade strategy 一节标注为 N/A:该功能无跨版本状态迁移需求,升级路径上不需要特殊处理。

小结

vm-migration-network设置以"空值即默认 mgmt 网络、非空即切换独立网络"的二态设计,把迁移数据面与控制平面解耦。整条链路是:Webhook 先做覆盖、地址、容量、互斥与在途迁移状态校验;控制器用 SHA-1 注解做变更去重,渲染 bridge + Whereabouts 的 NAD,写入 KubeVirt 的migrationConfiguration.network;API server 再用 KubeVirt 的Available/AllComponentsReady条件守住重启窗口期。对使用者而言,只需保证集群网络已就绪、CIDR 至少留出"非 witness 节点数"个可用 IP,即可安全地在两套网络配置之间切换与回滚。

  • 云原生
  • 虚拟化
  • 存储
  • 后端
  • 高可用

【免费下载链接】harvester

Open source hyperconverged infrastructure (HCI) software

项目地址:https://gitcode.com/gh_mirrors/ha/harvester
点击查看免费下载
上一篇:OpenCore Legacy Patcher:让旧Mac焕发新生的终极指南
下一篇:如何彻底告别百度网盘下载等待:终极命令行解决方案指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询