安装 global 集群
本文档介绍如何在不可变基础设施上安装 global 集群。global 集群是平台控制平面,通过 Cluster API 进行置备。当平台控制平面必须运行在不可变操作系统(例如 Alauda OS)上时,请使用此路径。
对于 Huawei DCS 上的 global 集群,请在选择 DCS Provider 软件包和 VM 模板之前查看Alauda OS 和 Provider 兼容性。
何时使用此路径
在满足以下所有条件时选择此安装路径:
- 您希望
global 集群运行在不可变操作系统上。目前支持的镜像为 Alauda OS。
- 您的基础设施属于已记录的提供商之一:Huawei DCS、Huawei Cloud Stack、VMware vSphere 或 Bare Metal。
- 您可以运行一个临时 bootstrap 主机,该主机能够访问目标 IaaS 平台。
对于 Ubuntu 或 RHEL 等传统操作系统,请改用 标准安装路径。
通用先决条件
以下先决条件适用于所有提供商:
- 满足Bootstrap 主机要求的 bootstrap 主机。
- 来自 customer portal 的 Core Package。
- Alauda Container Platform Kubeadm Provider 软件包。
- 适用于目标平台的基础设施提供商软件包。
- bootstrap 主机与目标 IaaS 平台 API 端点之间的网络连通性。请参阅网络。
- 为
global 控制平面和工作节点规划 IP 和主机名。有关各提供商使用的资源模型,请参阅基础设施资源。
global 集群的稳定 Kubernetes API 端点。创建集群之前,请在规划控制平面端点中选择并验证特定于提供商的端点模式。
- 平台访问地址、注册表地址以及 Pod 和 Service CIDR 范围。
- 对于使用 ACP 提供的 Alauda OS 镜像的 x86_64 节点,底层 CPU 必须支持
x86-64-v2 ISA 基线。请参阅OS 支持矩阵。
命名约定(必需)
此规则适用于此安装路径支持的每个基础设施提供商——Huawei DCS、Huawei Cloud Stack、VMware vSphere,以及未来添加的任何提供商。您在步骤 4 中编写的每个 manifest 都必须遵循此规则。错误命名这些资源会导致两种不同的故障,具体如下:一种会导致初始置备失败,另一种只会在灾难恢复期间出现。
- CAPI
Cluster 和提供商的基础设施集群资源(例如 Huawei DCS 的 DCSCluster 或 Huawei Cloud Stack 的 HCSCluster;每个提供商都有自己的对应资源)必须严格命名为 global。cpaas-installer 按字面名称查找这些资源,并且只有当基础设施集群命名为 global 时,Huawei Cloud Stack provider 才会分配 global ELB 监听器端口(注册表和控制台使用 11443,DR etcd 同步使用 2379,Web 访问使用 443)。使用其他名称会静默破坏注册表拉取、DR etcd 同步和 Web 控制台。
- 每个其他 CAPI 资源(
KubeadmControlPlane、KubeadmConfigTemplate、MachineDeployment)以及每个其他提供商基础设施资源(machine template、IP/主机名池、machine config pool 及其他特定于提供商的资源)都必须使用带有 global- 前缀的名称。DR(故障转移)机制使用此前缀来识别由 global 集群拥有的资源。不带 global- 前缀的 global 集群资源对 DR 不可见,并会导致备用集群的机器在故障转移时被删除——集群将正常置备并运行,但在首次执行 DR 时会丢失节点。这是硬性要求,而不是风格约定。
Cluster.spec.controlPlaneRef.name 以及任何其他交叉引用都必须与带前缀的名称完全匹配。
Bootstrap 主机要求
bootstrap 主机是一台临时机器,用于从 Core Package 运行 setup.sh。在安装期间,它承载基于 KIND 的 minialauda 管理集群、嵌入式平台 registry、Cluster API bootstrap 和基础设施 provider,以及 cpaas-installer。它不会加入 global 集群,并会在交接完成后移除 — 请参阅停用 Bootstrap 集群。
请根据它所提供的镜像负载而非稳定状态下的负载来确定其规格:在置备集群期间,每个 global 节点都会从此主机拉取平台镜像集。
硬件
CPU 和内存最低要求与 Prerequisites 中记录的控制平面节点逐节点最低要求一致。在传统安装路径中,运行 setup.sh 的机器会成为首个控制平面节点,因此必须满足该要求;在此路径中,bootstrap 主机承担相当的工作 — 提取 Core Package、提供平台 registry,以及运行 bootstrap 控制平面 — 因此其规格应达到相同的最低要求。
对于 arm64,请采用同一页面中的平台 ARM 约定:至少达到 x86_64 配置的 1.5 倍,最好达到 2 倍,即最低配置为 12 个核心 / 24 GB,建议配置为 16 个核心 / 32 GB。
磁盘是最常导致 bootstrap 过程停滞的资源。请从以下三部分进行规划:
操作系统
bootstrap 主机运行传统操作系统,而不是 Alauda OS。它需要一个具有 Bash 和 root 权限的 64 位 Linux 发行版,以便 setup.sh 能够启动第 2 步中所述的 bootstrap 集群。
Node Preprocessing 中的要求 — 经过验证的 OS 和内核列表、SSH 用户以及 sshd_config 设置 — 不适用于此安装路径中的 bootstrap 主机。这些要求适用于平台通过 SSH 加入集群的传统操作系统机器,而 bootstrap 主机不会加入 global 集群。这与 the traditional install path 不同,在传统安装路径中,运行 setup.sh 的机器会成为首个控制平面节点,因此必须满足这些要求。
如果没有其他约束,仍可将该经过验证的列表中的某个发行版作为安全的默认选择。
x86-64-v2 基线适用于集群节点,不适用于 bootstrap 主机
OS 支持矩阵中的 x86-64-v2 ISA 基线适用于从 ACP 提供的 Alauda OS 镜像创建的集群节点。对于运行用户提供的传统操作系统的 x86_64 机器,平台不强制要求满足该基线,因此 bootstrap 主机自身无需满足此要求。请参阅 Node Preprocessing。
如果 bootstrap 主机是位于将作为集群节点基础设施的同一基础设施上的虚拟机,可以检查其 CPU flags,以便及早发现缺失的基线。但这不能替代在实际运行 Alauda OS 节点的主机上验证 x86-64-v2,因为 guest 可见的 flags 取决于 hypervisor 的 CPU model。
网络
bootstrap 主机需要一个在整个安装生命周期内保持不变的静态 IP 地址。HOST_IP 会写入置备节点用于拉取镜像的 cpaas.io/registry-address annotation;对于 Bare Metal,还会写入 registration endpoint 以及其服务证书的 SAN。安装中途更改地址会导致节点置备失败。
兼容性和版本输入
安装前,记录交付包支持的版本集:
安装生命周期

使用以下阶段图确定安装各部分的输入、预期结果以及首个诊断位置。
操作步骤
步骤 1 — 准备通用变量
在 bootstrap 主机上设置通用变量。
export HOST_IP="<bootstrap-host-ip>"
export LOCAL_REGISTRY_ADDRESS="127.0.0.1:11443"
export BOOTSTRAP_REGISTRY_ADDRESS="172.18.0.1:11443"
export NODE_REGISTRY_ADDRESS="${HOST_IP}:11443"
export CONTROL_PLANE_VIP="<global-control-plane-vip>"
export PLATFORM_HOST="<platform-access-domain-or-vip>"
export REGISTRY_DOMAIN="<platform-registry-domain-or-vip>:11443"
export CLUSTER_CIDR="100.3.0.0/16"
export SERVICE_CIDR="100.4.0.0/16"
export KUBE_OVN_JOIN_CIDR="<kube-ovn-join-cidr>"
export K8S_VERSION="<target-kubernetes-version>"
export INGRESS_CLASS_NAME="global-alb2"
export PROVIDER_SECRET_NAME="global-secret"
# Use v-prefixed semver that matches the target Alauda OS image.
从 bootstrap 主机推送软件包时使用 LOCAL_REGISTRY_ADDRESS。在 AppRelease chart repository 值中使用 BOOTSTRAP_REGISTRY_ADDRESS,因为 provider Pod 会从 bootstrap 集群网络内部读取 chart repository。在 Cluster API registry annotation 中使用 NODE_REGISTRY_ADDRESS(bootstrap 主机的 registry,<bootstrap-host-ip>:11443),因为置备的 global 节点在置备期间必须通过其子网可访问的地址拉取镜像。这是一个临时值:global 集群自身的 registry 启动后,安装程序会自动将 cpaas.io/registry-address annotation 重写为永久平台 registry,将 Cluster 和 DCSCluster 上的 annotation 更新为该值,因此后续调谐会从 global 集群而不是 bootstrap 主机拉取镜像。
请区分 Registry 的格式:
/v2/ 片段仅属于 Registry HTTP API 请求。请勿将其添加到镜像引用、cpaas.io/registry-address annotation 或 AppRelease repoURL 中。
步骤 2 — 创建 Bootstrap 集群
使用 Bash 运行 Core Package 提供的 bootstrap 脚本。该脚本会在 bootstrap 主机上启动一个名为 minialauda 的临时、基于 KIND 的 bootstrap 集群——这是仅用于置备 global 集群的临时 Cluster API 管理集群。脚本完成后,从 bootstrap 控制平面容器中取出匹配的 kubectl 客户端,使其可在 bootstrap 主机上使用,并配置导出的 kubeconfig。
mkdir -p /root/cpaas-install
tar -xvf <core-package> -C /root/cpaas-install
cd /root/cpaas-install/installer
bash setup.sh
mkdir -p "${HOME}/.local/bin" ~/.kube
if ! command -v kubectl >/dev/null 2>&1; then
nerdctl cp minialauda-control-plane:/usr/bin/kubectl \
"${HOME}/.local/bin/kubectl"
chmod 0755 "${HOME}/.local/bin/kubectl"
export PATH="${HOME}/.local/bin:${PATH}"
fi
cp /var/cpaas/data/alauda.kubeconfig ~/.kube/config
kubectl get nodes
bootstrap 脚本会置备内嵌 registry、Cluster API 控制平面以及驱动 global 集群安装的安装程序组件。
默认情况下,内嵌的 bootstrap Registry 为匿名访问,不会创建 global-registry-auth Secret。如果在 bootstrap 设置期间同时配置了 Registry 用户名和密码,setup.sh 会在 cpaas-system 中创建该 Secret。
步骤 3 — 上传并安装 Provider 软件包
将 Kubeadm provider 软件包和 infrastructure provider 软件包上传到本地 registry。
以下 AppRelease 示例使用默认的匿名 bootstrap Registry,因此不会引用 global-registry-auth。
如果 bootstrap Registry 需要身份验证,请在创建任何 provider AppRelease 之前确认 Secret 已存在:
kubectl -n cpaas-system get secret global-registry-auth -o name
然后在应用每个 provider AppRelease 之前,为其添加两个身份验证字段:
spec:
source:
chartPullSecret: global-registry-auth
values:
global:
registry:
imagePullSecrets:
- global-registry-auth
如果 Registry 为匿名访问,请勿创建占位 Secret,也不要添加这些字段。bootstrap Secret 也不是凭证传输机制:默认情况下,业务集群通过 global 集群的 public-registry-credential 流程接收其 Registry 拉取 Secret;业务集群也可以按照为业务集群选择镜像 Registry中的说明绑定到专用 registry。请勿将 bootstrap global-registry-auth Secret 复制到业务集群中。
为什么每个 provider 的 cluster.type 都是 Baremetal
以下选项卡中的 AppRelease 值都设置了 global.cluster.type: Baremetal。这是 chart 内部分类器,而不是 IaaS provider 名称。对于 Huawei DCS、VMware vSphere、Huawei Cloud Stack 和 Bare Metal 的 global 安装,请保留 Baremetal。该值决定平台如何配置节点级组件;它不会选择 infrastructure provider。
设置 provider 软件包路径和 chart 版本。
export DCS_PROVIDER_PACK="/root/cluster-api-provider-dcs.amd64.<version>.tgz"
export KUBEADM_PROVIDER_PACK="/root/cluster-api-provider-kubeadm.amd64.<version>.tgz"
export DCS_PROVIDER_VERSION="<dcs-provider-chart-version>"
export KUBEADM_PROVIDER_VERSION="<kubeadm-provider-chart-version>"
上传软件包。
/root/cpaas-install/installer/res/amd64/packtool pack push \
-r "${LOCAL_REGISTRY_ADDRESS}" -c "${DCS_PROVIDER_PACK}"
/root/cpaas-install/installer/res/amd64/packtool pack push \
-r "${LOCAL_REGISTRY_ADDRESS}" -c "${KUBEADM_PROVIDER_PACK}"
创建并应用 Kubeadm provider 和 DCS provider 的 AppRelease 资源。
mkdir -p /root/yamls
export DCS_PROVIDER_APPRELEASES="/root/yamls/dcs-provider-appreleases.yaml"
cat > "${DCS_PROVIDER_APPRELEASES}" <<EOF
---
apiVersion: operator.alauda.io/v1alpha1
kind: AppRelease
metadata:
annotations:
auto-recycle: "true"
interval-sync: "true"
name: cluster-api-provider-kubeadm
namespace: cpaas-system
spec:
destination:
cluster: ""
namespace: ""
source:
charts:
- name: ait/chart-cluster-api-provider-kubeadm
releaseName: cluster-api-provider-kubeadm
targetRevision: ${KUBEADM_PROVIDER_VERSION}
repoURL: ${BOOTSTRAP_REGISTRY_ADDRESS}
timeout: 120
values:
global:
albName: ${INGRESS_CLASS_NAME}
auth:
default_admin: admin@cpaas.io
cluster:
isGlobal: true
name: global
networkType: kube-ovn
type: Baremetal
host: ${PLATFORM_HOST}
ingress:
ingressClassName: ${INGRESS_CLASS_NAME}
labelBaseDomain: cpaas.io
namespace: cpaas-system
platformUrl: https://${PLATFORM_HOST}
protectSecretFiles:
enabled: false
region: global
registry:
address: ${BOOTSTRAP_REGISTRY_ADDRESS}
replicas: 1
scheme: https
---
apiVersion: operator.alauda.io/v1alpha1
kind: AppRelease
metadata:
annotations:
auto-recycle: "true"
interval-sync: "true"
name: cluster-api-provider-dcs
namespace: cpaas-system
spec:
destination:
cluster: ""
namespace: ""
source:
charts:
- name: ait/chart-cluster-api-provider-dcs
releaseName: cluster-api-provider-dcs
targetRevision: ${DCS_PROVIDER_VERSION}
repoURL: ${BOOTSTRAP_REGISTRY_ADDRESS}
timeout: 120
values:
global:
albName: ${INGRESS_CLASS_NAME}
auth:
default_admin: admin@cpaas.io
cluster:
isGlobal: true
name: global
networkType: kube-ovn
type: Baremetal
host: ${PLATFORM_HOST}
ingress:
ingressClassName: ${INGRESS_CLASS_NAME}
labelBaseDomain: cpaas.io
namespace: cpaas-system
platformUrl: https://${PLATFORM_HOST}
protectSecretFiles:
enabled: false
region: global
registry:
address: ${BOOTSTRAP_REGISTRY_ADDRESS}
replicas: 1
scheme: https
EOF
kubectl apply -f "${DCS_PROVIDER_APPRELEASES}"
until kubectl get crd kubeadmcontrolplanes.controlplane.cluster.x-k8s.io --ignore-not-found 2>/dev/null | grep -q kubeadmcontrolplanes.controlplane.cluster.x-k8s.io; do
sleep 10
done
until kubectl get crd dcsclusters.infrastructure.cluster.x-k8s.io --ignore-not-found 2>/dev/null | grep -q dcsclusters.infrastructure.cluster.x-k8s.io; do
sleep 10
done
设置 provider 软件包路径和 chart 版本。
export VSPHERE_PROVIDER_PACK="/root/cluster-api-provider-vsphere.amd64.<version>.tgz"
export KUBEADM_PROVIDER_PACK="/root/cluster-api-provider-kubeadm.amd64.<version>.tgz"
export VSPHERE_PROVIDER_VERSION="<vsphere-provider-chart-version>"
export KUBEADM_PROVIDER_VERSION="<kubeadm-provider-chart-version>"
上传软件包。
/root/cpaas-install/installer/res/amd64/packtool pack push \
-r "${LOCAL_REGISTRY_ADDRESS}" -c "${VSPHERE_PROVIDER_PACK}"
/root/cpaas-install/installer/res/amd64/packtool pack push \
-r "${LOCAL_REGISTRY_ADDRESS}" -c "${KUBEADM_PROVIDER_PACK}"
创建并应用 Kubeadm provider 和 VMware vSphere provider 的 AppRelease 资源。
mkdir -p /root/yamls
export VSPHERE_PROVIDER_APPRELEASES="/root/yamls/vsphere-provider-appreleases.yaml"
cat > "${VSPHERE_PROVIDER_APPRELEASES}" <<EOF
---
apiVersion: operator.alauda.io/v1alpha1
kind: AppRelease
metadata:
annotations:
auto-recycle: "true"
interval-sync: "true"
name: cluster-api-provider-kubeadm
namespace: cpaas-system
spec:
destination:
cluster: ""
namespace: ""
source:
charts:
- name: ait/chart-cluster-api-provider-kubeadm
releaseName: cluster-api-provider-kubeadm
targetRevision: ${KUBEADM_PROVIDER_VERSION}
repoURL: ${BOOTSTRAP_REGISTRY_ADDRESS}
timeout: 120
values:
global:
albName: ${INGRESS_CLASS_NAME}
auth:
default_admin: admin@cpaas.io
cluster:
isGlobal: true
name: global
networkType: kube-ovn
type: Baremetal
host: ${PLATFORM_HOST}
ingress:
ingressClassName: ${INGRESS_CLASS_NAME}
labelBaseDomain: cpaas.io
namespace: cpaas-system
platformUrl: https://${PLATFORM_HOST}
protectSecretFiles:
enabled: false
region: global
registry:
address: ${BOOTSTRAP_REGISTRY_ADDRESS}
replicas: 1
scheme: https
---
apiVersion: operator.alauda.io/v1alpha1
kind: AppRelease
metadata:
annotations:
auto-recycle: "true"
interval-sync: "true"
name: cluster-api-provider-vsphere
namespace: cpaas-system
spec:
destination:
cluster: ""
namespace: ""
source:
charts:
- name: ait/chart-cluster-api-provider-vsphere
releaseName: cluster-api-provider-vsphere
targetRevision: ${VSPHERE_PROVIDER_VERSION}
repoURL: ${BOOTSTRAP_REGISTRY_ADDRESS}
timeout: 120
values:
global:
albName: ${INGRESS_CLASS_NAME}
auth:
default_admin: admin@cpaas.io
cluster:
isGlobal: true
name: global
networkType: kube-ovn
type: Baremetal
host: ${PLATFORM_HOST}
ingress:
ingressClassName: ${INGRESS_CLASS_NAME}
labelBaseDomain: cpaas.io
namespace: cpaas-system
platformUrl: https://${PLATFORM_HOST}
protectSecretFiles:
enabled: false
region: global
registry:
address: ${BOOTSTRAP_REGISTRY_ADDRESS}
replicas: 1
scheme: https
EOF
kubectl apply -f "${VSPHERE_PROVIDER_APPRELEASES}"
until kubectl get crd kubeadmcontrolplanes.controlplane.cluster.x-k8s.io --ignore-not-found 2>/dev/null | grep -q kubeadmcontrolplanes.controlplane.cluster.x-k8s.io; do
sleep 10
done
until kubectl get crd vsphereclusters.infrastructure.cluster.x-k8s.io --ignore-not-found 2>/dev/null | grep -q vsphereclusters.infrastructure.cluster.x-k8s.io; do
sleep 10
done
设置 provider 软件包路径和 chart 版本。
export HCS_PROVIDER_PACK="/root/cluster-api-provider-hcs.amd64.<version>.tgz"
export KUBEADM_PROVIDER_PACK="/root/cluster-api-provider-kubeadm.amd64.<version>.tgz"
export HCS_PROVIDER_VERSION="<hcs-provider-chart-version>"
export KUBEADM_PROVIDER_VERSION="<kubeadm-provider-chart-version>"
上传软件包。
/root/cpaas-install/installer/res/amd64/packtool pack push \
-r "${LOCAL_REGISTRY_ADDRESS}" -c "${HCS_PROVIDER_PACK}"
/root/cpaas-install/installer/res/amd64/packtool pack push \
-r "${LOCAL_REGISTRY_ADDRESS}" -c "${KUBEADM_PROVIDER_PACK}"
创建并应用 Kubeadm provider 和 HCS provider 的 AppRelease 资源。
mkdir -p /root/yamls
export HCS_PROVIDER_APPRELEASES="/root/yamls/hcs-provider-appreleases.yaml"
cat > "${HCS_PROVIDER_APPRELEASES}" <<EOF
---
apiVersion: operator.alauda.io/v1alpha1
kind: AppRelease
metadata:
annotations:
auto-recycle: "true"
interval-sync: "true"
name: cluster-api-provider-kubeadm
namespace: cpaas-system
spec:
destination:
cluster: ""
namespace: ""
source:
charts:
- name: ait/chart-cluster-api-provider-kubeadm
releaseName: cluster-api-provider-kubeadm
targetRevision: ${KUBEADM_PROVIDER_VERSION}
repoURL: ${BOOTSTRAP_REGISTRY_ADDRESS}
timeout: 120
values:
global:
albName: ${INGRESS_CLASS_NAME}
auth:
default_admin: admin@cpaas.io
cluster:
isGlobal: true
name: global
networkType: kube-ovn
type: Baremetal
host: ${PLATFORM_HOST}
ingress:
ingressClassName: ${INGRESS_CLASS_NAME}
labelBaseDomain: cpaas.io
namespace: cpaas-system
platformUrl: https://${PLATFORM_HOST}
protectSecretFiles:
enabled: false
region: global
registry:
address: ${BOOTSTRAP_REGISTRY_ADDRESS}
replicas: 1
scheme: https
---
apiVersion: operator.alauda.io/v1alpha1
kind: AppRelease
metadata:
annotations:
auto-recycle: "true"
interval-sync: "true"
name: cluster-api-provider-hcs
namespace: cpaas-system
spec:
destination:
cluster: ""
namespace: ""
source:
charts:
- name: ait/chart-cluster-api-provider-hcs
releaseName: cluster-api-provider-hcs
targetRevision: ${HCS_PROVIDER_VERSION}
repoURL: ${BOOTSTRAP_REGISTRY_ADDRESS}
timeout: 120
values:
global:
albName: ${INGRESS_CLASS_NAME}
auth:
default_admin: admin@cpaas.io
cluster:
isGlobal: true
name: global
networkType: kube-ovn
type: Baremetal
host: ${PLATFORM_HOST}
ingress:
ingressClassName: ${INGRESS_CLASS_NAME}
labelBaseDomain: cpaas.io
namespace: cpaas-system
platformUrl: https://${PLATFORM_HOST}
protectSecretFiles:
enabled: false
region: global
registry:
address: ${BOOTSTRAP_REGISTRY_ADDRESS}
replicas: 1
scheme: https
EOF
kubectl apply -f "${HCS_PROVIDER_APPRELEASES}"
until kubectl get crd kubeadmcontrolplanes.controlplane.cluster.x-k8s.io --ignore-not-found 2>/dev/null | grep -q kubeadmcontrolplanes.controlplane.cluster.x-k8s.io; do
sleep 10
done
until kubectl get crd hcsclusters.infrastructure.cluster.x-k8s.io --ignore-not-found 2>/dev/null | grep -q hcsclusters.infrastructure.cluster.x-k8s.io; do
sleep 10
done
设置 provider 软件包路径和 chart 版本。
export BAREMETAL_PROVIDER_PACK="/root/cluster-api-provider-baremetal.amd64.<version>.tgz"
export KUBEADM_PROVIDER_PACK="/root/cluster-api-provider-kubeadm.amd64.<version>.tgz"
export BAREMETAL_PROVIDER_VERSION="<baremetal-provider-chart-version>"
export KUBEADM_PROVIDER_VERSION="<kubeadm-provider-chart-version>"
上传软件包。
/root/cpaas-install/installer/res/amd64/packtool pack push \
-r "${LOCAL_REGISTRY_ADDRESS}" -c "${BAREMETAL_PROVIDER_PACK}"
/root/cpaas-install/installer/res/amd64/packtool pack push \
-r "${LOCAL_REGISTRY_ADDRESS}" -c "${KUBEADM_PROVIDER_PACK}"
将 Bare Metal Alauda OS 镜像导入同一个本地 registry。provider 软件包不包含这些镜像。Bare Metal OS 下载内容是一个 OCI 镜像布局归档,其中包含两个具有相同 tag 的镜像,并且两个镜像都是必需的——请参阅按 Provider 划分的镜像格式:
使用 ctr 将归档导入 bootstrap 主机上的专用 containerd namespace,并列出其创建的镜像名称。/var/lib/containerd 至少需要与归档大小相等的可用空间:
export OS_IMAGE_ARCHIVE="/root/<bare-metal-os-archive>.tar"
ctr -n bare-metal-os images import --all-platforms --no-unpack "${OS_IMAGE_ARCHIVE}"
ctr -n bare-metal-os images ls -q
输出会列出具有相同 tag 的两个镜像:
baremetal-base-image-iso:<os-image-tag>
baremetal-base-image:<os-image-tag>
将 OS_IMAGE_TAG 设置为该 tag,然后推送两个镜像,并确认 registry 为每个镜像列出了该 tag:
export OS_IMAGE_TAG="<os-image-tag>"
for repo in baremetal-base-image baremetal-base-image-iso; do
ctr -n bare-metal-os images push --plain-http \
"${LOCAL_REGISTRY_ADDRESS}/tkestack/${repo}:${OS_IMAGE_TAG}" "${repo}:${OS_IMAGE_TAG}"
curl -s "http://${LOCAL_REGISTRY_ADDRESS}/v2/tkestack/${repo}/tags/list"
done
如果 bootstrap Registry 需要身份验证,请通过 -u <username>:<password> 将其凭证传递给两个命令。将镜像保留在 bare-metal-os namespace 中:将基础镜像上传到 global Registry会在安装后再次推送这些镜像。
创建并应用 Kubeadm provider 和 Bare Metal provider 的 AppRelease 资源。
Bare Metal provider AppRelease 还会填充镜像目录。该 chart 创建一个不包含条目的 elemental-image-catalog ConfigMap,因此 provider.imageCatalog.images 必须将 ${K8S_VERSION} 映射到上文导入的 baremetal-base-image;该 chart 会在 repository 前添加 global.registry.address 前缀。没有该条目时,每个 global BaremetalMachine 都会以 ImageResolved=False / Reason=ImageCatalogMiss 进入 Failed,且不会置备任何节点。
Bare Metal Bootstrap Endpoint
在 bootstrap 期间,global 集群尚未将控制权移交给最终 VIP。将裸金属注册路径保留在 bootstrap 主机上:global.platformUrl 指向 bootstrap 主机,而 elemental.server.url 指向 https://<bootstrap-host-ip>:12443。在此阶段,请勿在用于 global 机器的 MachineRegistration 上设置 baremetal.cluster.io/system-agent-server-url。对于 Bare Metal DR,请将 baremetal.cluster.io/system-agent-auth-scope: global 添加到该 bootstrap MachineRegistration;这是移交前唯一需要的 DR 专用 system-agent annotation。之后,DR 移交 Job 会将这些机器移动到 https://<CONTROL_PLANE_VIP>:6443 处的本地直接 API endpoint。
安装 Bare Metal provider 之前,准备好 global-alb2 使用的 bootstrap HTTPS 证书。elemental-operator 会从 cpaas-system/dex.tls 挂载 CA,而当 elemental.tls.agentTLSMode 为 strict 时,elemental-system-agent 会使用该 CA。Secret 必须包含 tls.crt、tls.key 以及在 AppRelease 中配置的 CA bundle key,以下示例中为 ca.crt。由于 bootstrap endpoint 为 https://${HOST_IP}:12443,服务证书必须将 ${HOST_IP} 包含为 IP SAN。
如果 bootstrap 集群中可用 cert-manager,请使用 bootstrap 本地 CA 创建或刷新 dex.tls:
kubectl get crd certificates.cert-manager.io issuers.cert-manager.io
kubectl -n cpaas-system apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: baremetal-bootstrap-selfsigned
namespace: cpaas-system
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: baremetal-bootstrap-ca
namespace: cpaas-system
spec:
secretName: baremetal-bootstrap-ca
commonName: baremetal-bootstrap-ca
duration: 87600h
renewBefore: 720h
isCA: true
privateKey:
algorithm: RSA
size: 2048
usages:
- cert sign
- crl sign
issuerRef:
name: baremetal-bootstrap-selfsigned
kind: Issuer
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: baremetal-bootstrap-ca
namespace: cpaas-system
spec:
ca:
secretName: baremetal-bootstrap-ca
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: dex-tls-bootstrap
namespace: cpaas-system
spec:
secretName: dex.tls
commonName: ${HOST_IP}
duration: 87600h
renewBefore: 720h
privateKey:
algorithm: RSA
size: 2048
usages:
- digital signature
- key encipherment
- server auth
ipAddresses:
- "${HOST_IP}"
- "127.0.0.1"
dnsNames:
- global-alb2
- global-alb2.cpaas-system.svc
- global-alb2.cpaas-system.svc.cluster.local
issuerRef:
name: baremetal-bootstrap-ca
kind: Issuer
EOF
kubectl -n cpaas-system wait certificate/baremetal-bootstrap-ca \
--for=condition=Ready \
--timeout=120s
kubectl -n cpaas-system wait certificate/dex-tls-bootstrap \
--for=condition=Ready \
--timeout=120s
验证 Secret 是否包含预期的 key,以及证书是否对 bootstrap endpoint 有效:
kubectl -n cpaas-system get secret dex.tls \
-o jsonpath='{.data.tls\.crt}{" "}{.data.tls\.key}{" "}{.data.ca\.crt}{"\n"}'
tmp_dir=$(mktemp -d)
trap 'rm -rf "${tmp_dir}"' EXIT
kubectl -n cpaas-system get secret dex.tls \
-o jsonpath='{.data.ca\.crt}' | base64 -d > "${tmp_dir}/ca.crt"
kubectl -n cpaas-system get secret dex.tls \
-o jsonpath='{.data.tls\.crt}' | base64 -d > "${tmp_dir}/tls.crt"
openssl verify -CAfile "${tmp_dir}/ca.crt" "${tmp_dir}/tls.crt"
openssl x509 -in "${tmp_dir}/tls.crt" -noout -text | grep "IP Address:${HOST_IP}"
echo | openssl s_client \
-connect "${HOST_IP}:12443" \
-servername "${HOST_IP}" \
-CAfile "${tmp_dir}/ca.crt" \
-verify_return_error 2>&1 | grep "Verify return code: 0 (ok)"
如果 bootstrap ALB 仍提供旧证书,请重启它并再次验证:
kubectl -n cpaas-system rollout restart deploy/global-alb2
kubectl -n cpaas-system rollout status deploy/global-alb2
请勿将此 bootstrap dex.tls 包含在 dcs-import-extra-resources 中。它仅用于临时 bootstrap endpoint。最终 global 集群的 dex.tls 由安装程序的平台证书流程创建或维护。对于 DR,请按照步骤 8 中的 thirdParty 控制台证书指导操作,使两端都提供针对稳定平台域名的证书。只有当客户端确实通过该 VIP 直接访问平台 HTTPS 时,控制平面 VIP 才需要包含在该平台证书中。端口 11443 上的 Global registry 提供其自身的 global-registry-server 证书链,不由 console.cert 或 dex.tls 配置。
mkdir -p /root/yamls
export BAREMETAL_PROVIDER_APPRELEASES="/root/yamls/baremetal-provider-appreleases.yaml"
cat > "${BAREMETAL_PROVIDER_APPRELEASES}" <<EOF
---
apiVersion: operator.alauda.io/v1alpha1
kind: AppRelease
metadata:
annotations:
auto-recycle: "true"
interval-sync: "true"
name: cluster-api-provider-kubeadm
namespace: cpaas-system
spec:
destination:
cluster: ""
namespace: ""
source:
charts:
- name: ait/chart-cluster-api-provider-kubeadm
releaseName: cluster-api-provider-kubeadm
targetRevision: ${KUBEADM_PROVIDER_VERSION}
repoURL: ${BOOTSTRAP_REGISTRY_ADDRESS}
timeout: 120
values:
global:
albName: ${INGRESS_CLASS_NAME}
auth:
default_admin: admin@cpaas.io
cluster:
isGlobal: true
name: global
networkType: kube-ovn
type: Baremetal
host: ${PLATFORM_HOST}
ingress:
ingressClassName: ${INGRESS_CLASS_NAME}
labelBaseDomain: cpaas.io
namespace: cpaas-system
platformUrl: https://${PLATFORM_HOST}
protectSecretFiles:
enabled: false
region: global
registry:
address: ${BOOTSTRAP_REGISTRY_ADDRESS}
replicas: 1
scheme: https
---
apiVersion: operator.alauda.io/v1alpha1
kind: AppRelease
metadata:
annotations:
auto-recycle: "true"
interval-sync: "true"
name: cluster-api-provider-baremetal
namespace: cpaas-system
spec:
destination:
cluster: ""
namespace: ""
source:
charts:
- name: ait/chart-cluster-api-provider-baremetal
releaseName: cluster-api-provider-baremetal
targetRevision: ${BAREMETAL_PROVIDER_VERSION}
repoURL: ${BOOTSTRAP_REGISTRY_ADDRESS}
timeout: 120
values:
global:
albName: ${INGRESS_CLASS_NAME}
auth:
default_admin: admin@cpaas.io
cluster:
isGlobal: true
name: global
networkType: kube-ovn
type: Baremetal
host: ${PLATFORM_HOST}
ingress:
ingressClassName: ${INGRESS_CLASS_NAME}
tls:
secretName: dex.tls
labelBaseDomain: cpaas.io
namespace: cpaas-system
platformUrl: https://${HOST_IP}
protectSecretFiles:
enabled: false
region: global
registry:
address: ${BOOTSTRAP_REGISTRY_ADDRESS}
replicas: 1
scheme: https
provider:
imageCatalog:
images:
${K8S_VERSION}:
repository: tkestack/baremetal-base-image
tag: ${OS_IMAGE_TAG}
handoffHook:
# Normal non-DR bootstrap baseline. Bare Metal DR must patch this to true
# before calling the installer API, as shown in Optional Disaster Recovery Deployment.
directAPIServer: false
controlPlaneVIP: ${CONTROL_PLANE_VIP}
delivery:
enabled: true
mode: always
elemental:
server:
url: https://${HOST_IP}:12443
systemAgent:
authMode: shared
# Normal non-DR bootstrap baseline. Bare Metal DR must enable the split
# and set sharedAuthReadOnly according to this side's active/standby role.
splitAuthEnabled: false
serviceAccountName: baremetal-system-agent
globalServiceAccountName: baremetal-global-system-agent
sharedAuthReadOnly: false
tls:
agentTLSMode: strict
caCertSecretName: dex.tls
caCertSecretKey: ca.crt
EOF
kubectl apply -f "${BAREMETAL_PROVIDER_APPRELEASES}"
until kubectl get crd kubeadmcontrolplanes.controlplane.cluster.x-k8s.io --ignore-not-found 2>/dev/null | grep -q kubeadmcontrolplanes.controlplane.cluster.x-k8s.io; do
sleep 10
done
until kubectl get crd baremetalclusters.infrastructure.cluster.x-k8s.io --ignore-not-found 2>/dev/null | grep -q baremetalclusters.infrastructure.cluster.x-k8s.io; do
sleep 10
done
until kubectl get crd machineinventories.elemental.cattle.io --ignore-not-found 2>/dev/null | grep -q machineinventories.elemental.cattle.io; do
sleep 10
done
上面的完整 AppRelease 有意展示了常规的非 DR 基线。对于 Bare Metal DR 安装,请勿在这些基线值保持不变的情况下调用安装程序 API。请先应用可选灾难恢复部署中的特定角色 DR patch,然后验证该 bootstrap 集群上的生效 AppRelease 值。
provider 启动后,验证 chart 值是否已被接受。如果出现带有 --system-agent-auth-mode 等未知 flag 的 CrashLoopBackOff,表示 AppRelease chart 与 elemental-operator 镜像不匹配;请先安装来自同一发行版负载的 chart 和镜像,然后再继续。
kubectl -n cpaas-system get pods | grep -E 'cluster-api-provider-baremetal|elemental'
kubectl -n cpaas-system logs deploy/elemental-operator --tail=100
kubectl -n cpaas-system get configmap elemental-image-catalog -o jsonpath='{.data}'
目录输出必须包含 ${K8S_VERSION} 作为 key。空输出或 {} 表示 provider.imageCatalog.images 值未传递到 release;请在应用 global manifest 之前修复 AppRelease。
步骤 4 — 配置特定于 Provider 的 global Manifest
为 global 集群创建一个特定于 provider 的 manifest。该 manifest 使用与业务集群相同的 provider 资源,但还必须包含平台控制平面所需的 global 专用标签、annotation、registry 值、与安装程序兼容的 kubeadm 设置以及持久化数据路径。
请将 provider 创建指南作为详细的资源参考:
将通用先决条件中的命名约定应用于你将在下文编写的 manifest 中的每个资源。
将 KubeadmControlPlane.spec.kubeadmConfigSpec.format 设置为目标 provider 接受的值。这是一个 API 字段:输入 cloud-config,而不是 cloud-init。对于以下 provider,cloud-init 是接收并应用生成的 cloud-config 数据的客户机操作系统软件。Provider controller 会强制执行该字段值:
在渲染前,为 DCS global manifest 设置输出路径。
export GLOBAL_DCS_YAML="/root/yamls/new-global.yaml"
按照云凭证中的说明,在组装 manifest 前创建 DCS API 凭证 Secret。不要在 manifest 中将其编写为 YAML:第 5 步使用 kubectl apply 应用 manifest 时,会将凭证记录在 Secret 的 kubectl.kubernetes.io/last-applied-configuration annotation 中。manifest 通过 DCSCluster.spec.credentialSecretRef 引用该凭证。
DCS global manifest 随后必须在 cpaas-system namespace 中包含以下资源:
使用在 Huawei DCS 上创建集群和Huawei DCS 基础设施资源中的 DCS 资源字段。对于 global 集群,还需满足以下要求:
- 将
Cluster.metadata.name 和 DCSCluster.metadata.name 设置为 global(基础设施集群与 CAPI 共享 Cluster 名称)。为其他每个 CAPI 资源和 provider 资源添加 global- 前缀;下方的连接片段使用 KubeadmControlPlane.metadata.name: global-kcp。
- 将
DCSCluster.spec.credentialSecretRef.name 设置为 ${PROVIDER_SECRET_NAME}。第 7 步会将此 Secret 导入最终的 global 集群。
- 添加
Cluster.metadata.labels.is-global: "true" 和 Cluster.metadata.labels.cluster-type: DCS。
- 添加带有
${NODE_REGISTRY_ADDRESS} 的 Cluster.metadata.annotations["cpaas.io/registry-address"]。
- 为 Alauda OS 设置
KubeadmControlPlane.spec.kubeadmConfigSpec.format: ignition。
- 对于 DCS Provider
v1.0.22,使用外部 LoadBalancer 并设置 DCSCluster.spec.controlPlaneLoadBalancer.type: external;或者在与 ACP v4.4+ 搭配时使用 type: internal Self-built VIP。对于内部模式,在控制平面 Layer 2 网络中预留 IPv4 VIP,并使用在 Huawei DCS 上创建集群中的 YAML 工作流。对于外部模式,在规划控制平面端点中验证监听器、后端、运行状况检查和可达性。
- 保留
KubeadmControlPlane.spec.kubeadmConfigSpec.users 条目,并确保 sshAuthorizedKeys 列表非空(boot 用户)。DCS ignition 格式会拒绝空 SSH 密钥列表,因此即使是不计划通过 SSH 访问的 global 集群,此字段也是必需的。如果不需要交互式密钥,请参阅解析占位符值了解应提供的内容。
- 保留未加密的 kubeadm 文件、kubelet 补丁、审计策略和安装程序 RBAC 条目。文件内容(PodSecurity admission 配置、kubelet 补丁和审计策略),以及完整的
clusterConfiguration、preKubeadmCommands、postKubeadmCommands 和初始化及加入节点注册补丁,都与业务集群相同。请从完整的 KubeadmControlPlane 配置附录中复制这些内容,或引用其中说明的 dcs-kubernetes-<major.minor>-files Secret。下方的连接片段只展示叠加在基础配置之上的 global 特定字段。
- 对于普通的非 DR 部署,不要设置
DCSCluster.spec.encryptionProviderConfigRef。保留附录中的 clusterConfiguration.apiServer.extraArgs.encryption-provider-config:DCS provider 会自行将 /etc/kubernetes/encryption-provider.conf 写入每个控制平面节点——第一台机器使用随机密钥,其余机器使用正在运行的 API server 文件——因此该参数能够解析,并且 global 集群会使用 etcd 静态加密的 Secret 运行。不要将该文件添加到 KubeadmControlPlane.spec.kubeadmConfigSpec.files;provider 会删除该路径的任何条目,因此添加在那里也会被静默忽略。请参阅加密 provider 配置的交付方式。
- 将
/var/cpaas 保留为平台状态。如果需要磁盘在滚动替换后继续保留,请在 DCSIpHostnamePool.spec.pool[].persistentDisk 中声明它;不要依赖 DCSMachineTemplate 模板磁盘作为保留状态。
- 对 DCS 本地存储使用具体的
datastoreName 值,除非你已验证所选 datastore 集群可以将卷放置在能够运行目标 VM 的主机上。
片段范围
以下 YAML 是差异片段,而不是可以直接应用的完整 manifest。请将这些特定于 global 的更改合并到根据 DCS 创建集群参考资料准备的 manifest 中,然后应用完整的 manifest 文件。如果希望从完整文件开始,也可以在本页末尾调整操作示例:Huawei DCS 的完整 global Manifest,而不是从片段组装。
以下片段展示了特定于 global 的 Cluster API 连接配置。请使用上面的 DCS 创建集群参考资料填写 provider 资源字段。
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: global
namespace: cpaas-system
labels:
cluster-type: DCS
is-global: "true"
annotations:
capi.cpaas.io/resource-group-version: infrastructure.cluster.x-k8s.io/v1beta1
capi.cpaas.io/resource-kind: DCSCluster
cpaas.io/registry-address: "${NODE_REGISTRY_ADDRESS}"
spec:
clusterNetwork:
pods:
cidrBlocks:
- ${CLUSTER_CIDR}
services:
cidrBlocks:
- ${SERVICE_CIDR}
controlPlaneRef:
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
name: global-kcp
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DCSCluster
name: global
---
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
name: global-kcp
namespace: cpaas-system
annotations:
controlplane.cluster.x-k8s.io/skip-kube-proxy: ""
spec:
replicas: 3
version: ${K8S_VERSION}
rolloutStrategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
machineTemplate:
nodeDrainTimeout: 1m
nodeDeletionTimeout: 5m
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DCSMachineTemplate
name: global-cp-template
kubeadmConfigSpec:
format: ignition
clusterConfiguration:
etcd:
local:
serverCertSANs:
- "${CONTROL_PLANE_VIP}"
- "${PLATFORM_HOST}"
在渲染前,为 VMware vSphere global manifest 设置输出路径。
export GLOBAL_VSPHERE_YAML="/root/yamls/new-global.yaml"
在组装 manifest 前创建两个凭证 Secret,并将其排除在 manifest 之外。第 5 步使用 kubectl apply 应用 manifest,这会将它们的内容记录在每个 Secret 的 kubectl.kubernetes.io/last-applied-configuration annotation 中。
- vCenter 凭证,必须命名为
global-vsphere-credentials——第 7 步的导入 ConfigMap 和 DR 部分会硬编码此确切名称。按照在 VMware vSphere 上创建集群中的说明,使用该固定名称创建凭证,并从 VSphereCluster.spec.identityRef.name 引用它。
- vSphere CPI 凭证,由
ClusterResourceSet 引用。按照同一指南中的说明创建该凭证。
VMware vSphere global manifest 随后必须在 cpaas-system namespace 中包含以下资源:
使用VMware vSphere 基础设施准备准备 vSphere 输入值。使用在 VMware vSphere 上创建集群和VMware vSphere Provider作为基础参考来准备 global 集群 manifest。创建集群指南通过向现有的 global 管理集群应用 manifest 来创建业务集群;它不会自行创建 global 集群。只有在应用以下特定于 global 的要求后,才能复用其中的 vSphere 资源定义:
- 将
Cluster.metadata.name 和 VSphereCluster.metadata.name 设置为 global(基础设施集群与 CAPI 共享 Cluster 名称)。为其他每个 CAPI 资源和 provider 资源添加 global- 前缀;下方的连接片段使用 KubeadmControlPlane.metadata.name: global-kcp。
- 添加
Cluster.metadata.labels.is-global: "true" 和 Cluster.metadata.labels.cluster-type: VSphere。
- 添加带有
${NODE_REGISTRY_ADDRESS} 的 Cluster.metadata.annotations["cpaas.io/registry-address"]。
- 保留平台 controllers 所需的 VMware vSphere annotations,包括 VMware vSphere 创建集群指南中的网络和 CPI annotations。
- 将
VSphereMachineTemplate.spec.template.spec.folder 设置为 /<datacenter>/vm/global,以便 operator 在 vCenter 中识别 global 集群 VM。在 DR 部署中,为主集群和备用集群使用不同的子文件夹,例如 /<datacenter>/vm/global/primary 和 /<datacenter>/vm/global/standby。
- 将
VSphereCluster.spec.identityRef.name 设置为 global-vsphere-credentials。此固定 Secret 名称仅适用于 VMware vSphere global 安装路径;非 global VMware vSphere 集群遵循通用创建集群指南。
- 对于 VMware vSphere Provider
v1.0.16,在应用 manifest 前预配外部 LoadBalancer。使用规划控制平面端点中的约定;此 provider 版本不会部署 Self-built VIP。
- 设置
KubeadmControlPlane.spec.kubeadmConfigSpec.format: cloud-config;如果 provider 将此 API 字段默认为 cloud-config,也可以不设置该字段。cloud-init 是处理该数据的客户机软件;它不是此字段的有效值。
- 保留发布 manifest 的 kubeadm 文件,包括 VMware vSphere 的
/etc/kubernetes/encryption-provider.conf 文件条目、kubelet 补丁、审计策略和安装程序 RBAC 条目。VMware vSphere 通过 KubeadmControlPlane.spec.kubeadmConfigSpec.files 交付此文件;不要遵循 DCS 的 DCSCluster.spec.encryptionProviderConfigRef 模式。
片段范围
以下 YAML 是差异片段,而不是可以直接应用的完整 manifest。请将这些特定于 global 的更改合并到根据 VMware vSphere 创建集群指南准备的 manifest 中,然后应用完整的 manifest 文件。
以下片段展示了特定于 global 的 Cluster API 连接配置。请使用上面的 VMware vSphere 创建集群参考资料填写 provider 资源字段。
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: global
namespace: cpaas-system
labels:
cluster-type: VSphere
is-global: "true"
addons.cluster.x-k8s.io/vsphere-cpi: "enabled"
annotations:
capi.cpaas.io/resource-group-version: infrastructure.cluster.x-k8s.io/v1beta1
capi.cpaas.io/resource-kind: VSphereCluster
cpaas.io/alb-address-type: ClusterAddress
cpaas.io/network-type: kube-ovn
cpaas.io/registry-address: "${NODE_REGISTRY_ADDRESS}"
spec:
clusterNetwork:
pods:
cidrBlocks:
- ${CLUSTER_CIDR}
services:
cidrBlocks:
- ${SERVICE_CIDR}
controlPlaneRef:
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
name: global-kcp
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: VSphereCluster
name: global
---
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
name: global-kcp
namespace: cpaas-system
spec:
replicas: 3
version: "${K8S_VERSION}"
rolloutStrategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
machineTemplate:
nodeDrainTimeout: 1m
nodeDeletionTimeout: 5m
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: VSphereMachineTemplate
name: global-cp-machine-template
kubeadmConfigSpec:
format: cloud-config
clusterConfiguration:
etcd:
local:
serverCertSANs:
- "${CONTROL_PLANE_VIP}"
- "${PLATFORM_HOST}"
在渲染 HCS global manifest 之前,设置其输出路径。
export GLOBAL_HCS_YAML="/root/yamls/new-global.yaml"
HCS global manifest 必须在 cpaas-system 命名空间中包含以下资源:
使用在 Huawei Cloud Stack 上创建集群和Huawei Cloud Stack 基础设施资源中的 HCS 资源字段。对于 global 集群,还需满足以下要求:
- 将
Cluster.metadata.name 和 HCSCluster.metadata.name 设置为 global(基础设施集群与 CAPI 共享 Cluster 名称)。为其他每个 CAPI 资源和 provider 资源添加 global- 前缀;下方的连接片段使用 KubeadmControlPlane.metadata.name: global-kcp。
- 将
HCSCluster.spec.identityRef.name 设置为 ${PROVIDER_SECRET_NAME}。第 7 步会将此 Secret 导入最终的 global 集群。
- 添加
Cluster.metadata.labels.is-global: "true" 和 Cluster.metadata.labels.cluster-type: HCS。
- 添加带有
${NODE_REGISTRY_ADDRESS} 的 Cluster.metadata.annotations["cpaas.io/registry-address"]。
- 设置
KubeadmControlPlane.spec.kubeadmConfigSpec.format: cloud-config;如果 provider 将此 API 字段默认为 cloud-config,也可以不设置该字段。cloud-init 是客户机软件名称,而不是字段值。
- 保留发布 manifest 中的非加密 kubeadm 文件、kubelet 补丁、审计策略和安装程序 RBAC 条目。
- 对于正常的非 DR 部署,不要将
/etc/kubernetes/encryption-provider.conf 添加到 KubeadmControlPlane.spec.kubeadmConfigSpec.files。
- 将
/var/cpaas 作为平台状态保留。需要在节点替换后继续保留时,在 HCSMachineConfigPool.spec.configs[].persistentDisks[] 中声明它;不要依赖 HCSMachineTemplate.spec.template.spec.dataVolumes[] 作为保留状态。
- 为
global 集群使用高可用控制平面。单控制平面的 HCS 集群仅适用于创建阶段,不是推荐的 global 升级路径。
片段范围
以下 YAML 是差异片段,而不是可以直接应用的完整 manifest。请将这些特定于 global 的更改合并到根据 HCS 创建集群参考资料准备的 manifest 中,然后应用完整的 manifest 文件。
以下片段展示了特定于 global 的 Cluster API 连接配置。请使用上面的 HCS 创建集群参考资料填写 provider 资源字段。
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: global
namespace: cpaas-system
labels:
cluster-type: HCS
is-global: "true"
annotations:
capi.cpaas.io/resource-group-version: infrastructure.cluster.x-k8s.io/v1beta1
capi.cpaas.io/resource-kind: HCSCluster
cpaas.io/registry-address: "${NODE_REGISTRY_ADDRESS}"
spec:
clusterNetwork:
pods:
cidrBlocks:
- ${CLUSTER_CIDR}
services:
cidrBlocks:
- ${SERVICE_CIDR}
controlPlaneRef:
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
name: global-kcp
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: HCSCluster
name: global
---
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
name: global-kcp
namespace: cpaas-system
spec:
replicas: 3
version: "${K8S_VERSION}"
rolloutStrategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
machineTemplate:
nodeDrainTimeout: 1m
nodeDeletionTimeout: 5m
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: HCSMachineTemplate
name: global-cp-machine-template
kubeadmConfigSpec:
format: cloud-config
clusterConfiguration:
etcd:
local:
serverCertSANs:
- "${CONTROL_PLANE_VIP}"
- "${PLATFORM_HOST}"
在渲染裸金属 global manifest 之前,设置其输出路径。
export GLOBAL_BAREMETAL_YAML="/root/yamls/new-global.yaml"
裸金属 global manifest 必须在 cpaas-system 命名空间中包含以下资源:
使用在裸金属上创建集群、管理裸金属上的节点和裸金属 Provider作为资源参考。对于 global 集群,还需满足以下要求:
- 将
Cluster.metadata.name 和 BaremetalCluster.metadata.name 设置为 global。为其他每个 CAPI、裸金属和 elemental 资源添加 global- 前缀。
- 添加
Cluster.metadata.labels.cluster-type: ProviderBaremetal。
- 添加带有
${NODE_REGISTRY_ADDRESS} 的 Cluster.metadata.annotations["cpaas.io/registry-address"]。
- 将
SeedImage.spec.baseImage 设置为第 3 步导入的 ISO 镜像 ${BOOTSTRAP_REGISTRY_ADDRESS}/tkestack/baremetal-base-image-iso:${OS_IMAGE_TAG}。重新置备镜像来自第 3 步设置的 elemental-image-catalog 条目;provider 会将该条目的 registry 部分替换为 cpaas.io/registry-address annotation,因此主机会从 ${NODE_REGISTRY_ADDRESS} 拉取该镜像。
- 添加
Cluster.metadata.annotations["cpaas.io/kube-ovn-join-cidr"]、Cluster.metadata.annotations["cpaas.io/sentry-deploy-type"]: Baremetal 和 Cluster.metadata.annotations["cpaas.io/alb-address-type"]: ClusterAddress。
- 设置
KubeadmControlPlane.spec.kubeadmConfigSpec.format: cloud-config;如果 provider 将此 API 字段默认为 cloud-config,也可以不设置该字段。生成的数据由节点上的 cloud-init 处理。
- 设置
KubeadmControlPlane.spec.rolloutStrategy.rollingUpdate.maxSurge: 0。裸金属池无法超额配置物理主机。
- 当发布 manifest 使用 kube-ovn 时,在
KubeadmControlPlane 上保留 controlplane.cluster.x-k8s.io/skip-kube-proxy: ""。
- 将
${CONTROL_PLANE_VIP} 和 ${PLATFORM_HOST} 放入 KubeadmControlPlane.spec.kubeadmConfigSpec.clusterConfiguration.etcd.local.serverCertSANs。
- 在规划控制平面端点中选择裸金属端点模式。对于
Internal,设置 VIP、端口 6443 以及唯一的 vrid。对于 External,设置外部负载均衡器前端地址和端口,并省略 vrid。
- 对于正常的非 DR 部署,可以省略
BaremetalCluster.spec.encryptionProviderConfigRef。对于 DR 部署,按照可选灾难恢复部署中的说明设置它;不要通过将 /etc/kubernetes/encryption-provider.conf 添加到 KubeadmControlPlane.spec.kubeadmConfigSpec.files 来传递该字段。
- 对于裸金属 DR,将
baremetal.cluster.io/system-agent-auth-scope: global 添加到用于 bootstrap global 主机的 MachineRegistration 中。bootstrap 期间不要设置 baremetal.cluster.io/system-agent-server-url。bootstrap ISO 必须通过 bootstrap 主机进行注册;交接作业随后会将 global 机器移动到本地直连 API 端点。
- 如果
global VM 或物理主机在 live-ISO 启动期间没有 DHCP,请在等待 MachineInventory 注册之前,从主机控制台手动配置 NIC。使用在裸金属上创建集群中所述的相同 NetworkManager 操作步骤,将示例地址、网关、DNS 和连接名称替换为该主机的值。
- 在每台
global 主机进行 live-ISO 启动之前,从固件设置时钟,并确保主机之间的时间偏差符合平台的 10 秒要求。请参阅主机时间同步。
- 不要依赖 OS 主机名的副作用。裸金属 provider 会根据 CAPI 和清单对象规范化 kubeadm 节点名称和 provider ID。
- 在 bootstrap 集群中创建的
SeedImage 是 bootstrap 工件。交接后,在活动的 global 集群上创建任何新的 MachineRegistration 或 SeedImage。
片段范围
以下 YAML 是差异片段,而不是可以直接应用的完整 manifest。请将这些特定于 global 的更改合并到根据裸金属创建集群参考资料准备的 manifest 中,然后应用完整的 manifest 文件。
以下片段展示了特定于 global 的 Cluster API 连接配置。请使用裸金属创建集群参考资料填写清单名称、注册配置、镜像引用和可选的工作节点资源。
对于 provider 管理的 Alive,将 <control-plane-load-balancer-type> 设置为 Internal;对于用户预配的 LoadBalancer,将其设置为 External。使用 External 时,删除 vrid。有关每种模式的要求,请参阅规划控制平面端点。
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: global
namespace: cpaas-system
labels:
cluster-type: ProviderBaremetal
annotations:
capi.cpaas.io/resource-group-version: infrastructure.cluster.x-k8s.io/v1beta1
capi.cpaas.io/resource-kind: BaremetalCluster
cpaas.io/kube-ovn-join-cidr: "${KUBE_OVN_JOIN_CIDR}"
cpaas.io/registry-address: "${NODE_REGISTRY_ADDRESS}"
cpaas.io/sentry-deploy-type: Baremetal
cpaas.io/alb-address-type: ClusterAddress
spec:
clusterNetwork:
pods:
cidrBlocks:
- ${CLUSTER_CIDR}
services:
cidrBlocks:
- ${SERVICE_CIDR}
controlPlaneRef:
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
name: global-kcp
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: BaremetalCluster
name: global
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: BaremetalCluster
metadata:
name: global
namespace: cpaas-system
spec:
controlPlaneLoadBalancer:
type: <control-plane-load-balancer-type>
host: ${CONTROL_PLANE_VIP}
port: 6443
# Required only for Internal. Remove this field for External.
vrid: <unique-vrid>
# vipMode defaults to nic. Set it explicitly only when the environment
# requires another supported mode, such as arp or policy_route.
# vipMode: nic
# Required for DR. Omit this field for a normal non-DR deployment unless
# you need to provide a pre-existing encryption-provider.conf.
# encryptionProviderConfigRef:
# name: global-encryption-provider-config
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: MachineInventoryPool
metadata:
name: global-control-plane-pool
namespace: cpaas-system
spec:
clusterName: global
machineInventories:
- global-cp-1
- global-cp-2
- global-cp-3
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: BaremetalMachineTemplate
metadata:
name: global-control-plane-template
namespace: cpaas-system
spec:
template:
spec:
machineInventoryPoolRef:
name: global-control-plane-pool
---
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
name: global-kcp
namespace: cpaas-system
annotations:
controlplane.cluster.x-k8s.io/skip-kube-proxy: ""
spec:
replicas: 3
version: "${K8S_VERSION}"
rolloutStrategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
machineTemplate:
nodeDrainTimeout: 1m
nodeDeletionTimeout: 5m
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: BaremetalMachineTemplate
name: global-control-plane-template
kubeadmConfigSpec:
format: cloud-config
clusterConfiguration:
etcd:
local:
serverCertSANs:
- "${CONTROL_PLANE_VIP}"
- "${PLATFORM_HOST}"
步骤 5 — 应用全局清单
将特定于 provider 的清单应用到 bootstrap 集群。
kubectl apply -f "${GLOBAL_DCS_YAML}"
kubectl apply -f "${GLOBAL_VSPHERE_YAML}"
kubectl apply -f "${GLOBAL_HCS_YAML}"
kubectl apply -f "${GLOBAL_BAREMETAL_YAML}"
在期望 Cluster API 调和继续进行之前,请等待 bootstrap 注册生成预期的清单。
kubectl -n cpaas-system get machineinventory.elemental.cattle.io
kubectl -n cpaas-system get machineinventorypool
kubectl -n cpaas-system get baremetalcluster,baremetalmachine
步骤 6 — 等待控制平面
等待 Cluster API provider 配置机器并启动 Kubernetes 控制平面。
kubectl get clusters.cluster.x-k8s.io -n cpaas-system
kubectl get kubeadmcontrolplane -n cpaas-system
kubectl get machines -n cpaas-system
当 KubeadmControlPlane 报告 Ready: True,且 Cluster 报告 Phase: Provisioned 时,控制平面即已就绪。
步骤 7 — 导入 provider 资源
在触发安装程序之前,对于需要额外资源导入的 provider,请在 cpaas-system 命名空间中创建 dcs-import-extra-resources ConfigMap。为保持历史安装程序兼容性,ConfigMap 名称保留 dcs 前缀,即使 provider 不是 Huawei DCS。
每个 provider 都使用此 ConfigMap 将 IaaS 凭证 Secret 导入新的 global 集群。对于 VMware vSphere、Huawei Cloud Stack 和裸金属,它还会导入 provider 的 Cluster API 资源;对于 Huawei DCS,这些资源由内置流程迁移,因此在正常安装中,DCS ConfigMap 只需要凭证 Secret 条目。VMware vSphere、Huawei Cloud Stack 和裸金属在正常安装和灾难恢复 global 安装中都需要该 ConfigMap。
对于灾难恢复安装,通过 Secret 引用提供加密 provider 配置的两个 provider — Huawei DCS(DCSCluster.spec.encryptionProviderConfigRef)和裸金属(BaremetalCluster.spec.encryptionProviderConfigRef)— 还必须通过此 ConfigMap 导入该 Secret。内置 DCS 迁移流程仅迁移 Cluster API 资源;不会复制被引用的 Secret。
DCS provider 的 Cluster API 资源由内置流程迁移,因此在正常安装中,ConfigMap 只需要导入 DCSCluster.spec.credentialSecretRef.name 引用的凭证 Secret。使用在 global 清单中设置的相同 Secret 名称。
mkdir -p /root/yamls
cat > /root/yamls/dcs-import-extra-resources.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: dcs-import-extra-resources
namespace: cpaas-system
data:
resources.yaml: |
resources:
- resource: "secrets"
names: ["${PROVIDER_SECRET_NAME}"]
method: kubectl
EOF
kubectl apply -f /root/yamls/dcs-import-extra-resources.yaml
对于 DR 安装,请改用以下形式。它会添加 DCSCluster.spec.encryptionProviderConfigRef.name 引用的加密 provider Secret,该 provider 必须到达最终的 global 集群。DCS_ENCRYPTION_PROVIDER_SECRET 来自准备共享 DR 变量;如果未导出该变量,第一行会明确失败,因为否则 heredoc 会将其展开为空的 Secret 名称。
mkdir -p /root/yamls
: "${DCS_ENCRYPTION_PROVIDER_SECRET:?export it first — see Prepare Shared DR Variables}"
cat > /root/yamls/dcs-import-extra-resources.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: dcs-import-extra-resources
namespace: cpaas-system
data:
resources.yaml: |
resources:
- resource: "secrets"
names:
- "${PROVIDER_SECRET_NAME}"
# The name must match the Secret referenced by
# DCSCluster.spec.encryptionProviderConfigRef.
- "${DCS_ENCRYPTION_PROVIDER_SECRET}"
method: kubectl
EOF
kubectl apply -f /root/yamls/dcs-import-extra-resources.yaml
kubectl -n cpaas-system get cm dcs-import-extra-resources -o yaml
继续之前,请检查渲染后的 ConfigMap:两个 names 条目都必须非空,且第二个条目必须与 DCSCluster.spec.encryptionProviderConfigRef.name 完全匹配。
对于 DR,安装后请验证最终的 global 集群包含已导入的加密 provider Secret。
kubectl --kubeconfig <global-kubeconfig> -n cpaas-system \
get secret "${DCS_ENCRYPTION_PROVIDER_SECRET}" \
-o jsonpath='{.data.encryption-provider\.conf}{"\n"}'
在触发安装程序之前,创建并应用 VMware vSphere 导入 ConfigMap。正常安装和灾难恢复 global 安装都需要此 ConfigMap。global-vsphere-credentials Secret 存储 vCenter 用户名和密码,其名称必须与 VMware vSphere global 清单中 VSphereCluster.spec.identityRef.name 引用的 Secret 名称相同。
mkdir -p /root/yamls
cat > /root/yamls/dcs-import-extra-resources.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: dcs-import-extra-resources
namespace: cpaas-system
data:
resources.yaml: |
resources:
- resource: "vsphereclusters.infrastructure.cluster.x-k8s.io"
names: ["global"]
etcdKeyBase: "/registry/infrastructure.cluster.x-k8s.io/vsphereclusters/cpaas-system/"
method: etcdctl
- resource: "vspheremachinetemplates.infrastructure.cluster.x-k8s.io"
etcdKeyBase: "/registry/infrastructure.cluster.x-k8s.io/vspheremachinetemplates/cpaas-system/"
method: etcdctl
- resource: "vspheremachines.infrastructure.cluster.x-k8s.io"
etcdKeyBase: "/registry/infrastructure.cluster.x-k8s.io/vspheremachines/cpaas-system/"
method: etcdctl
- resource: "vspherevms.infrastructure.cluster.x-k8s.io"
etcdKeyBase: "/registry/infrastructure.cluster.x-k8s.io/vspherevms/cpaas-system/"
method: etcdctl
- resource: "vspheremachineconfigpools.infrastructure.cluster.x-k8s.io"
etcdKeyBase: "/registry/infrastructure.cluster.x-k8s.io/vspheremachineconfigpools/cpaas-system/"
method: etcdctl
- resource: "secrets"
names: ["global-vsphere-credentials"]
method: kubectl
EOF
kubectl apply -f /root/yamls/dcs-import-extra-resources.yaml
在触发安装程序之前,创建并应用 HCS 导入 ConfigMap。正常安装和灾难恢复 global 安装都需要此 ConfigMap。将 PROVIDER_SECRET_NAME 设置为 HCSCluster.spec.identityRef.name 使用的相同 Secret 名称。
mkdir -p /root/yamls
cat > /root/yamls/dcs-import-extra-resources.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: dcs-import-extra-resources
namespace: cpaas-system
data:
resources.yaml: |
resources:
- resource: "secrets"
names: ["${PROVIDER_SECRET_NAME}"]
method: kubectl
- resource: "hcsclusters.infrastructure.cluster.x-k8s.io"
names: ["global"]
etcdKeyBase: "/registry/infrastructure.cluster.x-k8s.io/hcsclusters/cpaas-system/"
method: etcdctl
- resource: "hcsmachinetemplates.infrastructure.cluster.x-k8s.io"
etcdKeyBase: "/registry/infrastructure.cluster.x-k8s.io/hcsmachinetemplates/cpaas-system/"
method: etcdctl
- resource: "hcsmachineconfigpools.infrastructure.cluster.x-k8s.io"
etcdKeyBase: "/registry/infrastructure.cluster.x-k8s.io/hcsmachineconfigpools/cpaas-system/"
method: etcdctl
- resource: "hcsmachines.infrastructure.cluster.x-k8s.io"
etcdKeyBase: "/registry/infrastructure.cluster.x-k8s.io/hcsmachines/cpaas-system/"
method: etcdctl
EOF
kubectl apply -f /root/yamls/dcs-import-extra-resources.yaml
在触发安装程序之前,创建并应用裸金属导入 ConfigMap。此 ConfigMap 会导入全新安装交接用于枚举已安装 Global 机器的持久裸金属和 elemental 所有者资源。不得导入 Elemental plan Secret 或 kubeadm bootstrap data Secret。
在 DCS API 之前导入
在调用 POST /cpaas-installer/api/config/dcs 之前创建 dcs-import-extra-resources。如果缺少该对象,由于新的 global 集群不包含描述 bootstrap global 机器的 BaremetalMachine、MachineInventory 或 MachineRegistration 对象,交接作业可能会使用空的目标列表运行。不要通过将其 plan Secret 或 kubeadm bootstrap data Secret 添加到 ConfigMap 来补救。
从已调和的 BaremetalMachine 对象中收集确切的 Global MachineInventory 名称,并确定这些主机使用的 bootstrap MachineRegistration。对于 DR,还要收集 BaremetalCluster.spec.encryptionProviderConfigRef 引用的 Secret 名称,以便最终的 global 集群包含相同的加密 provider 配置。不要导入任意平台凭证 Secret 或 MachineRegistration token Secret。
kubectl -n cpaas-system get baremetalmachine \
-l cluster.x-k8s.io/cluster-name=global \
-o custom-columns='NAME:.metadata.name,INVENTORY:.status.machineInventoryRef.name'
kubectl -n cpaas-system get machineregistration.elemental.cattle.io
kubectl -n cpaas-system get baremetalcluster global \
-o jsonpath='{.spec.encryptionProviderConfigRef.name}{"\n"}'
创建 ConfigMap。将 inventory 和 MachineRegistration 占位符替换为上述命令的值。对于 DR,还要替换加密 provider Secret 占位符;对于未设置 BaremetalCluster.spec.encryptionProviderConfigRef 的非 DR 安装,请删除该 Secret 条目。
mkdir -p /root/yamls
cat > /root/yamls/dcs-import-extra-resources.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: dcs-import-extra-resources
namespace: cpaas-system
data:
resources.yaml: |
resources:
- resource: "customresourcedefinitions.apiextensions.k8s.io"
names:
- baremetalclusters.infrastructure.cluster.x-k8s.io
- baremetalmachines.infrastructure.cluster.x-k8s.io
- baremetalmachinetemplates.infrastructure.cluster.x-k8s.io
- machineinventorypools.infrastructure.cluster.x-k8s.io
- machineinventories.elemental.cattle.io
- machineregistrations.elemental.cattle.io
- seedimages.elemental.cattle.io
method: kubectl
- resource: "baremetalclusters.infrastructure.cluster.x-k8s.io"
names: ["global"]
etcdKeyBase: "/registry/infrastructure.cluster.x-k8s.io/baremetalclusters/cpaas-system/"
method: etcdctl
- resource: "baremetalmachinetemplates.infrastructure.cluster.x-k8s.io"
etcdKeyBase: "/registry/infrastructure.cluster.x-k8s.io/baremetalmachinetemplates/cpaas-system/"
method: etcdctl
- resource: "baremetalmachines.infrastructure.cluster.x-k8s.io"
etcdKeyBase: "/registry/infrastructure.cluster.x-k8s.io/baremetalmachines/cpaas-system/"
method: etcdctl
- resource: "machineinventorypools.infrastructure.cluster.x-k8s.io"
etcdKeyBase: "/registry/infrastructure.cluster.x-k8s.io/machineinventorypools/cpaas-system/"
method: etcdctl
- resource: "machineinventories.elemental.cattle.io"
names:
- "<global-machine-inventory-1>"
- "<global-machine-inventory-2>"
- "<global-machine-inventory-3>"
etcdKeyBase: "/registry/elemental.cattle.io/machineinventories/cpaas-system/"
method: etcdctl
- resource: "machineregistrations.elemental.cattle.io"
names: ["<global-machine-registration>"]
etcdKeyBase: "/registry/elemental.cattle.io/machineregistrations/cpaas-system/"
method: etcdctl
# Required for DR when BaremetalCluster.spec.encryptionProviderConfigRef is set.
# The name must match the Secret referenced by BaremetalCluster.
- resource: "secrets"
names:
- "<global-encryption-provider-config-secret>"
method: kubectl
EOF
kubectl apply -f /root/yamls/dcs-import-extra-resources.yaml
kubectl -n cpaas-system get cm dcs-import-extra-resources -o yaml
不要导入 Plan 或 Kubeadm Bootstrap Secret
安装程序会在批量 etcd 导入完成创建其所有者对象之前应用 method: kubectl 额外资源。此时导入的 Elemental plan Secret 仍引用 bootstrap MachineInventory UID;该所有者 UID 不标识之后导入的 Global MachineInventory,因此 Kubernetes 垃圾回收可能会删除该 Secret。不要将 Elemental plan Secret、KubeadmConfig 对象或 Machine.spec.bootstrap.dataSecretName Secret 添加到此 ConfigMap。
第一版本的全新安装交接会显式处理此顺序。它会验证所有目标 Global MachineInventory 对象都存在,从仍在运行的 bootstrap 集群读取每个源 plan Secret,并使用活动 Global MachineInventory UID 作为其控制器所有者,创建 Global plan Secret。
KubeadmConfig 对象和 kubeadm bootstrap data Secret 不会被第一版本的全新安装交接使用,不得添加到此 ConfigMap。
继续执行步骤 8 之前,请检查渲染后的 ConfigMap:
- 它包含裸金属 CRD、
BaremetalCluster/global、裸金属机器资源、确切的 Global MachineInventory 对象以及 Global MachineRegistration。
- 它不包含
SeedImage 对象、MachineRegistration token Secret、Elemental plan Secret、KubeadmConfig 对象或 kubeadm bootstrap data Secret。
- 对于 DR,它仍包含加密 provider Secret,且其名称与
BaremetalCluster.spec.encryptionProviderConfigRef.name 完全匹配。
bootstrap SeedImage 会生成指向 bootstrap 环境的 ISO;在 global 集群完成交接后,它不再是正确的生命周期对象。导入 seedimages.elemental.cattle.io CRD 只是为了让新的 global 集群理解该 API 类型。
对于 DR,安装后请验证最终的 global 集群包含已导入的加密 provider Secret。
kubectl --kubeconfig <global-kubeconfig> -n cpaas-system \
get secret <global-encryption-provider-config-secret> \
-o jsonpath='{.data.encryption-provider\.conf}{"\n"}'
步骤 8 — 触发平台安装
向嵌入式安装程序 REST API 提交平台安装请求。安装程序会将 Cluster API 资源导入新的 global 集群,部署基础 operator,并安装选定的插件。
export INSTALLER_IP=$(kubectl get pods -n cpaas-system -l service_name=cpaas-installer \
-o jsonpath='{.items[0].status.podIP}')
网络范围
INSTALLER_IP 是 bootstrap 集群中嵌入式安装程序的 Pod IP。该端点仅在安装期间使用。
在当前 bootstrap 主机上创建特定于 provider 的安装程序配置 JSON 文件,然后将其提交到安装程序端点。此安装路径中的所有 provider 都使用相同的端点路径,但请求正文不同。
每个 console.host 条目都是裸地址:不包含 https:// 前缀,也不包含端口。端口来自 console.httpPort 和 console.httpsPort。
DCS 安装程序请求包含 global 控制平面 VIP。使用外部 LoadBalancer 时,这是外部预配的 VIP。使用 DCS Provider v1.0.22+ 和 ACP v4.4+ Self-built VIP 时,请使用由 provider 管理的 alive 运行时所拥有的预留 VIP。
mkdir -p /root/yamls
export INSTALLER_CONFIG_JSON="/root/yamls/installer-config-dcs.json"
cat > "${INSTALLER_CONFIG_JSON}" <<EOF
{
"basic": {
"username": "admin@cpaas.io",
"password": "<base64-platform-admin-password>"
},
"registry": {
"domain": "${REGISTRY_DOMAIN}",
"username": "<registry-username>",
"password": "<base64-registry-password>"
},
"console": {
"host": [
"${CONTROL_PLANE_VIP}"
],
"globalHost": "${PLATFORM_HOST}",
"httpPort": 80,
"httpsPort": 443,
"cert": {
"selfSigned": {}
}
},
"cluster": {
"clusterCIDR": "${CLUSTER_CIDR}",
"serviceCIDR": "${SERVICE_CIDR}",
"features": {
"ha": {
"vip": "${CONTROL_PLANE_VIP}",
"vport": 6443,
"isThirdParty": true
}
}
},
"product": [
"base",
"acp"
],
"deployMode": "normal",
"hostIP": "${HOST_IP}"
}
EOF
curl -k -X POST "http://${INSTALLER_IP}:8080/cpaas-installer/api/config/dcs" \
-H 'Content-Type: application/json' \
-d @"${INSTALLER_CONFIG_JSON}"
将 console.host 设置为本地 global 控制平面 VIP,即 ${CONTROL_PLANE_VIP}。不要在 console.host 中使用平台域名;应使用 console.globalHost 作为平台访问地址。cluster.features.ha.vip 使用相同的地址。
VMware vSphere 使用与 DCS 相同的安装程序端点路径,但其请求正文不包含 cluster.features.ha。控制平面端点在 VSphereCluster.spec.controlPlaneEndpoint.host 中声明,集群 CIDR 在 VMware vSphere Cluster 清单中声明。
mkdir -p /root/yamls
export INSTALLER_CONFIG_JSON="/root/yamls/installer-config-vsphere.json"
cat > "${INSTALLER_CONFIG_JSON}" <<EOF
{
"basic": {
"username": "admin@cpaas.io",
"password": "<base64-platform-admin-password>"
},
"registry": {
"domain": "${REGISTRY_DOMAIN}",
"username": "<registry-username>",
"password": "<base64-registry-password>"
},
"console": {
"host": [
"${CONTROL_PLANE_VIP}"
],
"globalHost": "${PLATFORM_HOST}",
"httpPort": 80,
"httpsPort": 443,
"cert": {
"selfSigned": {}
}
},
"product": [
"base",
"acp"
],
"deployMode": "normal",
"hostIP": "${HOST_IP}"
}
EOF
curl -k -X POST "http://${INSTALLER_IP}:8080/cpaas-installer/api/config/dcs" \
-H 'Content-Type: application/json' \
-d @"${INSTALLER_CONFIG_JSON}"
将 console.host 设置为本地 global 控制平面 VIP,即 ${CONTROL_PLANE_VIP}。不要在 console.host 中使用平台域名;应使用 console.globalHost 作为平台访问地址。VSphereCluster.spec.controlPlaneEndpoint.host 使用相同的地址。
HCS 使用与 DCS 相同的安装程序端点路径,但其请求正文不包含 cluster.features.ha。控制平面 VIP 由 HCSCluster.spec.controlPlaneLoadBalancer 中声明的 HCS ELB 所拥有。
mkdir -p /root/yamls
export INSTALLER_CONFIG_JSON="/root/yamls/installer-config-hcs.json"
cat > "${INSTALLER_CONFIG_JSON}" <<EOF
{
"basic": {
"username": "admin@cpaas.io",
"password": "<base64-platform-admin-password>"
},
"registry": {
"domain": "${REGISTRY_DOMAIN}",
"username": "<registry-username>",
"password": "<base64-registry-password>"
},
"console": {
"host": [
"${CONTROL_PLANE_VIP}"
],
"globalHost": "${PLATFORM_HOST}",
"httpPort": 80,
"httpsPort": 443,
"cert": {
"selfSigned": {}
}
},
"product": [
"base",
"acp"
],
"deployMode": "normal",
"hostIP": "${HOST_IP}"
}
EOF
curl -k -X POST "http://${INSTALLER_IP}:8080/cpaas-installer/api/config/dcs" \
-H 'Content-Type: application/json' \
-d @"${INSTALLER_CONFIG_JSON}"
将 console.host 设置为本地 global 控制平面 VIP,即 ${CONTROL_PLANE_VIP}。不要在 console.host 中使用平台域名;应使用 console.globalHost 作为平台访问地址。HCSCluster.spec.controlPlaneLoadBalancer.vipAddress 使用相同的地址。
Bare Metal 安装程序请求包含交接后使用的控制平面端点。在 Internal 模式下,Alive 暴露自建 VIP。在 External 模式下,User-Provisioned 负载均衡器暴露相同的端点,并维护控制平面后端。将 REGISTRY_DOMAIN 设置为 ${PLATFORM_HOST}:11443。保持 registry.externalAddress 未设置,以便安装程序部署并填充本地平台 Registry。不要在此字段中使用 bootstrap Registry 地址。
在 DR 对中,两个 global 集群使用相同的 ${PLATFORM_HOST}:11443 Registry 地址。这样可以使两侧的 ProductBase.spec.registry.address 和两个 cpaas.io/registry-address Cluster 注释保持一致,因此 etcd Synchronizer 会将它们作为空操作进行复制,而不会用主集群的值覆盖备用集群的值。每个 global 集群仍运行自己的 Registry;平台域名只需解析到当前处于活动状态的集群。
Registry 凭证遵循平台域名
每个 global 集群都有自己的 cpaas-system/registry-admin 凭证,但平台域名一次只解析到其中一个集群。当你将构件推送到 ${PLATFORM_HOST}:11443 时(例如 provider 软件包或 etcd Synchronizer 软件包),请使用域名当前指向的集群的 Registry 凭证进行身份验证。故障转移后,域名会指向另一个集群,因此要使用的凭证也会随之变化。错误的凭证会导致 unable to retrieve auth token: invalid username/password,但这并不表示你访问了错误的集群。匿名拉取可跨站点工作,不受影响。
mkdir -p /root/yamls
export INSTALLER_CONFIG_JSON="/root/yamls/installer-config-baremetal.json"
cat > "${INSTALLER_CONFIG_JSON}" <<EOF
{
"basic": {
"username": "admin@cpaas.io",
"password": "<base64-platform-admin-password>"
},
"registry": {
"domain": "${REGISTRY_DOMAIN}",
"username": "<registry-username>",
"password": "<base64-registry-password>"
},
"console": {
"host": [
"${CONTROL_PLANE_VIP}"
],
"globalHost": "${PLATFORM_HOST}",
"httpPort": 80,
"httpsPort": 443,
"cert": {
"selfSigned": {}
}
},
"cluster": {
"clusterCIDR": "${CLUSTER_CIDR}",
"serviceCIDR": "${SERVICE_CIDR}",
"features": {
"ha": {
"vip": "${CONTROL_PLANE_VIP}",
"vport": 6443,
"isThirdParty": true
}
}
},
"product": [
"base",
"acp"
],
"deployMode": "normal",
"hostIP": "${HOST_IP}"
}
EOF
curl -k -X POST "http://${INSTALLER_IP}:8080/cpaas-installer/api/config/dcs" \
-H 'Content-Type: application/json' \
-d @"${INSTALLER_CONFIG_JSON}"
将 console.host 设置为本地 global 控制平面 VIP,即 ${CONTROL_PLANE_VIP}。不要在 console.host 中使用平台域名;应使用 console.globalHost 作为平台访问地址。cluster.features.ha.vip 使用相同的地址。
第三方控制台证书
示例使用自签名控制台证书。如果环境要求使用第三方证书,请在提交安装程序请求之前,将 console.cert 替换为包含 base64 完整证书链、私钥和可选 PKCS#12 值的 thirdParty 块。此证书仅用于平台 HTTPS 入口;不会配置根据 KubeadmControlPlane 生成的 kube-apiserver 或 etcd 证书。端口 11443 上的 Global Registry 使用独立管理的 global-registry-server 证书链,而不是 console.cert 或 dex.tls。
DR 证书要求
对于主/备用 Bare Metal global DR 部署,不要让两侧分别生成互不相关的自签名证书。请在两侧配置相同的受信任 thirdParty 平台证书。其必需的 SAN 是稳定的 ${PLATFORM_HOST} 域名。默认情况下,不要添加主集群或备用集群控制平面 VIP,也不要添加内部 Service 名称。仅当客户端有意通过该 VIP 直接访问平台 HTTPS 时,才添加 VIP SAN。通过端口 11443 访问 Registry 不使用此证书;Registry 证书管理是独立的,不属于此证书步骤。交接给 https://<control-plane-vip>:6443 的 Global 主机验证的是独立的 kube-apiserver 证书和 CA,而不是 console.cert 或 dex.tls。
步骤 9 — 监控安装
安装程序接受请求后,安装会经历多个阶段,这些阶段可以从 bootstrap 主机上观察到。典型的不可变 OS global 集群需要 30–60 分钟;总耗时取决于 IaaS 预配速度、镜像拉取时间以及所选插件的数量。
可以观察到的阶段
安装期间的信号
同时查看安装程序进度 API 和安装程序日志。如果其中一个似乎停滞,请直接在 bootstrap 主机上检查底层 Cluster API 资源。
# Installer progress and live log
curl "http://${INSTALLER_IP}:8080/cpaas-installer/api/progress"
tail -f /var/cpaas/data/installer.log
# Cluster API resources on the bootstrap host
kubectl get clusters.cluster.x-k8s.io -A
kubectl get kubeadmcontrolplane -A
kubectl get machines -A
安装程序日志会记录每次阶段转换。瞬态错误会按较短的时间间隔重试;持久性错误会保留在日志中,并在进度 API 中显示为停滞阶段。
安装程序报告成功后,检查 global 集群。
kubectl --kubeconfig <global-kubeconfig> get nodes
kubectl --kubeconfig <global-kubeconfig> get pods -n cpaas-system
kubectl --kubeconfig <global-kubeconfig> get clustermodule global
常见停滞情况及查看位置
此处未列出的问题通常指向特定于环境的原因。请收集安装程序日志、进度 API 响应以及相关的 kubectl describe 输出,然后上报。
可选的灾难恢复部署
在为灾难恢复部署主集群和备用 global 集群时使用本节。在为每个 global 集群应用提供商专用清单之前,完成以下附加配置。
开始之前,请完成双向 DR 网络要求。在两个集群 VIP 都配置了所需的负载均衡器侦听器,且集群间网络规则允许所需流量双向传输之前,不要开始任一安装。正常运行期间使用的方向为备用集群到主集群,但故障转移后方向会反转。
仅适用于 Bare Metal 的全新安装范围
本节中的 Bare Metal 分离认证操作步骤仅涵盖主集群和备用 global 集群的全新安装。不定义或验证现有 Bare Metal DR 对的原地升级或迁移,也不涵盖故障环境的修复或恢复。对于这些生命周期任务,请使用经过单独验证的运维操作手册。
以下并非每个小节都适用于所有提供商。完成适用于你基础设施的标记项:
将仅适用于 Bare Metal 的步骤应用于其他提供商,会更改该提供商不期望的对象——尤其不要将 Bare Metal KubeadmControlPlane 签名密钥 files 条目添加到 DCS、VMware vSphere 或 Huawei Cloud Stack 清单中。
所有提供商:主集群和备用集群必须使用相同的加密提供商配置。对于 DCS 和 Bare Metal,提供商专用集群资源引用包含 encryption-provider.conf 的 Secret;对于 HCS,正常的非 DR 部署不会将 /etc/kubernetes/encryption-provider.conf 添加到 KubeadmControlPlane.spec.kubeadmConfigSpec.files。VMware vSphere 保留 release 清单的 /etc/kubernetes/encryption-provider.conf 文件条目。
仅适用于 Bare Metal:主集群和备用集群还必须使用相同的 Kubernetes ServiceAccount 签名密钥,以便主集群上创建的固定 baremetal-system-agent 令牌在故障转移后能被备用 API 服务器接受。组成每个 global 集群的机器仍使用各自独立的集群本地身份。
准备共享 DR 变量
在主集群和备用集群的安装环境中设置相同的加密密钥值。
export ENCRYPTION_PROVIDER_CONF="/root/yamls/encryption-provider.conf"
export ENCRYPTION_PROVIDER_SECRET_B64="<base64-shared-etcd-encryption-key>"
export PRIMARY_CLUSTER_VIP="<primary-ha-vip>"
export STANDBY_CLUSTER_VIP="<standby-ha-vip>"
export DCS_ENCRYPTION_PROVIDER_SECRET="encryption-provider-config"
export BAREMETAL_ENCRYPTION_PROVIDER_SECRET="global-encryption-provider-config"
export SERVICE_ACCOUNT_ISSUER="https://kubernetes.default.svc.cluster.local"
在两个安装环境中创建加密提供商配置文件。
mkdir -p "$(dirname "${ENCRYPTION_PROVIDER_CONF}")"
cat > "${ENCRYPTION_PROVIDER_CONF}" <<EOF_CONF
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: ${ENCRYPTION_PROVIDER_SECRET_B64}
EOF_CONF
Bare Metal:准备共享 ServiceAccount 签名密钥
仅适用于 Bare Metal
只有 Bare Metal 需要共享的 ServiceAccount 签名密钥。Huawei DCS、VMware vSphere 和 Huawei
Cloud Stack 部署跳过本节——它们共享加密提供商配置,但两侧各自保留自己的签名密钥。不要将这些 files 条目添加到非 Bare Metal 的
KubeadmControlPlane 中。
仅生成一次 ServiceAccount 签名密钥,并在主集群和备用 KubeadmControlPlane 清单中使用相同的文件。只有当两侧使用相同密钥进行签名时,主集群上创建的固定 baremetal-system-agent 令牌才会被备用 API 服务器接受。
mkdir -p /root/global-dr-sa
openssl genrsa -out /root/global-dr-sa/sa.key 2048
openssl rsa -in /root/global-dr-sa/sa.key -pubout -out /root/global-dr-sa/sa.pub
chmod 0600 /root/global-dr-sa/sa.key
chmod 0644 /root/global-dr-sa/sa.pub
kubectl -n cpaas-system create secret generic global-sa-signing-key \
--from-file=sa.key=/root/global-dr-sa/sa.key \
--from-file=sa.pub=/root/global-dr-sa/sa.pub \
--dry-run=client -o yaml | kubectl apply -f -
将以下条目添加到主集群和备用集群的 KubeadmControlPlane.spec.kubeadmConfigSpec 中。两侧的文件内容以及 issuer/audience 值必须完全相同。
files:
- path: /etc/kubernetes/pki/sa.key
owner: root:root
permissions: "0600"
contentFrom:
secret:
name: global-sa-signing-key
key: sa.key
- path: /etc/kubernetes/pki/sa.pub
owner: root:root
permissions: "0644"
contentFrom:
secret:
name: global-sa-signing-key
key: sa.pub
clusterConfiguration:
apiServer:
extraArgs:
service-account-key-file: /etc/kubernetes/pki/sa.pub
service-account-signing-key-file: /etc/kubernetes/pki/sa.key
service-account-issuer: https://kubernetes.default.svc.cluster.local
api-audiences: https://kubernetes.default.svc.cluster.local
controllerManager:
extraArgs:
service-account-private-key-file: /etc/kubernetes/pki/sa.key
集群安装完成后,从两侧各选择一个控制平面节点,验证文件和 kubeadm 静态 Pod 参数。
sha256sum /etc/kubernetes/pki/sa.key /etc/kubernetes/pki/sa.pub
grep -E 'service-account-issuer|api-audiences|service-account-key-file|service-account-signing-key-file' \
/etc/kubernetes/manifests/kube-apiserver.yaml
grep -E 'service-account-private-key-file' \
/etc/kubernetes/manifests/kube-controller-manager.yaml
将 DR etcd 服务器证书 SAN 添加到 KubeadmControlPlane
在第 4 步生成的清单中,将主集群和备用集群的控制平面 VIP、平台访问地址以及 etcd.kube-system 都包含在 KubeadmControlPlane.spec.kubeadmConfigSpec.clusterConfiguration.etcd.local.serverCertSANs 中。在两个安装环境中使用相同的 SAN 列表。这些值配置由 kubeadm 生成的 etcd 服务器证书;它们独立于平台 console.cert,不得复制到其 thirdParty SAN 列表中。
serverCertSANs:
- "${PRIMARY_CLUSTER_VIP}"
- "${STANDBY_CLUSTER_VIP}"
- "${PLATFORM_HOST}"
- "etcd.kube-system"
添加提供商专用 DR 字段
在两个安装环境的 bootstrap 集群中创建加密提供商 Secret。该 Secret 必须与 DCSCluster 位于同一命名空间,并且必须包含名为 encryption-provider.conf 的密钥。
kubectl create secret generic "${DCS_ENCRYPTION_PROVIDER_SECRET}" \
--from-file=encryption-provider.conf="${ENCRYPTION_PROVIDER_CONF}" \
-n cpaas-system \
--dry-run=client -o yaml | kubectl apply -f -
将该 Secret 引用添加到 DCSCluster.spec。
encryptionProviderConfigRef:
name: encryption-provider-config
DCS 使用 DCSCluster.spec.encryptionProviderConfigRef 传递灾难恢复加密提供商配置。每次构建控制平面 bootstrap 数据时,DCS 提供商都会读取此 Secret。对于 DCS DR 路径,不要将 /etc/kubernetes/encryption-provider.conf 添加到 KubeadmControlPlane.spec.kubeadmConfigSpec.files。
此 Secret 还必须包含在第 7 步的 DCS dcs-import-extra-resources ConfigMap 中。不能只将其保留在 bootstrap 集群中,因为已移交的 global 集群会保留 DCSCluster 对象及其 encryptionProviderConfigRef,并且后续提供商协调也需要引用的 Secret。内置 DCS 迁移流程会移动 Cluster API 资源,但不会复制此 Secret。
在两个安装环境中创建 DCS dcs-import-extra-resources ConfigMap,使用第 7 步中的 ConfigMap DR 形式,而不是普通形式。将 PROVIDER_SECRET_NAME 设置为 DCSCluster.spec.credentialSecretRef.name 使用的相同 Secret 名称,并使 DCS_ENCRYPTION_PROVIDER_SECRET 与 DCSCluster.spec.encryptionProviderConfigRef.name 保持一致。
缺少 Secret 只会在替换控制平面机器时导致后续失败
设置 DCSCluster.spec.encryptionProviderConfigRef 后,DCS 提供商要求必须存在被引用的 Secret,不会回退到生成或读取配置。不创建控制平面机器的协调不会受到影响,因此当 Secret 仍留在 bootstrap 集群中时,安装看似正常,直到需要创建新的控制平面机器——例如 Kubernetes 升级、KubeadmControlPlane 滚动更新或控制平面节点替换。此时新的 Machine 会停留在 Provisioning 中而不会创建 VM,提供商日志会记录 failed to create vm: failed to resolve user data: get encryption provider config secret。
检查现有 DR 对的两侧,如果缺少 Secret,则补齐:
kubectl --kubeconfig <global-kubeconfig> -n cpaas-system get dcscluster global \
-o jsonpath='{.spec.encryptionProviderConfigRef.name}{"\n"}'
kubectl --kubeconfig <global-kubeconfig> -n cpaas-system get secret <name>
使用安装时采用的相同 encryption-provider.conf 重新创建该 Secret,以便新控制平面机器继续使用运行中 API 服务器已经使用的密钥。如果 bootstrap 主机上已不存在该文件,请从同一集群的运行中控制平面节点复制(/etc/kubernetes/encryption-provider.conf)。使用不同的密钥会破坏该集群对已经持有的 etcd 数据的解密。
不需要 VSphereCluster 加密 Secret 引用。对于 VMware vSphere,在两个主、备用安装环境的 KubeadmControlPlane.spec.kubeadmConfigSpec.files 中保留此文件条目。渲染后的 /etc/kubernetes/encryption-provider.conf 内容必须在两侧完全相同,包括提供商顺序、密钥名称和 base64 密钥值。在两个安装环境中还要创建第 7 步的 VMware vSphere dcs-import-extra-resources ConfigMap,以便安装程序导入 vSphere 基础设施资源和 global-vsphere-credentials Secret。
- path: /etc/kubernetes/encryption-provider.conf
owner: "root:root"
append: false
permissions: "0644"
content: |
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: ${ENCRYPTION_PROVIDER_SECRET_B64}
在两个主、备用安装环境中保留相同的 DR serverCertSANs 列表。
不需要 HCSCluster 加密 Secret 引用。对于 HCS,将此文件条目追加到两个主、备用安装环境的 KubeadmControlPlane.spec.kubeadmConfigSpec.files 中。渲染后的 /etc/kubernetes/encryption-provider.conf 内容必须在两侧完全相同,包括提供商顺序、密钥名称和 base64 密钥值。
- path: /etc/kubernetes/encryption-provider.conf
owner: "root:root"
append: false
permissions: "0644"
content: |
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: ${ENCRYPTION_PROVIDER_SECRET_B64}
在两个主、备用安装环境中保留相同的 DR serverCertSANs 列表。
在两个安装环境中创建第 7 步的 HCS dcs-import-extra-resources ConfigMap。将 PROVIDER_SECRET_NAME 设置为 HCSCluster.spec.identityRef.name 使用的相同 Secret 名称。
在两个主、备用安装环境的 minialauda 中创建加密提供商 Secret。该 Secret 必须与 BaremetalCluster 位于同一命名空间,并且必须包含名为 encryption-provider.conf 的密钥。
kubectl create secret generic "${BAREMETAL_ENCRYPTION_PROVIDER_SECRET}" \
--from-file=encryption-provider.conf="${ENCRYPTION_PROVIDER_CONF}" \
-n cpaas-system \
--dry-run=client -o yaml | kubectl apply -f -
从 BaremetalCluster.spec.encryptionProviderConfigRef 引用该 Secret。
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: BaremetalCluster
metadata:
name: global
namespace: cpaas-system
spec:
encryptionProviderConfigRef:
name: global-encryption-provider-config
Bare Metal 提供商会读取此 Secret,并将 /etc/kubernetes/encryption-provider.conf 注入生成的控制平面 bootstrap 数据。对于 Bare Metal DR,不要再手动将该文件添加到 KubeadmControlPlane.spec.kubeadmConfigSpec.files;BaremetalCluster 引用是唯一事实来源。
此 Secret 还必须包含在第 7 步的 Bare Metal dcs-import-extra-resources ConfigMap 中。不能只将其保留在 bootstrap 集群中,因为已移交的 global 集群会保留导入的 BaremetalCluster 对象,并且后续提供商协调也需要引用的 Secret。
在两个主、备用安装环境中保留相同的 DR serverCertSANs 列表。
还要保留准备共享 ServiceAccount 签名密钥中的共享 ServiceAccount 签名密钥配置。没有该密钥,备用 API 服务器无法验证现有主机在故障转移前收到的 baremetal-system-agent 令牌。
在两个安装环境中创建第 7 步的 Bare Metal dcs-import-extra-resources ConfigMap。该 ConfigMap 必须导入移交所需的持久 Bare Metal 和 elemental 所有者资源,包括 BaremetalCluster.spec.encryptionProviderConfigRef 引用的加密提供商 Secret。不得导入 SeedImage、Elemental 计划 Secret、KubeadmConfig 对象或 kubeadm bootstrap 数据 Secret;全新安装的移交流程只有在所有目标 Global MachineInventory 对象都存在后,才会创建初始 Global 计划 Secret。
两侧的 Bare Metal 提供商 AppRelease 都必须启用 Global-local 身份、共享 workload 身份以及 Global 机器的直接 kube-apiserver 移交。在创建 AppRelease 后、调用安装程序 API 前,在两侧的 bootstrap 集群上分别应用以下补丁之一。
在主 bootstrap 集群上:
kubectl -n cpaas-system patch apprelease cluster-api-provider-baremetal \
--type=merge \
-p '{"spec":{"values":{"handoffHook":{"directAPIServer":true},"elemental":{"systemAgent":{"authMode":"shared","splitAuthEnabled":true,"serviceAccountName":"baremetal-system-agent","globalServiceAccountName":"baremetal-global-system-agent","sharedAuthReadOnly":false}}}}}'
在备用 bootstrap 集群上:
kubectl -n cpaas-system patch apprelease cluster-api-provider-baremetal \
--type=merge \
-p '{"spec":{"values":{"handoffHook":{"directAPIServer":true},"elemental":{"systemAgent":{"authMode":"shared","splitAuthEnabled":true,"serviceAccountName":"baremetal-system-agent","globalServiceAccountName":"baremetal-global-system-agent","sharedAuthReadOnly":true}}}}}'
在每个 bootstrap 集群上验证生效值:
export EXPECTED_SHARED_AUTH_READ_ONLY=false # Use true on the standby bootstrap cluster.
kubectl -n cpaas-system get apprelease cluster-api-provider-baremetal -o json | \
jq -e --argjson expected_read_only "${EXPECTED_SHARED_AUTH_READ_ONLY}" '
.spec.values.handoffHook.directAPIServer == true and
.spec.values.elemental.systemAgent.splitAuthEnabled == true and
.spec.values.elemental.systemAgent.sharedAuthReadOnly == $expected_read_only
'
在此命令成功前不要继续。主集群必须使用 sharedAuthReadOnly: false;在主集群到备用集群的同步处于活动状态时,备用集群必须使用 sharedAuthReadOnly: true。
生成的 bootstrap AppRelease 值如下:
handoffHook:
controlPlaneVIP: <current-side-control-plane-vip>
directAPIServer: true
delivery:
enabled: true
mode: always
elemental:
systemAgent:
authMode: shared
splitAuthEnabled: true
serviceAccountName: baremetal-system-agent
globalServiceAccountName: baremetal-global-system-agent
tls:
agentTLSMode: strict
caCertSecretName: dex.tls
caCertSecretKey: ca.crt
保持 bootstrap 证书输入和最终证书输入相互独立。第 3 步创建的 bootstrap dex.tls 包含专用的 ca.crt 密钥,且仅用于 https://<bootstrap-host-ip>:12443。不要将其导入最终集群。在每个最终 Global 集群中,安装程序管理的 dex.tls 会在 tls.crt 中包含平台完整证书链;因此,最终 Bare Metal 提供商 AppRelease 必须使用 elemental.tls.caCertSecretKey: tls.crt:
Final Global AppRelease
elemental:
tls:
agentTLSMode: strict
caCertSecretName: dex.tls
caCertSecretKey: tls.crt
最终 operator 使用此证书链作为其注册端点。直接 API 移交仍会从 Secret/cpaas-system/baremetal-global-system-agent-token 的 ca.crt 密钥获取 kube-apiserver CA;不要在 Global 主机的直接 kubeconfig 中使用 dex.tls/tls.crt 替换该 CA。
elemental.systemAgent.splitAuthEnabled 是 elemental-operator 和移交作业共用的唯一开关。不要配置单独的移交身份验证启用开关。保持单一事实来源,可防止 Global 主机在 operator 已将共享 Role 收窄为 workload 计划后仍保留共享令牌。
在两个 AppRelease 对象上分别设置共享捆绑包所有权。活动的主集群负责协调 workload 权限,而非活动的备用集群以只读方式保留同步的共享捆绑包:
Primary active Global
elemental:
systemAgent:
sharedAuthReadOnly: false
Standby inactive Global
elemental:
systemAgent:
sharedAuthReadOnly: true
安装使用两组互不重叠的 system-agent 权限:
Role/cpaas-system/baremetal-global-system-agent 仅包含属于本地 Cluster/global 的机器所使用的计划 Secret 名称。其 ServiceAccount 和令牌 Secret 属于本地 global 集群,不得同步到对等集群。
Role/cpaas-system/baremetal-system-agent 仅包含非 global workload 计划 Secret 名称。其 ServiceAccount、令牌 Secret、Role 和 RoleBinding 会从主集群同步到备用集群。
使用带命名空间的 Role 和 RoleBinding 对象,而不是 ClusterRoleBinding。不要授予命名空间范围的 Secret 访问权限,也不要包含 registry、bootstrap、集群访问凭证或平台凭证 Secret。
使用标准 system-agent 名称
保持 Global-local 对象名称为 baremetal-global-system-agent 和 baremetal-global-system-agent-token,并保持共享 workload 对象名称为 baremetal-system-agent 和 baremetal-system-agent-token。etcd-sync 精确同步规则和忽略规则使用这些字面名称。除非同时更新并验证每条对应的 etcd-sync 规则,否则不要自定义这些名称;必须在同步开始前完成更新和验证。
对于此次全新的 DR 安装,每个最终 Global 集群都必须在 ConfigMap/cpaas-system/baremetal-system-agent-handoff 中报告 data.ready: "true"。这是 bootstrap 清理信号,并非完整的验收结果:还要验证下面的运行时端点、令牌、CA、计划反馈和 RBAC 边界。全新安装不使用 RoleBinding/cpaas-system/baremetal-global-system-agent-handoff-bridge;该字段必须不存在。不要手动修补完成信号。
安装主集群和备用集群
对主集群和备用 global 集群均执行步骤 1 至 9。
仅适用于 Bare Metal:使用两个独立的 bootstrap 主机
使用两个相互独立的 bootstrap 主机,一个用于主集群安装,另一个用于备用集群安装。不要为两端复用同一个 bootstrap 集群。bootstrap 环境保存安装程序状态、AppRelease 对象、Registry Secret、MachineRegistration、SeedImage 和交接状态;共享该环境会污染两个 global 安装,并可能导致交接或清理操作作用于错误的一端。
对两端均使用 provider 特定的安装程序配置差异:
对于主集群,确保平台域名解析到主控制平面 VIP。在步骤 8 中,将 hostIP 设置为主 bootstrap 主机 IP,并将 console.host 以及上表列出的 provider 字段设置为主控制平面 VIP。
主集群安装成功后,按照 DR 操作步骤将平台域名切换到备用控制平面 VIP。然后安装备用集群。备用集群安装前必须完成此 DNS 切换,因为多个平台资源会使用平台域名进行渲染,在备用安装程序运行期间,这些域名必须解析到备用入口。在备用 bootstrap 主机上执行步骤 8 时,将 hostIP 设置为备用 bootstrap 主机 IP。将 console.host 和上表列出的 provider 字段设置为备用控制平面 VIP。对于 Bare Metal,将 REGISTRY_DOMAIN 保持为 ${PLATFORM_HOST}:11443 — 即主集群安装使用的相同值。从备用 bootstrap 主机上的 cpaas-installer Pod 获取 INSTALLER_IP;不要复用主 bootstrap 主机的值。
仅适用于 Bare Metal
从此处到本小节末尾的所有内容仅适用于 Bare Metal 路径。Huawei DCS、VMware vSphere 和 Huawei Cloud Stack 部署在两次安装均报告成功后,继续执行
安装 etcd-sync。
在安装 etcd-sync 之前,验证每个最终 global 集群上的 Registry 传播。将 ProductBase.spec.registry.preferPlatformURL 保持为安装程序生成的值。两端必须在 spec.registry.address 和两个 Cluster 注解中报告相同的 ${PLATFORM_HOST}:11443 值;正是这个一致的值使 etcd Synchronizer 能够复制这些密钥而不会覆盖任何内容。
verify_baremetal_global_registry() {
kubeconfig=$1
expected_registry=$2
test "$(kubectl --kubeconfig "${kubeconfig}" \
get productbase.product.alauda.io base \
-o jsonpath='{.spec.registry.address}')" = "${expected_registry}"
test "$(kubectl --kubeconfig "${kubeconfig}" \
get cluster.platform.tkestack.io global \
-o jsonpath='{.metadata.annotations.cpaas\.io/registry-address}')" = \
"${expected_registry}"
test "$(kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get cluster.cluster.x-k8s.io global \
-o jsonpath='{.metadata.annotations.cpaas\.io/registry-address}')" = \
"${expected_registry}"
test "$(kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get secret public-registry-credential -o jsonpath='{.data.registry}' | \
base64 -d)" = "${expected_registry}"
# Only deployments that enabled Registry authentication have this Secret. On an
# anonymous Registry it does not exist, and checking it unconditionally aborts
# the whole function under `set -e` before the remaining assertions run.
if kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get secret global-registry-auth >/dev/null 2>&1; then
kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get secret global-registry-auth -o jsonpath='{.data.\.dockerconfigjson}' | \
base64 -d | jq -e --arg registry "${expected_registry}" \
'.auths[$registry] != null' >/dev/null
fi
kubectl --kubeconfig "${kubeconfig}" get apprelease -A -o json | \
jq -e --arg registry "${expected_registry}" '
all(.items[];
.spec.source.repoURL == $registry and
.spec.values.global.registry.address == $registry)
' >/dev/null
}
export GLOBAL_REGISTRY_ADDRESS="${PLATFORM_HOST}:11443"
verify_baremetal_global_registry \
"${PRIMARY_GLOBAL_KUBECONFIG}" "${GLOBAL_REGISTRY_ADDRESS}"
verify_baremetal_global_registry \
"${STANDBY_GLOBAL_KUBECONFIG}" "${GLOBAL_REGISTRY_ADDRESS}"
在弃用任一 bootstrap KIND 集群之前,先在两端执行完整的交接检查。GLOBAL_HOSTS 是该端已安装 Global 机器的空格分隔管理地址列表。请从能够使用两个 Global kubeconfig 并通过 SSH 连接到这些机器的安全主机执行此操作。保持 shell 跟踪处于禁用状态,因为运行时连接文件包含 bearer token。
set -euo pipefail
set +x
export PRIMARY_GLOBAL_KUBECONFIG="<path-to-primary-global-kubeconfig>"
export STANDBY_GLOBAL_KUBECONFIG="<path-to-standby-global-kubeconfig>"
export PRIMARY_BOOTSTRAP_KUBECONFIG="<path-to-primary-bootstrap-kubeconfig>"
export STANDBY_BOOTSTRAP_KUBECONFIG="<path-to-standby-bootstrap-kubeconfig>"
export PRIMARY_GLOBAL_HOSTS="<primary-global-host-1> <primary-global-host-2> <primary-global-host-3>"
export STANDBY_GLOBAL_HOSTS="<standby-global-host-1> <standby-global-host-2> <standby-global-host-3>"
export PRIMARY_GLOBAL_REGISTRATION_NAME="<primary-global-machine-registration-name>"
export STANDBY_GLOBAL_REGISTRATION_NAME="<standby-global-machine-registration-name>"
verify_global_auth_scope() {
kubeconfig=$1
registration_name=$2
kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get machineregistration.elemental.cattle.io "${registration_name}" -o json | \
jq -e '
.metadata.annotations["baremetal.cluster.io/system-agent-auth-scope"] ==
"global"
' >/dev/null
global_inventory_names="$({
kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get baremetalmachines.infrastructure.cluster.x-k8s.io -o json | \
jq -c '[
.items[]
| select(
.metadata.labels["cluster.x-k8s.io/cluster-name"] == "global"
)
| .status.machineInventoryRef.name? // empty
] | unique | sort'
})"
printf '%s\n' "${global_inventory_names}" | \
jq -e 'length > 0' >/dev/null
while IFS= read -r inventory_name; do
kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get machineinventory.elemental.cattle.io "${inventory_name}" -o json | \
jq -e '
.metadata.annotations["baremetal.cluster.io/system-agent-auth-scope"] ==
"global"
' >/dev/null
done < <(printf '%s\n' "${global_inventory_names}" | jq -r '.[]')
}
verify_baremetal_handoff() {
bootstrap_kubeconfig=$1
kubeconfig=$2
control_plane_vip=$3
global_hosts=$4
registration_name=$5
expected_endpoint="https://${control_plane_vip}:6443"
kubectl --kubeconfig "${bootstrap_kubeconfig}" -n cpaas-system \
wait --for=condition=complete \
job/baremetal-system-agent-handoff --timeout=30m
kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get configmap baremetal-system-agent-handoff -o json | \
jq -e '.data.ready == "true"' >/dev/null
verify_global_auth_scope "${kubeconfig}" "${registration_name}"
if kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get rolebinding baremetal-global-system-agent-handoff-bridge \
>/dev/null 2>&1; then
echo "temporary handoff bridge still exists" >&2
return 1
fi
kubectl --kubeconfig "${kubeconfig}" -n cpaas-system get \
serviceaccount/baremetal-global-system-agent \
secret/baremetal-global-system-agent-token \
role/baremetal-global-system-agent \
rolebinding/baremetal-global-system-agent >/dev/null
global_plan_names="$({
kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get role baremetal-global-system-agent -o json | \
jq -c '[
.rules[]?
| select(any(.apiGroups[]?; . == ""))
| select(any(.resources[]?; . == "secrets"))
| .resourceNames[]?
] | unique | sort'
})"
printf '%s\n' "${global_plan_names}" | jq -e 'length > 0' >/dev/null
while IFS= read -r plan_secret; do
test "$({
kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get secret "${plan_secret}" -o jsonpath='{.type}'
})" = "elemental.cattle.io/plan"
done < <(printf '%s\n' "${global_plan_names}" | jq -r '.[]')
expected_token_sha="$({
kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get secret baremetal-global-system-agent-token \
-o jsonpath='{.data.token}' | base64 -d | \
sha256sum | awk '{print $1}'
})"
expected_ca_sha="$({
kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get secret baremetal-global-system-agent-token \
-o jsonpath='{.data.ca\.crt}' | base64 -d | \
sha256sum | awk '{print $1}'
})"
for host in ${global_hosts}; do
actual_endpoint="$({
ssh "root@${host}" \
'cat /var/lib/elemental/agent/elemental_connection.json' | \
jq -r '.kubeConfig' | \
awk '$1 == "server:" {print $2; exit}'
})"
test "${actual_endpoint}" = "${expected_endpoint}"
actual_namespace="$({
ssh "root@${host}" \
'cat /var/lib/elemental/agent/elemental_connection.json' | \
jq -r '.namespace'
})"
test "${actual_namespace}" = "cpaas-system"
actual_plan_secret="$({
ssh "root@${host}" \
'cat /var/lib/elemental/agent/elemental_connection.json' | \
jq -r '.secretName'
})"
printf '%s\n' "${global_plan_names}" | \
jq -e --arg name "${actual_plan_secret}" 'index($name) != null' \
>/dev/null
actual_token_sha="$({
ssh "root@${host}" \
'cat /var/lib/elemental/agent/elemental_connection.json' | \
jq -r '.kubeConfig' | \
awk '$1 == "token:" {print $2; exit}' | \
tr -d '\r\n' | sha256sum | awk '{print $1}'
})"
test "${actual_token_sha}" = "${expected_token_sha}"
actual_ca_sha="$({
ssh "root@${host}" \
'cat /var/lib/elemental/agent/elemental_connection.json' | \
jq -r '.kubeConfig' | \
awk '$1 == "certificate-authority-data:" {print $2; exit}' | \
base64 -d | sha256sum | awk '{print $1}'
})"
test "${actual_ca_sha}" = "${expected_ca_sha}"
ssh "root@${host}" \
"grep -Fq -- '${expected_endpoint}' /oem/elemental-system-agent.yaml"
ssh "root@${host}" \
'test "$(stat -c %a /oem/elemental-system-agent.yaml)" = 600'
ssh "root@${host}" \
'grep -Fqx -- "CATTLE_AGENT_STRICT_VERIFY=\"true\"" /etc/rancher/elemental/agent/envs'
ssh "root@${host}" \
'systemctl is-active --quiet elemental-system-agent.service'
done
}
verify_baremetal_handoff \
"${PRIMARY_BOOTSTRAP_KUBECONFIG}" "${PRIMARY_GLOBAL_KUBECONFIG}" \
"${PRIMARY_CLUSTER_VIP}" \
"${PRIMARY_GLOBAL_HOSTS}" "${PRIMARY_GLOBAL_REGISTRATION_NAME}"
verify_baremetal_handoff \
"${STANDBY_BOOTSTRAP_KUBECONFIG}" "${STANDBY_GLOBAL_KUBECONFIG}" \
"${STANDBY_CLUSTER_VIP}" \
"${STANDBY_GLOBAL_HOSTS}" "${STANDBY_GLOBAL_REGISTRATION_NAME}"
同时验证两端的 Global-local 权限边界。在第一个非 global workload 计划存在之前,共享的 baremetal-system-agent ServiceAccount、令牌 Secret、Role 和 RoleBinding 可能在两个集群中都不存在。不要仅为满足 Global 安装检查而手动创建或复制该资源包;活动的 operator 会在第一个共享范围注册被调和时创建它,然后 etcd-sync 将其复制到备用集群。
verify_global_local_system_agent_rbac() {
kubeconfig=$1
kubectl --kubeconfig "${kubeconfig}" -n cpaas-system get \
serviceaccount/baremetal-global-system-agent \
secret/baremetal-global-system-agent-token \
role/baremetal-global-system-agent \
rolebinding/baremetal-global-system-agent >/dev/null
local_plan_names="$({
kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get role baremetal-global-system-agent -o json | \
jq -c '[
.rules[]?
| select(any(.apiGroups[]?; . == ""))
| select(any(.resources[]?; . == "secrets"))
| .resourceNames[]?
] | unique | sort'
})"
printf '%s\n' "${local_plan_names}" | jq -e 'length > 0' >/dev/null
expected_local_plan_names="$({
kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get machineinventories.elemental.cattle.io -o json | \
jq -c '[
.items[]
| select(
.metadata.annotations["baremetal.cluster.io/system-agent-auth-scope"] ==
"global"
)
| .status.plan.secretRef.name? // empty
] | unique | sort'
})"
test "${local_plan_names}" = "${expected_local_plan_names}"
while IFS= read -r plan_secret; do
test "$({
kubectl --kubeconfig "${kubeconfig}" -n cpaas-system \
get secret "${plan_secret}" -o jsonpath='{.type}'
})" = "elemental.cattle.io/plan"
test "$({
kubectl --kubeconfig "${kubeconfig}" \
--as=system:serviceaccount:cpaas-system:baremetal-global-system-agent \
-n cpaas-system auth can-i get "secret/${plan_secret}"
})" = yes
test "$({
kubectl --kubeconfig "${kubeconfig}" \
--as=system:serviceaccount:cpaas-system:baremetal-global-system-agent \
-n cpaas-system auth can-i patch "secret/${plan_secret}"
})" = yes
test "$({
kubectl --kubeconfig "${kubeconfig}" \
--as=system:serviceaccount:cpaas-system:baremetal-system-agent \
-n cpaas-system auth can-i get "secret/${plan_secret}"
})" = no
done < <(printf '%s\n' "${local_plan_names}" | jq -r '.[]')
for verb in list create delete; do
test "$({
kubectl --kubeconfig "${kubeconfig}" \
--as=system:serviceaccount:cpaas-system:baremetal-global-system-agent \
-n cpaas-system auth can-i "${verb}" secrets
})" = no
done
test "$({
kubectl --kubeconfig "${kubeconfig}" \
--as=system:serviceaccount:cpaas-system:baremetal-global-system-agent \
-n cpaas-system auth can-i get secret/global-registry-auth
})" = no
}
verify_global_local_system_agent_rbac "${PRIMARY_GLOBAL_KUBECONFIG}"
verify_global_local_system_agent_rbac "${STANDBY_GLOBAL_KUBECONFIG}"
不要仅依据 ready 信号进行验收。运行时令牌或 CA 不匹配、持久化 OEM 文件中存在过期的 bootstrap 端点、计划探测失败或权限边界不正确,都意味着全新交接尚未完成,即使 data.ready 为 true 也是如此。
备用集群安装成功后,将平台域名切回主入口。首次同步期间,主集群是活动源。
仅适用于 Bare Metal
只有 Bare Metal 需要自定义的 etcd-sync 规则源。Huawei DCS、VMware vSphere 和 Huawei
Cloud Stack 部署跳过本节,直接转到
安装 etcd-sync,此时安装顺序无关紧要。
在安装 etcd-sync 之前,在主集群和备用集群上应用此 ConfigMap。
将相同的 baremetal-dr-rules ConfigMap 应用到主集群和备用集群。使用 Bare Metal etcd-sync 规则 中的模板。将计划占位符替换为两个集群中当前所有 Global 计划 Secret 名称。在两端的交接完成后,从 MachineInventory.status.plan.secretRef.name 读取这些名称 — 全新交接会创建 Global 计划 Secret,因此更早收集的名称已过期。插件 chart 会提供其余 Bare Metal 规则。每当添加或替换 Global 计划 Secret 时,都要更新此 ConfigMap。
在镜像开始前创建规则源
使用已设置 active_cluster_vip 和活动集群令牌安装 etcd-sync 会立即打开同步路径。如果 Bare Metal 规则源尚不存在,首次同步会在没有 Global 计划 Secret 排除规则的情况下运行,并使用主集群的计划 Secret 覆盖备用集群的计划 Secret — 这是 故障排除 中列为 A Global plan is overwritten 的故障模式。随后,备用 Global 主机会接收到属于主集群的计划。
先创建 ConfigMap 不会产生副作用:在安装插件之前,它只是一个普通的带标签 ConfigMap,不会被任何对象读取。如果必须先安装插件,请保持 active_cluster_vip 未设置,直到规则源报告 accepted 后再设置。
安装 etcd-sync(ACP 4.4.0+)
仅在当前备用 Global 集群上安装 v4.4.0 或更高版本的插件。不要同时在两端安装或运行镜像。
插件包发布为 etcd-sync,而不是 global-etcd-sync:从软件包服务器下载 packages/etcd-sync/<minor>/etcd-sync.amd64.<version>.tgz。它不属于安装程序 bundle,也不存在于 bootstrap Registry 中,因此请像上传其他插件包一样将其上传到目标集群。
使用以下配置:
在安装前于备用集群上创建令牌 Secret。该值是活动集群的 cpaas-system/k8sadmin ServiceAccount 的 bearer token:
# On the active cluster:
kubectl -n cpaas-system get secret k8sadmin -o jsonpath='{.data.token}' | base64 -d
# On the standby cluster, with the value copied from above:
ACTIVE_CLUSTER_TOKEN='<paste-the-token-from-the-active-cluster>'
kubectl -n cpaas-system create secret generic etcd-sync-active-cluster-token \
--from-literal=token="${ACTIVE_CLUSTER_TOKEN}" \
--dry-run=client -o yaml | kubectl apply -f -
安装期间,etcd-sync-bootstrap Job 会先于 etcd-sync Deployment 启动。只有在该 Job 准备好 remote-etcd-ca、remote-etcd-issuer 和 remote-etcd-client 后,插件安装才会继续。
打开同步路径前,确认规则源报告 accepted,并显示在 ConfigMap/etcd-sync-rule-snapshot 中:
kubectl --kubeconfig <standby-kubeconfig> -n cpaas-system \
get configmap baremetal-dr-rules \
-o jsonpath='{.metadata.annotations.etcd-sync\.cpaas\.io/status}{"\n"}'
kubectl --kubeconfig <standby-kubeconfig> -n cpaas-system \
get configmap etcd-sync-rule-snapshot \
-o jsonpath='{.data.snapshot\.json}'
被拒绝的源不会处于活动状态。
启动并验证同步
触发一次 etcd-sync 监控检查。对于 HTTP 425,重试直到允许检查,然后要求丢失密钥数为零且多余密钥数为零。确认共享 workload system-agent 资源包在两端均不存在,或在两端均完整存在;部分存在则视为失败。
不要仅将 AppRelease 就绪状态作为 DR 验收结果。有关故障转移序列和故障转移后的测试,请继续参阅 Global 集群灾难恢复。
重启必须重新加载 DR 和端点配置的 Pod。在主控制平面节点和备用控制平面节点上执行相同的命令。
sudo kubectl delete po -n cpaas-system -l 'service_name in (alertmanager,vmselect,vminsert)'
sudo kubectl delete po -n cpaas-system -l service_name=cpaas-elasticsearch
sudo kubectl delete po -n cpaas-system -l service_name=cluster-transformer
有关安装后的 DR 生命周期,请参阅 Global 集群灾难恢复。
验证
安装程序报告完成后,验证 global 集群运行状况正常。
kubectl --kubeconfig <global-kubeconfig> get nodes
kubectl --kubeconfig <global-kubeconfig> get clusters.platform.tkestack.io global \
-o jsonpath='{.status.phase}'
kubectl --kubeconfig <global-kubeconfig> get pods -n cpaas-system
kubectl --kubeconfig <global-kubeconfig> get clustermodule global
满足以下所有条件时,安装成功:
- 安装程序进度 API 报告
status: Success 和 type: Complete。
- 所有
global 集群节点均为 Ready。
cpaas-system 中的关键 Pod 为 Running 或 Completed。
ClusterModule/global 报告基础模块运行状况正常。
裸金属:将 global 集群准备为管理集群
仅适用于裸金属
本节仅适用于裸金属安装路径。Huawei DCS、VMware vSphere 和
Huawei Cloud Stack 部署无需执行这些步骤:其 global 集群会在安装期间接收所需的
provider 组件和构件。
上述步骤仅安装 global 集群本身。之后创建裸金属业务集群是另一项工作:
global 集群随后会充当 Cluster API 管理集群,并且需要引导准备阶段未放置在其中的
provider 组件和构件。对于 DR 对中的两个 global 集群,都必须完成本节:
故障转移后,备用集群会成为活动管理集群,并且必须能够协调相同的业务集群。
在安装期间导入 Cluster API 资源只会创建 CRD,不会在 global 集群上安装
裸金属或 Kubeadm provider。
设置本节使用的值。针对每个 global 集群分别运行一次,并让
GLOBAL_KUBECONFIG 指向正在准备的集群。
export GLOBAL_KUBECONFIG="<path-to-this-global-kubeconfig>"
# Tag of the Bare Metal Alauda OS images imported in Step 3.
export OS_IMAGE_TAG="<os-image-tag>"
从正在准备的集群自身的 ProductBase 和 cpaas-system/registry-admin Secret 中读取该集群的 Registry 地址和密码:
GLOBAL_REGISTRY_ADDRESS="$(kubectl --kubeconfig "${GLOBAL_KUBECONFIG}" \
get productbase.product.alauda.io base -o jsonpath='{.spec.registry.address}')"
GLOBAL_REGISTRY_PASSWORD="$(kubectl --kubeconfig "${GLOBAL_KUBECONFIG}" -n cpaas-system \
get secret registry-admin -o jsonpath='{.data.password}' | base64 -d)"
在 global 集群上安装 Cluster API provider
将用于引导集群的相同裸金属和 Kubeadm provider 软件包上传到
global 集群,然后安装这两个插件。按照引导版本中的设置方式,准确设置裸金属 provider 的 DR 字段:
两端均设置 elemental.systemAgent.splitAuthEnabled: true,
在活动集群上设置 sharedAuthReadOnly: false,在备用集群上设置 true。
继续之前,验证 manager 和 elemental-operator 正在运行:
kubectl --kubeconfig "${GLOBAL_KUBECONFIG}" -n cpaas-system get deploy \
cluster-api-provider-baremetal-manager elemental-operator
将 provider 指向实际存在的平台证书
裸金属 provider 从
elemental.tls.caCertSecretName / caCertSecretKey 挂载用于 platformUrl 的 CA bundle,默认值为 dex.tls 和 ca.crt。
这些默认值假设存在由 cert-manager 签发的 dex.tls。在 global 集群上,dex.tls 改由安装程序的
console.cert 流程提供,而 thirdParty 证书生成的 Secret 仅包含
tls.crt 和 tls.key。使用默认密钥时,operator 永远不会启动:
MountVolume.SetUp failed for volume "elemental-ca-cert":
references non-existent secret key: ca.crt
检查 Secret 实际包含哪些密钥,并据此设置 caCertSecretKey。对于自签名的平台证书,证书本身就是其 CA,因此 tls.crt 是正确的密钥:
kubectl --kubeconfig "${GLOBAL_KUBECONFIG}" -n cpaas-system \
get secret dex.tls -o jsonpath='{.data}' | jq -r 'keys | join(",")'
将基础镜像上传到 global Registry
业务 SeedImage 构建在 global 集群上运行,并从 ProductBase.spec.registry.address 中记录的 Registry
拉取基础 ISO。交接后,重新置备 global 节点也会从该 Registry
拉取 baremetal-base-image,因为安装程序会将 global
集群的 cpaas.io/registry-address 注解重写为该 Registry。第 3 步导入的镜像仅存在于
引导 Registry 中,因此在将这两个镜像推送到 global Registry 之前,ISO 构建会因
MANIFEST_UNKNOWN 失败。在引导主机上,从 bare-metal-os containerd
命名空间推送这两个镜像。--skip-verify 需要 --local:
for repo in baremetal-base-image baremetal-base-image-iso; do
ctr -n bare-metal-os images push --local --skip-verify \
-u "admin:${GLOBAL_REGISTRY_PASSWORD}" \
"${GLOBAL_REGISTRY_ADDRESS}/tkestack/${repo}:${OS_IMAGE_TAG}" "${repo}:${OS_IMAGE_TAG}"
curl -sk -u "admin:${GLOBAL_REGISTRY_PASSWORD}" \
"https://${GLOBAL_REGISTRY_ADDRESS}/v2/tkestack/${repo}/tags/list"
done
每个响应都必须列出 ${OS_IMAGE_TAG}。完成后,使用
ctr -n bare-metal-os images rm "baremetal-base-image:${OS_IMAGE_TAG}" "baremetal-base-image-iso:${OS_IMAGE_TAG}" 删除本地副本。
如果引导主机上已不再存在这些镜像,则按照 已导入 OS 镜像并填充镜像目录 中的说明,从 OS 归档导入这些镜像。
创建 elemental 镜像目录
provider 通过由 --image-catalog-namespace 和 --image-catalog-name 定位的 ConfigMap,将 Kubernetes 版本映射到 elemental 升级镜像,默认值为 cpaas-system 和
elemental-image-catalog。provider chart 会创建此 ConfigMap,但不包含任何版本条目,
而且 global 集群不会继承在引导版本中设置的条目。在添加条目之前,业务集群的首次
BaremetalMachine 会立即失败:
ImageResolved=False ImageCatalogMiss:
no elemental upgrade image mapped for Kubernetes version "<version>"
为此 global 集群管理的每个 Kubernetes 版本添加一个条目,包括其自身节点使用的
${K8S_VERSION}。应修补现有 ConfigMap,而不是替换它,以保留之前添加的条目:
kubectl --kubeconfig "${GLOBAL_KUBECONFIG}" -n cpaas-system \
patch configmap elemental-image-catalog --type merge \
-p "{\"data\":{\"${K8S_VERSION}\":\"${GLOBAL_REGISTRY_ADDRESS}/tkestack/baremetal-base-image:${OS_IMAGE_TAG}\"}}"
kubectl --kubeconfig "${GLOBAL_KUBECONFIG}" -n cpaas-system \
get configmap elemental-image-catalog -o jsonpath='{.data}'
ImageCatalogMiss 不会自行恢复
遇到 ImageCatalogMiss 的 BaremetalMachine 在修复目录后仍会保持为 Failed。
重新协调该对象和重启 provider 都不会改变其状态;协调器会有意将缺少映射视为终止状态,而不是回退到默认镜像。
删除 Machine,使其所有者 KubeadmControlPlane 或 MachineSet 重新创建它。
在应用第一个业务集群 manifest 之前填充目录,即可完全避免此问题。
业务注册使用平台域名
业务 MachineRegistration 不得携带
baremetal.cluster.io/system-agent-auth-scope: global 注解。其注册 URL 会解析为
https://<platform-domain>/...,生成的 MachineInventory 会带有
system-agent-auth-scope: shared 注解。该共享身份与共享 ServiceAccount
签名密钥共同使业务主机能够在故障转移后继续通过平台域名进行报告。
停用引导集群
安装程序报告成功,并且完成将 global 集群准备为管理集群后,global 集群会运行其自身的 Cluster API providers。一般验证和下方 provider 特定的交接门禁均通过后,从引导主机中移除临时引导集群 — 仅删除本地引导集群(minialauda)及其 KIND 容器网络。
裸金属:必须通过最终交接门禁
不要仅因为 minialauda 为 Ready、安装程序报告成功,或 baremetal-system-agent-handoff Job 为 Complete,就移除 KubeadmControlPlane。每个已导入的 Global 主机都必须完成来自最终 global 集群的探测,然后才能停用引导端点。
从新的 global 集群读取最终交接记录:
kubectl --kubeconfig <global-kubeconfig> -n cpaas-system \
get configmap baremetal-system-agent-handoff \
-o jsonpath='{.data.ready}{"\n"}{.data.profile}{"\n"}{.data.config}{"\n"}'
仅当当前安装程序交接钩子已使用已部署的 provider 配置重新创建或运行 Job 并成功完成、data.ready 为 true、data.profile 与选定的身份验证和端点模式匹配,并且 data.config 为 64 字符的小写十六进制摘要时,才能继续。交接二进制文件会根据有效端点、TLS 和身份验证输入、身份名称以及完整的已导入目标计划集计算此摘要;不要尝试通过目视比较来重构或批准它。当前 Job 成功,才能证明存储的摘要与这些输入匹配。
还要确认每个已导入的目标对象以及所引用的计划 Secret 都存在于最终集群中。如果当前 Job 失败或超时、其最终验证拒绝了 ConfigMap、目标集不完整,或所引用的计划 Secret 缺失,请保持 minialauda 运行,并调查交接 Job 和 provider 日志。
验证 DCS Secret 已到达 global 集群
DCS API 凭证 Secret 会在安装期间通过你在第 7 步创建的 global ConfigMap 复制到 dcs-import-extra-resources 集群 — 它会导入 DCSCluster.spec.credentialSecretRef 中指定名称的 Secret。在移除引导主机之前,验证该 Secret 是否存在:kubectl --kubeconfig <global-kubeconfig> get secret <name> -n cpaas-system。如果缺失,请先将其复制过去 — 没有它,global 集群的 DCS provider 没有 DCS API 凭证,无法进行协调(例如,后续扩容会失败)。
对于 DR 安装,请对 DCSCluster.spec.encryptionProviderConfigRef 中指定名称的 Secret 执行相同检查。DCSCluster 对象会携带该引用完成交接,因此引导集群消失后,缺失的 Secret 会导致后续每个控制平面机器创建失败 — 请参阅添加 provider 特定的 DR 字段。
不要删除 Cluster API 对象来执行清理
不要运行 kubectl delete cluster global,也不要将 Cluster、KubeadmControlPlane 或 provider 基础设施对象作为清理步骤删除。安装完成后,这些对象会拥有运行中的 global 控制平面机器,因此删除它们会级联删除控制平面 VM,并摧毁刚刚安装的集群。停用操作仅限于在引导主机上移除本地引导集群(其 KIND 容器);请保留 Cluster API 对象。
后续步骤
工作示例:Huawei DCS 的完整 global Manifest
这是一个适用于 Huawei DCS 上三副本控制平面 global 集群的完整单文件 Manifest。它包含第 4 步中所述的同一组资源,已预先组装完成,因此无需跨页面合并片段。示例仅使用文档专用的示例值:替换每个 <placeholder>,并重新使用你在第 1 步导出的 ${...} 变量。在第 5 步应用它。
此示例面向非 DR 集群。为避免维护两份副本,这里不重复 KubeadmControlPlane kubeadmConfigSpec 正文 — 它与业务集群使用的内容完全相同,取自完整 KubeadmControlPlane 配置附录;资源 3 中内联标注了两个 global / 非 DR 差异。
该 Manifest 不会创建 DCS API 凭证 Secret。该 Secret 已在前置条件中创建,此处通过 DCSCluster.spec.credentialSecretRef 按名称引用。第 7 步会通过 dcs-import-extra-resources ConfigMap 将其复制到 global 集群;其中的 names 列表必须与同一个 Secret 名称保持一致。
应用前:在 DCS 上准备以下内容
Manifest 会引用以下内容,但不会创建它们。请先按顺序准备:
-
DCS API 访问权限及由此创建的凭证 Secret — 端点(https://<host>:7443)、一个 administrator 用户及密码,以及站点 ID。四项内容都会写入凭证 Secret;provider 会从其中读取站点 ID,并在该字段为空时将其写入 DCSCluster.spec.site,因此下面的 Manifest 不会设置该字段。
按照云凭证中的说明,在 cpaas-system 中创建名为 ${PROVIDER_SECRET_NAME} 的 Secret。不要将其编写为 YAML 后应用:kubectl apply 会在 Secret 的 kubectl.kubernetes.io/last-applied-configuration 注解中记录密码。
global 安装还要求使用其他 Manifest 资源都带有的集群名称标签:
kubectl label secret "${PROVIDER_SECRET_NAME}" --namespace cpaas-system \
--overwrite cpaas.io/cluster-name=global
-
VM 模板 — 上传 Alauda OS 镜像,并据此创建 VM 模板;记录其名称并填入 vmTemplateName。使用 4.2.1+ 模板,以便在节点替换期间分离并重新挂载 /var/cpaas 持久磁盘。请参阅机器模板。
-
计算集群、分布式虚拟交换机、端口组和数据存储 — 选择目标 DCS 计算集群、分布式虚拟交换机、端口组,以及具有足够可用容量的数据存储。它们分别填入 resource、dvSwitchName、portGroupName 和 datastoreName。请参阅DCS 平台容量和放置。
-
IP 和 API 端点 — 为控制平面准备三个可用节点 IP(包括网关和 DNS);这些 IP 填入 DCSIpHostnamePool。然后选择以下任一项:遵循控制平面端点契约并将 TCP 6443 转发到三个节点的外部 LoadBalancer;或者,在同一 Layer 2 网络中为 DCS Provider v1.0.22+ Self-built VIP 和 ACP v4.4+ 使用单独的 IPv4 VIP。所选端点主机和端口分别填入 DCSCluster.spec.controlPlaneLoadBalancer 和 controlPlaneEndpoint。请参阅在 Huawei DCS 上创建集群。
-
要读取的版本和 ID — ${K8S_VERSION},以及从 cpaas.io/dcs-vm-template ConfigMap 中读取的 CoreDNS 和 etcd 镜像标签(请参阅解析占位符值);从OS 支持矩阵中读取 kube-ovn chart 版本;以及你在第 1 步导出的 Registry 地址、VIP 和 CIDR 值。
准备好这些内容后,应用下面的 Manifest。此工作示例使用外部 LoadBalancer,因此对所有受支持的 DCS provider 版本均有效。如果选择 DCS Provider v1.0.22+ 以及 ACP v4.4+ Self-built VIP,请在创建 global 集群之前,应用 Manifest 后面所示的内部模式替换内容。
---
# 1. Control-plane IP / hostname pool (one entry per control-plane replica).
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DCSIpHostnamePool
metadata:
name: global-cp-pool
namespace: cpaas-system
labels:
cpaas.io/cluster-name: "global"
spec:
pool:
# /var/cpaas holds platform state and must survive node replacement, so it is
# declared here as a persistentDisk bound to the IP slot (not as a
# DCSMachineTemplate disk). Requires a DCS VM template 4.2.1+ and maxSurge: 0.
- ip: "192.0.2.11"
mask: "24"
gateway: "192.0.2.1"
dns: "192.0.2.2"
hostname: "global-cp-1"
machineName: "global-cp-1"
persistentDisk:
- {slot: 0, quantityGB: 100, datastoreName: <datastore-name>, path: /var/cpaas, format: xfs, mountOptions: [defaults]}
- ip: "192.0.2.12"
mask: "24"
gateway: "192.0.2.1"
dns: "192.0.2.2"
hostname: "global-cp-2"
machineName: "global-cp-2"
persistentDisk:
- {slot: 0, quantityGB: 100, datastoreName: <datastore-name>, path: /var/cpaas, format: xfs, mountOptions: [defaults]}
- ip: "192.0.2.13"
mask: "24"
gateway: "192.0.2.1"
dns: "192.0.2.2"
hostname: "global-cp-3"
machineName: "global-cp-3"
persistentDisk:
- {slot: 0, quantityGB: 100, datastoreName: <datastore-name>, path: /var/cpaas, format: xfs, mountOptions: [defaults]}
---
# 2. Control-plane VM spec.
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DCSMachineTemplate
metadata:
name: global-cp-template
namespace: cpaas-system
labels:
cpaas.io/cluster-name: "global"
spec:
template:
spec:
vmTemplateName: <vm-template-name>
# Places the cloned VMs in a DCS compute cluster.
resource:
type: cluster
name: <dcs-cluster-name>
vmConfig:
dvSwitchName: <dvswitch-name>
portGroupName: <port-group-name>
dcsMachineCpuSpec: {quantity: 16}
dcsMachineMemorySpec: {quantity: 32768} # MB
dcsMachineDiskSpec:
- {quantity: 0, datastoreName: <datastore-name>, systemVolume: true}
- {quantity: 10, datastoreName: <datastore-name>, path: /var/lib/etcd, format: xfs}
- {quantity: 100, datastoreName: <datastore-name>, path: /var/lib/kubelet, format: xfs}
- {quantity: 100, datastoreName: <datastore-name>, path: /var/lib/containerd, format: xfs}
# /var/cpaas is intentionally NOT a template disk — it is declared as a
# persistentDisk on the IP pool above so it survives node replacement.
ipHostPoolRef:
name: global-cp-pool
---
# 3. Control plane.
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
name: global-kcp
namespace: cpaas-system
labels:
cpaas.io/cluster-name: "global"
annotations:
controlplane.cluster.x-k8s.io/skip-kube-proxy: ""
spec:
replicas: 3
version: ${K8S_VERSION}
rolloutStrategy:
type: RollingUpdate
rollingUpdate: {maxSurge: 0}
machineTemplate:
nodeDrainTimeout: 1m
nodeDeletionTimeout: 5m
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DCSMachineTemplate
name: global-cp-template
kubeadmConfigSpec:
format: ignition
users:
- name: boot
sshAuthorizedKeys:
- "ssh-ed25519 AAAA...replace-with-your-public-key... global-boot"
# The rest of kubeadmConfigSpec — files, clusterConfiguration,
# preKubeadmCommands, postKubeadmCommands, initConfiguration,
# joinConfiguration — is identical to a workload cluster, so it is not
# duplicated here. Take it verbatim from the Complete KubeadmControlPlane
# Configuration appendix in the DCS create-cluster guide
# (#complete-kubeadmcontrolplane-configuration). The three large files
# (psa-config.yaml, control-plane-kubelet-patch.json, audit-policy.yaml) may
# use contentFrom the dcs-kubernetes-<major.minor>-files Secret. Apply these
# global / non-DR deltas to that body:
# 1. Add clusterConfiguration.etcd.local.serverCertSANs:
# ["${CONTROL_PLANE_VIP}", "${PLATFORM_HOST}"]
# A DR pair uses a different list — see Optional Disaster Recovery
# Deployment. etcd.kube-system belongs to that list, not this one.
# 2. Omit the /etc/kubernetes/encryption-provider.conf files entry: the
# provider writes that file on control-plane nodes and drops any
# files entry for it. Keep apiServer.extraArgs.encryption-provider-
# config — see the note after this example.
---
# 4. DCS infrastructure cluster.
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DCSCluster
metadata:
name: "global"
namespace: cpaas-system
labels:
cpaas.io/cluster-name: "global"
annotations:
cpaas.io/registry-address: "${NODE_REGISTRY_ADDRESS}"
spec:
controlPlaneLoadBalancer: {host: "${CONTROL_PLANE_VIP}", port: 6443, type: external}
controlPlaneEndpoint: {host: "${CONTROL_PLANE_VIP}", port: 6443}
credentialSecretRef: {name: "${PROVIDER_SECRET_NAME}"} # created in the prerequisites above
networkType: kube-ovn
site: <dcs-site-id>
---
# 5. Top-level CAPI Cluster: global wiring, labels, and annotations.
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: global
namespace: cpaas-system
labels:
cpaas.io/cluster-name: "global"
cluster-type: DCS
is-global: "true"
annotations:
capi.cpaas.io/resource-group-version: infrastructure.cluster.x-k8s.io/v1beta1
capi.cpaas.io/resource-kind: DCSCluster
capi.cpaas.io/kubernetes: ${K8S_VERSION} # same value as KubeadmControlPlane.spec.version
cpaas.io/registry-address: "${NODE_REGISTRY_ADDRESS}"
cpaas.io/nodes-mode: self-managed # node lifecycle managed by CAPI + the DCS provider
cpaas.io/kube-ovn-join-cidr: <kube-ovn-join-cidr> # a /16 you choose; must not overlap the pod / service CIDRs or another cluster's join CIDR
cpaas.io/kube-ovn-version: <kube-ovn-chart-version>
spec:
clusterNetwork:
pods: {cidrBlocks: ["${CLUSTER_CIDR}"]}
services: {cidrBlocks: ["${SERVICE_CIDR}"]}
controlPlaneRef:
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
name: global-kcp
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DCSCluster
name: global
内部 Self-built VIP 变体
上面的工作示例使用 type: external。应用前,DCS Provider v1.0.22+ 与 ACP v4.4+ 可以将资源 4 的端点字段替换为以下内部配置。VIP 必须在控制平面 Layer 2 网络中预留,并且 vrid 必须在该网络中唯一:
spec:
controlPlaneLoadBalancer:
host: "${CONTROL_PLANE_VIP}"
port: 6443
type: internal
vrid: <unique-vrid-1-255>
# interface: eth0 # optional; omit for provider/alive auto-detection
controlPlaneEndpoint: {host: "${CONTROL_PLANE_VIP}", port: 6443}
要替换的值
Secret 加密和灾难恢复
此示例启用了 etcd 静态 Secret 加密。apiServer.extraArgs.encryption-provider-config 保留在你从附录复制的 clusterConfiguration 中,DCS provider 会提供其指向的文件:为第一个控制平面机器生成的随机密钥,以及之后每个控制平面机器上运行中的 API server 自有文件。不要将 /etc/kubernetes/encryption-provider.conf 添加到 KubeadmControlPlane.spec.kubeadmConfigSpec.files — provider 会删除该路径对应的任何条目。请参阅如何交付加密 provider 配置。
只有在确实希望在不进行静态加密的情况下运行时,才删除该参数。对于 DR 对,双方的密钥必须相同,这意味着必须按照可选灾难恢复部署中的说明,使用 DCSCluster.spec.encryptionProviderConfigRef 固定该密钥。