在裸金属上管理节点

本文档说明如何在裸金属 provider 上部署 worker 节点、对其进行扩缩容、替换特定机器,以及恢复故障节点。节点管理使用通过 MachineDeployment 编排的 Cluster API Machine 资源;provider 会从池中为每个 Machine 绑定一个 MachineInventory,并通过 clean / reprovision 计划驱动主机。

先决条件

WARNING

重要先决条件

  • 控制平面必须已经运行。请参见 创建集群。
  • worker MachineInventoryPool 中可用的 Available inventory 数量必须至少等于目标副本数,再加上 rollout 策略所需的额外余量。
  • 目标 Machine.spec.version 必须是 elemental-image-catalog ConfigMap 中的一个键。
  • 任何 spec.storage.volumes[] 非空的 inventory,都必须先报告 status.storage.phase=Prepared 且 StoragePrepared=True,之后才能计入 Available。在将新注册的、带存储管理的 inventory 添加到 worker 池之前,请先参考在 Bare-Metal 主机上管理数据盘。
INFO

配置指南

在处理本文档中的配置时:

  • 只修改 <> 括号中的值。
  • 将占位符值替换为适合您环境的配置。
  • 除非明确要求,否则保留所有其他默认配置。

概述

构成 worker 节点组的四个资源:

  1. MachineInventoryPool(<cluster-name>-worker-pool)——允许使用的 MachineInventory 名称集合。已在创建集群 → 第 2 步中创建。
  2. BaremetalMachineTemplate(<cluster-name>-worker-template)——指向 worker 池。CAPI 要求每次底层池引用或分配策略发生变化时,都必须替换该模板(新的 metadata.name)。
  3. KubeadmConfigTemplate(<cluster-name>-worker-bootstrap)——用于 kubeadm join 的 cloud-init user-data。裸金属 provider 会在 reprovision 时对该 user-data 进行标准化处理(hostname、provider-id、criSocket);operator 不应预先填充这些字段。
  4. MachineDeployment——控制副本数、版本和 rollout 策略。

受管数据盘不属于 BaremetalMachineTemplate。它们的声明及其保留的数据都属于各自长期存在的 MachineInventory,因此,同一 worker 池中的两个成员可能暴露不同的主机本地数据集,除非 operator 对其存储声明进行标准化并验证。

Cluster API 对模板的约定:运行中的 Machine 集合会保留上一版模板的内存快照,因此直接就地编辑模板不会触发 rollout。worker 升级时会创建新的 BaremetalMachineTemplate,并通过补丁更新 MachineDeployment.spec.template.spec.infrastructureRef.name 指向它。

Worker 节点部署

第 1 步:确认 Worker 池容量

kubectl -n cpaas-system get machineinventorypools.infrastructure.cluster.x-k8s.io <cluster-name>-worker-pool

status.available 必须至少等于目标副本数。如果需要更多容量,请注册更多主机(在这些主机上启动 SeedImage ISO —— 参见创建集群 → 第 1 步),在每个 inventory 仍未分配时准备好任何受管存储,然后将这些主机名称添加到 spec.machineInventories 中。

通过这种方式添加的主机会经历与原始节点相同的首次启动流程,因此必须满足相同的先决条件:包括网络连通性中的网络路径要求,以及主机时间同步中的时钟门限要求。请针对新主机分别确认这两项,而不要假设交付时获得的批准已覆盖其子网和硬件时钟。

第 2 步:配置 Worker BaremetalMachineTemplate

apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: BaremetalMachineTemplate
metadata:
  name: <cluster-name>-worker-template
  namespace: cpaas-system
spec:
  template:
    spec:
      machineInventoryPoolRef:
        name: <cluster-name>-worker-pool

关键参数:

参数类型描述必填
.spec.template.spec.machineInventoryPoolRef.namestringworker 池名称(同一 namespace)。模板创建后即不可变。是

allocationPolicy 预留用于未来扩展。provider 当前将每个池都视为 Ordered——按声明顺序选择第一个 Available inventory。

第 3 步:配置 Bootstrap 模板

imagePullCredentialsVerificationPolicy: NeverVerify 仅从 Kubernetes 1.35 开始才是必需的。使用 Kubernetes 1.34 或更早版本创建 worker 时,请省略此参数。

apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: KubeadmConfigTemplate
metadata:
  name: <cluster-name>-worker-bootstrap
  namespace: cpaas-system
spec:
  template:
    spec:
      users:
        - name: boot
          sudo: ALL=(ALL) NOPASSWD:ALL
          shell: /bin/bash
          sshAuthorizedKeys:
            - "<ssh-authorized-keys>"
      files:
        - path: /etc/kubernetes/patches/kubeletconfiguration0+strategic.json
          owner: "root:root"
          permissions: "0644"
          content: |
            {
              "apiVersion": "kubelet.config.k8s.io/v1beta1",
              "kind": "KubeletConfiguration",
              "imagePullCredentialsVerificationPolicy": "NeverVerify",
              "protectKernelDefaults": true,
              "staticPodPath": null,
              "tlsCertFile": "/etc/kubernetes/pki/kubelet.crt",
              "tlsPrivateKeyFile": "/etc/kubernetes/pki/kubelet.key",
              "streamingConnectionIdleTimeout": "5m",
              "clientCAFile": "/etc/kubernetes/pki/ca.crt"
            }
      joinConfiguration:
        patches:
          directory: /etc/kubernetes/patches

provider 在将 bootstrap user-data 写入 reprovision 计划之前,会对其进行最小化标准化处理,因此请不要在模板中包含以下字段:

  • Hostname / FQDN — 根据 MachineInventoryPool.spec.machineInventories[].hostname 自动设置(若未指定,则使用 inventory 名称)。
  • kubeletExtraArgs.provider-id — 自动设置为 baremetal:///<inventory-name>。
  • nodeRegistration.criSocket — 在未设置时自动设置为 unix:///var/run/containerd/containerd.sock。

标准化后的 user-data 也会写回 bootstrap Secret 的 data["resolved-value"] 中用于调试;原始的 data["value"] 保持不变。

第 4 步:配置 MachineDeployment

apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
  name: <cluster-name>-workers
  namespace: cpaas-system
spec:
  clusterName: <cluster-name>
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 0        # Bare-metal pools cannot over-provision.
      maxUnavailable: 1
  selector:
    matchLabels: {}
  template:
    metadata:
      labels:
        cluster.x-k8s.io/cluster-name: <cluster-name>
        pool.name: <cluster-name>-workers
    spec:
      clusterName: <cluster-name>
      version: <kubernetes-version>
      nodeDrainTimeout: 5m
      nodeVolumeDetachTimeout: 5m
      bootstrap:
        configRef:
          apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
          kind: KubeadmConfigTemplate
          name: <cluster-name>-worker-bootstrap
          namespace: cpaas-system
      infrastructureRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
        kind: BaremetalMachineTemplate
        name: <cluster-name>-worker-template
        namespace: cpaas-system

关键参数:

参数类型描述必填
.spec.clusterNamestring目标集群名称。是
.spec.replicasintworker 节点数量。必须满足 replicas ≤ MachineInventoryPool.status.available + status.allocated(针对 worker 池)。是
.spec.template.spec.versionstringworker 的 Kubernetes 版本。必须是 elemental-image-catalog 中的一个键。可以与控制平面版本不同,但需遵守标准的 kubelet skew 策略。是
.spec.strategy.rollingUpdate.maxSurgeint裸金属不进行超额预配——请保持为 0。provider 没有可先行拉起额外节点的备用物理主机。是
.spec.strategy.rollingUpdate.maxUnavailableint当 maxSurge=0 时必须 > 0。worker 会在此预算内逐个回收替换。是

应用并观察:

kubectl apply -f workers.yaml
kubectl -n cpaas-system get machinedeployments.cluster.x-k8s.io
kubectl -n cpaas-system get baremetalmachines.infrastructure.cluster.x-k8s.io -w
kubectl get nodes -o wide                     # workload cluster

节点管理操作

扩容 Worker 节点

添加 Worker 节点

使用场景:增加集群容量。

先决条件:

  • worker 池具有足够的 Available inventory。否则,请注册额外主机(启动 SeedImage ISO 并确认新的 MachineInventory 对象)、准备任何受管存储,然后将它们追加到 MachineInventoryPool.spec.machineInventories[] 中。

操作步骤:

  1. 检查当前状态

    kubectl -n cpaas-system get machines.cluster.x-k8s.io \
      -l cluster.x-k8s.io/deployment-name=<cluster-name>-workers
    kubectl -n cpaas-system get machineinventorypools.infrastructure.cluster.x-k8s.io \
      <cluster-name>-worker-pool

    确认 MachineInventoryPool.status.available 足以满足计划中的增量。

  2. 在需要更多 inventory 时扩展 worker 池

    kubectl -n cpaas-system edit machineinventorypool <cluster-name>-worker-pool

    追加新的 MachineInventory 名称——它们必须已经完成注册并处于 Ready。带受管卷的 inventory 也必须已 Prepared;否则它们会增加 status.total,但仍不可用于分配。保存并退出,然后确认 status.available 按预期增加。

  3. 扩容 MachineDeployment

    kubectl -n cpaas-system patch machinedeployment <cluster-name>-workers \
      --type='json' -p='[{"op":"replace","path":"/spec/replicas","value":<new-replica-count>}]'
  4. 监控

    kubectl -n cpaas-system get baremetalmachines.infrastructure.cluster.x-k8s.io -w
    kubectl get nodes                # workload cluster

    新的 BaremetalMachine 对象会按 Pending → Allocated → Reprovisioning → Running 推进;随着新节点绑定,池的 available 计数会减少。

缩减 Worker 节点

支持两种策略,其形态与上游 Cluster API 约定一致:

策略适用场景
随机移除可以移除任意节点,例如临时降低容量。
定向移除需要释放特定物理主机(硬件维护、替换、IP 恢复)。
INFO

Inventory 回收

clean 计划会停止 kubelet、停用声明的存储挂载、清理 CRI 工作负载并停止 containerd。它不会擦除受管文件系统、Kubernetes 持久化目录,也不会重置 OS——OS 清理发生在稍后,当相同或不同的 inventory 被选中用于新的 BaremetalMachine 并执行 reprovision 计划时。在此之前,inventory 会回到 Available,并可能再次被分配。删除 Machine 执行的是 Deactivate,而不是存储 Release,因此存储声明、文件系统 UUID 和数据都会保留在该 inventory 上。

随机移除
kubectl -n cpaas-system patch machinedeployment <cluster-name>-workers \
  --type='json' -p='[{"op":"replace","path":"/spec/replicas","value":<new-replica-count>}]'

CAPI 会按其标准顺序选择要删除的机器。每个被选中的 BaremetalMachine 都会进入 Preparing,provider 写入一个 clean 计划,而当该计划报告 Applied 后,inventory 会回到 Available。

定向移除
  1. 识别机器

    kubectl -n cpaas-system get machines.cluster.x-k8s.io \
      -l cluster.x-k8s.io/deployment-name=<cluster-name>-workers
  2. 为目标机器添加注解

    kubectl -n cpaas-system patch machine <machine-name> \
      --type='merge' -p='{"metadata":{"annotations":{"cluster.x-k8s.io/delete-machine":"true"}}}'

    对每台要移除的机器重复此操作。

  3. 按恰好等于已注解机器数量的幅度缩容

    kubectl -n cpaas-system patch machinedeployment <cluster-name>-workers \
      --type='json' -p='[{"op":"replace","path":"/spec/replicas","value":<new-replica-count>}]'

    缩减得更少会使已注解机器保持不变;缩减得更多则会让随机机器也经过 clean 计划。

  4. 验证清理结果

    kubectl -n cpaas-system get baremetalmachines.infrastructure.cluster.x-k8s.io
    kubectl -n cpaas-system get machineinventories.elemental.cattle.io <inventory-name> -o yaml

    释放后的 inventory 必须显示 baremetal.alauda.io/allocation-state=Available,baremetal.alauda.io/owner-* 注解必须被清除,而 pool 注解必须保留。MachineInventory 本身不会被删除。如果它声明了受管卷,则必须回到 status.storage.phase=Prepared 且 StorageActive=False/Inactive;其业务挂载路径必须处于非活动状态,而文件系统 UUID 和数据保持不变。

替换单个故障节点

如果某个 BaremetalMachine 进入 Failed,最安全的恢复方式是删除该故障 Machine。CAPI 会立即创建一个替代的 Machine(因为 replicas 没有变化),而裸金属 provider 会从同一池中选择一个 Available inventory。多数情况下,替代项会是不同的 MachineInventory;provider 不保证刚刚释放的 inventory 会再次被选中。主机本地受管数据不会随替代的 Machine 一起迁移。如果应用必须复用特定 inventory 上的数据,请使用专用池或有意设计顺序的池,并在依赖该数据之前验证 BaremetalMachine.status.machineInventoryRef。

kubectl -n cpaas-system delete machine <machine-name>
kubectl -n cpaas-system get baremetalmachines.infrastructure.cluster.x-k8s.io -w

请单独排查最初的失败原因:读取执行该计划的主机上的 BaremetalMachine.status.conditions、MachineInventory.status.plan.state 以及失败 plan Secret 的 failed-output 键。常见根因记录在下面的常见失败模式部分。

升级 Machine 基础设施

BaremetalMachineTemplate 只包含池引用和分配策略——模板中没有 CPU、memory 或 disk 声明。受管存储的变更需在未分配、非活动的 MachineInventory 上单独进行;请参见在 Bare-Metal 主机上管理数据盘。需要通过替换模板才能生效的基础设施侧变更仅限于:

  • 将 MachineDeployment 迁移到不同的池。
  • 调整分配策略(在新增更多策略后)。

要安全地切换模板:

  1. 创建一个新的 BaremetalMachineTemplate,使用新的 metadata.name 并引用新的池。

  2. 应用新模板。

  3. 为 MachineDeployment 打补丁:

    kubectl -n cpaas-system patch machinedeployment <cluster-name>-workers \
      --type='merge' \
      -p='{"spec":{"template":{"spec":{"infrastructureRef":{"name":"<new-template-name>"}}}}}'
  4. 观察滚动替换完成(maxSurge=0,一次一个节点)。

更新 Bootstrap 模板

KubeadmConfigTemplate 与 BaremetalMachineTemplate 一样,也是不可变模板。就地修改现有模板不会触发现有机器的 rollout;只有新创建的机器才会采用这些变更。

要滚动更新 bootstrap 变更:

  1. 导出现有模板:

    kubectl -n cpaas-system get kubeadmconfigtemplate <cluster-name>-worker-bootstrap -o yaml \
      > new-worker-bootstrap.yaml
  2. 修改 metadata.name,删除服务端生成的字段(resourceVersion、uid、creationTimestamp、managedFields、kubectl.kubernetes.io/last-applied-configuration)以及整个 status,并编辑所需字段。对于 Kubernetes 1.35 或更高版本,请将所需的 kubelet 补丁设置添加到 /etc/kubernetes/patches/kubeletconfiguration0+strategic.json。

  3. 应用新模板:

    kubectl apply -f new-worker-bootstrap.yaml
  4. 为 MachineDeployment 打补丁,使其引用新的模板:

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

    这将触发滚动替换。

升级 Kubernetes 版本

关于裸金属上的 Kubernetes 升级,请参见在裸金属上升级集群。升级路径始终会替换节点——不存在就地 kubeadm upgrade 步骤。


池与 Inventory 可观测性

provider 在每个 MachineInventory 上维护的注解,是判断“这台主机当前被用于什么用途”的权威来源:

注解值含义
baremetal.alauda.io/pool池名称该 inventory 归属哪个池。
baremetal.alauda.io/allocation-stateAvailable / Allocated / Preparing / Reprovisioning / Unavailable从 provider 视角看所处的生命周期阶段。
baremetal.alauda.io/owner-cluster集群名称当前正在使用该 inventory 的集群(释放时清除)。
baremetal.alauda.io/owner-machineMachine 名称所属的 Cluster API Machine。
baremetal.alauda.io/owner-baremetalmachineBaremetalMachine 名称所属的 provider 机器。

每个 inventory 的默认 plan Secret 还会携带 baremetal.alauda.io/plan.type。正常的节点生命周期使用 clean 或 reprovision;inventory 存储协调还会使用 storage-prepare 和 storage-release。在诊断存储问题时,请结合 plan 类型一起查看 MachineInventory.status.storage.conditions 和 .operation。


常见失败模式

场景预期状态需要检查的内容
池不存在BaremetalMachine 保持 Pending,InventoryAllocated=False / Reason=PoolMissingBaremetalMachineTemplate.spec.template.spec.machineInventoryPoolRef.name 和 namespace
池耗尽BaremetalMachine 保持 Pending,InventoryAllocated=False / Reason=PoolExhaustedMachineInventoryPool.status.available;inventory 所有权注解
成员缺失MachineInventoryPool.MembersValid=False / Ready=False池的 status.unavailable、失败条件上的消息
Inventory 未 Ready计入 status.unavailableMachineInventory.status.conditions[Ready]、plan Secret 是否存在
plan Secret 缺失inventory 不具备分配资格MachineInventory.status.plan.secretRef、被引用 Secret 是否存在
存储未准备inventory 不计为 Available;StoragePrepared=FalseMachineInventory.status.storage、status.observedStorage 的新鲜度和内容,以及 storage-prepare 计划输出
Bootstrap Secret 缺失BootstrapReady=False / Reason=BootstrapWaitingMachine.spec.bootstrap.dataSecretName
Catalog 未命中BaremetalMachine 变为 Failed,ImageResolved=False / Reason=ImageCatalogMiss,且不写入 planelemental-image-catalog 的键
Registry 注解缺失ImageResolved=False / Reason=ImageRegistryMissingCluster.metadata.annotations["cpaas.io/registry-address"]
Reprovision 计划失败BaremetalMachine 变为 Failed,inventory 被标记为 Unavailableplan Secret 的 failed-output;主机串口控制台;可达的平台 registry
受管卷激活失败BaremetalMachine 的 StorageReady=False;必需卷的节点就绪被阻塞稳定的设备标识、文件系统 UUID、挂载单元、重启后的最新观测结果,以及 plan 的 failed-output
Clean 或 Deactivate 失败删除被 finalizer 阻塞;inventory 被标记为 Unavailableclean 计划输出、主机挂载点,以及 MachineInventory.status.storage.operation

如需查看完整的 operator 侧状态机参考,请参见Provider 概述 → BaremetalMachine。


下一步