裸金属上的集群升级

本指南说明如何完成裸金属集群升级流程的第 2 阶段。在升级 Kubernetes 之前,请先完成 Upgrading Clusters 中描述的 Distribution Version 升级。

INFO

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

本页面仅涵盖升级中的 Kubernetes 步骤。完整的 ACP 升级流程——包括升级制品同步、通过 CVO 升级 ACP Core、Aligned plugin 升级,以及从 Marketplace 升级 Agnostic plugin——已在 ACP 产品文档中说明。在开始本页的 Kubernetes 步骤之前,请先完成这些步骤:

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

关键注意事项

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

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

运维人员在开始前必须理解两个结构性后果:

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

升级顺序

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

  1. (前提条件) 升级 global 集群上的 ACP 平台。这会将裸金属 provider、elemental-operator 以及相关的 CAPI 组件升级到能够理解新 schema 的版本。只有在管理侧控制器完成滚动并进入 Ready 之后,才触发业务集群升级。
  2. 在业务集群上升级 Distribution Version(Aligned Extensions)。参见 Upgrading Distribution Version
  3. 确认目标 Kubernetes 版本已存在于 elemental-image-catalog 中(并且对于未来任何主机重新注册,匹配的 -iso 仓库可用)。
  4. 将 Kube-OVN 升级到目标 ACP 版本所需的 chart 版本,并等待 AppRelease 达到 Success
  5. 升级 control plane 的 Kubernetes 版本(一次替换一个 control-plane 节点)。
  6. 将 worker 节点升级到目标 Kubernetes 版本(在 maxUnavailable 预算内替换所有 worker 节点)。

Cluster API 会编排滚动替换。

WARNING

跳过步骤 1 存在两种失败模式:旧 provider 会静默忽略新的 schema 字段;或者在滚动过程中切换 controller 镜像会中断 plan secret 状态机。在触碰业务工作负载滚动之前,务必先完成管理侧升级。

Provider 升级期间的存储兼容性

如果某个现有集群是在受管存储引入之前创建的,只要 MachineInventory.spec.storage 仍然缺失或其值为 volumes: [],它仍然兼容。在这种未受管模式下,storage controller 不会执行任何磁盘操作,也不会仅因为 management-side provider 已升级就重写正在运行的 Machine 的 plan。

这种兼容性意味着可以在所有旧主机上就地启用受管存储:

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

有关准备和验证操作步骤,请参见 Manage Data Disks on Bare-Metal Hosts

前提条件

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

  • Distribution Version 升级已完成。
  • 已安装的 Bare Metal provider 版本为 v0.0.1 或更高,并且其 AppRelease 报告的 requested revision 与 installed revision 相同,且 phase=Success
  • control plane 可通过现有的 <control-plane-vip>:<control-plane-port> 访问。
  • 当前所有节点均健康且处于 Ready 状态。
  • 在更改 Kube-OVN 或 reprovision 任何节点之前,请使用 ACP Cluster Enhancer 创建当前的按需 etcd 备份。验证最新的 status.records 条目报告 result: Success,记录其 backupTimestampfileName,并确认对应的备份归档存在,且如果任何 control-plane 节点被 reprovision 或变得不可用时仍可检索。
  • 目标 Kubernetes 版本是 elemental-image-catalog 中的一个 key。如果不是,请在开始前添加它(参见 更新镜像目录)。
  • 已从 OS Support Matrix 中对应的 ACP 行读取目标 kube-ovn (chart) 版本。
  • BaremetalCluster.spec.networkTypekube-ovn;provider 会跳过任何其他值的 Kube-OVN reconciliation。
  • 平台 registry 可从集群中的每台主机访问。
  • KubeadmControlPlane.spec.rolloutStrategy.rollingUpdate.maxSurge = 0MachineDeployment.spec.strategy.rollingUpdate.maxSurge = 0 均已设置——裸金属不会对物理主机进行超额预配。
  • 相关 pool 具备足够容量,能够一次替换一个节点而不低于期望副本数。
  • 每个带有非空存储声明的 inventory 都报告 StoragePrepared=True,并且当前所有已分配的 inventory 都报告 StorageActive=True。在滚动前记录每个 BaremetalMachine.status.machineInventoryRef 以及受管文件系统 UUID,以便在数据未随 Machine 迁移时能够检测到选中了不同的 inventory。
INFO

默认位于 /var/cpaas/backup 下的本地归档可用于恢复;不要求使用 S3 兼容存储。不过,裸金属替换可能会使原始节点本地副本不可用,并且不保证替代的 Machine 会使用同一个 MachineInventory。如果你的恢复设计无法保证在节点替换或故障后仍能访问本地归档,请在开始第 2 阶段之前,将一份节点外副本保存在 S3 兼容存储或其他节点外的安全位置。

更新镜像目录

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

INFO

在大多数升级中,你不需要手动编辑这个 ConfigMap。裸金属 provider plugin 会在 Distribution Version 升级期间重新应用时,用新 distribution 随附的 Kubernetes 版本重新渲染 elemental-image-catalog(第 1 阶段)。你的任务通常只是验证目标版本是否存在(参见下面的验证步骤)。以下两个选项仅在技术支持提供了经批准的带外镜像引用时才需要。

选项 A — 添加 chart override。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 — 直接 patch ConfigMap。 仅当需要将经批准的镜像引用直接固定在目录中时才使用此方式:

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

该 key 必须包含前导 v(例如 v1.34.5)。如果在 CAPI 创建替代 Machine 时该 key 缺失,生成的 BaremetalMachine 会进入 Failed,其 ImageResolved=False / Reason=ImageCatalogMiss,并且不会写入 reprovision 计划——稍后添加该 key 会自动重新触发 reconciliation。

当新版本对应一个新的 Alauda OS 发行版时,请从 Alauda technical support 获取匹配的 base-imagebase-image-iso 引用。裸金属 provider 使用 base-image 进行 reprovision,不会自动重建 SeedImages。在未来进行主机接入之前,请使用受支持的 base-image-iso 引用刷新 MachineRegistration / SeedImage。不要从一个镜像引用推导另一个,也不要用独立构建的镜像替代。

在 control plane 之前升级 Kube-OVN

请遵循 在 control plane 之前升级 Kube-OVN 中的 Bare Metal v0.0.1+ 流程。本档没有更早的受支持 provider 流程。

该通用操作步骤会验证 BaremetalCluster.spec.networkType,更新 CAPI Cluster 注解,让 provider 对完整的 chart 源和规范进行 reconciliation,并检查 chart 名称、requested revision、installed revision、phase 和 conditions。不要手动 patch AppRelease source。

升级 control plane

在继续之前,请完成通用的 在 control plane 之前升级 Kube-OVN 操作步骤,并验证 cni-kube-ovn 已以目标 installed revision 运行且 phase=Success

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

kubectl -n cpaas-system edit kubeadmcontrolplane <cluster-name>-control-plane
  • spec.version ← 目标 Kubernetes 版本(必须与 elemental-image-catalog 的某个 key 匹配)。
  • 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:该 template 只携带 pool 引用,而升级镜像则通过 Machine.spec.version 经由目录解析。只有当你希望将 control plane 移动到不同的 pool 时,才需要新 template。

由于 maxSurge=0,Cluster API 会一次滚动一个 control-plane 节点:

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 计划;受管卷会被停用但不会被擦除,inventory 最终变为 Available,其所有者注解被清除,存储状态回到 Prepared
  3. CAPI 创建一个新的 Machine(使用目标版本);同时创建一个新的 BaremetalMachine
  4. provider 从 control-plane pool 中分配一个 Available 的 inventory,解析新镜像,并写入 reprovision 计划。
  5. 主机为所选 inventory 的已准备卷执行激活暂存,运行 elemental upgrade --system <new-image>,重启,在 kubelet 之前启动所需挂载单元,并 kubeadm join 到存活的 control plane。只有在所需存储处于 active 状态后,BaremetalMachine.status.phase 才会变为 Running

重复步骤 1–5,直到所有 control-plane 节点都已替换完成。

在整个滚动过程中,alive 会持续管理 VIP。随着 control-plane 成员关系变化,裸金属 provider 会重新渲染 alive chart values(peer 列表和 ipvs.ips),并滚动静态 Pod 清单。

升级 worker

将每个 MachineDeployment.spec.template.spec.version patch 为新的 Kubernetes 版本。CAPI 会在 maxUnavailable 预算内替换 worker——裸金属 provider 会复用同一个 worker pool,并使用与 control plane 相同的镜像目录解析逻辑。

对于 Kubernetes 1.35 或更高版本,首先创建一个新的 worker KubeadmConfigTemplate,其中包含所需的 kubelet patch,然后在同一次 MachineDeployment 编辑中,将 spec.template.spec.bootstrap.configRef.name 与版本升级一起更新。下面的命令包含了该引用。对于不需要更改 bootstrap 的较早目标版本,请省略 bootstrap 对象。按照 更新 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

worker 升级相较于 control plane 的可调项更少:MachineDeployment 上没有 clusterConfiguration 块,因此没有 DNS / etcd 标签需要更新。worker 节点上的 Kubernetes 组件版本会跟随新的基础镜像。

对于更早的版本,只有在发行版更改了 cloud-init 或 kubeletExtraArgs 等设置时,才需要变更 bootstrap template。

跨版本升级

Kubernetes control plane 必须按受支持的 minor 版本跳跃进行升级。请使用 Preparing for Cross-Version Upgrades 找出并预先准备每个中间 ACP 行。

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

  1. 确保该行的 Kubernetes 版本存在于 elemental-image-catalog 中。
  2. 将 Kube-OVN 升级到该行的 kube-ovn (chart) 版本,并等待 phase=Success
  3. 将 control plane 升级到该行的 Kubernetes 版本,并等待所有 control-plane 节点都变为 Ready
  4. 将每个 worker MachineDeployment 升级到相同的 Kubernetes 版本,并等待滚动完成后再开始下一次跳跃。

这些版本值始终来自 OS Support Matrix;本指南中的示例并非固定值。裸金属滚动策略已经对节点替换进行了串行化,因此跨版本升级不需要超出同 minor 升级所需的额外 pool 容量。

从失败的 Phase 2 升级中恢复

不要将 Kubernetes minor 降级视为普通回滚:

  1. 如果尚未创建任何目标版本的 control-plane Machine,请使用 在 Stage-1 恢复期间恢复 Kube-OVN 恢复 Kube-OVN,然后恢复之前的 KubeadmControlPlaneMachineDeployment 清单值。
  2. 如果已有目标 minor 的 control-plane machine 加入集群,请停止进一步滚动,并在目标 minor 上向前修复。如果向前修复无法恢复集群健康,请按照 ACP etcd 恢复操作步骤 的说明,使用已验证的升级前备份恢复集群。不要将 Kubernetes、CoreDNS 或 etcd patch 回之前的 minor 版本。

恢复 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-ovnAppRelease 应报告目标 installed revision 且 phase=Success。对于目标集群,所有当前的 BaremetalMachine 都应为 Running,所有 CAPI Machine 都应报告新的 spec.version,并且所有业务 Node 都应为 Ready 且使用新的 kubelet 版本。

对于每个当前的 BaremetalMachine,使用 INVENTORYPLAN_SECRET 列来验证支撑正在运行节点的 inventory 和 plan:

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,每个引用的 plan Secret 都应报告 reprovision。此检查仅适用于当前 BaremetalMachine 对象所引用的 inventory。未参与滚动的备用 inventory 可能没有 reprovision 计划。已完成 clean 计划但未被选中作为替代 Machine 的 inventory 仍可能报告 clean;这两种情况都不意味着滚动尚未完成。

对于包含受管卷的 inventory,还要验证 status.storage.phase=ActiveStoragePrepared=TrueStorageActive=True 以及 BaremetalMachine.status.conditions[StorageReady]=True/AllVolumesReady。将其当前文件系统 UUID 与升级前记录进行比较。不同的 UUID 通常意味着选择了不同的 inventory;不能将其解释为原始数据已迁移。

MachineInventoryPool.status 应满足 available + allocated + preparing + reprovisioning + unavailable = total,且 preparing = reprovisioning = 0。当 pool 包含备用 inventory 时,available 仍可能大于 0;这些 inventory 不属于上面的 plan 验证范围。

故障排查

问题检查内容
Kube-OVN 注解已更改,但 chart 名称、目标 revision 或 installed revision 未收敛确认已安装的 Bare Metal provider 为 v0.0.1 或更高版本,然后检查 AppRelease conditions 和 provider 日志。按照通用的 Kube-OVN 验证 进行;不要手动 patch source。
滚动卡在第一个新节点检查 BaremetalMachine.status.conditions[ImageResolved]——确认新 key 已存在于 elemental-image-catalog 中,并且 Cluster 带有 cpaas.io/registry-address
clean 之后 inventory 未变为可分配检查 MachineInventory.status.storagestatus.observedStorage。在 inventory 重新回到分配集合之前,必须先通过一次新的主机观测确认受管卷已停用且处于 Prepared。
替代 Machine 一直等待 StorageReady验证所选 inventory 是否为预期 inventory、所需 device ID 和文件系统 UUID 是否仍然匹配、所需挂载单元是否处于 active 状态,以及主机是否在重启后产出了新的观测结果。
主机重启后进入从未加入集群的状态MachineInventory.status.plan.stateFailed 为终态);检查主机串口控制台中的 elemental upgrade 错误。验证平台 registry 可从该主机访问。
新的 control-plane 节点 kubeadm join 失败确认 VIP 仍然可达;alive 可能已经丢失锁。检查 kubectl -n kube-system get leaseipvsadm -Ln 以及静态 Pod 中 keepalived 的日志。
worker 滚动在设置了 MachineDeployment.spec.strategy.rollingUpdate.maxSurge > 0 时停滞裸金属 pool 无法接受超额预配。将 maxSurge 设为 0,并提高 maxUnavailable,以便在 pool 内允许更多并行替换。
滚动过程中 pool 容量耗尽MachineInventoryPool.status.available=0,而 allocated 仍包含尚未替换的节点。向 pool 中添加另一个 MachineInventory 以释放队列,或等待正在进行中的 Preparing 节点完成清空。

有关完整的 operator 侧状态机参考(每个 condition reason 和恢复操作),请参见 Provider 概览


其他资源