跳到内容跳到页面导航:上一页 [access key p]/下一页 [access key n]
documentation.suse.com / SUSE Edge 文档 / 产品文档 / 全自动定向网络置备

31 全自动定向网络置备

31.1 简介

定向网络置备是用于自动置备下游群集的功能。如果您有许多下游群集需要置备并希望自动完成该过程,此功能将非常有用。

管理群集(第 29 章 “设置管理群集)会自动部署以下组件:

  • SUSE Linux Enterprise Micro RT(操作系统)。可以根据用例自定义网络、存储、用户和内核参数等配置。

  • RKE2(Kubernetes 群集)。默认的 CNI 插件为 Cilium。根据具体的用例,可以使用 Cilium+Multus 等某些 CNI 插件。

  • Longhorn(存储解决方案)。

  • NeuVector(安全解决方案)。

  • MetalLB 可用作高可用性多节点群集的负载平衡器。

注意
注意

有关 SUSE Linux Enterprise Micro 的详细信息,请参见第 7 章 “SLE Micro。有关 RKE2 的详细信息,请参见第 14 章 “RKE2。有关 Longhorn 的详细信息,请参见第 15 章 “Longhorn。有关 NeuVector 的详细信息,请参见第 16 章 “NeuVector

以下章节介绍了不同的定向网络置备工作流程,以及可添加到置备过程的一些附加功能:

31.2 为联网方案准备下游群集映像

Edge Image Builder(第 9 章 “Edge Image Builder)用于准备下游群集主机上置备的经过修改的 SLEMicro 基础映像。

可以通过 Edge Image Builder 完成大部分配置,但本指南仅介绍设置下游群集所需的最低限度配置。

31.2.1 联网方案的先决条件

31.2.2 联网方案的映像配置

运行 Edge Image Builder 时,将从主机挂载一个目录,因此需要创建一个目录结构来存储用于定义目标映像的配置文件。

├── downstream-cluster-config.yaml
├── base-images/
│   └ SLE-Micro.x86_64-5.5.0-Default-RT-GM.raw
├── network/
|   └ configure-network.sh
└── custom/
    └ scripts/
        └ 01-fix-growfs.sh

31.2.2.1 下游群集映像定义文件

downstream-cluster-config.yaml 文件是下游群集映像的主配置文件。下面是通过 Metal3 进行部署的极简示例:

apiVersion: 1.0
image:
  imageType: RAW
  arch: x86_64
  baseImage: SLE-Micro.x86_64-5.5.0-Default-RT-GM.raw
  outputImageName: eibimage-slemicro55rt-telco.raw
operatingSystem:
  kernelArgs:
    - ignition.platform.id=openstack
    - net.ifnames=1
  systemd:
    disable:
      - rebootmgr
  users:
    - username: root
      encryptedPassword: ${ROOT_PASSWORD}
      sshKeys:
      - ${USERKEY1}

${ROOT_PASSWORD} 是 root 用户的已加密口令,可用于测试/调试。可以使用 openssl passwd-6 PASSWORD 命令生成此口令

对于生产环境,建议使用可添加到 users 块的 SSH 密钥(将该块中的 ${USERKEY1} 替换为实际 SSH 密钥)。

注意
注意

net.ifnames=1 会启用可预测网络接口命名

这与 metal3 chart 的默认配置相匹配,但设置必须与配置的 chart predictableNicNames 值相匹配。

另请注意,ignition.platform.id=openstack 是必需的,如果不指定此参数,在 Metal3 自动化流程中通过 ignition 进行 SLEMicro 配置将会失败。

31.2.2.2 Growfs 脚本

目前,在置备后首次引导时,需要使用一个自定义脚本 (custom/scripts/01-fix-growfs.sh) 来增大文件系统,使之与磁盘大小匹配。01-fix-growfs.sh 脚本包含以下信息:

#!/bin/bash
growfs() {
  mnt="$1"
  dev="$(findmnt --fstab --target ${mnt} --evaluate --real --output SOURCE --noheadings)"
  # /dev/sda3 -> /dev/sda, /dev/nvme0n1p3 -> /dev/nvme0n1
  parent_dev="/dev/$(lsblk --nodeps -rno PKNAME "${dev}")"
  # Last number in the device name: /dev/nvme0n1p42 -> 42
  partnum="$(echo "${dev}" | sed 's/^.*[^0-9]\([0-9]\+\)$/\1/')"
  ret=0
  growpart "$parent_dev" "$partnum" || ret=$?
  [ $ret -eq 0 ] || [ $ret -eq 1 ] || exit 1
  /usr/lib/systemd/systemd-growfs "$mnt"
}
growfs /
注意
注意

使用相同的方法添加您自己的自定义脚本,以便在置备过程中执行。有关详细信息,请参见第 3 章 “使用 Edge Image Builder 配置独立群集

与此解决方法相关的 bug 为 https://bugzilla.suse.com/show_bug.cgi?id=1217430

31.2.2.3 电信工作负载的附加配置

要启用 dpdksr-iovFEC 等电信功能,可能需要提供附加软件包,如以下示例中所示。

apiVersion: 1.0
image:
  imageType: RAW
  arch: x86_64
  baseImage: SLE-Micro.x86_64-5.5.0-Default-RT-GM.raw
  outputImageName: eibimage-slemicro55rt-telco.raw
operatingSystem:
  kernelArgs:
    - ignition.platform.id=openstack
    - net.ifnames=1
  systemd:
    disable:
      - rebootmgr
  users:
    - username: root
      encryptedPassword: ${ROOT_PASSWORD}
      sshKeys:
      - ${user1Key1}
  packages:
    packageList:
      - jq
      - dpdk22
      - dpdk22-tools
      - libdpdk-23
      - pf-bb-config
    additionalRepos:
      - url: https://download.opensuse.org/repositories/isv:/SUSE:/Edge:/Telco/SLEMicro5.5/
    sccRegistrationCode: ${SCC_REGISTRATION_CODE}

其中 ${SCC_REGISTRATION_CODE} 是从 SUSE Customer Center 复制的注册码,软件包列表包含用于电信配置文件的最少量软件包。要使用 pf-bb-config 软件包(用于启用 FEC 功能并与驱动程序绑定),必须包含 additionalRepos 块,以添加 SUSE Edge Telco 储存库。

31.2.2.4 高级网络配置的附加脚本

如果您需要配置静态 IP 或第 31.6 节 “高级网络配置”中所述的更高级网络方案,则需要提供以下附加配置。

network 文件夹中创建以下 configure-network.sh 文件 - 这会在首次引导时使用配置驱动器数据,并使用 NM Configurator 工具来配置主机网络。

#!/bin/bash

set -eux

# Attempt to statically configure a NIC in the case where we find a network_data.json
# In a configuration drive

CONFIG_DRIVE=$(blkid --label config-2 || true)
if [ -z "${CONFIG_DRIVE}" ]; then
  echo "No config-2 device found, skipping network configuration"
  exit 0
fi

mount -o ro $CONFIG_DRIVE /mnt

NETWORK_DATA_FILE="/mnt/openstack/latest/network_data.json"

if [ ! -f "${NETWORK_DATA_FILE}" ]; then
  umount /mnt
  echo "No network_data.json found, skipping network configuration"
  exit 0
fi

DESIRED_HOSTNAME=$(cat /mnt/openstack/latest/meta_data.json | tr ',{}' '\n' | grep '\"metal3-name\"' | sed 's/.*\"metal3-name\": \"\(.*\)\"/\1/')

mkdir -p /tmp/nmc/{desired,generated}
cp ${NETWORK_DATA_FILE} /tmp/nmc/desired/${DESIRED_HOSTNAME}.yaml
umount /mnt

./nmc generate --config-dir /tmp/nmc/desired --output-dir /tmp/nmc/generated
./nmc apply --config-dir /tmp/nmc/generated

31.2.3 映像创建

按照前面的章节准备好目录结构后,运行以下命令来构建映像:

podman run --rm --privileged -it -v $PWD:/eib \
 registry.suse.com/edge/edge-image-builder:1.0.2 \
 build --definition-file downstream-cluster-config.yaml

这会根据上述定义创建名为 eibimage-slemicro55rt-telco.raw 的输出 ISO 映像文件。

然后必须通过 Web 服务器提供输出映像,该服务器可以是根据管理群集文档(注意)启用的媒体服务器容器,也可以是其他某个本地可访问的服务器。在下面的示例中,此服务器是 imagecache.local:8080

31.3 为隔离方案准备下游群集映像

Edge Image Builder(第 9 章 “Edge Image Builder)用于准备下游群集主机上置备的经过修改的 SLEMicro 基础映像。

可以通过 Edge Image Builder 完成大部分配置,但本指南仅介绍为隔离方案设置下游群集所需的最低限度配置。

31.3.1 隔离方案的先决条件

  • 需要安装 PodmanRancher Desktop 等容器运行时,以便能够运行 Edge Image Builder。

  • 必须从 SUSE Customer CenterSUSE 下载页面下载 SLE-Micro.x86_64-5.5.0-Default-RT-GM.raw 基础映像。

  • 如果您要使用 SR-IOV 或任何其他需要容器映像的工作负载,则必须部署并事先配置一个本地专用注册表(设置/未设置 TLS 和/或身份验证)。此注册表用于存储 Helm chart OCI 映像及其他映像。

31.3.2 隔离方案的映像配置

运行 Edge Image Builder 时,将从主机挂载一个目录,因此需要创建一个目录结构来存储用于定义目标映像的配置文件。

  • downstream-cluster-airgap-config.yaml 是映像定义文件,有关更多细节,请参见第 3 章 “使用 Edge Image Builder 配置独立群集

  • 下载的基础映像已经过 xz 压缩,必须使用 unxz 将其解压缩,并将其复制/移动到 base-images 文件夹中。

  • network 文件夹是可选的,有关更多细节,请参见第 31.2.2.4 节 “高级网络配置的附加脚本”

  • custom/scripts 目录包含首次引导时运行的脚本;目前需要使用 01-fix-growfs.sh 脚本来调整部署中的操作系统根分区的大小。对于隔离方案,需要使用 02-airgap.sh 脚本在映像创建过程中将映像复制到正确的位置。

  • custom/files 目录包含映像创建过程中要复制到该映像的 rke2cni 映像。

├── downstream-cluster-airgap-config.yaml
├── base-images/
│   └ SLE-Micro.x86_64-5.5.0-Default-RT-GM.raw
├── network/
|   └ configure-network.sh
└── custom/
    └ files/
    |   └ install.sh
    |   └ rke2-images-cilium.linux-amd64.tar.zst
    |   └ rke2-images-core.linux-amd64.tar.zst
    |   └ rke2-images-multus.linux-amd64.tar.zst
    |   └ rke2-images.linux-amd64.tar.zst
    |   └ rke2.linux-amd64.tar.zst
    |   └ sha256sum-amd64.txt
    └ scripts/
        └ 01-fix-growfs.sh
        └ 02-airgap.sh

31.3.2.1 下游群集映像定义文件

downstream-cluster-airgap-config.yaml 文件是下游群集映像的主配置文件,上一节(第 31.2.2.3 节 “电信工作负载的附加配置”)已介绍其内容。

31.3.2.2 Growfs 脚本

目前,在置备后首次引导时,需要使用一个自定义脚本 (custom/scripts/01-fix-growfs.sh) 来增大文件系统,使之与磁盘大小匹配。01-fix-growfs.sh 脚本包含以下信息:

#!/bin/bash
growfs() {
  mnt="$1"
  dev="$(findmnt --fstab --target ${mnt} --evaluate --real --output SOURCE --noheadings)"
  # /dev/sda3 -> /dev/sda, /dev/nvme0n1p3 -> /dev/nvme0n1
  parent_dev="/dev/$(lsblk --nodeps -rno PKNAME "${dev}")"
  # Last number in the device name: /dev/nvme0n1p42 -> 42
  partnum="$(echo "${dev}" | sed 's/^.*[^0-9]\([0-9]\+\)$/\1/')"
  ret=0
  growpart "$parent_dev" "$partnum" || ret=$?
  [ $ret -eq 0 ] || [ $ret -eq 1 ] || exit 1
  /usr/lib/systemd/systemd-growfs "$mnt"
}
growfs /

31.3.2.3 隔离脚本

在映像创建过程中,需要使用以下脚本 (custom/scripts/02-airgap.sh) 将映像复制到正确的位置:

#!/bin/bash

# create the folder to extract the artifacts there
mkdir -p /opt/rke2-artifacts
mkdir -p /var/lib/rancher/rke2/agent/images

# copy the artifacts
cp install.sh /opt/
cp rke2-images*.tar.zst rke2.linux-amd64.tar.gz sha256sum-amd64.txt /opt/rke2-artifacts/

31.3.2.4 隔离方案的自定义文件

custom/files 目录包含映像创建过程中要复制到该映像的 rke2cni 映像。为了轻松生成映像,请使用以下脚本此处的映像列表在本地准备这些映像,以生成需要包含在 custom/files 中的项目。另外,可以从此处下载最新的 rke2-install 脚本。

$ ./edge-save-rke2-images.sh -o custom/files -l ~/edge-release-rke2-images.txt

下载映像后,目录结构应如下所示:

└── custom/
    └ files/
        └ install.sh
        └ rke2-images-cilium.linux-amd64.tar.zst
        └ rke2-images-core.linux-amd64.tar.zst
        └ rke2-images-multus.linux-amd64.tar.zst
        └ rke2-images.linux-amd64.tar.zst
        └ rke2.linux-amd64.tar.zst
        └ sha256sum-amd64.txt

31.3.2.5 预加载包含隔离方案和 SR-IOV 所需映像的专用注册表(可选)

如果您要在隔离方案中使用 SR-IOV 或要使用任何其他工作负载映像,必须按照以下步骤预加载包含这些映像的本地专用注册表:

  • 下载、提取 helm-chart OCI 映像并将其推送到专用注册表

  • 下载、提取所需的其余映像并将其推送到专用注册表

可使用以下脚本下载、提取映像并将其推送到专用注册表。本节将通过一个示例来说明如何预加载 SR-IOV 映像,但您也可以使用相同的方法来预加载任何其他自定义映像:

  1. 预加载 SR-IOV 的 helm-chart OCI 映像:

    1. 必须创建一个包含所需 helm-chart OCI 映像的列表:

      $ cat > edge-release-helm-oci-artifacts.txt <<EOF
      edge/sriov-network-operator-chart:1.2.2
      edge/sriov-crd-chart:1.2.2
      EOF
    2. 使用以下脚本和上面创建的列表生成本地 tarball 文件:

      $ ./edge-save-oci-artefacts.sh -al ./edge-release-helm-oci-artifacts.txt -s registry.suse.com
      Pulled: registry.suse.com/edge/sriov-network-operator-chart:1.2.2
      Pulled: registry.suse.com/edge/sriov-crd-chart:1.2.2
      a edge-release-oci-tgz-20240705
      a edge-release-oci-tgz-20240705/sriov-network-operator-chart-1.2.2.tgz
      a edge-release-oci-tgz-20240705/sriov-crd-chart-1.2.2.tgz
    3. 使用以下脚本将 tarball 文件上载到专用注册表(例如 myregistry:5000),以预加载包含上一步骤中下载的 helm chart OCI 映像的注册表:

      $ tar zxvf edge-release-oci-tgz-20240705.tgz
      $ ./edge-load-oci-artefacts.sh -ad edge-release-oci-tgz-20240705 -r myregistry:5000
  2. 预加载 SR-IOV 所需的其余映像:

    1. 在这种情况下,必须包含电信工作负载的 sr-iov 容器映像(例如,作为参考,您可以从 helm-chart 值获取这些映像)

      $ cat > edge-release-images.txt <<EOF
      rancher/hardened-sriov-network-operator:v1.2.0-build20240327
      rancher/rancher/hardened-sriov-network-config-daemon:v1.2.0-build20240327
      rancher/hardened-sriov-cni:v1.2.0-build20240327
      rancher/hardened-ib-sriov-cni:v1.2.0-build20240327
      rancher/hardened-sriov-network-device-plugin:v1.2.0-build20240327
      rancher/hardened-sriov-network-resources-injector:v1.2.0-build20240327
      rancher/hardened-sriov-network-webhook:v1.2.0-build20240327
      EOF
    2. 必须使用以下脚本和上面创建的列表,在本地生成包含所需映像的 tarball 文件:

      $ ./edge-save-images.sh -al ./edge-release-images.txt -s registry.suse.com
      Pulled: registry.suse.com/rancher/hardened-sriov-network-operator:v1.2.0-build20240327
      Pulled: registry.suse.com/rancher/rancher/hardened-sriov-network-config-daemon:v1.2.0-build20240327
      Pulled: registry.suse.com/rancher/hardened-sriov-cni:v1.2.0-build20240327
      Pulled: registry.suse.com/rancher/hardened-ib-sriov-cni:v1.2.0-build20240327
      Pulled: registry.suse.com/rancher/hardened-sriov-network-device-plugin:v1.2.0-build20240327
      Pulled: registry.suse.com/rancher/hardened-sriov-network-resources-injector:v1.2.0-build20240327
      Pulled: registry.suse.com/rancher/hardened-sriov-network-webhook:v1.2.0-build20240327
      a edge-release-images-tgz-20240705
      a edge-release-images-tgz-20240705/hardened-sriov-network-operator-v1.2.0-build20240327.tar.gz
      a edge-release-images-tgz-20240705/hardened-sriov-network-config-daemon-v1.2.0-build20240327.tar.gz
      a edge-release-images-tgz-20240705/hardened-sriov-cni-v1.2.0-build20240327.tar.gz
      a edge-release-images-tgz-20240705/hardened-ib-sriov-cni-v1.2.0-build20240327.tar.gz
      a edge-release-images-tgz-20240705/hardened-sriov-network-device-plugin-v1.2.0-build20240327.tar.gz
      a edge-release-images-tgz-20240705/hardened-sriov-network-resources-injector-v1.2.0-build20240327.tar.gz
      a edge-release-images-tgz-20240705/hardened-sriov-network-webhook-v1.2.0-build20240327.tar.gz
    3. 使用以下脚本将 tarball 文件上载到专用注册表(例如 myregistry:5000),以预加载包含上一步骤中下载的映像的专用注册表:

      $ tar zxvf edge-release-images-tgz-20240705.tgz
      $ ./edge-load-images.sh -ad edge-release-images-tgz-20240705 -r myregistry:5000

31.3.3 为隔离方案创建映像

按照前面的章节准备好目录结构后,运行以下命令来构建映像:

podman run --rm --privileged -it -v $PWD:/eib \
 registry.suse.com/edge/edge-image-builder:1.0.2 \
 build --definition-file downstream-cluster-airgap-config.yaml

这会根据上述定义创建名为 eibimage-slemicro55rt-telco.raw 的输出 ISO 映像文件。

然后必须通过 Web 服务器提供输出映像,该服务器可以是根据管理群集文档(注意)启用的媒体服务器容器,也可以是其他某个本地可访问的服务器。在下面的示例中,此服务器是 imagecache.local:8080

31.4 使用定向网络置备来置备下游群集(单节点)

本节介绍用于通过定向网络置备自动置备单节点下游群集的工作流程。这是自动置备下游群集的最简单方法。

要求

工作流程

下图显示了用于通过定向网络置备自动置备单节点下游群集的工作流程:

ATIP 单节点群集自动置备 1

可以执行两个不同的步骤来使用定向网络置备自动置备单节点下游群集:

  1. 登记裸机主机,使其在置备过程中可用。

  2. 置备裸机主机,以安装并配置操作系统和 Kubernetes 群集。

登记裸机主机

第一步是在管理群集中登记新的裸机主机,使其可供置备。为此,必须在管理群集中创建以下文件 (bmh-example.yaml),以指定要使用的 BMC 身份凭证以及要登记的 BaremetalHost 对象:

apiVersion: v1
kind: Secret
metadata:
  name: example-demo-credentials
type: Opaque
data:
  username: ${BMC_USERNAME}
  password: ${BMC_PASSWORD}
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
  name: flexran-demo
  labels:
    cluster-role: control-plane
spec:
  online: true
  bootMACAddress: ${BMC_MAC}
  rootDeviceHints:
    deviceName: /dev/nvme0n1
  bmc:
    address: ${BMC_ADDRESS}
    disableCertificateVerification: true
    credentialsName: example-demo-credentials

其中:

  • ${BMC_USERNAME} — 新裸机主机的 BMC 用户名。

  • ${BMC_PASSWORD} — 新裸机主机的 BMC 口令。

  • ${BMC_MAC} — 要使用的新裸机主机 MAC 地址。

  • ${BMC_ADDRESS} — 裸机主机 BMCURL(例如 redfish-virtualmedia://192.168.200.75/redfish/v1/Systems/1/)。要了解有关硬件提供商支持的不同选项的详细信息,请访问此链接

创建该文件后,必须在管理群集中执行以下命令,才能在管理群集中开始登记新的裸机主机:

$ kubectl apply -f bmh-example.yaml

随后会登记新裸机主机对象,其状态将从正在注册依次更改为正在检查和可用。可使用以下命令检查状态更改:

$ kubectl get bmh
注意
注意

在验证 BMC 身份凭证之前,BaremetalHost 对象将一直处于正在注册状态。验证身份凭证后,BaremetalHost 对象的状态将更改为正在检查,此步骤可能需要一段时间(最长 20 分钟),具体取决于所用的硬件。在检查阶段,将检索硬件信息并更新 Kubernetes 对象。使用以下命令检查信息:kubectl get bmh -o yaml

置备步骤

裸机主机已登记并可供使用后,下一步是置备裸机主机,以安装并配置操作系统和 Kubernetes 群集。为此,必须在管理群集中创建以下文件 (capi-provisioning-example.yaml) 并在其中包含以下信息(可以通过联接以下块来生成 capi-provisioning-example.yaml)。

注意
注意

只需将 ${...} 中的值替换为实际值。

下面的块是群集定义,可在其中使用 podsservices 块来配置网络。此外,它还包含对要使用的控制平面和基础架构(使用 Metal3 提供程序)对象的引用。

apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
  name: single-node-cluster
  namespace: default
spec:
  clusterNetwork:
    pods:
      cidrBlocks:
        - 192.168.0.0/18
    services:
      cidrBlocks:
        - 10.96.0.0/12
  controlPlaneRef:
    apiVersion: controlplane.cluster.x-k8s.io/v1alpha1
    kind: RKE2ControlPlane
    name: single-node-cluster
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
    kind: Metal3Cluster
    name: single-node-cluster

Metal3Cluster 对象指定要配置的控制平面端点(请替换 ${DOWNSTREAM_CONTROL_PLANE_IP})以及 noCloudProvider,因为使用了一个裸机节点。

apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: Metal3Cluster
metadata:
  name: single-node-cluster
  namespace: default
spec:
  controlPlaneEndpoint:
    host: ${DOWNSTREAM_CONTROL_PLANE_IP}
    port: 6443
  noCloudProvider: true

RKE2ControlPlane 对象指定要使用的控制平面配置,Metal3MachineTemplate 对象指定要使用的控制平面映像。此外,它还包含有关要使用的复本数(在本例中为 1)以及要使用的 CNI 插件(在本例中为 Cilium)的信息。agentConfig 块包含要使用的 Ignition 格式以及用于配置 RKE2 节点的 additionalUserData,其中包含名为 rke2-preinstall.service 的 systemd 等信息,用于在置备过程中使用 Ironic 信息自动替换 BAREMETALHOST_UUIDnode-name。最后一个信息块包含要使用的 Kubernetes 版本。${RKE2_VERSION} 是要使用的 RKE2 版本,请替换此值(例如替换为 v1.28.9+rke2r1)。

apiVersion: controlplane.cluster.x-k8s.io/v1alpha1
kind: RKE2ControlPlane
metadata:
  name: single-node-cluster
  namespace: default
spec:
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
    kind: Metal3MachineTemplate
    name: single-node-cluster-controlplane
  replicas: 1
  serverConfig:
    cni: cilium
  agentConfig:
    format: ignition
    additionalUserData:
      config: |
        variant: fcos
        version: 1.4.0
        systemd:
          units:
            - name: rke2-preinstall.service
              enabled: true
              contents: |
                [Unit]
                Description=rke2-preinstall
                Wants=network-online.target
                Before=rke2-install.service
                ConditionPathExists=!/run/cluster-api/bootstrap-success.complete
                [Service]
                Type=oneshot
                User=root
                ExecStartPre=/bin/sh -c "mount -L config-2 /mnt"
                ExecStart=/bin/sh -c "sed -i \"s/BAREMETALHOST_UUID/$(jq -r .uuid /mnt/openstack/latest/meta_data.json)/\" /etc/rancher/rke2/config.yaml"
                ExecStart=/bin/sh -c "echo \"node-name: $(jq -r .name /mnt/openstack/latest/meta_data.json)\" >> /etc/rancher/rke2/config.yaml"
                ExecStartPost=/bin/sh -c "umount /mnt"
                [Install]
                WantedBy=multi-user.target
    kubelet:
      extraArgs:
        - provider-id=metal3://BAREMETALHOST_UUID
    version: ${RKE2_VERSION}
    nodeName: "localhost.localdomain"

Metal3MachineTemplate 对象指定以下信息:

apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: Metal3MachineTemplate
metadata:
  name: single-node-cluster-controlplane
  namespace: default
spec:
  template:
    spec:
      dataTemplate:
        name: single-node-cluster-controlplane-template
      hostSelector:
        matchLabels:
          cluster-role: control-plane
      image:
        checksum: http://imagecache.local:8080/eibimage-slemicro55rt-telco.raw.sha256
        checksumType: sha256
        format: raw
        url: http://imagecache.local:8080/eibimage-slemicro55rt-telco.raw

Metal3DataTemplate 对象指定下游群集的 metaData

apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: Metal3DataTemplate
metadata:
  name: single-node-cluster-controlplane-template
  namespace: default
spec:
  clusterName: single-node-cluster
  metaData:
    objectNames:
      - key: name
        object: machine
      - key: local-hostname
        object: machine
      - key: local_hostname
        object: machine

通过联接上述块创建该文件后,必须在管理群集中执行以下命令才能开始置备新的裸机主机:

$ kubectl apply -f capi-provisioning-example.yaml

31.5 使用定向网络置备来置备下游群集(多节点)

本节介绍用于通过定向网络置备和 MetalLB(用作负载平衡器策略)自动置备多节点下游群集的工作流程。这是自动置备下游群集的最简单方法。下图显示了用于通过定向网络置备和 MetalLB 自动置备多节点下游群集的工作流程。

要求

工作流程

下图显示了用于通过定向网络置备自动置备多节点下游群集的工作流程:

ATIP 多节点群集自动置备 1
  1. 登记三个裸机主机,使其在置备过程中可用。

  2. 置备三个裸机主机,以使用 MetalLB 安装并配置操作系统和 Kubernetes 群集。

登记裸机主机

第一步是在管理群集中登记三个裸机主机,使其可供置备。为此,必须在管理群集中创建以下文件(bmh-example-node1.yamlbmh-example-node2.yamlbmh-example-node3.yaml),以指定要使用的 BMC 身份凭证,以及要在管理群集中登记的 BaremetalHost 对象。

注意
注意
  • 只需将 ${...} 中的值替换为实际值。

  • 本节只会指导您完成一个主机的置备过程。这些步骤同样适用于另外两个节点。

apiVersion: v1
kind: Secret
metadata:
  name: node1-example-credentials
type: Opaque
data:
  username: ${BMC_NODE1_USERNAME}
  password: ${BMC_NODE1_PASSWORD}
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
  name: node1-example
  labels:
    cluster-role: control-plane
spec:
  online: true
  bootMACAddress: ${BMC_NODE1_MAC}
  bmc:
    address: ${BMC_NODE1_ADDRESS}
    disableCertificateVerification: true
    credentialsName: node1-example-credentials

其中:

  • ${BMC_NODE1_USERNAME} — 第一个裸机主机的 BMC 用户名。

  • ${BMC_NODE1_PASSWORD} — 第一个裸机主机的 BMC 口令。

  • ${BMC_NODE1_MAC} — 第一个裸机主机的要使用的 MAC 地址。

  • ${BMC_NODE1_ADDRESS} — 第一个裸机主机的 BMC 的 URL(例如 redfish-virtualmedia://192.168.200.75/redfish/v1/Systems/1/)。要了解有关硬件提供商支持的不同选项的详细信息,请访问此链接

创建该文件后,必须在管理群集中执行以下命令,才能在管理群集中开始登记裸机主机:

$ kubectl apply -f bmh-example-node1.yaml
$ kubectl apply -f bmh-example-node2.yaml
$ kubectl apply -f bmh-example-node3.yaml

随后会登记新裸机主机对象,其状态将从正在注册依次更改为正在检查和可用。可使用以下命令检查状态更改:

$ kubectl get bmh -o wide
注意
注意

在验证 BMC 身份凭证之前,BaremetalHost 对象将一直处于正在注册状态。验证身份凭证后,BaremetalHost 对象的状态将更改为正在检查,此步骤可能需要一段时间(最长 20 分钟),具体取决于所用的硬件。在检查阶段,将检索硬件信息并更新 Kubernetes 对象。使用以下命令检查信息:kubectl get bmh -o yaml

置备步骤

三个裸机主机已登记并可供使用后,下一步是置备裸机主机,以安装和配置操作系统与 Kubernetes 群集,并创建用于管理该操作系统和群集的负载平衡器。为此,必须在管理群集中创建以下文件 (capi-provisioning-example.yaml) 并在其中包含以下信息(可以通过联接以下块来生成 capi-provisioning-example.yaml)。

注意
注意
  • 只需将 ${...} 中的值替换为实际值。

  • VIP 地址是尚未分配给任何节点的预留 IP 地址,用于配置负载平衡器。

下面为群集定义,可在其中使用 podsservices 块来配置群集网络。此外,它还包含对要使用的控制平面和基础架构(使用 Metal3 提供程序)对象的引用。

apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
  name: multinode-cluster
  namespace: default
spec:
  clusterNetwork:
    pods:
      cidrBlocks:
        - 192.168.0.0/18
    services:
      cidrBlocks:
        - 10.96.0.0/12
  controlPlaneRef:
    apiVersion: controlplane.cluster.x-k8s.io/v1alpha1
    kind: RKE2ControlPlane
    name: multinode-cluster
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
    kind: Metal3Cluster
    name: multinode-cluster

Metal3Cluster 对象指定要配置的、使用预留的 VIP 地址(请替换 ${DOWNSTREAM_VIP_ADDRESS})的控制平面端点以及 noCloudProvider,因为使用了三个裸机节点。

apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: Metal3Cluster
metadata:
  name: multinode-cluster
  namespace: default
spec:
  controlPlaneEndpoint:
    host: ${EDGE_VIP_ADDRESS}
    port: 6443
  noCloudProvider: true

RKE2ControlPlane 对象指定要使用的控制平面配置,Metal3MachineTemplate 对象指定要使用的控制平面映像。

  • 要使用的复本数(在本例中为 3)。

  • 负载平衡器要使用的广告模式(address 使用 L2 实现),以及要使用的地址(请将 ${EDGE_VIP_ADDRESS} 替换为 VIP 地址)。

  • serverConfig,其中包含要使用的 CNI 插件(在本例中为 Cilium);用于配置 VIP 地址的 tlsSan

  • agentConfig 块包含要使用的 Ignition 格式以及用于配置 RKE2 节点的 additionalUserData,其中的信息如下:

    • 名为 rke2-preinstall.service 的 systemd 服务,用于在置备过程中使用 Ironic 信息自动替换 BAREMETALHOST_UUIDnode-name

    • storage 块,其中包含用于安装 MetalLBendpoint-copier-operator 的 Helm chart。

    • metalLB 自定义资源文件,其中包含要使用的 IPaddressPoolL2Advertisement(请将 ${EDGE_VIP_ADDRESS} 替换为 VIP 地址)。

    • 用于配置 kubernetes-vip 服务的 end-svc.yaml 文件,MetalLB 使用该服务来管理 VIP 地址。

  • 最后一个信息块包含要使用的 Kubernetes 版本。${RKE2_VERSION} 是要使用的 RKE2 版本,请替换此值(例如替换为 v1.28.9+rke2r1)。

apiVersion: controlplane.cluster.x-k8s.io/v1alpha1
kind: RKE2ControlPlane
metadata:
  name: multinode-cluster
  namespace: default
spec:
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
    kind: Metal3MachineTemplate
    name: multinode-cluster-controlplane
  replicas: 3
  registrationMethod: "address"
  registrationAddress: ${EDGE_VIP_ADDRESS}
  serverConfig:
    cni: cilium
    tlsSan:
      - ${EDGE_VIP_ADDRESS}
      - https://${EDGE_VIP_ADDRESS}.sslip.io
  agentConfig:
    format: ignition
    additionalUserData:
      config: |
        variant: fcos
        version: 1.4.0
        systemd:
          units:
            - name: rke2-preinstall.service
              enabled: true
              contents: |
                [Unit]
                Description=rke2-preinstall
                Wants=network-online.target
                Before=rke2-install.service
                ConditionPathExists=!/run/cluster-api/bootstrap-success.complete
                [Service]
                Type=oneshot
                User=root
                ExecStartPre=/bin/sh -c "mount -L config-2 /mnt"
                ExecStart=/bin/sh -c "sed -i \"s/BAREMETALHOST_UUID/$(jq -r .uuid /mnt/openstack/latest/meta_data.json)/\" /etc/rancher/rke2/config.yaml"
                ExecStart=/bin/sh -c "echo \"node-name: $(jq -r .name /mnt/openstack/latest/meta_data.json)\" >> /etc/rancher/rke2/config.yaml"
                ExecStartPost=/bin/sh -c "umount /mnt"
                [Install]
                WantedBy=multi-user.target
        storage:
          files:
            - path: /var/lib/rancher/rke2/server/manifests/endpoint-copier-operator.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: helm.cattle.io/v1
                  kind: HelmChart
                  metadata:
                    name: endpoint-copier-operator
                    namespace: kube-system
                  spec:
                    chart: oci://registry.suse.com/edge/endpoint-copier-operator-chart
                    targetNamespace: endpoint-copier-operator
                    version: 0.2.0
                    createNamespace: true
            - path: /var/lib/rancher/rke2/server/manifests/metallb.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: helm.cattle.io/v1
                  kind: HelmChart
                  metadata:
                    name: metallb
                    namespace: kube-system
                  spec:
                    chart: oci://registry.suse.com/edge/metallb-chart
                    targetNamespace: metallb-system
                    version: 0.14.3
                    createNamespace: true

            - path: /var/lib/rancher/rke2/server/manifests/metallb-cr.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: metallb.io/v1beta1
                  kind: IPAddressPool
                  metadata:
                    name: kubernetes-vip-ip-pool
                    namespace: metallb-system
                  spec:
                    addresses:
                      - ${EDGE_VIP_ADDRESS}/32
                    serviceAllocation:
                      priority: 100
                      namespaces:
                        - default
                      serviceSelectors:
                        - matchExpressions:
                          - {key: "serviceType", operator: In, values: [kubernetes-vip]}
                  ---
                  apiVersion: metallb.io/v1beta1
                  kind: L2Advertisement
                  metadata:
                    name: ip-pool-l2-adv
                    namespace: metallb-system
                  spec:
                    ipAddressPools:
                      - kubernetes-vip-ip-pool
            - path: /var/lib/rancher/rke2/server/manifests/endpoint-svc.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: v1
                  kind: Service
                  metadata:
                    name: kubernetes-vip
                    namespace: default
                    labels:
                      serviceType: kubernetes-vip
                  spec:
                    ports:
                    - name: rke2-api
                      port: 9345
                      protocol: TCP
                      targetPort: 9345
                    - name: k8s-api
                      port: 6443
                      protocol: TCP
                      targetPort: 6443
                    type: LoadBalancer
    kubelet:
      extraArgs:
        - provider-id=metal3://BAREMETALHOST_UUID
    version: ${RKE2_VERSION}
    nodeName: "Node-multinode-cluster"

Metal3MachineTemplate 对象指定以下信息:

apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: Metal3MachineTemplate
metadata:
  name: multinode-cluster-controlplane
  namespace: default
spec:
  template:
    spec:
      dataTemplate:
        name: multinode-cluster-controlplane-template
      hostSelector:
        matchLabels:
          cluster-role: control-plane
      image:
        checksum: http://imagecache.local:8080/eibimage-slemicro55rt-telco.raw.sha256
        checksumType: sha256
        format: raw
        url: http://imagecache.local:8080/eibimage-slemicro55rt-telco.raw

Metal3DataTemplate 对象指定下游群集的 metaData

apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: Metal3DataTemplate
metadata:
  name: single-node-cluster-controlplane-template
  namespace: default
spec:
  clusterName: single-node-cluster
  metaData:
    objectNames:
      - key: name
        object: machine
      - key: local-hostname
        object: machine
      - key: local_hostname
        object: machine

通过联接上述块创建该文件后,必须在管理群集中执行以下命令才能开始置备三个新的裸机主机:

$ kubectl apply -f capi-provisioning-example.yaml

31.6 高级网络配置

定向网络置备工作流程允许使用静态 IP、绑定、VLAN 等下游群集网络配置。

以下章节将介绍使用高级网络配置置备下游群集所要执行的附加步骤。

要求

配置

基于以下两个章节的内容登记和置备主机:

需要做出以下更改才能启用高级网络配置:

  • 登记步骤:以下新示例文件中的某个密钥包含有关 networkData(用于配置下游群集的静态 IPVLAN 等设置)的信息

apiVersion: v1
kind: Secret
metadata:
  name: controlplane-0-networkdata
type: Opaque
stringData:
  networkData: |
    interfaces:
    - name: ${CONTROLPLANE_INTERFACE}
      type: ethernet
      state: up
      mtu: 1500
      mac-address: "${CONTROLPLANE_MAC}"
      ipv4:
        address:
        - ip:  "${CONTROLPLANE_IP}"
          prefix-length: "${CONTROLPLANE_PREFIX}"
        enabled: true
        dhcp: false
    - name: floating
      type: vlan
      state: up
      vlan:
        base-iface: ${CONTROLPLANE_INTERFACE}
        id: ${VLAN_ID}
    dns-resolver:
      config:
        server:
        - "${DNS_SERVER}"
    routes:
      config:
      - destination: 0.0.0.0/0
        next-hop-address: "${CONTROLPLANE_GATEWAY}"
        next-hop-interface: ${CONTROLPLANE_INTERFACE}

此文件包含用于配置下游群集的高级网络配置(例如静态 IPVLAN)的、采用 nmstate 格式的 networkData。可以看到,该示例显示了启用采用静态 IP 的接口的配置,以及启用采用基础接口的 VLAN 的配置。可以定义任何其他 nmstate 示例来配置下游群集的网络以适应特定要求。必须将示例中的以下变量替换为实际值:

  • ${CONTROLPLANE1_INTERFACE} — 用于边缘群集的控制平面接口(例如 eth0)。

  • ${CONTROLPLANE1_IP} — 用作边缘群集端点的 IP 地址(必须与 kubeapi-server 端点匹配)。

  • ${CONTROLPLANE1_PREFIX} — 用于边缘群集的 CIDR(例如,如果您要使用 /24 子网掩码,请指定 24;也可以指定 255.255.255.0)。

  • ${CONTROLPLANE1_GATEWAY} — 用于边缘群集的网关(例如 192.168.100.1)。

  • ${CONTROLPLANE1_MAC} — 用于控制平面接口的 MAC 地址(例如 00:0c:29:3e:3e:3e)。

  • ${DNS_SERVER} — 用于边缘群集的 DNS(例如 192.168.100.2)。

  • ${VLAN_ID} — 用于边缘群集的 VLAN ID(例如 100)。

另外,需要在该文件末尾的 BaremetalHost 对象中,使用 preprovisioningNetworkDataName 引用该密钥,以便能够在管理群集中登记。

apiVersion: v1
kind: Secret
metadata:
  name: example-demo-credentials
type: Opaque
data:
  username: ${BMC_USERNAME}
  password: ${BMC_PASSWORD}
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
  name: flexran-demo
  labels:
    cluster-role: control-plane
spec:
  online: true
  bootMACAddress: ${BMC_MAC}
  rootDeviceHints:
    deviceName: /dev/nvme0n1
  bmc:
    address: ${BMC_ADDRESS}
    disableCertificateVerification: true
    credentialsName: example-demo-credentials
  preprovisioningNetworkDataName: controlplane-0-networkdata
注意
注意

如果需要部署多节点群集,必须对其他节点执行相同的过程。

  • 置备步骤:必须去除与网络数据相关的信息块,因为平台会将网络数据配置包含在 controlplane-0-networkdata 密钥中。

apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: Metal3DataTemplate
metadata:
  name: multinode-cluster-controlplane-template
  namespace: default
spec:
  clusterName: multinode-cluster
  metaData:
    objectNames:
      - key: name
        object: machine
      - key: local-hostname
        object: machine
      - key: local_hostname
        object: machine
注意
注意

目前不支持 Metal3DataTemplatenetworkDataMetal3 IPAM;只有通过静态密钥进行的配置才完全受支持。

31.7 电信功能(DPDK、SR-IOV、CPU 隔离、大页、NUMA 等)

定向网络置备工作流程允许将下游群集中使用的电信功能自动化,以便在这些服务器上运行电信工作负载。

要求

配置

基于以下两个章节的内容登记和置备主机:

本节将介绍以下电信功能:

  • DPDK 和 VF 创建

  • 工作负载使用的 SR-IOV 和 VF 分配

  • CPU 隔离和性能微调

  • 大页配置

  • 内核参数微调

注意
注意

有关电信功能的详细信息,请参见第 30 章 “电信功能配置

启用上述电信功能所要做出的更改都可以在 capi-provisioning-example.yaml 置备文件的 RKE2ControlPlane 块中完成。capi-provisioning-example.yaml 文件中的其余信息与置备相关章节(第 31.4 节 “使用定向网络置备来置备下游群集(单节点)”)中提供的信息相同。

明确地说,为启用电信功能而需要在该块 (RKE2ControlPlane) 中所做的更改如下:

  • 指定 preRKE2Commands,用于在 RKE2 安装过程开始之前执行命令。本例使用 modprobe 命令启用 vfio-pciSR-IOV 内核模块。

  • 指定 ignition 文件 /var/lib/rancher/rke2/server/manifests/configmap-sriov-custom-auto.yaml,用于定义要创建的并向工作负载公开的接口、驱动程序及 VF 数量。

    • 只有 sriov-custom-auto-config 配置映射中的值可以替换为实际值。

      • ${RESOURCE_NAME1} — 用于第一个 PF 接口的资源名称(例如 sriov-resource-du1)。它将添加到前缀 rancher.io 的后面,供工作负载用作标签(例如 rancher.io/sriov-resource-du1)。

      • ${SRIOV-NIC-NAME1} — 要使用的第一个 PF 接口的名称(例如 eth0)。

      • ${PF_NAME1} — 要使用的第一个物理功能 PF 的名称。可以使用此名称生成更复杂的过滤器(例如 eth0#2-5)。

      • ${DRIVER_NAME1} — 用于第一个 VF 接口的驱动程序名称(例如 vfio-pci)。

      • ${NUM_VFS1} — 要为第一个 PF 接口创建的 VF 数量(例如 8)。

  • 提供 /var/sriov-auto-filler.sh,用作高级配置映射 sriov-custom-auto-config 与包含低级硬件信息的 sriovnetworknodepolicy 之间的转换器。创建此脚本的目的是让用户免于提前知道复杂的硬件信息。不需要在此文件中进行更改,但如果我们需要启用 sr-iov 并创建 VF,则应该提供此脚本。

  • 用于启用以下功能的内核参数:

参数

说明

isolcpus

1-30,33-62

隔离核心 1-30 和 33-62。

skew_tick

1

允许内核在隔离的 CPU 之间偏斜计时器中断。

nohz

on

允许内核在系统空闲时在单个 CPU 上运行计时器滴答周期。

nohz_full

1-30,33-62

内核引导参数是当前用于配置完整 dynticks 及 CPU 隔离的主接口。

rcu_nocbs

1-30,33-62

允许内核在系统空闲时在单个 CPU 上运行 RCU 回调。

kthread_cpus

0,31,32,63

允许内核在系统空闲时在单个 CPU 上运行 kthreads。

irqaffinity

0,31,32,63

允许内核在系统空闲时在单个 CPU 上运行中断。

processor.max_cstate

1

防止 CPU 在空闲时进入休眠状态。

intel_idle.max_cstate

0

禁用 intel_idle 驱动程序并允许使用 acpi_idle。

iommu

pt

允许为 dpdk 接口使用 vfio。

intel_iommu

on

允许为 VF 使用 vfio。

hugepagesz

1G

允许将大页的大小设置为 1 G。

hugepages

40

先前定义的大页数量。

default_hugepagesz

1G

用于启用大页的默认值。

  • 以下 systemd 服务用于启用下述功能:

    • rke2-preinstall.service,用于在置备过程中使用 Ironic 信息自动替换 BAREMETALHOST_UUIDnode-name

    • cpu-performance.service,用于启用 CPU 性能微调。必须将 ${CPU_FREQUENCY} 替换为实际值(例如,替换为 2500000 会将 CPU 频率设置为 2.5GHz)。

    • cpu-partitioning.service,用于启用 CPU 核心隔离(例如 1-30,33-62)。

    • sriov-custom-auto-vfs.service,用于安装 sriov Helm chart,等待创建自定义资源并运行 /var/sriov-auto-filler.sh,以替换配置映射 sriov-custom-auto-config 中的值并创建工作负载要使用的 sriovnetworknodepolicy

  • ${RKE2_VERSION} 是要使用的 RKE2 版本,请替换此值(例如替换为 v1.28.9+rke2r1)。

做出上述所有更改后,capi-provisioning-example.yaml 中的 RKE2ControlPlane 块如下所示:

apiVersion: controlplane.cluster.x-k8s.io/v1alpha1
kind: RKE2ControlPlane
metadata:
  name: single-node-cluster
  namespace: default
spec:
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
    kind: Metal3MachineTemplate
    name: single-node-cluster-controlplane
  replicas: 1
  serverConfig:
    cni: cilium
    cniMultusEnable: true
  preRKE2Commands:
    - modprobe vfio-pci enable_sriov=1 disable_idle_d3=1
  agentConfig:
    format: ignition
    additionalUserData:
      config: |
        variant: fcos
        version: 1.4.0
        storage:
          files:
            - path: /var/lib/rancher/rke2/server/manifests/configmap-sriov-custom-auto.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: v1
                  kind: ConfigMap
                  metadata:
                    name: sriov-custom-auto-config
                    namespace: kube-system
                  data:
                    config.json: |
                      [
                         {
                           "resourceName": "${RESOURCE_NAME1}",
                           "interface": "${SRIOV-NIC-NAME1}",
                           "pfname": "${PF_NAME1}",
                           "driver": "${DRIVER_NAME1}",
                           "numVFsToCreate": ${NUM_VFS1}
                         },
                         {
                           "resourceName": "${RESOURCE_NAME2}",
                           "interface": "${SRIOV-NIC-NAME2}",
                           "pfname": "${PF_NAME2}",
                           "driver": "${DRIVER_NAME2}",
                           "numVFsToCreate": ${NUM_VFS2}
                         }
                      ]
              mode: 0644
              user:
                name: root
              group:
                name: root
            - path: /var/lib/rancher/rke2/server/manifests/sriov-crd.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: helm.cattle.io/v1
                  kind: HelmChart
                  metadata:
                    name: sriov-crd
                    namespace: kube-system
                  spec:
                    chart: oci://registry.suse.com/edge/sriov-crd-chart
                    targetNamespace: sriov-network-operator
                    version: 1.2.2
                    createNamespace: true
            - path: /var/lib/rancher/rke2/server/manifests/sriov-network-operator.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: helm.cattle.io/v1
                  kind: HelmChart
                  metadata:
                    name: sriov-network-operator
                    namespace: kube-system
                  spec:
                    chart: oci://registry.suse.com/edge/sriov-network-operator-chart
                    targetNamespace: sriov-network-operator
                    version: 1.2.2
                    createNamespace: true
            - path: /var/sriov-auto-filler.sh
              overwrite: true
              contents:
                inline: |
                  #!/bin/bash
                  cat <<- EOF > /var/sriov-networkpolicy-template.yaml
                  apiVersion: sriovnetwork.openshift.io/v1
                  kind: SriovNetworkNodePolicy
                  metadata:
                    name: atip-RESOURCENAME
                    namespace: sriov-network-operator
                  spec:
                    nodeSelector:
                      feature.node.kubernetes.io/network-sriov.capable: "true"
                    resourceName: RESOURCENAME
                    deviceType: DRIVER
                    numVfs: NUMVF
                    mtu: 1500
                    nicSelector:
                      pfNames: ["PFNAMES"]
                      deviceID: "DEVICEID"
                      vendor: "VENDOR"
                      rootDevices:
                        - PCIADDRESS
                  EOF

                  export KUBECONFIG=/etc/rancher/rke2/rke2.yaml; export KUBECTL=/var/lib/rancher/rke2/bin/kubectl
                  while [ $(${KUBECTL} --kubeconfig=${KUBECONFIG} get sriovnetworknodestates.sriovnetwork.openshift.io -n sriov-network-operator -ojson | jq -r '.items[].status.syncStatus') != "Succeeded" ]; do sleep 1; done
                  input=$(${KUBECTL} --kubeconfig=${KUBECONFIG} get cm sriov-custom-auto-config -n kube-system -ojson | jq -r '.data."config.json"')
                  jq -c '.[]' <<< $input | while read i; do
                    interface=$(echo $i | jq -r '.interface')
                    pfname=$(echo $i | jq -r '.pfname')
                    pciaddress=$(${KUBECTL} --kubeconfig=${KUBECONFIG} get sriovnetworknodestates.sriovnetwork.openshift.io -n sriov-network-operator -ojson | jq -r ".items[].status.interfaces[]|select(.name==\"$interface\")|.pciAddress")
                    vendor=$(${KUBECTL} --kubeconfig=${KUBECONFIG} get sriovnetworknodestates.sriovnetwork.openshift.io -n sriov-network-operator -ojson | jq -r ".items[].status.interfaces[]|select(.name==\"$interface\")|.vendor")
                    deviceid=$(${KUBECTL} --kubeconfig=${KUBECONFIG} get sriovnetworknodestates.sriovnetwork.openshift.io -n sriov-network-operator -ojson | jq -r ".items[].status.interfaces[]|select(.name==\"$interface\")|.deviceID")
                    resourceName=$(echo $i | jq -r '.resourceName')
                    driver=$(echo $i | jq -r '.driver')
                    sed -e "s/RESOURCENAME/$resourceName/g" \
                        -e "s/DRIVER/$driver/g" \
                        -e "s/PFNAMES/$pfname/g" \
                        -e "s/VENDOR/$vendor/g" \
                        -e "s/DEVICEID/$deviceid/g" \
                        -e "s/PCIADDRESS/$pciaddress/g" \
                        -e "s/NUMVF/$(echo $i | jq -r '.numVFsToCreate')/g" /var/sriov-networkpolicy-template.yaml > /var/lib/rancher/rke2/server/manifests/$resourceName.yaml
                  done
              mode: 0755
              user:
                name: root
              group:
                name: root
        kernel_arguments:
          should_exist:
            - intel_iommu=on
            - intel_pstate=passive
            - processor.max_cstate=1
            - intel_idle.max_cstate=0
            - iommu=pt
            - mce=off
            - hugepagesz=1G hugepages=40
            - hugepagesz=2M hugepages=0
            - default_hugepagesz=1G
            - kthread_cpus=${NON-ISOLATED_CPU_CORES}
            - irqaffinity=${NON-ISOLATED_CPU_CORES}
            - isolcpus=${ISOLATED_CPU_CORES}
            - nohz_full=${ISOLATED_CPU_CORES}
            - rcu_nocbs=${ISOLATED_CPU_CORES}
            - rcu_nocb_poll
            - nosoftlockup
            - nohz=on
        systemd:
          units:
            - name: rke2-preinstall.service
              enabled: true
              contents: |
                [Unit]
                Description=rke2-preinstall
                Wants=network-online.target
                Before=rke2-install.service
                ConditionPathExists=!/run/cluster-api/bootstrap-success.complete
                [Service]
                Type=oneshot
                User=root
                ExecStartPre=/bin/sh -c "mount -L config-2 /mnt"
                ExecStart=/bin/sh -c "sed -i \"s/BAREMETALHOST_UUID/$(jq -r .uuid /mnt/openstack/latest/meta_data.json)/\" /etc/rancher/rke2/config.yaml"
                ExecStart=/bin/sh -c "echo \"node-name: $(jq -r .name /mnt/openstack/latest/meta_data.json)\" >> /etc/rancher/rke2/config.yaml"
                ExecStartPost=/bin/sh -c "umount /mnt"
                [Install]
                WantedBy=multi-user.target
            - name: cpu-performance.service
              enabled: true
              contents: |
                [Unit]
                Description=CPU perfomance
                Wants=network-online.target
                After=network.target network-online.target
                [Service]
                User=root
                Type=forking
                TimeoutStartSec=900
                ExecStart=/bin/sh -c "cpupower frequency-set -g performance; cpupower frequency-set -u ${CPU_FREQUENCY}; cpupower frequency-set -d ${CPU_FREQUENCY}"
                RemainAfterExit=yes
                KillMode=process
                [Install]
                WantedBy=multi-user.target
            - name: cpu-partitioning.service
              enabled: true
              contents: |
                [Unit]
                Description=cpu-partitioning
                Wants=network-online.target
                After=network.target network-online.target
                [Service]
                Type=oneshot
                User=root
                ExecStart=/bin/sh -c "echo isolated_cores=${ISOLATED_CPU_CORES} > /etc/tuned/cpu-partitioning-variables.conf"
                ExecStartPost=/bin/sh -c "tuned-adm profile cpu-partitioning"
                ExecStartPost=/bin/sh -c "systemctl enable tuned.service"
                [Install]
                WantedBy=multi-user.target
            - name: sriov-custom-auto-vfs.service
              enabled: true
              contents: |
                [Unit]
                Description=SRIOV Custom Auto VF Creation
                Wants=network-online.target  rke2-server.target
                After=network.target network-online.target rke2-server.target
                [Service]
                User=root
                Type=forking
                TimeoutStartSec=900
                ExecStart=/bin/sh -c "while ! /var/lib/rancher/rke2/bin/kubectl --kubeconfig=/etc/rancher/rke2/rke2.yaml wait --for condition=ready nodes --all ; do sleep 2 ; done"
                ExecStartPost=/bin/sh -c "while [ $(/var/lib/rancher/rke2/bin/kubectl --kubeconfig=/etc/rancher/rke2/rke2.yaml get sriovnetworknodestates.sriovnetwork.openshift.io --ignore-not-found --no-headers -A | wc -l) -eq 0 ]; do sleep 1; done"
                ExecStartPost=/bin/sh -c "/var/sriov-auto-filler.sh"
                RemainAfterExit=yes
                KillMode=process
                [Install]
                WantedBy=multi-user.target
    kubelet:
      extraArgs:
        - provider-id=metal3://BAREMETALHOST_UUID
    version: ${RKE2_VERSION}
    nodeName: "localhost.localdomain"

通过联接上述块创建该文件后,必须在管理群集中执行以下命令才能开始使用电信功能置备新的下游群集:

$ kubectl apply -f capi-provisioning-example.yaml

31.8 专用注册表

可以配置专用注册表作为工作负载使用的映像的镜像。

为此,我们可以创建密钥,并在其中包含有关下游群集要使用的专用注册表的信息。

apiVersion: v1
kind: Secret
metadata:
  name: private-registry-cert
  namespace: default
data:
  tls.crt: ${TLS_CERTIFICATE}
  tls.key: ${TLS_KEY}
  ca.crt: ${CA_CERTIFICATE}
type: kubernetes.io/tls
---
apiVersion: v1
kind: Secret
metadata:
  name: private-registry-auth
  namespace: default
data:
  username: ${REGISTRY_USERNAME}
  password: ${REGISTRY_PASSWORD}

tls.crttls.keyca.crt 是用于对专用注册表进行身份验证的证书。usernamepassword 是用于对专用注册表进行身份验证的身份凭证。

注意
注意

必须以 base64 格式对 tls.crttls.keyca.crtusernamepassword 进行编码才能在密钥中使用它们。

做出上述所有更改后,capi-provisioning-example.yaml 中的 RKE2ControlPlane 块如下所示:

apiVersion: controlplane.cluster.x-k8s.io/v1alpha1
kind: RKE2ControlPlane
metadata:
  name: single-node-cluster
  namespace: default
spec:
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
    kind: Metal3MachineTemplate
    name: single-node-cluster-controlplane
  replicas: 1
  privateRegistriesConfig:
    mirrors:
      "registry.example.com":
        endpoint:
          - "https://registry.example.com:5000"
    configs:
      "registry.example.com":
        authSecret:
          apiVersion: v1
          kind: Secret
          namespace: default
          name: private-registry-auth
        tls:
          tlsConfigSecret:
            apiVersion: v1
            kind: Secret
            namespace: default
            name: private-registry-cert
  serverConfig:
    cni: cilium
  agentConfig:
    format: ignition
    additionalUserData:
      config: |
        variant: fcos
        version: 1.4.0
        systemd:
          units:
            - name: rke2-preinstall.service
              enabled: true
              contents: |
                [Unit]
                Description=rke2-preinstall
                Wants=network-online.target
                Before=rke2-install.service
                ConditionPathExists=!/run/cluster-api/bootstrap-success.complete
                [Service]
                Type=oneshot
                User=root
                ExecStartPre=/bin/sh -c "mount -L config-2 /mnt"
                ExecStart=/bin/sh -c "sed -i \"s/BAREMETALHOST_UUID/$(jq -r .uuid /mnt/openstack/latest/meta_data.json)/\" /etc/rancher/rke2/config.yaml"
                ExecStart=/bin/sh -c "echo \"node-name: $(jq -r .name /mnt/openstack/latest/meta_data.json)\" >> /etc/rancher/rke2/config.yaml"
                ExecStartPost=/bin/sh -c "umount /mnt"
                [Install]
                WantedBy=multi-user.target
    kubelet:
      extraArgs:
        - provider-id=metal3://BAREMETALHOST_UUID
    version: ${RKE2_VERSION}
    nodeName: "localhost.localdomain"

其中,registry.example.com 是下游群集使用的专用注册表的示例名称,应将其替换为实际值。

31.9 在隔离方案中置备下游群集

定向网络置备工作流程允许在隔离方案中自动置备下游群集。

31.9.1 隔离方案的要求

  1. 使用 EIB 生成的原始映像必须包含用于在隔离方案中运行下游群集的特定容器映像(helm-chart OCI 和容器映像)。有关详细信息,请参见第 31.3 节 “为隔离方案准备下游群集映像”一节。

  2. 如果使用 SR-IOV 或任何其他自定义工作负载,则必须按照有关预加载专用注册表的章节(第 31.3.2.5 节 “预加载包含隔离方案和 SR-IOV 所需映像的专用注册表(可选)”)中所述,在专用注册表中预加载用于运行工作负载的映像。

31.9.2 在隔离方案中登记裸机主机

在管理群集中登记裸机主机的过程与上一节(第 31.4 节 “使用定向网络置备来置备下游群集(单节点)”)中所述的过程相同。

31.9.3 在隔离方案中置备下游群集

需要做出一些重要更改才能在隔离方案中置备下游群集:

  1. capi-provisioning-example.yaml 文件中的 RKE2ControlPlane 块必须包含 spec.agentConfig.airGapped: true 指令。

  2. 必须按照有关专用注册表的章节(第 31.8 节 “专用注册表”)中所述,将专用注册表配置包含在 capi-provisioning-airgap-example.yaml 文件中的 RKE2ControlPlane 块中。

  3. 如果您正在使用 SR-IOV 或任何其他需要安装 helm-chart 的 AdditionalUserData 配置(combustion 脚本),则必须修改配置内容以引用专用注册表,而不是使用公共注册表。

以下示例显示了 capi-provisioning-airgap-example.yaml 文件的 AdditionalUserData 块中的 SR-IOV 配置,以及为了引用专用注册表而需要做出的修改

  • 专用注册表密钥引用

  • 在 Helm-Chart 定义中使用专用注册表而不是公共 OCI 映像。

# secret to include the private registry certificates
apiVersion: v1
kind: Secret
metadata:
  name: private-registry-cert
  namespace: default
data:
  tls.crt: ${TLS_BASE64_CERT}
  tls.key: ${TLS_BASE64_KEY}
  ca.crt: ${CA_BASE64_CERT}
type: kubernetes.io/tls
---
# secret to include the private registry auth credentials
apiVersion: v1
kind: Secret
metadata:
  name: private-registry-auth
  namespace: default
data:
  username: ${REGISTRY_USERNAME}
  password: ${REGISTRY_PASSWORD}
---
apiVersion: controlplane.cluster.x-k8s.io/v1alpha1
kind: RKE2ControlPlane
metadata:
  name: single-node-cluster
  namespace: default
spec:
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
    kind: Metal3MachineTemplate
    name: single-node-cluster-controlplane
  replicas: 1
  privateRegistriesConfig:       # Private registry configuration to add your own mirror and credentials
    mirrors:
      docker.io:
        endpoint:
          - "https://$(PRIVATE_REGISTRY_URL)"
    configs:
      "192.168.100.22:5000":
        authSecret:
          apiVersion: v1
          kind: Secret
          namespace: default
          name: private-registry-auth
        tls:
          tlsConfigSecret:
            apiVersion: v1
            kind: Secret
            namespace: default
            name: private-registry-cert
          insecureSkipVerify: false
  serverConfig:
    cni: cilium
    cniMultusEnable: true
  preRKE2Commands:
    - modprobe vfio-pci enable_sriov=1 disable_idle_d3=1
  agentConfig:
    airGapped: true       # Airgap true to enable airgap mode
    format: ignition
    additionalUserData:
      config: |
        variant: fcos
        version: 1.4.0
        storage:
          files:
            - path: /var/lib/rancher/rke2/server/manifests/configmap-sriov-custom-auto.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: v1
                  kind: ConfigMap
                  metadata:
                    name: sriov-custom-auto-config
                    namespace: sriov-network-operator
                  data:
                    config.json: |
                      [
                         {
                           "resourceName": "${RESOURCE_NAME1}",
                           "interface": "${SRIOV-NIC-NAME1}",
                           "pfname": "${PF_NAME1}",
                           "driver": "${DRIVER_NAME1}",
                           "numVFsToCreate": ${NUM_VFS1}
                         },
                         {
                           "resourceName": "${RESOURCE_NAME2}",
                           "interface": "${SRIOV-NIC-NAME2}",
                           "pfname": "${PF_NAME2}",
                           "driver": "${DRIVER_NAME2}",
                           "numVFsToCreate": ${NUM_VFS2}
                         }
                      ]
              mode: 0644
              user:
                name: root
              group:
                name: root
            - path: /var/lib/rancher/rke2/server/manifests/sriov.yaml
              overwrite: true
              contents:
                inline: |
                  apiVersion: v1
                  data:
                    .dockerconfigjson: ${REGISTRY_AUTH_DOCKERCONFIGJSON}
                  kind: Secret
                  metadata:
                    name: privregauth
                    namespace: kube-system
                  type: kubernetes.io/dockerconfigjson
                  ---
                  apiVersion: v1
                  kind: ConfigMap
                  metadata:
                    namespace: kube-system
                    name: example-repo-ca
                  data:
                    ca.crt: |-
                      -----BEGIN CERTIFICATE-----
                      ${CA_BASE64_CERT}
                      -----END CERTIFICATE-----
                  ---
                  apiVersion: helm.cattle.io/v1
                  kind: HelmChart
                  metadata:
                    name: sriov-crd
                    namespace: kube-system
                  spec:
                    chart: oci://${PRIVATE_REGISTRY_URL}/sriov-crd
                    dockerRegistrySecret:
                      name: privregauth
                    repoCAConfigMap:
                      name: example-repo-ca
                    createNamespace: true
                    set:
                      global.clusterCIDR: 192.168.0.0/18
                      global.clusterCIDRv4: 192.168.0.0/18
                      global.clusterDNS: 10.96.0.10
                      global.clusterDomain: cluster.local
                      global.rke2DataDir: /var/lib/rancher/rke2
                      global.serviceCIDR: 10.96.0.0/12
                    targetNamespace: sriov-network-operator
                    version: ${SRIOV_CRD_VERSION}
                  ---
                  apiVersion: helm.cattle.io/v1
                  kind: HelmChart
                  metadata:
                    name: sriov-network-operator
                    namespace: kube-system
                  spec:
                    chart: oci://${PRIVATE_REGISTRY_URL}/sriov-network-operator
                    dockerRegistrySecret:
                      name: privregauth
                    repoCAConfigMap:
                      name: example-repo-ca
                    createNamespace: true
                    set:
                      global.clusterCIDR: 192.168.0.0/18
                      global.clusterCIDRv4: 192.168.0.0/18
                      global.clusterDNS: 10.96.0.10
                      global.clusterDomain: cluster.local
                      global.rke2DataDir: /var/lib/rancher/rke2
                      global.serviceCIDR: 10.96.0.0/12
                    targetNamespace: sriov-network-operator
                    version: ${SRIOV_OPERATOR_VERSION}
              mode: 0644
              user:
                name: root
              group:
                name: root
            - path: /var/sriov-auto-filler.sh
              overwrite: true
              contents:
                inline: |
                  #!/bin/bash
                  cat <<- EOF > /var/sriov-networkpolicy-template.yaml
                  apiVersion: sriovnetwork.openshift.io/v1
                  kind: SriovNetworkNodePolicy
                  metadata:
                    name: atip-RESOURCENAME
                    namespace: sriov-network-operator
                  spec:
                    nodeSelector:
                      feature.node.kubernetes.io/network-sriov.capable: "true"
                    resourceName: RESOURCENAME
                    deviceType: DRIVER
                    numVfs: NUMVF
                    mtu: 1500
                    nicSelector:
                      pfNames: ["PFNAMES"]
                      deviceID: "DEVICEID"
                      vendor: "VENDOR"
                      rootDevices:
                        - PCIADDRESS
                  EOF

                  export KUBECONFIG=/etc/rancher/rke2/rke2.yaml; export KUBECTL=/var/lib/rancher/rke2/bin/kubectl
                  while [ $(${KUBECTL} --kubeconfig=${KUBECONFIG} get sriovnetworknodestates.sriovnetwork.openshift.io -n sriov-network-operator -ojson | jq -r '.items[].status.syncStatus') != "Succeeded" ]; do sleep 1; done
                  input=$(${KUBECTL} --kubeconfig=${KUBECONFIG} get cm sriov-custom-auto-config -n sriov-network-operator -ojson | jq -r '.data."config.json"')
                  jq -c '.[]' <<< $input | while read i; do
                    interface=$(echo $i | jq -r '.interface')
                    pfname=$(echo $i | jq -r '.pfname')
                    pciaddress=$(${KUBECTL} --kubeconfig=${KUBECONFIG} get sriovnetworknodestates.sriovnetwork.openshift.io -n sriov-network-operator -ojson | jq -r ".items[].status.interfaces[]|select(.name==\"$interface\")|.pciAddress")
                    vendor=$(${KUBECTL} --kubeconfig=${KUBECONFIG} get sriovnetworknodestates.sriovnetwork.openshift.io -n sriov-network-operator -ojson | jq -r ".items[].status.interfaces[]|select(.name==\"$interface\")|.vendor")
                    deviceid=$(${KUBECTL} --kubeconfig=${KUBECONFIG} get sriovnetworknodestates.sriovnetwork.openshift.io -n sriov-network-operator -ojson | jq -r ".items[].status.interfaces[]|select(.name==\"$interface\")|.deviceID")
                    resourceName=$(echo $i | jq -r '.resourceName')
                    driver=$(echo $i | jq -r '.driver')
                    sed -e "s/RESOURCENAME/$resourceName/g" \
                        -e "s/DRIVER/$driver/g" \
                        -e "s/PFNAMES/$pfname/g" \
                        -e "s/VENDOR/$vendor/g" \
                        -e "s/DEVICEID/$deviceid/g" \
                        -e "s/PCIADDRESS/$pciaddress/g" \
                        -e "s/NUMVF/$(echo $i | jq -r '.numVFsToCreate')/g" /var/sriov-networkpolicy-template.yaml > /var/lib/rancher/rke2/server/manifests/$resourceName.yaml
                  done
              mode: 0755
              user:
                name: root
              group:
                name: root
        kernel_arguments:
          should_exist:
            - intel_iommu=on
            - intel_pstate=passive
            - processor.max_cstate=1
            - intel_idle.max_cstate=0
            - iommu=pt
            - mce=off
            - hugepagesz=1G hugepages=40
            - hugepagesz=2M hugepages=0
            - default_hugepagesz=1G
            - kthread_cpus=${NON-ISOLATED_CPU_CORES}
            - irqaffinity=${NON-ISOLATED_CPU_CORES}
            - isolcpus=${ISOLATED_CPU_CORES}
            - nohz_full=${ISOLATED_CPU_CORES}
            - rcu_nocbs=${ISOLATED_CPU_CORES}
            - rcu_nocb_poll
            - nosoftlockup
            - nohz=on
        systemd:
          units:
            - name: rke2-preinstall.service
              enabled: true
              contents: |
                [Unit]
                Description=rke2-preinstall
                Wants=network-online.target
                Before=rke2-install.service
                ConditionPathExists=!/run/cluster-api/bootstrap-success.complete
                [Service]
                Type=oneshot
                User=root
                ExecStartPre=/bin/sh -c "mount -L config-2 /mnt"
                ExecStart=/bin/sh -c "sed -i \"s/BAREMETALHOST_UUID/$(jq -r .uuid /mnt/openstack/latest/meta_data.json)/\" /etc/rancher/rke2/config.yaml"
                ExecStart=/bin/sh -c "echo \"node-name: $(jq -r .name /mnt/openstack/latest/meta_data.json)\" >> /etc/rancher/rke2/config.yaml"
                ExecStartPost=/bin/sh -c "umount /mnt"
                [Install]
                WantedBy=multi-user.target
            - name: cpu-partitioning.service
              enabled: true
              contents: |
                [Unit]
                Description=cpu-partitioning
                Wants=network-online.target
                After=network.target network-online.target
                [Service]
                Type=oneshot
                User=root
                ExecStart=/bin/sh -c "echo isolated_cores=${ISOLATED_CPU_CORES} > /etc/tuned/cpu-partitioning-variables.conf"
                ExecStartPost=/bin/sh -c "tuned-adm profile cpu-partitioning"
                ExecStartPost=/bin/sh -c "systemctl enable tuned.service"
                [Install]
                WantedBy=multi-user.target
            - name: sriov-custom-auto-vfs.service
              enabled: true
              contents: |
                [Unit]
                Description=SRIOV Custom Auto VF Creation
                Wants=network-online.target  rke2-server.target
                After=network.target network-online.target rke2-server.target
                [Service]
                User=root
                Type=forking
                TimeoutStartSec=900
                ExecStart=/bin/sh -c "while ! /var/lib/rancher/rke2/bin/kubectl --kubeconfig=/etc/rancher/rke2/rke2.yaml wait --for condition=ready nodes --all ; do sleep 2 ; done"
                ExecStartPost=/bin/sh -c "while [ $(/var/lib/rancher/rke2/bin/kubectl --kubeconfig=/etc/rancher/rke2/rke2.yaml get sriovnetworknodestates.sriovnetwork.openshift.io --ignore-not-found --no-headers -A | wc -l) -eq 0 ]; do sleep 1; done"
                ExecStartPost=/bin/sh -c "/var/sriov-auto-filler.sh"
                RemainAfterExit=yes
                KillMode=process
                [Install]
                WantedBy=multi-user.target
    kubelet:
      extraArgs:
        - provider-id=metal3://BAREMETALHOST_UUID
    version: ${RKE2_VERSION}
    nodeName: "localhost.localdomain"