使用 Descheduler 重新平衡 KubeVirt 虚拟机
Alauda Container Platform Descheduler 插件可以请求驱逐
virt-launcher Pod。KubeVirt 会拦截该请求,并对 VMI 执行实时迁移,而不是停止 VM。请将其用作次要且有界的重新平衡机制。对于发生故障或需要有意维护的节点,请改为遵循
KubeVirt 节点维护。
本页面介绍 ACP ModulePlugin 软件包。该软件包通过自身的资源进行配置——策略 ConfigMap 和 CronJob(或 Deployment)——不会安装 descheduler CRD;也不存在 operator 级别的 API 可用于对其进行配置。
以下操作步骤已在 ACP 4.4(Kubernetes v1.35)以及 KubeVirt
v1.8.4(HCO 1.18.1)上完成验证,使用的 Descheduler 软件包构建版本默认渲染
EvictionsInBackground 功能门(任何新于
v0.36.0-4 的软件包版本)。请从运行中的集群读取已安装的版本;软件包和 KubeVirt 独立演进。
目录
Descheduler 驱逐如何变为实时迁移先决条件软件包渲染功能门VM 使用迁移驱逐策略时支持实时迁移发现插件为什么默认策略永远不会迁移 VM配置有界 VM 再平衡策略验证重新平衡停止并回滚兼容性检查Descheduler 驱逐如何变为实时迁移
- Descheduler 选择一个
virt-launcherPod,并调用 Eviction API。Eviction 对象携带注解requested-by: sigs.k8s.io/descheduler。 - KubeVirt 会在创建 每个
virt-launcherPod 时添加注解descheduler.alpha.kubernetes.io/request-evict-only: "",无论驱逐策略是什么。Descheduler 只会对携带此注解的 Pod 应用其后台驱逐处理。 - KubeVirt webhook
virt-launcher-eviction-interceptor.kubevirt.io根据 VMI 的有效驱逐策略决定后续行为: 如果已设置,则使用 VMI 的spec.evictionStrategy;否则使用KubeVirt资源中的集群默认值(spec.configuration.evictionStrategy,由 HCO 根据HyperConverged.spec.evictionStrategy渲染)。
当 VMI 被标记为待疏散时,KubeVirt 会在 VMI 上设置
status.evacuationNodeName,向 VMI 添加注解
kubevirt.io/eviction-source: descheduler,并创建一个名为
kubevirt-evacuation-<suffix> 的 VirtualMachineInstanceMigration,其带有注解
kubevirt.io/evacuationMigration: <source-node>。迁移处于等待状态期间,KubeVirt 还会使用
descheduler.alpha.kubernetes.io/eviction-in-progress: "true" 对源 Pod 添加注解。迁移完成后,
virt-handler 会移除 eviction-source 并清除
evacuationNodeName,因此这两个标记仅在疏散进行期间可见。
启用 EvictionsInBackground=true 后,Descheduler 会将该特定的 429 视为成功的驱逐请求:它会记录 Eviction in background assumed,将该请求计入 maxNoOfPodsToEvict*,并记住该 Pod。在下一周期中,它会看到 eviction-in-progress 注解,并记录
Eviction in background already requested (ignoring),而不是再次发起请求。没有该功能门时,429 会被记录为驱逐失败,并且同一个 launcher 可能在每个周期再次被提名。
当 VM 的有效策略为 None,或为
LiveMigrateIfPossible 且 VMI 不可迁移时,Descheduler 驱逐其 launcher 后,VM 会重启而不会迁移。在允许驱逐 VM launcher 之前,请验证其策略。
先决条件
软件包渲染功能门
当前软件包会根据自身的 values 渲染功能门;无需管理员配置。在运行 VM 的业务集群上验证:
预期输出:
如果缺少该参数,说明已安装的软件包早于此变更:
升级 Alauda Container Platform Descheduler 插件。不要手动编辑生成的
CronJob 或 Deployment。要查看下文使用的 Eviction in background ... 日志行,还需要
--v=3 详细程度。
VM 使用迁移驱逐策略时支持实时迁移
VM 必须使用可从目标节点访问的共享存储(例如 RWX 块存储),报告
LiveMigratable=True,并将 LiveMigrate 或 LiveMigrateIfPossible 作为有效驱逐策略。
ACP 随附的 HCO 示例会设置 spec.evictionStrategy: LiveMigrateIfPossible;在 ACP 上,使用空
spec 创建的 HyperConverged 默认为
None。检查这两个层级:
VMI 值会在创建 VMI 时固定。更改
HyperConverged.spec.evictionStrategy 会立即传播到 KubeVirt 资源,
但只会影响之后创建的 VMI;现有 VMI 会保留其记录的值(例如
None),直到 VM 重启。
发现插件
按照工作负载重新平衡(Descheduler)中的说明,从 管理员 > Marketplace > 集群插件 安装 Alauda Container Platform Descheduler。
ModuleInfo 仅由 global(管理)集群提供;在业务集群上运行
该命令会失败,并显示
the server doesn't have a resource type "moduleinfo"。策略 ConfigMap 和 CronJob 位于业务集群上,位于软件包创建的
descheduler 命名空间中。
在 global 集群上:
在业务集群上:
该策略是原始的 descheduler/v1alpha2 DeschedulerPolicy:使用本页面所述的上游插件和参数进行配置;它没有预定义的 KubeVirt 配置文件或解除调度模式设置。
(LowNodeUtilization.args.evictionLimits.node 是常规的上游字段,可以与顶层
maxNoOfPodsToEvict* 限制结合使用。)
为什么默认策略永远不会迁移 VM
该软件包提供一个 default 配置文件,其 DefaultEvictor 配置为:
PodsWithPVC会保护挂载 PersistentVolumeClaim 的每个 Pod。因此,PVC 支持的 VM 的所有virt-launcherPod 都会被跳过,所以默认策略对 VM 是安全的,但无法对其进行再平衡。PodsWithLocalStorage必须保持禁用:每个virt-launcherPod 都包含多个emptyDir卷(private、public、sockets、ephemeral-disks、container-disks、hotplug-disks,……)。重新启用该保护后,即使没有 PVC,launcher 也无法被驱逐。
要对 VM 进行再平衡,必须在限定为目标 VM 的配置文件中,从 extraEnabled 中移除 PodsWithPVC。
配置有界 VM 再平衡策略
编辑 ConfigMap 是受支持的配置路径。ACP 会将此编辑记录为插件命名空间中的 ResourcePatch(API server 会打印 Warning: The operation is allowed, and ResourcePatch rp-<suffix> is created),因此图表协调不会覆盖此更改。每次 CronJob 运行时都会在启动时读取 ConfigMap;无需重启。
首先备份生成的策略:
以下 data.policy.yaml 用于验证运行。它将 Descheduler 限制为测试中的两个工作节点,仅处理测试 VM 的 launcher,并允许每个周期执行一次迁移:
字段说明:
nodeSelector同时限制被分类的节点和驱逐来源。测试时不要包含 control-plane 节点。DefaultEvictor.args.labelSelector限制候选 Pod。VM 的spec.template.metadata.labels中设置的标签会复制到其virt-launcherPod,因此只需设置 VM 标签即可限定测试范围。LowNodeUtilization.args.evictableNamespaces仅接受exclude。include列表会导致每次运行都因only Exclude namespaces can be set, inclusion is not supported失败,并且 CronJob 会持续重试失败的 Job,直到修复策略。LowNodeUtilization根据资源请求而非使用量对节点进行分类。virt-launcher会请求 VM 内存加上 KubeVirt 开销(对于 512Mi guest,约为 270Mi),以及 100m CPU,除非 VM 设置了 CPU 请求。应从 Descheduler 日志中的分类行(见下文)获取阈值,而不是从kubectl top获取。maxNoOfPodsToEvict*限制(以及设置了LowNodeUtilization.args.evictionLimits.node时的该限制)会统计后台驱逐请求,因此可以限制每个周期的并发迁移数量。
仅通过此安装的策略 ConfigMap 和 CronJob(或 Deployment)更改行为。
验证重新平衡
在至少有两个可调度工作节点的临时命名空间中运行测试:
-
使用范围标签创建测试 VM,等待
LiveMigratable=True, 并记录源节点和 VMI UID: -
创建有界且可逆的不平衡状态(例如,使用首选节点亲和性将测试 VM 放置在 一个工作节点上),使一个工作节点高于
targetThresholds,另一个低于thresholds。 -
等待下一次 CronJob 运行(默认每两分钟一次)并读取其 日志:
成功的请求如下所示:
totalEvicted=0 evictionRequests=1是预期结果:Pod 未被驱逐,而是请求了迁移。 -
观察 KubeVirt 完成迁移:
VirtualMachineInstanceMigration与 Descheduler 请求在同一秒创建, 名称为kubevirt-evacuation-<suffix>。一个 512Mi 的测试 VM 约在十秒内完成预拷贝迁移。 -
仅当 VMI 在不同节点上运行、
VirtualMachineInstanceMigration达到Succeeded且源节点和目标节点符合预期、VMI UID 未改变、 VM 仍处于Running/Ready状态,并且旧 launcher 已终止时,测试才算通过。单独的 Descheduler 日志 行、Eviction API 响应或发生变化的 Pod 名称,都不能作为迁移结果。
Descheduler 会在每次运行时重新评估集群。使用上述限制时,
它每个周期移动一个 VM,直到没有节点低于 thresholds,此时
日志会显示 No node is underutilized, nothing to do here。如果两个节点
持续在两个类别之间来回切换,说明对于工作负载的大小而言阈值过于接近;应扩大
thresholds 与 targetThresholds 之间的差距,
而不是降低驱逐限制。
要在迁移进行期间区分由 Descheduler 触发的迁移和由 drain 触发的迁移,请读取 VMI 注解:
迁移完成后会移除该注解;之后,将 kubevirt-evacuation-* 迁移时间戳与
Descheduler Job 日志中的 Eviction in background assumed 行进行关联。
停止并回滚
如果迁移重复发生或一直处于待处理状态,请暂停 CronJob,然后移除策略覆盖。删除 ResourcePatch 会在几秒内恢复由 chart 管理的策略;或者重新应用备份文件。
在重新启用 CronJob 之前,请等待所有 VirtualMachineInstanceMigration 对象达到 Succeeded
或 Failed。
兼容性检查
作为本页面依据的 HCO 兼容性套件会在安装可选插件后断言以下内容:
- 存在带标签的 Descheduler ConfigMap,并且
data.policy.yaml可解析为带有 profile 的DeschedulerPolicy; - 使用该策略的 CronJob 或 Deployment 会以
--feature-gates=EvictionsInBackground=true渲染(这是针对 package 的回归检查,而非管理员设置); - 如果提供了独立的
KubeDeschedulerCRD,则会独立检查其 profile 和字段 schema; - KubeVirt HCO 和 VM 驱逐策略路径仍可被发现。
实际的重新平衡结果仍需要一个真实的双工作节点 KubeVirt 集群。 每次运行都记录 ACP release、Kubernetes server version、KubeVirt version、HCO version 和 Descheduler plugin version。如果任何 API 或参数发生变化,请同时更新此页面和 兼容性测试。