裸金属上的集群升级

本指南介绍如何完成裸金属集群升级工作流的第 2 阶段。在升级 Kubernetes 之前,请先完成集群升级中所述的 Distribution Version 升级。

INFO

此页面在完整 ACP 升级流程中的位置

此页面仅介绍升级中的 Kubernetes 步骤。完整的 ACP 升级流程(包括升级制品同步、通过 CVO 升级 ACP Core、Aligned 插件升级,以及从 Marketplace 升级 Agnostic 插件)记录在 ACP 产品文档中。在开始此页面所述的 Kubernetes 步骤之前,请完成以下步骤:

当同一集群运行在物理主机的不可变操作系统上时,请使用此页面,因为裸金属上的 Kubernetes 步骤会使用新的 elemental upgrade 镜像替换每个节点,而不是在原位置升级二进制文件。

关键注意事项

裸金属升级会替换每个节点,而不会在现有操作系统上运行 kubeadm upgrade。其机制如下:

  1. CAPI 根据发布策略删除一个 Machine。
  2. provider 将 clean 计划写入 inventory 的 plan secret。
  3. 主机停止 kubelet,停用所有受管理的数据挂载,清除 CRI 工作负载,停止 containerd,然后返回同一池中的 Available。
  4. CAPI 创建替代的 Machine。
  5. provider 从同一池中选择一个 Available inventory,根据 elemental-image-catalog 解析新的 Kubernetes 版本,并写入 reprovision 计划。
  6. 主机暂存受管理卷的激活,运行 cloud-init clean → elemental upgrade --system <new-image> → 重启。initramfs 清除 Kubernetes 持久状态;受管理的挂载单元会在 kubelet 之前启动,cloud-init 重新执行,并针对新的控制平面运行 kubeadm init / kubeadm join。

操作员在开始之前必须理解以下两个结构性影响:

  • Kubernetes 管理的系统状态不会保留。每次重新置备的 initramfs 清理步骤都会清除 /var/lib/kubelet、/var/lib/containerd、/var/lib/etcd 和 /etc/kubernetes。Storage v2 不允许将这些受保护路径作为受管理的数据挂载点。
  • 已声明的数据卷可以保留在 inventory 上。清理计划会停用这些卷,但不会擦除其文件系统;重新置备会在 kubelet 启动之前激活所选 inventory 中已准备好的卷。这是主机本地保留机制,不是备份或数据迁移机制。
  • 可能不会重新选择同一个 MachineInventory。provider 不保证由 clean 计划释放的 inventory 会重新分配给替代的 Machine;数据不会自动跟随替代节点。替换会在 maxSurge=0(新旧节点不重叠)时以先删除后添加的方式执行,因此池容量只需满足所需的 Replica 数量,发布期间旧节点和新节点不会同时存在。

升级顺序

按以下顺序升级裸金属集群:

  1. 前提条件:升级 global 集群上的 ACP 平台。这会将裸金属 provider、elemental-operator 以及相关 CAPI 组件升级到能够识别新 schema 的版本。只有在管理侧控制器完成发布并变为 Ready 后,才能触发业务集群升级。
  2. 在业务集群上升级 Distribution Version(Aligned Extensions)。请参阅升级 Distribution Version。
  3. 将目标 Alauda OS 镜像对导入平台 registry,并将目标 Kubernetes 版本添加到 elemental-image-catalog。请参阅更新镜像目录。
  4. 将 Kube-OVN 升级到目标 ACP 版本所需的 chart 版本,并等待 AppRelease 达到 Success。
  5. 升级控制平面的 Kubernetes 版本(逐个替换所有控制平面节点)。
  6. 将工作节点升级到目标 Kubernetes 版本(在 maxUnavailable 预算范围内替换所有工作节点)。

Cluster API 负责协调滚动替换。

WARNING

跳过步骤 1 可能导致两种故障:旧版 provider 静默忽略新的 schema 字段;或者控制器镜像在发布过程中切换,从而中断计划 secret 状态机。务必先完成管理侧升级,再进行业务集群发布。

provider 升级期间的存储兼容性

在引入受管理存储之前创建的现有集群仍保持兼容,前提是 MachineInventory.spec.storage 保持缺失或具有 volumes: []。在此非受管理模式下,存储控制器不会执行磁盘操作,也不会仅因管理侧 provider 已升级而重写正在运行的 Machine 的计划。

此兼容性并不意味着可以在每个旧主机上原地启用受管理存储:

  • 仅在 global 集群上升级 provider 和 elemental-operator,不会在旧版主机操作系统中安装 elemental-storage-observer.service 或早期启动存储集成。
  • 在此类主机上保持 spec.storage 为空。若要稍后启用受管理存储,请使用获批准的具备 observer 功能的镜像重新安装、重置或替换主机,然后在 inventory 未分配时验证 observer 服务和新的 status.observedStorage 报告。
  • 只有在满足这些主机镜像前提条件后,才能配置和 Prepare 受管理卷。不要向正在运行或已分配的 inventory 添加非空存储声明。

有关准备和验证操作步骤,请参阅管理裸金属主机上的数据磁盘。

前提条件

开始前,请确保满足以下所有前提条件:

  • Distribution Version 升级已完成。
  • 已安装的 Bare Metal provider 为 v1.0.0 或更高版本,并且其 AppRelease 报告相同的请求修订版本和已安装修订版本,以及 phase=Success。
  • 控制平面可通过现有的 <control-plane-vip>:<control-plane-port> 访问。
  • 所有当前节点均运行正常且为 Ready。
  • 在更改 Kube-OVN 或重新置备任何节点之前,使用 ACP Cluster Enhancer 创建最新的按需 etcd 备份。确认最新的 status.records 条目报告 result: Success,记录其 backupTimestamp 和 fileName,并确认相应的备份存档存在;如果任何控制平面节点被重新置备或变得不可用,该存档仍可检索。
  • 目标 Kubernetes 版本是 elemental-image-catalog 中的一个键。如果不是,请在开始前添加该版本(参阅更新镜像目录)。
  • 已从 OS Support Matrix 中匹配的 ACP 行读取目标 kube-ovn(chart) 版本。
  • BaremetalCluster.spec.networkType 为 kube-ovn;对于任何其他值,provider 都会跳过 Kube-OVN reconciliation。
  • 平台 registry 可从集群中的每台主机访问。
  • rollout 可能重新置备的每台主机在其 state 分区上至少有 4 GiB 可用空间,并且没有早期失败升级遗留的部分快照(参阅检查 COS_STATE 可用空间)。
  • KubeadmControlPlane.spec.rolloutStrategy.rollingUpdate.maxSurge = 0 和 MachineDeployment.spec.strategy.rollingUpdate.maxSurge = 0 均已设置 — 裸金属不会过量配置物理主机。
  • 相关池具有足够的容量,可以一次替换一个节点,同时不会低于所需的 Replica 数量。
  • 每个具有非空存储声明的 inventory 均报告 StoragePrepared=True,并且每个当前已分配的 inventory 均报告 StorageActive=True。在 rollout 前记录每个 BaremetalMachine.status.machineInventoryRef 以及受管理文件系统 UUID,以便检测是否选择了不同的 inventory,而不是假定数据随 Machine 一起移动。
INFO

/var/cpaas/backup 下的默认本地存档可用于恢复;不要求使用 S3-compatible 存储。但是,裸金属替换可能导致原始节点本地副本不可用,并且无法保证替换后的 Machine 使用相同的 MachineInventory。如果您的恢复设计无法保证在节点替换或故障后访问本地存档,请在开始第 2 阶段之前,将节点外副本保存在 S3-compatible 存储或节点外的其他安全位置。

检查 COS_STATE 可用空间

每次重新置备都会在 COS_STATE 上创建新的快照,并将完整的目标系统写入其中。由于升级目标是不同的镜像,因此该分区必须同时容纳两个彼此无关、大小约为 3.5 GiB 的系统。空间不足的主机会在中途因 no space left on device 而失败,并且失败尝试会留下部分快照 — 因此,每次自动重试开始时的可用空间都会比上一次更少。在 8 GiB 分区上重试三次就足以将其完全填满。

在开始 rollout 前检查此项。检查受影响池中的每台主机,而不仅是预计会被替换的主机:clean 计划释放的 inventory 不保证会被再次选中,因此任何 Available inventory 都可能接收新镜像。

在每台主机上:

# Free space on COS_STATE. Use btrfs, not df — df does not account for snapshot extents.
btrfs filesystem usage -h /

# Retained snapshots
snapper --no-dbus list

Free (estimated) 必须至少为 4 GiB — 目标系统约为 3.5 GiB,此外还需要为复制期间写入的 btrfs 元数据预留空间。如果低于此值,请在开始 rollout 前释放空间。

移除失败升级遗留的部分快照

Elemental 在向快照写入时会将其标记为 update-in-progress=yes,并仅在升级完成后清除该标记。仍带有该标记、且既不是默认快照也不是正在运行的快照的快照,就是失败尝试的残留,可以安全删除。

使用以下三个用于判断的列列出快照:

snapper --no-dbus --csvout list --columns number,default,active,userdata

仅当以下三个条件全部满足时,才删除快照 — default 为 no,active 为 no,并且 userdata 列包含 update-in-progress=yes:

snapper --no-dbus delete --sync <number>
WARNING

切勿删除 default 或 active 列为 yes 的快照。前者是主机下一次将要启动的快照,后者是主机当前正在运行的快照;删除其中任何一个都会导致主机无法启动。

之后重新运行 btrfs filesystem usage -h /,确认空间确实已回收。在已经达到 100% 使用率的分区上,snapper delete 本身可能会因缺少元数据空间而失败 — 这与导致部分快照不断累积的情况相同。如果删除失败,应将该主机视为需要重新安装,而不是针对它重试 rollout。

如果删除所有部分快照后主机仍少于 4 GiB 可用空间,则分区本身过小。在应用 COS_STATE 大小调整 之前安装的主机会使用 8 GiB 的 COS_STATE,并且分区大小在安装时固定 — 扩大分区需要重新安装。请单独规划此项;不要针对这类主机开始 rollout 并期望其成功。

更新镜像目录

elemental-image-catalog 是向裸金属 provider 引入 Kubernetes 版本的资源。

INFO

裸金属 provider plugin 不包含任何 Kubernetes 版本或 OS 镜像。其 chart 创建的 elemental-image-catalog 不包含任何条目,升级 plugin 或 Distribution Version 都不会添加条目。每个新的 Kubernetes 版本都必须在其 OS 镜像已添加到平台 registry 后显式添加。

首先,按照导入 OS 镜像并填充镜像目录中的说明,将目标版本的 baremetal-base-image 和 baremetal-base-image-iso 从 Bare Metal OS archive 导入平台 registry。然后使用以下任一选项添加该版本。

选项 A — 添加 chart 覆盖配置。 在 provider.imageCatalog.images 下追加新版本(使用 global.registry.address 作为 registry):

provider:
  imageCatalog:
    images:
      <new-kubernetes-version>:
        repository: tkestack/baremetal-base-image
        tag: <official-image-tag>

重新应用裸金属 provider plugin。该 chart 会重新渲染 ConfigMap;provider 的进程内 watch 会热加载缓存,无需重启。

选项 B — 直接修补 ConfigMap。 当 plugin 安装时未使用 chart 覆盖配置,或对于必须直接固定在目录中的已批准镜像引用时,使用此选项:

kubectl -n cpaas-system patch configmap elemental-image-catalog \
  --type='json' \
  -p='[{"op":"add","path":"/data/<new-kubernetes-version>","value":"<registry-address>/tkestack/baremetal-base-image:<new-tag>"}]'

无论采用哪种方式,继续之前都请进行验证:

kubectl -n cpaas-system get configmap elemental-image-catalog -o yaml

键必须包含开头的 v(例如 v1.34.5)。如果 CAPI 创建替代的 Machine 时缺少该键,则生成的 BaremetalMachine 会进入 Failed,并带有 ImageResolved=False / Reason=ImageCatalogMiss,且不会写入 reprovision 计划。之后添加该键不会重新启动它:BaremetalMachine 会保持 Failed,直到删除该 Machine,并由其 owner 创建新的对象。对于 maxSurge: 0,此时正在替换的节点已经消失,因此请在更改任何版本之前添加该键。

裸金属 provider 使用 base-image 重新置备,不会自动重建 SeedImages。在后续纳管主机之前,请使用从同一 archive 导入的 base-image-iso 刷新 MachineRegistration / SeedImage。不要搭配来自不同 archive 的镜像,也不要替换为独立构建的镜像。

在控制平面之前升级 Kube-OVN

按照在控制平面之前升级 Kube-OVN中的裸金属 v1.0.0+ 流程操作。本文档中没有更早支持裸金属的 provider 流程。

共享操作步骤会验证 BaremetalCluster.spec.networkType,更新 CAPI Cluster 注解,使 provider 协调完整的 chart 源和规范,并检查 chart 名称、请求的修订版本、已安装的修订版本、阶段和条件。不要手动修补 AppRelease 源。

升级控制平面

继续之前,请完成共享的在控制平面之前升级 Kube-OVN操作步骤,并验证 cni-kube-ovn 已处于目标已安装修订版本且具有 phase=Success。

将 KubeadmControlPlane.spec.version 修补为新的 Kubernetes 版本。如果组件镜像标签已固定(DNS、etcd),请在同一次编辑中更新它们:

kubectl -n cpaas-system edit kubeadmcontrolplane <cluster-name>-control-plane
  • spec.version ← 目标 Kubernetes 版本(必须匹配某个 elemental-image-catalog 键)。
  • spec.kubeadmConfigSpec.clusterConfiguration.dns.imageTag ← 新版本对应的 CoreDNS 镜像标签。
  • spec.kubeadmConfigSpec.clusterConfiguration.etcd.local.imageTag ← 新版本对应的 etcd 镜像标签。
  • 当目标版本为 Kubernetes 1.35 或更高版本时,按照适用于 Kubernetes 1.35 的必需 kubelet patch中的说明,在同一次编辑中更新 spec.kubeadmConfigSpec.files 中的 /etc/kubernetes/patches/kubeletconfiguration0+strategic.json。

对于仅升级 Kubernetes 的操作,裸金属 provider 不需要新的 BaremetalMachineTemplate:模板仅包含池引用,升级镜像通过目录从 Machine.spec.version 解析。只有在需要将控制平面迁移到其他池时,才需要新模板。

Cluster API 会一次滚动一个控制平面节点(因为 maxSurge=0):

kubectl -n cpaas-system get kubeadmcontrolplane <cluster-name>-control-plane -w
kubectl -n cpaas-system get baremetalmachines.infrastructure.cluster.x-k8s.io
kubectl -n cpaas-system get machineinventorypools.infrastructure.cluster.x-k8s.io <cluster-name>-control-plane-pool
kubectl get nodes -o wide                                                    # workload cluster

每个节点的预期顺序如下:

  1. CAPI 将一个旧的 Machine 标记为删除;对应的 BaremetalMachine 转为 Preparing。
  2. provider 写入 clean 计划;托管卷会被停用但不会被擦除,清除 owner 注解后,清单最终变为 Available,存储返回到 Prepared。
  3. CAPI 创建新的 Machine(包含目标版本);随后创建新的 BaremetalMachine。
  4. provider 从控制平面池分配 Available 清单,解析新镜像,并写入 reprovision 计划。
  5. 主机为所选清单中已准备好的卷暂存激活,运行 elemental upgrade --system <new-image>,重新启动,在 kubelet 之前启动所需的挂载单元,并对仍存活的控制平面执行 kubeadm join。只有在所需存储处于活动状态后,BaremetalMachine.status.phase 才会变为 Running。

重复步骤 1–5,直到所有控制平面节点都已替换。

在整个发布过程中,alive 会继续管理 VIP。随着控制平面成员发生变化,裸金属 provider 会重新渲染 alive chart 值(包括 peer 列表和 ipvs.ips),并滚动发布静态 Pod 清单。

升级工作节点

将每个 MachineDeployment.spec.template.spec.version 修补到新的 Kubernetes 版本。CAPI 会在 maxUnavailable 预算内替换工作节点——裸金属 provider 会复用与控制平面相同的工作节点池和镜像目录解析逻辑。

对于 Kubernetes 1.35 或更高版本,首先创建一个新的工作节点 KubeadmConfigTemplate,其中包含所需的 kubelet 修补,然后在与版本升级相同的 MachineDeployment 编辑中更新 spec.template.spec.bootstrap.configRef.name。下面的命令包含该引用。对于不需要引导配置更改的较早目标版本,请省略 bootstrap 对象。按照更新引导模板创建不可变模板。

kubectl -n cpaas-system patch machinedeployment <cluster-name>-workers \
  --type='merge' \
  -p='{"spec":{"template":{"spec":{"version":"<new-kubernetes-version>","bootstrap":{"configRef":{"name":"<new-bootstrap-template>"}}}}}}'

监视:

kubectl -n cpaas-system get machinedeployments.cluster.x-k8s.io -w
kubectl -n cpaas-system get baremetalmachines.infrastructure.cluster.x-k8s.io
kubectl get nodes -o wide

工作节点升级所需调整的参数少于控制平面:MachineDeployment 上没有 clusterConfiguration 块,因此无需更新 DNS / etcd 标签。工作节点上的 Kubernetes 组件版本遵循新的基础镜像。

对于较早版本,仅当该版本更改了 cloud-init 或 kubeletExtraArgs 等设置时,才需要更改引导模板。

跨版本升级

Kubernetes 控制平面必须经过受支持的次要版本跃迁。使用准备跨版本升级来识别并预先暂存每个中间 ACP 行。

对于每个中间行,以及最终的目标行:

  1. 导入该行的 OS 镜像对,并将其 Kubernetes 版本添加到 elemental-image-catalog,如更新镜像目录中所述。
  2. 将 Kube-OVN 升级到该行的 kube-ovn(chart) 版本,并等待 phase=Success。
  3. 将控制平面升级到该行的 Kubernetes 版本,并等待每个控制平面节点变为 Ready。
  4. 将每个工作节点 MachineDeployment 升级到相同的 Kubernetes 版本,并等待滚动发布完成后,再开始下一次跃迁。

版本值始终来自 OS Support Matrix,而不是本指南中的固定示例。裸金属滚动发布策略已经将节点替换串行化,因此跨版本升级不需要超出同一小版本升级所需容量之外的额外池容量。

从失败的阶段 2 升级中恢复

不要将 Kubernetes 次要版本降级视为普通回滚:

  1. 如果尚未创建目标版本控制平面 Machine,请使用阶段 1 恢复期间恢复 Kube-OVN恢复 Kube-OVN,然后恢复之前的 KubeadmControlPlane 和 MachineDeployment 清单值。
  2. 如果目标次要版本的控制平面计算机已加入集群,请停止进一步发布,并在目标次要版本上向前修复。如果向前修复无法恢复集群健康状态,请按照 ACP etcd 恢复操作步骤,从经过验证的升级前备份恢复集群。不要将 Kubernetes、CoreDNS 或 etcd 修补回之前的次要版本。

恢复 etcd 是灾难恢复操作,而不是升级前验证步骤。不要仅为验证备份而在健康集群上运行恢复操作步骤。

每个完成 reprovision 计划的主机都已替换其 OS 镜像并清除 Kubernetes 管理的状态。更改版本字段不会重建该节点之前的状态。

验证

发布完成后:

kubectl -n cpaas-system get baremetalmachines.infrastructure.cluster.x-k8s.io \
  -l cluster.x-k8s.io/cluster-name=<cluster-name> \
  -o custom-columns='NAME:.metadata.name,PHASE:.status.phase,INVENTORY:.status.machineInventoryRef.name,PLAN_SECRET:.status.planSecretRef.name'
kubectl -n cpaas-system get machines.cluster.x-k8s.io \
  -l cluster.x-k8s.io/cluster-name=<cluster-name>
kubectl -n cpaas-system get apprelease cni-kube-ovn
kubectl get nodes -o wide

cni-kube-ovn AppRelease 应报告已安装的目标修订版本及 phase=Success。对于目标集群,每个当前的 BaremetalMachine 都应为 Running,每个 CAPI Machine 都应报告新的 spec.version,并且每个业务 Node 都应为 Ready,其版本为新的 kubelet。

对于每个当前的 BaremetalMachine,使用 INVENTORY 和 PLAN_SECRET 列验证支撑运行中节点的清单和计划:

kubectl -n cpaas-system get machineinventories.elemental.cattle.io <inventory-name> \
  -o jsonpath='{.status.plan.state}{" storage="}{.status.storage.phase}{"\n"}'
kubectl -n cpaas-system get secret <plan-secret-name> \
  -o jsonpath='{.metadata.annotations.baremetal\.alauda\.io/plan\.type}{"\n"}'

每个引用的 MachineInventory 都应报告 Applied,每个引用的计划 Secret 都应报告 reprovision。仅对当前 BaremetalMachine 对象引用的清单执行此检查。未参与发布的备用清单可能没有 reprovision 计划。已完成 clean 计划但未被选中用于替换 Machine 的清单可能仍会报告 clean;这两种情况都不表示发布未完成。

对于包含受管卷的清单,还应验证 status.storage.phase=Active、StoragePrepared=True、StorageActive=True 和 BaremetalMachine.status.conditions[StorageReady]=True/AllVolumesReady。将其当前文件系统 UUID 与升级前记录进行比较。不同的 UUID 通常表示选中了不同的清单;不得将其解释为原始数据已迁移。

MachineInventoryPool.status 应在 preparing = reprovisioning = 0 条件下满足 available + allocated + preparing + reprovisioning + unavailable = total。当池中包含备用清单时,available 可能仍大于零;这些清单不在上述计划验证范围内。

故障排查

问题检查内容
Kube-OVN annotation 已更改,但 chart 名称、目标修订版本或已安装修订版本未达成一致确认已安装的裸金属 provider 为 v1.0.0 或更高版本,然后检查 AppRelease 条件和 provider 日志。遵循共享的 Kube-OVN 验证;不要手动修补源对象。
发布卡在第一个新节点上BaremetalMachine.status.conditions[ImageResolved] — 确认新密钥位于 elemental-image-catalog 中,并且 Cluster 携带 cpaas.io/registry-address。
清理后清单未变为可分配状态检查 MachineInventory.status.storage 和 status.observedStorage。在清单可以返回分配集合之前,必须根据主机最新的观察结果确认受管卷处于非活动状态且已 Prepared。
替换 Machine 等待 StorageReady验证选中的清单是否为预期清单,所需设备 ID 和文件系统 UUID 是否仍然匹配,所需挂载单元是否处于活动状态,以及主机是否生成了重启后的最新观察结果。
主机重启后进入始终无法加入集群的状态MachineInventory.status.plan.state(Failed 为终态);检查主机串行控制台中的 elemental upgrade 错误。验证主机是否可以访问平台 registry。
elemental upgrade 失败并显示 no space left on devicestate 分区过小 — 8 GiB 的 COS_STATE 无法同时容纳正在运行的系统和升级到不同镜像所需的内容。每次失败的尝试还会遗留一个部分快照,因此可用空间会持续减少。按照移除失败升级遗留的部分快照中的说明移除这些快照,然后让计划重试。在应用 COS_STATE 大小调整 之前安装的主机需要重新安装才能扩大分区。
新控制平面节点执行 kubeadm join 失败确认 VIP 仍可访问;alive 可能已丢失锁。检查静态 Pod 中的 kubectl -n kube-system get lease、ipvsadm -Ln 和 keepalived 日志。
MachineDeployment.spec.strategy.rollingUpdate.maxSurge > 0 已设置时,worker 发布停滞裸金属池无法遵守超额配置。设置 maxSurge=0,并增大 maxUnavailable,以便为池中的并行替换分配更多预算。
发布过程中池容量耗尽MachineInventoryPool.status.available=0 和 allocated 仍包含尚未替换的节点。向池中添加另一个 MachineInventory 以释放队列,或等待正在进行的 Preparing 节点排空。

有关完整的 operator 侧状态机参考(包括每个条件原因和恢复操作),请参阅 Provider 概览。


其他资源