安装 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;每个提供商都有自己的对应资源)必须严格命名为 globalcpaas-installer 按字面名称查找这些资源,并且只有当基础设施集群命名为 global 时,Huawei Cloud Stack provider 才会分配 global ELB 监听器端口(注册表和控制台使用 11443,DR etcd 同步使用 2379,Web 访问使用 443)。使用其他名称会静默破坏注册表拉取、DR etcd 同步和 Web 控制台。
  • 每个其他 CAPI 资源(KubeadmControlPlaneKubeadmConfigTemplateMachineDeployment)以及每个其他提供商基础设施资源(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 节点都会从此主机拉取平台镜像集。

硬件

资源最低要求(x86_64)
CPU8 个核心
内存16 GB
磁盘300 GB 或更多,可供提取目录(/root/cpaas-install)和 /var/cpaas 使用

CPU 和内存最低要求与 Prerequisites 中记录的控制平面节点逐节点最低要求一致。在传统安装路径中,运行 setup.sh 的机器会成为首个控制平面节点,因此必须满足该要求;在此路径中,bootstrap 主机承担相当的工作 — 提取 Core Package、提供平台 registry,以及运行 bootstrap 控制平面 — 因此其规格应达到相同的最低要求。

对于 arm64,请采用同一页面中的平台 ARM 约定:至少达到 x86_64 配置的 1.5 倍,最好达到 2 倍,即最低配置为 12 个核心 / 24 GB,建议配置为 16 个核心 / 32 GB

磁盘是最常导致 bootstrap 过程停滞的资源。请从以下三部分进行规划:

使用者空间
提取后的 Core Package提取后至少需要 100 GB。请参阅 Installing
/var/cpaas 下的嵌入式 registry 数据目标版本的平台镜像负载,以及复制到 plugins/ 中的 Aligned Extension 软件包
暂存以供上传的 provider 软件包和 OS 构件Kubeadm 和基础设施 provider 软件包,以及每种节点架构各一个 Alauda OS 镜像或 VM 模板构件

操作系统

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。安装中途更改地址会导致节点置备失败。

来源目标端口用途
Bootstrap 主机目标 IaaS 平台 API特定于 provider:DCS VRM TCP/7443 和 DCS physical host MGMT(通常为 TCP/8443);vCenter TCP/443;HCS API 和 IAM endpointCluster API provider 在 minialauda 内运行,并通过 IaaS API 调谐机器。对于 DCS,此路径还用于上传 Ignition ISO;请参阅在 Huawei DCS 上创建集群
Bootstrap 主机global 控制平面 endpointTCP/6443创建控制平面时,Cluster API 必须能够访问该 endpoint。请参阅规划控制平面 endpoint
正在置备的 global 节点Bootstrap 主机 registryTCP/11443global 集群自身的 registry 接管之前,节点会从 NODE_REGISTRY_ADDRESS 拉取平台镜像
global 主机,仅限 Bare MetalBootstrap 主机TCP/12443bootstrap 阶段使用的 Elemental registration endpoint

兼容性和版本输入

安装前,记录交付包支持的版本集:

输入用途
Core Package 版本提供安装程序、本地 registry 和基础平台负载。
Kubeadm provider chart 版本必须与 global manifest 使用的 Cluster API 控制平面资源匹配。
Infrastructure provider chart 版本使用目标版本交付的 VMware vSphere、DCS、HCS 或 Bare Metal provider chart 版本。
Alauda OS 镜像或 VM 模板必须包含 K8S_VERSION 使用的 Kubernetes 版本。
K8S_VERSION使用与目标 Alauda OS 镜像匹配的带 v 前缀 semver,例如 v<major>.<minor>.<patch>

安装生命周期

global 集群安装生命周期

使用以下阶段图确定安装各部分的输入、预期结果以及首个诊断位置。

阶段输入应用预期结果首先诊断
Bootstrap 环境Core Package 和 bootstrap 主机运行 setup.shminialauda 管理集群、本地 registry 和安装程序 Pod 正在运行。cpaas-system 中 bootstrap 主机的容器和 Pod。
Provider 交付Kubeadm 和 infrastructure provider 软件包推送软件包并应用 provider AppRelease 对象。Provider CRD 和控制器处于 Ready 状态。AppRelease 状态、provider Pod 以及 registry 访问。
集群定义Provider 凭证、endpoint、网络、节点和镜像值应用完整的 Cluster API manifest。Cluster、infrastructure 集群、控制平面和 machine 资源出现。资源条件和 provider-controller 日志。
节点置备VM 模板或已注册的主机让 infrastructure provider 调谐 Machine。VM 或物理主机启动并接收 kubeadm bootstrap 数据。Provider Machine 对象、IaaS 事件和 guest 初始化日志。
控制平面Kubernetes 和组件版本等待 KubeadmControlPlane预期的控制平面副本处于 Ready 状态,且 API endpoint 响应正常。KubeadmControlPlane、Machine、etcd 和 endpoint 健康状况。
平台安装安装程序请求和导入资源触发安装程序。ACP Core 组件、registry、平台访问以及基础模块变为正常状态。cpaas-system 中的安装程序进度 API 和 Pod。
移交已导入的 provider 资源和凭证验证最终的 global 集群及每个 provider 特定的移交门控,然后仅停用 minialauda最终集群接管其 provider,不再依赖临时管理集群。对于 Bare Metal,system-agent 移交 ConfigMap 与当前 endpoint、身份验证配置文件和完整目标集匹配。已导入的资源、provider 凭证、最终集群 annotation 以及 provider 特定的移交 Job 和 ConfigMap。

操作步骤

步骤 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,将 ClusterDCSCluster 上的 annotation 更新为该值,因此后续调谐会从 global 集群而不是 bootstrap 主机拉取镜像。

请区分 Registry 的格式:

用途格式示例
Registry HTTP API 请求带有 /v2/ 路径的 URLhttp://${LOCAL_REGISTRY_ADDRESS}/v2/ait/chart-cluster-api-provider-kubeadm/tags/list
容器镜像引用Registry、repository 以及 tag 或 digest${BOOTSTRAP_REGISTRY_ADDRESS}/ait/cluster-api-provider-kubeadm:<tag>
cpaas.io/registry-address annotation仅使用 <host>:<port>${NODE_REGISTRY_ADDRESS}
AppRelease repoURL不带 /v2/ 的 Registry 地址${BOOTSTRAP_REGISTRY_ADDRESS}

/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。

Huawei DCS
VMware vSphere
Huawei Cloud Stack
Bare Metal

设置 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

步骤 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 会强制执行该字段值:

ProviderBootstrap userdata 格式
Huawei DCSignition(由 provider 强制执行;DCS provider 会因 invalid format, expected ignition, got <other> 拒绝任何其他格式)。
VMware vSpherecloud-config(CAPI bootstrap 数据由客户机的 cloud-init 软件处理;不支持设置 ignition)。
Huawei Cloud Stackcloud-config(HCS provider 会拒绝 ignition;客户机会使用 cloud-init 处理生成的数据)。
Bare Metalcloud-config(Bare Metal provider 使用 CAPI bootstrap 数据,并渲染由客户机通过 cloud-init 处理的 elemental plan)。
Huawei DCS
VMware vSphere
Huawei Cloud Stack
裸金属

在渲染前,为 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 中包含以下资源:

资源用途
用于控制平面节点的 DCSIpHostnamePool分配静态 IP、主机名、网络设置以及由资源池管理的持久化磁盘。
用于控制平面节点的 DCSMachineTemplate定义 DCS VM 模板、文件夹、CPU、内存和模板本地磁盘。
KubeadmControlPlane引导 Kubernetes 控制平面。将 spec.version 设置为 ${K8S_VERSION}
DCSCluster定义 DCS 基础设施集群和控制平面端点。
Cluster将 Cluster API Cluster 连接到 DCSClusterKubeadmControlPlane
用于工作节点的 DCSIpHostnamePoolDCSMachineTemplateKubeadmConfigTemplateMachineDeployment创建工作节点。

使用在 Huawei DCS 上创建集群Huawei DCS 基础设施资源中的 DCS 资源字段。对于 global 集群,还需满足以下要求:

  • Cluster.metadata.nameDCSCluster.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 补丁和审计策略),以及完整的 clusterConfigurationpreKubeadmCommandspostKubeadmCommands 和初始化及加入节点注册补丁,都与业务集群相同。请从完整的 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}"

步骤 5 — 应用全局清单

将特定于 provider 的清单应用到 bootstrap 集群。

Huawei DCS
VMware vSphere
Huawei Cloud Stack
裸金属
kubectl apply -f "${GLOBAL_DCS_YAML}"

步骤 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。

Huawei DCS
VMware vSphere
Huawei Cloud Stack
裸金属

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"}'

步骤 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 都使用相同的端点路径,但请求正文不同。

字段Huawei DCSVMware vSphereHuawei Cloud Stack裸金属
端点路径/cpaas-installer/api/config/dcs/cpaas-installer/api/config/dcs/cpaas-installer/api/config/dcs/cpaas-installer/api/config/dcs
console.host["${CONTROL_PLANE_VIP}"]["${CONTROL_PLANE_VIP}"]["${CONTROL_PLANE_VIP}"]["${CONTROL_PLANE_VIP}"]
console.globalHost平台访问地址平台访问地址平台访问地址平台访问地址
cluster.clusterCIDRcluster.serviceCIDR必需不设置;集群 CIDR 在 VMware vSphere Cluster 清单中声明不设置必需
cluster.features.ha必需,vip 为带有 isThirdParty: true${CONTROL_PLANE_VIP}不设置;控制平面端点在 VSphereCluster.spec.controlPlaneEndpoint.host 中声明不设置;HCS ELB 在 HCSCluster 中声明必需,vip 为带有 isThirdParty: true${CONTROL_PLANE_VIP}
hostIP当前 bootstrap 主机 IP当前 bootstrap 主机 IP当前 bootstrap 主机 IP当前 bootstrap 主机 IP

每个 console.host 条目都是裸地址:不包含 https:// 前缀,也不包含端口。端口来自 console.httpPortconsole.httpsPort

Huawei DCS
VMware vSphere
Huawei Cloud Stack
Bare Metal

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 使用相同的地址。

第三方控制台证书

示例使用自签名控制台证书。如果环境要求使用第三方证书,请在提交安装程序请求之前,将 console.cert 替换为包含 base64 完整证书链、私钥和可选 PKCS#12 值的 thirdParty 块。此证书仅用于平台 HTTPS 入口;不会配置根据 KubeadmControlPlane 生成的 kube-apiserver 或 etcd 证书。端口 11443 上的 Global Registry 使用独立管理的 global-registry-server 证书链,而不是 console.certdex.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.certdex.tls

步骤 9 — 监控安装

安装程序接受请求后,安装会经历多个阶段,这些阶段可以从 bootstrap 主机上观察到。典型的不可变 OS global 集群需要 30–60 分钟;总耗时取决于 IaaS 预配速度、镜像拉取时间以及所选插件的数量。

可以观察到的阶段

阶段正在发生的操作首先查看的位置
Bootstrapbootstrap 集群、嵌入式 Registry 和 Cluster API providers 正在 bootstrap 主机上运行。在步骤 2 和步骤 3 中已完成。bootstrap 主机终端;kubectl get pods -n cpaas-system
基础设施预配Cluster API provider 根据目标 IaaS 平台上的 Alauda OS 模板创建 VM。kubectl get machines -n cpaas-system
控制平面 bootstrapKubeadmControlPlane bootstrap 第一个控制平面节点,etcd 启动,其他控制平面节点加入。kubectl get kubeadmcontrolplane -n cpaas-system
网络和核心附加组件CAPI provider 在新集群上协调 Kube-OVN、CoreDNS 和 kube-proxy。kubectl --kubeconfig <global-kubeconfig> get pods -n kube-system
平台安装安装程序将 Cluster API 资源导入新的 global 集群,部署基础 operator,并安装所选插件。安装程序进度 API;安装程序日志
完成安装程序将请求标记为 Success,并将最终集群状态写入 ClusterModule/global安装程序进度 API;kubectl --kubeconfig <global-kubeconfig> get clustermodule global

安装期间的信号

同时查看安装程序进度 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

常见停滞情况及查看位置

症状首先查看的位置要查找的内容
Machine 保持在 Pending 状态或未出现kubectl describe machine -n cpaas-system <machine>Machine 的 BootstrapInfrastructure 条件中的 provider 特定失败原因。IaaS 配额、网络和凭证问题会显示在此处。
KubeadmControlPlane 未达到 Ready使用新集群 kubeconfig 的 kubectl get nodeskubectl describe kubeadmcontrolplane -n cpaas-system第一个控制平面节点上的 etcd 运行状况,以及其余节点的加入进度。
kube-system 中的 Pod 保持 Pending 状态或无法拉取镜像kubectl --kubeconfig <global-kubeconfig> describe pod -n kube-system <pod>镜像拉取错误通常意味着面向节点的 Registry 地址无法从新集群的子网访问。
安装程序进度 API 显示阶段停滞/var/cpaas/data/installer.log最新的阶段行和最新的错误消息。重试错误会按较短的时间间隔重复;持久性错误不会继续推进。
ClusterModule/global 未达到正常阶段kubectl --kubeconfig <global-kubeconfig> describe clustermodule globalStatus.conditions 会说明哪个模块阻止集群完成。

此处未列出的问题通常指向特定于环境的原因。请收集安装程序日志、进度 API 响应以及相关的 kubectl describe 输出,然后上报。

可选的灾难恢复部署

在为灾难恢复部署主集群和备用 global 集群时使用本节。在为每个 global 集群应用提供商专用清单之前,完成以下附加配置。

开始之前,请完成双向 DR 网络要求。在两个集群 VIP 都配置了所需的负载均衡器侦听器,且集群间网络规则允许所需流量双向传输之前,不要开始任一安装。正常运行期间使用的方向为备用集群到主集群,但故障转移后方向会反转。

仅适用于 Bare Metal 的全新安装范围

本节中的 Bare Metal 分离认证操作步骤仅涵盖主集群和备用 global 集群的全新安装。不定义或验证现有 Bare Metal DR 对的原地升级或迁移,也不涵盖故障环境的修复或恢复。对于这些生命周期任务,请使用经过单独验证的运维操作手册。

以下并非每个小节都适用于所有提供商。完成适用于你基础设施的标记项:

小节Huawei DCSVMware vSphereHuawei Cloud StackBare Metal
准备共享 DR 变量
Bare Metal:准备共享 ServiceAccount 签名密钥
添加 DR etcd 服务器证书 SAN
添加提供商专用 DR 字段
安装主集群和备用集群
Bare Metal:在停用前进行验证
Bare Metal:应用规则 ConfigMap
安装 etcd-sync
Bare Metal:验证动态规则源
启动并验证同步

将仅适用于 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 字段

Huawei DCS
VMware vSphere
Huawei Cloud Stack
Bare Metal

在两个安装环境的 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_SECRETDCSCluster.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 数据的解密。

安装主集群和备用集群

对主集群和备用 global 集群均执行步骤 1 至 9。

仅适用于 Bare Metal:使用两个独立的 bootstrap 主机

使用两个相互独立的 bootstrap 主机,一个用于主集群安装,另一个用于备用集群安装。不要为两端复用同一个 bootstrap 集群。bootstrap 环境保存安装程序状态、AppRelease 对象、Registry Secret、MachineRegistrationSeedImage 和交接状态;共享该环境会污染两个 global 安装,并可能导致交接或清理操作作用于错误的一端。

对两端均使用 provider 特定的安装程序配置差异:

Provider主集群安装备用集群安装
Huawei DCSconsole.host 设置为主控制平面 VIP。cluster.features.ha.vip 使用相同的地址。根据步骤 7 创建 DCS dcs-import-extra-resources ConfigMap,并保持 PROVIDER_SECRET_NAMEDCSCluster.spec.credentialSecretRef.name 对齐,以及 DCS_ENCRYPTION_PROVIDER_SECRETDCSCluster.spec.encryptionProviderConfigRef.name 对齐。console.host 设置为备用控制平面 VIP。cluster.features.ha.vip 使用相同的地址。根据步骤 7 创建 DCS dcs-import-extra-resources ConfigMap,并保持 PROVIDER_SECRET_NAMEDCSCluster.spec.credentialSecretRef.name 对齐,以及 DCS_ENCRYPTION_PROVIDER_SECRETDCSCluster.spec.encryptionProviderConfigRef.name 对齐。
VMware vSphereconsole.host 设置为主控制平面 VIP。VSphereCluster.spec.controlPlaneEndpoint.host 使用相同的地址。根据步骤 7 创建 VMware vSphere dcs-import-extra-resources ConfigMap,并保持 global-vsphere-credentialsVSphereCluster.spec.identityRef.name 对齐。console.host 设置为备用控制平面 VIP。VSphereCluster.spec.controlPlaneEndpoint.host 使用相同的地址。根据步骤 7 创建 VMware vSphere dcs-import-extra-resources ConfigMap,并保持 global-vsphere-credentialsVSphereCluster.spec.identityRef.name 对齐。
Huawei Cloud Stackconsole.host 设置为主控制平面 VIP。HCSCluster.spec.controlPlaneLoadBalancer.vipAddress 使用相同的地址。根据步骤 7 创建 HCS dcs-import-extra-resources ConfigMap,并保持 HCS_SECRET_NAMEHCSCluster.spec.identityRef.name 对齐。console.host 设置为备用控制平面 VIP。HCSCluster.spec.controlPlaneLoadBalancer.vipAddress 使用相同的地址。根据步骤 7 创建 HCS dcs-import-extra-resources ConfigMap,并保持 HCS_SECRET_NAMEHCSCluster.spec.identityRef.name 对齐。
Bare Metalconsole.host 设置为主控制平面 VIP。cluster.features.ha.vipBaremetalCluster.spec.controlPlaneLoadBalancer.hosthandoffHook.controlPlaneVIP 使用相同的地址。设置 handoffHook.directAPIServer: trueelemental.systemAgent.splitAuthEnabled: trueelemental.systemAgent.sharedAuthReadOnly: false。根据步骤 7 创建 Bare Metal dcs-import-extra-resources ConfigMap。console.host 设置为备用控制平面 VIP。cluster.features.ha.vipBaremetalCluster.spec.controlPlaneLoadBalancer.hosthandoffHook.controlPlaneVIP 使用相同的地址。设置 handoffHook.directAPIServer: trueelemental.systemAgent.splitAuthEnabled: trueelemental.systemAgent.sharedAuthReadOnly: true。根据步骤 7 创建 Bare Metal dcs-import-extra-resources ConfigMap。

对于主集群,确保平台域名解析到主控制平面 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:在弃用任一 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.readytrue 也是如此。

备用集群安装成功后,将平台域名切回主入口。首次同步期间,主集群是活动源。

Bare Metal:应用规则 ConfigMap

仅适用于 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 中,因此请像上传其他插件包一样将其上传到目标集群。

使用以下配置:

参数
活动 Global 集群 VIP活动 global 集群的控制平面 VIP
活动 Global 集群 ETCD 端点https://<active-control-plane-vip>:2379,除非负载均衡器转发端口 2379
备用集群 ETCD 端点默认值
活动 Global 集群令牌 Secretetcd-sync-active-cluster-token
数据检查间隔默认值
打印详细日志除非进行故障排除,否则禁用

在安装前于备用集群上创建令牌 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-caremote-etcd-issuerremote-etcd-client 后,插件安装才会继续。

Bare Metal:验证动态规则源

打开同步路径前,确认规则源报告 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: Successtype: Complete
  • 所有 global 集群节点均为 Ready
  • cpaas-system 中的关键 Pod 为 RunningCompleted
  • 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>"

从正在准备的集群自身的 ProductBasecpaas-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.tlsca.crt。 这些默认值假设存在由 cert-manager 签发的 dex.tls。在 global 集群上,dex.tls 改由安装程序的 console.cert 流程提供,而 thirdParty 证书生成的 Secret 仅包含 tls.crttls.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-systemelemental-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 不会自行恢复

遇到 ImageCatalogMissBaremetalMachine 在修复目录后仍会保持为 Failed。 重新协调该对象和重启 provider 都不会改变其状态;协调器会有意将缺少映射视为终止状态,而不是回退到默认镜像。 删除 Machine,使其所有者 KubeadmControlPlaneMachineSet 重新创建它。 在应用第一个业务集群 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.readytruedata.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,也不要将 ClusterKubeadmControlPlane 或 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 会引用以下内容,但不会创建它们。请先按顺序准备:

  1. 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
  2. VM 模板 — 上传 Alauda OS 镜像,并据此创建 VM 模板;记录其名称并填入 vmTemplateName。使用 4.2.1+ 模板,以便在节点替换期间分离并重新挂载 /var/cpaas 持久磁盘。请参阅机器模板

  3. 计算集群、分布式虚拟交换机、端口组和数据存储 — 选择目标 DCS 计算集群、分布式虚拟交换机、端口组,以及具有足够可用容量的数据存储。它们分别填入 resourcedvSwitchNameportGroupNamedatastoreName。请参阅DCS 平台容量和放置

  4. 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.controlPlaneLoadBalancercontrolPlaneEndpoint。请参阅在 Huawei DCS 上创建集群

  5. 要读取的版本和 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}

要替换的值

占位符 / 变量来源
${K8S_VERSION}${CONTROL_PLANE_VIP}${PLATFORM_HOST}${NODE_REGISTRY_ADDRESS}${CLUSTER_CIDR}${SERVICE_CIDR}${PROVIDER_SECRET_NAME}在第 1 步导出。
<dcs-api-user> / <dcs-api-password> / <dcs-api-host> / <dcs-site-id>DCS 平台凭证和站点。它们不会出现在 Manifest 中 — 而是写入用于在前置条件中创建 ${PROVIDER_SECRET_NAME} 的凭证文件。请参阅云凭证
<vm-template-name><dns-image-tag><etcd-image-tag>cpaas.io/dcs-vm-template ConfigMap。请参阅解析占位符值
<dcs-cluster-name><dvswitch-name><port-group-name><datastore-name>DCS 平台对象。请与 DCS 管理员确认。
192.0.2.x<kube-ovn-join-cidr><kube-ovn-chart-version>网络的节点 IP/网关/DNS;不与 pod / service CIDR 或其他集群的 join CIDR 重叠的 /16 Kube-OVN join CIDR;以及来自OS 支持矩阵的 kube-ovn chart 版本。
ssh-ed25519 AAAA...有效的 OpenSSH 公钥。ignition 格式不接受空列表;即使不计划通过 SSH 登录,也要提供任意有效密钥。
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 固定该密钥。