在 Huawei DCS 上创建集群

本文档介绍如何在 Huawei DCS 平台上创建 Kubernetes 集群。可以通过清单使用基于 YAML 的集群创建方式。如果已安装 Fleet Essentials,也可以通过 Web UI 创建集群。persistentDisk 在 DCSIpHostnamePool 上的声明仅支持通过 YAML 配置,在 Web UI 中不可用。对于 ACP v4.4 或更高版本,Self-built VIP 也支持通过 YAML 工作流进行配置。

INFO

Web UI 提供带验证功能的引导式工作流,而 YAML 则提供更灵活的自动化能力。

先决条件

创建集群前,请确保满足以下所有先决条件:

1. 基础设施资源

创建集群前,请配置以下基础设施资源:

  • Cloud Credential - DCS 平台访问信息
  • IP Pool - 集群节点、额外 NIC 以及任何 IP 槽位持久磁盘(例如 /var/cpaas)的网络配置
  • Machine Template - 控制平面和工作节点的 VM 规格,不包括由池管理的持久磁盘

有关详细配置说明,请参阅Huawei DCS 基础设施资源。

2. 安装所需插件

在 Huawei DCS 上创建首个业务集群之前,请先将以下插件上传并安装到 global 集群上。无论 global 集群本身运行在哪个 IaaS 平台上,均适用此要求:

  • Alauda Container Platform Cluster Bootstrap Provider
  • Alauda Container Platform Infrastructure Provider for Huawei DCS

有关操作步骤,请参阅安装 Provider。

3. 准备虚拟机模板

安装 Kubernetes 时,必须:

  • 将 Alauda OS 镜像上传到 DCS 平台。使用为集群上已安装的 ACP 版本发布的 Alauda OS 镜像;请参阅下载 Alauda OS。
  • 基于此镜像创建虚拟机模板
  • 确保模板包含所有必需的 Kubernetes 组件
  • 如果计划使用持久磁盘,请使用 4.2.1 或更高版本的 DCS VM 模板,因为安全关机和磁盘卸载依赖客户机工具
  • 对于依赖池管理的持久磁盘的集群,请使用逐个替换方式。在控制平面和工作节点池上保留 maxSurge: 0。

有关每个 VM 镜像中包含的 Kubernetes 组件的详细信息,请参阅 OS 支持矩阵。

4. 网络连通性

global 集群节点必须能够访问 DCS 平台上的两个不同目标:

来源目标端口用途
global 集群节点DCS VRM 虚拟 IPTCP/7443DCS REST API。涵盖集群生命周期调用和文件上传的第一步(applyUpload)。
global 集群节点DCS 物理主机 MGMT IP(可能接收克隆的每台主机)DCS 返回的端口;通常为 TCP/8443;请向 DCS 管理员确认上传 Ignition ISO 文件流。Provider 会将文件流式传输到 applyUpload 响应返回的 URL。

文件上传分为两个步骤:Provider 先通过 VRM 虚拟 IP(TCP/7443)调用 applyUpload;DCS 平台随后返回一个 URL,该 URL 指向特定物理主机的管理 IP 和端口;然后 Provider 将文件流式传输到此 URL。在创建集群之前,必须确保从端到端都能访问这两个目标。

如果 global 集群节点采用多 NIC 布局(例如,一个 NIC 位于 ACP 集群网络,另一个 NIC 位于部署了 DCS 的客户管理网络),请确保这两个目标——VRM 虚拟 IP 和每台 DCS 物理主机的 MGMT IP——都能通过相应的 NIC 路由访问。

要求:创建和管理集群时,必须能够连接到这两个目标。

5. LoadBalancer 配置

创建集群前,请配置一个稳定的 Kubernetes API 端点。该端点会分发或转发控制平面访问所需的 API 服务器流量。

模式版本管理方适用场景
外部 LoadBalancer所有受支持的版本在创建集群前预配并维护负载均衡器。适用于环境中已有硬件或软件负载均衡器的情况。
Self-built VIPACP v4.4+DCS Provider 创建并协调由 Provider 管理的 alive ModuleInfo。适用于需要高可用控制平面端点但没有外部负载均衡器的情况。
单控制平面直连端点所有受支持的版本不会创建负载均衡器。API 端点为单个控制平面节点 IP。仅适用于只有一个控制平面副本的开发或 PoC 集群。

有关外部 LoadBalancer 的要求,请参阅规划控制平面端点。有关 API 服务器前方没有负载均衡器的单控制平面部署,请参阅单控制平面(无外部 LB)布局。

6. 业务集群镜像仓库

确定此集群从哪个镜像仓库拉取其平台组件——Kube-OVN 以及 Alauda Container Platform 在集群上安装的平台模块。此选择不影响 kubeadm 管理的镜像,它们来自 KubeadmControlPlane.spec.kubeadmConfigSpec.clusterConfiguration.imageRepository。

选项配置内容适用场景
global 集群的镜像仓库无需配置。集群会继承该镜像仓库。业务节点能够以可接受的吞吐量访问 global 集群使用的镜像仓库。
专用镜像仓库cpaas-system 中的 RegistryCredential Secret,由 cpaas.io/registry-reference 标签从 Cluster 引用。业务节点位于不同地域或网络网段,应从靠近其所在位置的镜像仓库拉取镜像。

无论选择哪种方式,镜像仓库都必须已包含目标版本的 Alauda Container Platform Core,以及计划在此集群上安装的所有平台插件对应的软件包;每个业务集群节点都必须能够访问该镜像仓库地址。

有关专用镜像仓库的操作步骤及其限制,请参阅为业务集群选择镜像仓库。

WARNING

仅在首次创建集群时读取镜像仓库绑定。在现有 Cluster 上更改或移除 cpaas.io/registry-reference,不会将该集群切换到其他镜像仓库。专用镜像仓库只能通过 YAML 配置;Web UI 工作流始终使用 global 集群的镜像仓库。

7. 节点标签和污点

如需为池中的节点设置专属角色标签和污点(例如,为监控、日志记录或入口流量预留专用节点池),请按照 Infra Nodes 中的说明,在池的资源上声明这些配置。创建节点池前请做好规划。此要求适用于所有节点池,无论集群通过 Web UI 还是 YAML 创建;Web UI 的节点池步骤不会设置标签或污点。


使用 Web UI

INFO

Fleet Essentials 升级边界

Fleet Essentials 1.0.4 及更高版本可以通过 CVO 请求 ACP 4.3 及更高版本的 Distribution Version 升级。但它不会执行 DCS Kubernetes 和 Alauda OS 的替换。有关该 Phase 2 YAML 操作步骤,请参阅在 Huawei DCS 上升级 Kubernetes。通过 Fleet Essentials 创建集群不受此边界影响。

要求:此工作流需要 Fleet Essentials。如果使用由池管理的持久磁盘,请通过 YAML 配置 DCSIpHostnamePool.spec.pool[].persistentDisk,因为 Web UI 不提供此字段。

如果新集群将依赖由池管理的持久磁盘,请先使用 YAML 创建或更新作为后端的 DCSIpHostnamePool,然后使用 Web UI 完成其余集群工作流。

创建工作流

集群创建遵循一个包含 5 个步骤的向导:

Step 1: Basic Info
  ↓
Step 2: Control Plane Node Pool
  ↓
Step 3: Worker Node Pools
  ↓
Step 4: Networking
  ↓
Step 5: Review

导航路径:集群 → 集群 → 创建集群 → 选择 Huawei DCS

步骤 1:基本信息

字段类型必填描述
Infrastructure Credential下拉列表是选择现有的 Cloud Credential
名称文本是唯一的集群标识符(小写字母、数字、连字符)
显示名称文本否便于识别的自定义描述
Distribution Version只读-ACP 版本(与 global 集群匹配)
Kubernetes 版本只读-由 Distribution Version 确定
集群 API 地址文本是格式:https://<load-balancer-address>:6443。对于 Self-built VIP,请预留 VIP 并使用本文档中的 YAML 路径。

前置条件检查:

创建集群前,请确保:

  • DCS 平台中存在 DCS VM Template,并且 Alauda OS 版本与 Kubernetes 版本匹配
  • Kubernetes API 端点已就绪。对于外部 LoadBalancer,请在创建集群前创建它。对于 Self-built VIP,请预留 VIP 并使用 YAML 路径。

版本限制:只能创建平台支持的最新 Kubernetes 版本。

步骤 2:控制平面节点池

控制平面节点池固定为 3 个副本,以实现高可用性。

字段类型必填描述
Machine Template下拉列表是按类型筛选模板:Control Plane,以及兼容的 Kubernetes 版本
副本数只读-固定为 3
SSH Authorized Keys文本否添加多个用于访问节点的 SSH 公钥

验证:关联的 IP Pool 必须具有足够的可用 IP 地址(≥ 3)。

步骤 3:业务节点池

可以添加多个业务节点池。每个池具有以下配置:

字段类型必填描述
池名称文本是此节点池的唯一标识符
Machine Template下拉列表是按类型筛选模板:Worker Node,以及兼容的 Kubernetes 版本
副本数数字是默认值:3
Max Surge数字否默认值:0,必须 ≥ 0。如果节点池将使用由池管理的持久磁盘,则保持此值为 0
Max Unavailable数字否默认值:1,必须 ≥ 0。当 maxSurge = 0 时,maxUnavailable 必须 > 0 且 ≤ 副本数
SSH Authorized Keys文本否添加多个 SSH 公钥

验证规则:

  • 池名称在集群内必须唯一
  • IP Pool 必须具有足够的可用 IP 地址(≥ 副本数)
  • maxSurge 和 maxUnavailable 必须满足以下约束:如果 maxSurge = 0,则 maxUnavailable > 0
  • 如果集群将依赖由池管理的持久磁盘,请保留 maxSurge = 0,以便在未来升级期间逐个替换节点

提示:在池名称前加上集群名称和连字符(例如 mycluster-worker-1),以避免不同集群之间的命名冲突。

步骤 4:网络

字段类型必填描述
Pods CIDRCIDR是Pod 网络地址范围
Services CIDRCIDR是Service 网络地址范围
Join CIDRCIDR是Kube-OVN Join CIDR 参数

验证:Pods CIDR 和 Services CIDR 不得重叠。

步骤 5:审核

创建集群前,审核所有配置设置:

基本信息:

  • 名称、显示名称、Infrastructure Credential
  • Distribution Version、Kubernetes 版本
  • 集群 API 地址

控制平面节点池:

  • Machine Template,包括 VM Template 名称、OS 版本、Kubernetes 版本
  • CPU、内存、副本数、SSH 密钥

业务节点池(列表视图):

  • 池名称、Machine Template、副本数
  • Max Surge、Max Unavailable、SSH 密钥

如果集群将依赖由池管理的持久磁盘,请将业务节点池的 Max Surge 保持为 0。

网络:

  • Pods CIDR、Services CIDR、Join CIDR

点击 创建,开始集群创建过程。


使用 YAML

集群创建工作流

使用 YAML 时,请在 global 集群中创建 Cluster API 资源,以配置基础设施并引导功能完整的 Kubernetes 集群。

WARNING

重要的命名空间要求

为确保正确集成为业务集群,所有资源都必须部署在 cpaas-system 命名空间中。在其他命名空间部署资源可能会导致集成问题。

WARNING

业务集群命名

业务 cluster-name 不得为 global。该名称专用于 global 集群,重复使用会导致业务集群的资源与 cpaas-system 中的 global 集群资源发生冲突。global- 前缀专用于 global 集群 DR 工作流拥有的资源;请参阅通用前提条件。请勿将 global- 用于业务集群资源,因为故障转移操作可能会将这些资源识别为属于 global 集群。

按照惯例,CAPI Cluster 和 provider 集群资源(DCSCluster)应使用名称 <cluster-name>,非根级 CAPI 和 provider 资源(KubeadmControlPlane、KubeadmConfigTemplate、MachineDeployment、machine template、IP/主机名池等)则应以 <cluster-name>- 为前缀,例如示例清单中使用的 <cluster-name>-kcp。这是一项建议,并非由控制器强制执行的规则,但可以避免多个业务集群位于 cpaas-system 中时发生同一命名空间内的冲突,并使操作期间的资源归属一目了然。

配置工作流

按以下步骤依次操作,以配置功能完整的集群(控制平面和业务节点):

  1. 配置 KubeadmControlPlane(控制平面 spec 和 kubeadm 引导配置)。
  2. 配置 DCSCluster(基础设施绑定和负载均衡器引用)。
  3. 创建 Cluster 资源(用于关联上述两个资源的顶层 CAPI 对象)。
  4. 配置业务节点资源:KubeadmConfigTemplate(业务节点引导配置)、业务节点 DCSMachineTemplate、业务节点 DCSIpHostnamePool 和 MachineDeployment。仅有控制平面并不能构成可用的集群。 有关四个业务节点 YAML 资源,请参阅部署业务节点。

注意:基础设施资源(Secret、控制平面 DCSIpHostnamePool、控制平面 DCSMachineTemplate)应单独配置。相关说明请参阅Huawei DCS 的基础设施资源。

如果需要在滚动替换期间保留任何磁盘,请在相应的 DCSIpHostnamePool.spec.pool[].persistentDisk 条目中声明。这包括平台要求的 /var/cpaas 磁盘。provider 会创建新的持久卷,作为独立的持久普通卷。当 DCS 环境要求显式设置精简配置时,请设置 persistentDisk[].isThin;如果省略该项,provider 不会发送 isThin,DCS 将使用平台默认值。

如果集群需要额外的 NIC,请在创建相应的 Machines 之前,在 DCSIpHostnamePool.spec.pool[].additionNic[] 中声明。provider 仅会在创建新 VM 时添加额外的 NIC。之后编辑 Pool 时,现有 VM 不会热添加 NIC。

解析占位符值

以下示例清单使用 <placeholder> 语法表示与环境相关的值。其中有几个值具有规范数据来源,应查询这些来源,而不要自行选择:

占位符规范数据来源获取方式
<control-plane-kubernetes-version> / <worker-kubernetes-version>cpaas-system 命名空间中的 cpaas.io/dcs-vm-template ConfigMap,每个发行版版本对应一个。kubectl -n cpaas-system get cm -l cpaas.io/dcs-vm-template -o yaml — 读取 data.kubernetesVersion。
<dns-image-tag>同一 ConfigMap 中的 data.corednsTag。使用与上文相同的 kubectl get cm 查询。
<etcd-image-tag>同一 ConfigMap 中的 data.etcdTag。使用与上文相同的 kubectl get cm 查询。
<vm-template-name>(位于 DCSMachineTemplate.spec.template.spec.vmTemplateName 中)ConfigMap 的 cpaas.io/dcs-vm-template 标签值(必须与 DCS 平台中注册的 VM template 名称一致)。使用相同的 kubectl get cm 查询 — 读取该标签。
<ssh-authorized-keys>由 operator 提供。每个条目都是单行 OpenSSH 格式的公钥(ssh-ed25519 AAAA… / ssh-rsa AAAA…)。ignition 验证器要求此字段非空。对于不需要交互式 SSH 的测试或 PoC 集群,可提供任意语法有效的公钥(无需持有匹配的私钥);生产环境请使用 operator 团队的签名密钥。不适用 — 由 operator 生成或提供。
<auth-secret-name>你在基础设施 → 云凭证中创建的 Secret。kubectl -n cpaas-system get secret <name>
<cluster-name>由 operator 选择;必须符合 DNS-1123,且不得为 global(该名称专用于 global 集群)。此值会在 Cluster、DCSCluster 以及每个 Machine 上的 cluster.x-k8s.io/cluster-name 标签中重复使用。KubeadmControlPlane 使用带前缀的形式 <cluster-name>-kcp。完整约定请参阅业务集群命名。不适用
<load-balancer-ip>由 operator 提供,供客户端用于访问集群 API server。对于 type: external,此值为外部负载均衡器 VIP(IP 地址 — 集群端点不支持域名)。对于没有外部 LB 的单控制平面集群,此值为唯一控制平面节点的 IP(请参阅单控制平面布局)。不适用
<self-built-vip>由 operator 提供的 Self-built VIP IPv4 VIP。请在创建集群前预留此地址。此地址不得分配给节点主 IP 或 DCSIpHostnamePool.spec.pool[].additionNic[].ip。不适用
<pods-cidr> / <services-cidr> / <kube-ovn-join-cidr>由 operator 提供。不得与主机网络、global 集群的 CIDR 或你计划互连的任何其他 CAPI 集群的 CIDR 重叠。如果留空,将回退到 global 集群附带的 kube-ovn 默认值;生产环境中不建议这样做:显式设置 CIDR 可以避免同一 global 集群上的多个业务集群发生静默冲突。不适用

Magic-Token 占位符

示例清单中的少数值看起来像占位符,但它们实际上是由 DCS Provider 在机器加入时替换的字面令牌。请保持原样:

字面令牌含义替换方
PROVIDER_ID每个 Machine 的 provider ID(例如 dcs://<dcsmachine-name>)。DCS Provider — 在 kubeadm init / kubeadm join 运行前,覆盖生成的 kubelet 配置中的值。
NODE_IP分配自附加到此 Machine 的 DCSIpHostnamePool 条目的节点 IP。DCS Provider — 使用 DCSIpHostnamePool.spec.pool[].ip 的值覆盖。

手动替换或加引号会破坏加入流程,导致节点始终无法向控制平面注册。

网络规划和负载均衡器

创建控制平面资源前,请规划网络架构并选择控制平面端点模式。

要求:

  • 网络分段:规划控制平面节点的 IP 地址范围
  • 额外 NIC:如果节点需要存储、管理或隔离的应用网络,请为每个 IP 槽位规划 DCSIpHostnamePool.spec.pool[].additionNic[] 值,包括 DVS 和 Port Group 名称
  • API 端点模式:使用外部 LoadBalancer,或使用配合 ACP v4.4+ 的 Self-built VIP
  • API server 地址:为 Kubernetes API server 准备稳定的 VIP 或负载均衡器地址
  • 连通性:确保所有组件之间的网络连通

对于 Self-built VIP,请验证目标 DCS 端口组允许 VIP 所有权、GARP 或 ARP 更新,以及控制平面 VM 之间的 VRRP 流量。VIP 必须与承载它的 NIC 位于同一 Layer 2 网络中。 对于外部 LoadBalancer,请在规划控制平面端点中验证 Layer 4 监听器、TCP 6443 后端、HTTPS /healthz 检查、后端所有权和端点可达性。

配置 KubeadmControlPlane

KubeadmControlPlane 资源定义控制平面配置,包括 Kubernetes 版本、节点规格和引导设置。

Kubernetes 1.35 kubelet 设置

只有从 Kubernetes 1.35 开始才需要 imagePullCredentialsVerificationPolicy: NeverVerify。使用 Kubernetes 1.34 或更早版本创建集群时,请省略此参数。如果 contentFrom.secret 提供了补丁,请验证所引用的 Secret 键包含此 Kubernetes 1.35 参数。

TIP

完整配置参考

为便于阅读,以下示例省略了较长配置文件中的部分内容。完整配置(包括默认审计策略、准入控制和文件内容)请参阅附录中的完整 KubeadmControlPlane 配置。

kubeadmcontrolplane.yaml
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
  name: <cluster-name>-kcp
  namespace: cpaas-system
  annotations:
    controlplane.cluster.x-k8s.io/skip-coredns: ""
    controlplane.cluster.x-k8s.io/skip-kube-proxy: ""
spec:
  rolloutStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 0 # Required when the cluster relies on pool-managed persistent disks
  kubeadmConfigSpec:
    users:
    - name: boot
      sshAuthorizedKeys:
      - "<ssh-authorized-keys>"
    format: ignition
    files:
    - path: /etc/kubernetes/admission/psa-config.yaml
      owner: "root:root"
      permissions: "0644"
      content: |
        # ... (Admission Configuration Content) ...
    - path: /etc/kubernetes/patches/kubeletconfiguration0+strategic.json
      owner: "root:root"
      permissions: "0644"
      content: |
        {
          "apiVersion": "kubelet.config.k8s.io/v1beta1",
          "kind": "KubeletConfiguration",
          "imagePullCredentialsVerificationPolicy": "NeverVerify",
          "_comment": "... (Kubelet Configuration Content) ..."
        }
    # ... (other files) ...
    clusterConfiguration:
      imageRepository: cloud.alauda.io/alauda
      dns:
        imageTag: <dns-image-tag>
      etcd:
        local:
          imageTag: <etcd-image-tag>
      # ... (apiServer, controllerManager, scheduler) ...
    initConfiguration:
      patches:
        directory: /etc/kubernetes/patches
      nodeRegistration:
        kubeletExtraArgs:
          node-labels: "kube-ovn/role=master"
          provider-id: PROVIDER_ID
          volume-plugin-dir: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"
          protect-kernel-defaults: "true"
    joinConfiguration:
      patches:
        directory: /etc/kubernetes/patches
      nodeRegistration:
        kubeletExtraArgs:
          node-ip: NODE_IP
          node-labels: "kube-ovn/role=master"
          provider-id: PROVIDER_ID
          volume-plugin-dir: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"
          protect-kernel-defaults: "true"
  machineTemplate:
    nodeDrainTimeout: 1m
    nodeDeletionTimeout: 5m
    infrastructureRef:
      apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
      kind: DCSMachineTemplate
      name: <cp-dcs-machine-template-name>
  replicas: 3
  version: <control-plane-kubernetes-version>

参数说明:

参数类型描述必需
.spec.kubeadmConfigSpecobjectkubeadm bootstrap provider 启动参数是
.spec.machineTemplate.infrastructureRefobjectDCSMachineTemplate 引用是
.spec.replicasint控制平面副本数。必须满足 1 ≤ replicas ≤ IP Pool size。对于开发/PoC 单控制平面部署,请设置为 1(请参阅单控制平面布局);只有一个控制平面节点的集群无法升级。生产环境通常使用 3 以实现 HA。是
.spec.versionstringKubernetes 版本(必须与 VM 模板匹配 — 请参阅解析占位符值)是

有关组件版本(例如 <dns-image-tag>、<etcd-image-tag>),请参阅 OS Support Matrix。

配置 DCSCluster

DCSCluster 是基础设施集群声明,用于引用负载均衡器和 DCS 平台凭证。

当您在集群外部管理负载均衡器时,请使用 type: external。

dcscluster.yaml
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DCSCluster
metadata:
  name: "<cluster-name>"
  namespace: cpaas-system
spec:
  controlPlaneLoadBalancer:
    host: <load-balancer-ip>
    port: 6443
    type: external
  credentialSecretRef:
    name: <auth-secret-name>
  controlPlaneEndpoint:
    host: <load-balancer-ip>
    port: 6443
  # Optional. Enable only when the target DCS compute cluster has DRS enabled.
  controlPlaneHA:
    enabled: true
  networkType: kube-ovn
  site: <site>

对于 ACP v4.4+,使用 type: internal 启用 Self-built VIP:

dcscluster-self-built-vip.yaml
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DCSCluster
metadata:
  name: "<cluster-name>"
  namespace: cpaas-system
spec:
  controlPlaneLoadBalancer:
    host: <self-built-vip>
    port: 6443
    type: internal
    vrid: 42
    interface: eth0
  credentialSecretRef:
    name: <auth-secret-name>
  controlPlaneEndpoint:
    host: <self-built-vip>
    port: 6443
  networkType: kube-ovn
  site: <site>

参数说明:

参数类型描述必需
.spec.controlPlaneLoadBalancerobject控制平面 API server 暴露方式是
.spec.controlPlaneLoadBalancer.typestringexternal 使用由 operator 管理的负载均衡器。internal 启用 Self-built VIP,并要求使用 ACP v4.4+。是
.spec.controlPlaneLoadBalancer.hoststring对于 external,为外部负载均衡器 VIP(IP 地址)。对于 internal,为预留的 IPv4 VIP。是
.spec.controlPlaneLoadBalancer.portintKubernetes API server 端口。有效范围为 1-65535。是
.spec.controlPlaneLoadBalancer.vridintSelf-built VIP 使用的 Keepalived VRID。当 type 为 internal 时,请设置 1 到 255 范围内的值。当 type 为 external 时,将忽略此字段。有条件
.spec.controlPlaneLoadBalancer.interfacestringbootstrap VIP 和 alive 使用的可选 NIC 名称。留空时,将根据节点 IP 检测 NIC。对于多 NIC 集群,如果 VIP 必须由特定接口持有,请显式设置此项。否
.spec.credentialSecretRef.namestringDCS 身份验证 Secret 名称。该 Secret 定义 DCS Provider 使用接口互联用户(默认)还是域用户进行身份验证 — 请参阅凭证用户类型。是
.spec.controlPlaneEndpointobjectCAPI 和 kubeadm 使用的 Kubernetes API endpoint。将其设置为与 controlPlaneLoadBalancer 相同的主机和端口。是
.spec.controlPlaneHA.enabledbool启用由 provider 管理的 DCS DRS 控制平面 VM 互斥规则。仅当目标 DCS compute cluster 启用了 DRS,并且具有足够的主机和容量来进行跨主机放置时,才将其设置为 true。否
.spec.networkTypestring目前仅支持 "kube-ovn"是
.spec.sitestringDCS 平台站点 ID是

controlPlaneHA 是可选的。启用后,provider 会为此业务集群当前的控制平面 VM 创建并维护一条 DRS ruleType=2 互斥规则。DCS 执行实际的放置和运行时迁移。provider 不会运行 DRS 或应用 DRS 建议。如果规则已维护但放置尚未收敛,请等待 DCS 调度机制完成,或从 DCS 平台侧触发/应用 DRS。有关基础设施前提条件,请参阅控制平面跨主机高可用。

自建 VIP 的行为和限制

Self-built VIP 需要 ACP v4.4 或更高版本。请在创建集群前规划此模式。

建议:仅对具有三个控制平面副本的新建高可用集群使用 type: internal。对于使用 type: external 的现有集群,请保留外部负载均衡器,除非您已有经过测试的迁移计划。controlPlaneHA.enabled 是独立的 DRS 放置功能,Self-built VIP 不需要此功能。

DCS provider 分两个阶段处理 Self-built VIP:

  1. 在 bootstrap 期间,provider 仅在第一个 kubeadm init 控制平面节点上注入 dcs-bootstrap-vip.service。此服务会在 kubeadm 启动前添加 <self-built-vip>/32。
  2. 控制平面节点注册后,provider 会创建或更新由 provider 管理的 alive ModuleInfo。平台插件流程会渲染业务 AppRelease,而 alive 会在控制平面节点上安装 VIP 运行时。

重要限制:

  • 两种模式都只接受 IP 地址:type: external 接受外部负载均衡器 VIP,type: internal 接受预留的 IPv4 VIP。两种模式下,集群 endpoint 均不支持域名。
  • VIP 必须从 DCSIpHostnamePool 的主 IP 和附加 NIC IP 中排除。
  • 目标 DCS 端口组必须允许 VIP 所有权、GARP 或 ARP 更新以及 VRRP 流量。
  • interface 必须是长度不超过 15 个字符的 Linux 接口名称。当 VIP 必须绑定到特定 NIC(例如 eth0 或 eth1)时,请使用此项。
  • 在集群控制平面初始化后,不应更改 type、host、port、vrid 和 interface。
  • DCS provider 会创建或修补 alive ModuleInfo;平台插件流程会创建业务 AppRelease 及运行时资源。
  • DCS provider 不会注入或持久化 IPVS kernel modules 或 sysctl 参数。请使用随支持的 ACP 版本提供的 VM 模板和插件包。

验证自建 VIP

如果将 DCSCluster.spec.controlPlaneLoadBalancer.type 设置为 internal,请在集群创建后验证由 provider 管理的 VIP 路径。

kubectl -n cpaas-system get dcscluster <cluster-name> \
  -o jsonpath='{.spec.controlPlaneLoadBalancer.type}{" "}{.spec.controlPlaneEndpoint.host}{":"}{.spec.controlPlaneEndpoint.port}{"\n"}'
kubectl -n cpaas-system get cluster <cluster-name> \
  -o jsonpath='{.spec.controlPlaneEndpoint.host}{":"}{.spec.controlPlaneEndpoint.port}{"\n"}'
kubectl -n cpaas-system get moduleinfo \
  -l cpaas.io/cluster-name=<cluster-name>,cpaas.io/module-name=alive
kubectl --kubeconfig <workload-kubeconfig> -n cpaas-system get apprelease alive
kubectl --kubeconfig <workload-kubeconfig> -n kube-system get pods -l app=alive -o wide
kubectl --kubeconfig <vip-kubeconfig> get --raw=/version

预期结果包括:已配置的 VIP endpoint、由 provider 创建的 alive ModuleInfo、已同步且健康的 alive AppRelease、每个控制平面节点上均处于 Ready 状态的 alive pod,以及通过 VIP 成功执行的 /version 请求。

单控制平面(无外部 LB)布局

对于开发、PoC 或控制平面仅有 一个副本(KubeadmControlPlane.spec.replicas: 1)的任何部署,API server 前方都没有真正的负载均衡器。不过,仍有两个字段需要设置值:

  • .spec.controlPlaneLoadBalancer.host 和 .spec.controlPlaneEndpoint.host — 将两者都设置为唯一控制平面节点的 IP(即为该节点在控制平面 DCSIpHostnamePool 中分配的同一 IP)。
  • .spec.controlPlaneLoadBalancer.type — 保留 schema 兼容值 external;此布局不会创建负载均衡组件。

具体如下:

spec:
  controlPlaneLoadBalancer:
    host: 10.226.82.150     # same IP as the control plane node from the IP pool
    port: 6443
    type: external
  controlPlaneEndpoint:
    host: 10.226.82.150     # same as above
    port: 6443

此布局不具备 HA — 丢失唯一的控制平面节点后,集群 API 将无法访问,直到该节点恢复。请勿在此布局中使用 Self-built VIP。生产环境请使用带外部 LoadBalancer 的 replicas: 3 或 Self-built VIP。

控制平面副本少于三个的集群无法升级。

配置 Cluster

Cluster 资源声明集群,并引用控制平面和基础设施资源。

cluster.yaml
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
  annotations:
    capi.cpaas.io/resource-group-version: infrastructure.cluster.x-k8s.io/v1beta1
    capi.cpaas.io/resource-kind: DCSCluster
    cpaas.io/kube-ovn-join-cidr: <kube-ovn-join-cidr>
  labels:
    cluster-type: DCS
    # Optional. Bind this cluster to a dedicated image registry instead of the
    # registry of the `global` cluster. Read only at cluster creation.
    # cpaas.io/registry-reference: <registry-credential-name>
  name: <cluster-name>
  namespace: cpaas-system
spec:
  clusterNetwork:
    pods:
      cidrBlocks:
      - <pods-cidr>
    services:
      cidrBlocks:
      - <services-cidr>
  controlPlaneRef:
    apiVersion: controlplane.cluster.x-k8s.io/v1beta1
    kind: KubeadmControlPlane
    name: <cluster-name>-kcp
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
    kind: DCSCluster
    name: <cluster-name>

参数说明:

参数类型描述必需
.spec.clusterNetwork.pods.cidrBlocks[]stringPod CIDR。CAPI schema 中为可选项,但建议显式设置,以便多个 CAPI 集群可以共存而不发生重叠。如果未设置,kube-ovn 将回退到默认值,该默认值可能与同一 global 集群中的其他集群冲突。建议
.spec.clusterNetwork.services.cidrBlocks[]stringService CIDR。建议与 Pod CIDR 相同 — 显式设置以避免集群之间发生冲突。建议
.spec.controlPlaneRefobject控制平面引用是
.spec.infrastructureRefobject基础设施集群引用是

集群注解:

上面的示例显示了三个注解,但完整的 Cluster 资源还包含其他几个注解。下表列出了 operator 作者设置的注解(其他一些注解由 ACP controllers 写入,不应预先设置):

注解必需值来源用途
capi.cpaas.io/resource-group-version是字面值 infrastructure.cluster.x-k8s.io/v1beta1告知 CAPI 基础设施绑定应使用的 API 组。
capi.cpaas.io/resource-kind是字面值 DCSCluster告知 CAPI 基础设施绑定应绑定的 kind。
capi.cpaas.io/kubernetes是与 KubeadmControlPlane.spec.version 和 MachineDeployment.spec.template.spec.version 相同的值。显示标签,也由某些升级和清单工具使用。真实来源是 cpaas.io/dcs-vm-template ConfigMap 的 kubernetesVersion。
cpaas.io/kube-ovn-join-cidr是operator 选择的 /16 CIDR,不得与 Pod / Service CIDR 或任何其他集群的 join CIDR 重叠。Kube-OVN 节点间隧道网络。
cpaas.io/kube-ovn-version是global 集群随附的 kube-ovn 版本。从 CAPI global Cluster (kubectl get cluster global -n cpaas-system -o jsonpath='{.metadata.annotations.cpaas\.io/kube-ovn-version}') 上的 cpaas.io/kube-ovn-version 注解中读取。同一 global 集群上的每个健康业务集群都具有相同的值,因此如果存在,也可以从其中任意一个集群读取。固定安装到业务集群中的 kube-ovn 版本。
cpaas.io/registry-address是global 集群使用的镜像仓库(通常为 <registry-host>:11443)。从 global CAPI Cluster (kubectl get cluster global -n cpaas-system -o jsonpath='{.metadata.annotations.cpaas\.io/registry-address}') 上的 cpaas.io/registry-address 注解中读取。允许 day-2 调谐器在集群置备期间解析镜像。此注解不会选择仓库凭证:如需从专用仓库拉取平台镜像,请使用下面介绍的 cpaas.io/registry-reference 标签。
cpaas.io/nodes-mode是对于由 DCS Provider 置备的集群,使用字面值 self-managed。标记该集群的节点生命周期由 CAPI + 此 provider 管理。
cpaas.io/os-family仅 KubeOS当集群的 DCS VM 模板基于 KubeOS 镜像构建时,使用字面值 kubeos。对于 Alauda OS,请省略此项:DCS Provider 会将未设置的值或任何不是 kubeos 的值处理为 slemicro。告知 DCS Provider 不要将 /home 挂载写入节点 Ignition 配置。接收该挂载的 KubeOS 节点会在 Ignition 早期阶段失败并无法启动。请在创建 Cluster 时设置此项,且应早于任何机器置备。

集群启动后,ACP 控制器可能会写入其他只读注解(cpaas.io/cpu-cores-number、cpaas.io/memories、cpaas.io/nodes-number 等)——这些注解由系统计算得出,不得预先设置在要应用的 YAML 中。

Cluster 标签:

标签必需值来源用途
cluster-type是字面值 DCS。标记该集群由 DCS Provider 置备。
cpaas.io/registry-reference否cpaas-system 中 RegistryCredential Secret 的名称。将此集群绑定到专用镜像仓库。省略此项则继承 global 集群的仓库。仅在集群创建时读取——请参阅为业务集群选择镜像仓库。

部署工作节点

仅有控制平面还不能构成可用集群。请按以下顺序创建工作节点资源:

  1. IP-Hostname Pool 配置 - 工作节点的网络设置
  2. Machine Template 设置 - VM 规格
  3. Bootstrap 配置 - 节点初始化和加入设置
  4. Machine Deployment - 节点创建和管理的编排

步骤 1:配置 IP-Hostname Pool

IP-Hostname Pool 定义工作节点虚拟机的网络配置。部署前,必须规划并配置 IP 地址、主机名、DNS 服务器、其他 NIC 以及其他网络参数。

在 Huawei DCS 上,IP 池还用于声明必须在 VM 替换后保留的持久磁盘。对于平台所需的 /var/cpaas 磁盘,以及删除并重新创建操作期间必须保留的任何其他工作节点磁盘,请使用 persistentDisk。

当节点模板在 /var/lib/containerd 中声明专用数据磁盘时,DCS provider 的引导配置会将 Pod 日志存储在 /var/lib/containerd/pods 下,并将 /var/log/pods 链接到该路径。如果需要在替换节点时保留 Pod 日志数据,请将磁盘继续挂载到 /var/lib/containerd,以便数据保留在 containerd 数据磁盘上。

WARNING

池大小要求

池中包含的条目数至少必须等于计划部署的工作节点数。条目不足将导致节点无法部署。

示例:

创建名为 <cluster-name>-worker-pool 的 DCSIpHostnamePool:

apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DCSIpHostnamePool
metadata:
  name: <cluster-name>-worker-pool
  namespace: cpaas-system
spec:
  pool:
  - ip: "<worker-ip-1>"
    mask: "<worker-cidr-prefix-length>"
    gateway: "<worker-gateway>"
    dns: "<worker-dns>"
    hostname: "<worker-hostname-1>"
    machineName: "<worker-machine-name-1>"
    additionNic:
    - ip: "<worker-additional-ip-1>"
      mask: "<worker-additional-cidr-prefix-length>"
      gateway: "<worker-additional-gateway>"
      dns: "<worker-additional-dns>"
      dvSwitchName: "<additional-dvs>"
      portGroupName: "<additional-port-group>"
    persistentDisk:
    - slot: 0
      quantityGB: 40
      datastoreClusterName: <datastore-cluster-name>
      path: /var/cpaas
      format: xfs
  - ip: "<worker-ip-2>"
    mask: "<worker-cidr-prefix-length>"
    gateway: "<worker-gateway>"
    dns: "<worker-dns>"
    hostname: "<worker-hostname-2>"
    machineName: "<worker-machine-name-2>"
    persistentDisk:
    - slot: 0
      quantityGB: 40
      datastoreClusterName: <datastore-cluster-name>
      path: /var/cpaas
      format: xfs
  - ip: "<worker-ip-3>"
    mask: "<worker-cidr-prefix-length>"
    gateway: "<worker-gateway>"
    dns: "<worker-dns>"
    hostname: "<worker-hostname-3>"
    machineName: "<worker-machine-name-3>"
    persistentDisk:
    - slot: 0
      quantityGB: 40
      datastoreClusterName: <datastore-cluster-name>
      path: /var/cpaas
      format: xfs

关键参数:

参数类型描述必需
.spec.pool[].ipstring工作节点虚拟机的 IP 地址是
.spec.pool[].maskstringCIDR 前缀长度格式的子网掩码,不带 /,例如 24是
.spec.pool[].gatewaystring网关 IP 地址是
.spec.pool[].dnsstringDNS 服务器 IP 地址(多个地址使用分号分隔)否
.spec.pool[].machineNamestringDCS 平台中的虚拟机名称否
.spec.pool[].hostnamestring虚拟机的主机名否
.spec.pool[].additionNic[][]object绑定到此 IP 槽位的其他 NIC。用于存储、管理或隔离的应用网络。否
.spec.pool[].additionNic[].ipstring为生成的客户机网络配置设置的其他 NIC IP 地址。建议*
.spec.pool[].additionNic[].maskstringCIDR 前缀长度格式的其他 NIC 子网掩码,不带 /,例如 24。建议*
.spec.pool[].additionNic[].gatewaystring其他 NIC 网络的网关。设置此值前请确认路由行为。否
.spec.pool[].additionNic[].dnsstring其他 NIC 网络的 DNS 服务器 IP;需要多个值时使用分号分隔。否
.spec.pool[].additionNic[].dvSwitchNamestring用于解析其他 NIC 的 Port Group 的 DCS 分布式虚拟交换机。是*
.spec.pool[].additionNic[].portGroupNamestring用于附加其他 NIC 的 DCS Port Group。是*
.spec.pool[].persistentDisk[][]object绑定到此 IP 槽位的持久磁盘。用于 /var/cpaas 以及节点替换后必须保留的任何磁盘。否

*标记为 Yes* 的字段是 provider 解析目标网络并附加其他 NIC 所必需的。标记为 Recommended* 的字段是生成静态客户机网络配置所需的。CRD 不会强制要求这些字段。

步骤 2:配置 Machine Template

DCSMachineTemplate 定义工作节点虚拟机的规格,包括 VM 模板、计算资源、存储配置和网络设置。

此资源中的字段引用了多个 Huawei DCS 平台概念(DCS VM Template、DCS VM Folder、DCS Datastore 及相关项)。有关这些概念的定义,以及它们如何映射到 Cluster API 资源,请参阅Huawei DCS 概念和术语。

WARNING

必需的磁盘配置

以下磁盘挂载点是必需的。请勿删除:

  • 系统卷 (systemVolume: true)
  • /var/lib/kubelet - Kubelet 数据目录
  • /var/lib/containerd - 容器运行时数据

请在 IP 池中将 /var/cpaas 配置为持久磁盘,而不是在 DCSMachineTemplate 中配置。

可以添加其他模板磁盘,但必须保留这些必需的模板磁盘。

示例:

创建名为 <cluster-name>-worker-template 的 DCSMachineTemplate:

apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DCSMachineTemplate
metadata:
  name: <cluster-name>-worker-template
  namespace: cpaas-system
spec:
  template:
    spec:
      vmTemplateName: <vm-template-name>
      location:
        type: folder
        name: <folder-name>
      vmConfig:
        dvSwitchName: <dv-switch-name> # Optional
        portGroupName: <port-group-name> # Optional
        dcsMachineCpuSpec:
          quantity: <worker-cpu>
        dcsMachineMemorySpec: # MB
          quantity: <worker-memory>
        dcsMachineDiskSpec: # GB
        - quantity: 0
          datastoreClusterName: <datastore-cluster-name>
          systemVolume: true
        - quantity: 100
          datastoreClusterName: <datastore-cluster-name>
          path: /var/lib/kubelet
          format: xfs
        - quantity: 100
          datastoreClusterName: <datastore-cluster-name>
          path: /var/lib/containerd
          format: xfs
      ipHostPoolRef:
        name: <cluster-name>-worker-pool

关键参数:

参数类型描述必需
.spec.template.spec.vmTemplateNamestring在 DCS 平台上注册的 DCS VM Template 名称。是
.spec.template.spec.locationobject将克隆的虚拟机置于 DCS VM Folder 下,以便进行组织分组。如果省略,则不会将虚拟机置于任何文件夹中。有关 VM Folder 的定义,请参阅 Huawei DCS Concepts and Terminology;有关此字段在多集群场景中的使用,请参阅 Infrastructure → Advanced: Multi-Cluster Deployment。否
.spec.template.spec.location.typestring对于标准操作流程,设置为 folder。该文件夹必须已在 DCS 平台上存在。是*
.spec.template.spec.location.namestring现有 DCS VM Folder 的名称。是*
.spec.template.spec.vmConfigobject虚拟机配置是
.spec.template.spec.vmConfig.dvSwitchNamestring虚拟交换机名称(未指定时使用模板默认值)否
.spec.template.spec.vmConfig.portGroupNamestring端口组名称(必须属于指定的交换机)否
.spec.template.spec.vmConfig.dcsMachineCpuSpec.quantityintworker VM 的 CPU 核数是
.spec.template.spec.vmConfig.dcsMachineMemorySpec.quantityint内存大小,单位为 MB是
.spec.template.spec.vmConfig.dcsMachineDiskSpec[]object磁盘配置数组是
.spec.template.spec.vmConfig.dcsMachineDiskSpec[].quantityint磁盘大小,单位为 GB(系统磁盘为 0 时使用模板大小)是
.spec.template.spec.vmConfig.dcsMachineDiskSpec[].datastoreClusterNamestringDatastore 集群名称是
.spec.template.spec.vmConfig.dcsMachineDiskSpec[].systemVolumebool系统磁盘标志(只能有一个磁盘设置为 true)否
.spec.template.spec.vmConfig.dcsMachineDiskSpec[].pathstring挂载路径(省略时不挂载磁盘)否
.spec.template.spec.vmConfig.dcsMachineDiskSpec[].formatstring文件系统格式(例如 xfs、ext4)否
.spec.template.spec.ipHostPoolRef.namestring引用的 DCSIpHostnamePool 名称是

*父对象被指定时必需

由 IP 池管理的持久磁盘

在匹配的 DCSIpHostnamePool.spec.pool[].persistentDisk 条目中声明升级时保留的任何磁盘。

  • 将其用于平台要求的 /var/cpaas。
  • 为系统磁盘和可能随 VM 重新创建的模板本地磁盘保留 DCSMachineTemplate。
  • 为每个 IP 条目选择唯一的 slot。控制器使用 (ip, slot) 作为持久磁盘标识。
  • 在替换节点上,来宾磁盘设置逻辑会检查是否存在现有文件系统。如果磁盘已格式化,则跳过 mkfs,直接挂载磁盘。
  • 持久磁盘工作流要求逐个替换,因此请保留 MachineDeployment.spec.strategy.rollingUpdate.maxSurge = 0。
  • 可以追加新的 persistentDisk 条目,但不支持删除现有条目。控制器会在 DCS 侧将新添加的磁盘挂载到运行中的 VM,但不会在来宾 OS 内格式化或挂载磁盘。只有在 VM 被替换且替换后的 VM 在引导期间运行生成的磁盘设置时,来宾格式化和挂载才会生效。
  • 控制器会将新的持久卷创建为独立的持久普通卷。重新使用现有卷时,只要求该卷为独立且持久;不要求特定的 DCS 卷类型。
  • 创建后,将 format、options 和 pciType 视为不可变字段。
  • 仅当 DCS 环境要求为新创建的持久卷显式设置精简配置值时,才使用 isThin。如果省略,provider 不会发送 isThin,DCS 将使用平台默认值。
  • 将 quantityGB 和 Datastore 变更视为对发布敏感的变更。当具有足够的集群上下文时,webhook 会尽力根据 DCS 平台执行验证。isThin 仅在创建时生效:更改它不会验证、调和或转换现有卷,只会影响之后创建的持久卷。

要在节点操作期间检查持久磁盘的运行时状态,请检查池上的 status.persistentDiskStatus:

kubectl get dcsiphostnamepool <cluster-name>-worker-pool -n cpaas-system -o yaml

由 IP 池管理的其他 NIC

在 DCSIpHostnamePool.spec.pool[].additionNic[] 中声明 worker 的其他 NIC。

  • provider 仅在创建新 VM 时应用 additionNic[]。
  • 编辑 Pool 时,不会热更新现有 worker VM。
  • 新的 worker Machine 使用其所占用池槽位中的 additionNic[] 值。
  • 每个其他 NIC 都应包含 dvSwitchName 和 portGroupName,以便 provider 在克隆 VM 前解析目标 DCS Port Group。
  • 如果同时使用 persistentDisk[],请保留 MachineDeployment.spec.strategy.rollingUpdate.maxSurge = 0,以便固定 IP 槽位和保留的磁盘逐个迁移节点。
  • 如果其他 NIC 具有网关,请确认来宾 OS 路由表仍优先使用预期的主网络作为默认路由。

要检查其他 NIC 的运行时状态,请检查 DCSMachine.status.additionalNic:

kubectl -n cpaas-system get dcsmachine -l cluster.x-k8s.io/cluster-name=<cluster-name> \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.networkConfig.ip}{"\t"}{.status.additionalNic}{"\n"}{end}'

步骤 3:配置 Bootstrap Template

KubeadmConfigTemplate 定义 worker 节点的引导配置,包括用户帐户、SSH 密钥、系统文件和 kubeadm join 设置。

从 Kubernetes 1.35 开始,才要求使用 imagePullCredentialsVerificationPolicy: NeverVerify。使用 Kubernetes 1.34 或更早版本创建 worker 时,请省略此参数。如果 contentFrom.secret 提供了补丁,请确认被引用的 worker-kubelet-patch.json 包含适用于 Kubernetes 1.35 的此参数。

INFO

模板优化

该模板包含针对安全性和性能预先优化的配置。仅修改环境中需要自定义的参数。

示例:

apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: KubeadmConfigTemplate
metadata:
  name: <cluster-name>-worker-kct
  namespace: cpaas-system
spec:
  template:
    spec:
      format: ignition
      users:
      - name: boot
        sshAuthorizedKeys:
        - "<ssh-authorized-keys>"
      files:
      - path: /etc/kubernetes/patches/kubeletconfiguration0+strategic.json
        owner: "root:root"
        permissions: "0644"
        content: |
          {
            "apiVersion": "kubelet.config.k8s.io/v1beta1",
            "kind": "KubeletConfiguration",
            "imagePullCredentialsVerificationPolicy": "NeverVerify",
            "protectKernelDefaults": true,
            "staticPodPath": null,
            "tlsCertFile": "/etc/kubernetes/pki/kubelet.crt",
            "tlsPrivateKeyFile": "/etc/kubernetes/pki/kubelet.key",
            "streamingConnectionIdleTimeout": "5m",
            "clientCAFile": "/etc/kubernetes/pki/ca.crt"
          }
      preKubeadmCommands:
      - while ! ip route | grep -q "default via"; do sleep 1; done; echo "NetworkManager started"
      - mkdir -p /run/cluster-api && restorecon -Rv /run/cluster-api
      - if [ -f /etc/disk-setup.sh ]; then bash /etc/disk-setup.sh; fi
      postKubeadmCommands:
      - chmod 600 /var/lib/kubelet/config.yaml
      joinConfiguration:
        patches:
          directory: /etc/kubernetes/patches
        nodeRegistration:
          kubeletExtraArgs:
            node-ip: NODE_IP
            provider-id: PROVIDER_ID
            volume-plugin-dir: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"
            protect-kernel-defaults: "true"

PROVIDER_ID 和 NODE_IP 是 DCS Provider 在 Machine 加入集群时替换的字面量 magic token,必须保持原样。完整列表请参阅 Magic-Token Placeholders。

TIP

替代方案:引用集中管理的 Secret 以提供 worker kubelet 补丁

DCS Provider 插件会在 cpaas-system 命名空间中提供名为 dcs-kubernetes-<kubernetes-major-minor>-files 的 Secret(例如,适用于 Kubernetes 1.33 的 dcs-kubernetes-1.33-files)。除了向 KubeadmControlPlane 提供控制平面文件之外(请参阅 Complete KubeadmControlPlane Configuration),此 Secret 还包含适用于 worker KubeadmConfigTemplate 的 worker-kubelet-patch.json 键。当该 Secret 存在时,可以将上方内联的 files 条目替换为 contentFrom.secret 引用,这样可以使 worker kubelet 补丁与已安装的插件版本保持同步,并避免在集群升级时手动更新。

files:
- contentFrom:
    secret:
      key: worker-kubelet-patch.json
      name: dcs-kubernetes-1.33-files
  owner: "root:root"
  path: /etc/kubernetes/patches/kubeletconfiguration0+strategic.json
  permissions: "0644"

内联形式与 Secret 引用形式在功能上等效;当目标集群中存在插件 Secret 时,建议使用 Secret 形式。

步骤 4:配置 Machine Deployment

MachineDeployment 通过引用前面配置的 DCSMachineTemplate 和 KubeadmConfigTemplate 资源,协调 worker 节点的创建和管理。它管理所需的节点数量并处理滚动更新。

示例:

apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
  name: <cluster-name>-md-0
  namespace: cpaas-system
spec:
  strategy:
    rollingUpdate:
      maxSurge: 0 # Required when this node pool relies on persistent disks
      maxUnavailable: 1
    type: RollingUpdate
  clusterName: <cluster-name>
  replicas: 3
  selector:
    matchLabels: null
  template:
    metadata:
      labels:
        cluster.x-k8s.io/cluster-name: <cluster-name>
        pool.name: <cluster-name>-md-0
    spec:
      nodeDrainTimeout: 1m
      nodeDeletionTimeout: 5m
      bootstrap:
        configRef:
          apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
          kind: KubeadmConfigTemplate
          name: <cluster-name>-worker-kct
          namespace: cpaas-system
      clusterName: <cluster-name>
      infrastructureRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
        kind: DCSMachineTemplate
        name: <cluster-name>-worker-template
        namespace: cpaas-system
      version: <worker-kubernetes-version>

关键参数:

参数类型描述必需
.spec.clusterNamestring节点部署的目标集群名称是
.spec.replicasintworker 节点数量(不得超过 IP 池大小)是
.spec.template.spec.bootstrap.configRefobject对 KubeadmConfigTemplate 的引用是
.spec.template.spec.infrastructureRefobject对 DCSMachineTemplate 的引用是
.spec.template.spec.versionstringKubernetes 版本(必须与 VM Template 匹配)是
.spec.strategy.rollingUpdate.maxSurgeint更新期间超过所需数量的最大节点数。当节点池依赖持久磁盘时,将其保持为 0否
.spec.strategy.rollingUpdate.maxUnavailableint更新期间不可用节点的最大数量。当持久磁盘与 maxSurge = 0 一起使用时,将此值保持为大于 0 且不超过副本数否

集群验证

部署所有集群资源后,验证集群是否已成功创建并正常运行。

使用控制台

  1. 导航至 集群 → 集群
  2. 在集群列表中找到新创建的集群
  3. 验证集群状态是否显示为 运行中
  4. 检查所有控制平面节点和 worker 节点是否为 就绪

使用 kubectl

或者,使用 kubectl 命令验证集群:

# Check cluster status
kubectl get cluster -n cpaas-system <cluster-name>

# Verify control plane
kubectl get kubeadmcontrolplane -n cpaas-system <cluster-name>-kcp

# Check machine status
kubectl get machines -n cpaas-system

# Verify cluster deployment
kubectl get clustermodule <cluster-name> -o jsonpath='{.status.base.deployStatus}'

对于使用额外 NIC 的集群,还要验证 provider 是否已在匹配的 DCSMachine 对象中记录这些 NIC:

kubectl -n cpaas-system get dcsmachine -l cluster.x-k8s.io/cluster-name=<cluster-name> \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.networkConfig.ip}{"\t"}{.status.additionalNic}{"\n"}{end}'

验证控制平面 HA

如果启用了 DCSCluster.spec.controlPlaneHA.enabled,请先检查 DCSCluster 条件:

kubectl get dcscluster <cluster-name> -n cpaas-system \
  -o jsonpath='{range .status.conditions[?(@.type=="ControlPlaneHAReady")]}{.status}{" "}{.reason}{" "}{.message}{"\n"}{end}'

按如下方式解读该条件:

状态和原因含义后续步骤
True ControlPlaneHAReadyprovider 管理的 DRS 规则已存在,并且 provider 已观测到当前控制平面 VM 位于不同的 DCS 主机上。无需执行任何操作。
False ControlPlaneHAPendingprovider 正在等待可恢复状态,例如具有足够的 VM URN、足够的成员,或 DCS 侧完成放置。阅读消息。如果提到放置,请检查 DCS 主机容量、DRS 状态,以及运行时迁移是否受阻。
False ControlPlaneHAFailedprovider 无法查询、验证、创建、更新、删除或检查 DRS 规则。阅读消息,然后检查 DCS 凭证、DRS 设置和规则名称冲突;如有需要,再检查 controller 日志。

检查观测到的规则和成员快照:

kubectl get dcscluster <cluster-name> -n cpaas-system \
  -o jsonpath='rule={.status.controlPlaneHA.ruleName}{" index="}{.status.controlPlaneHA.ruleIndex}{" cluster="}{.status.controlPlaneHA.clusterUrn}{"\n"}{range .status.controlPlaneHA.members[*]}{.machineName}{" "}{.vmUrn}{"\n"}{end}'

members[] 列表包含规则中每个控制平面 VM 的 CAPI Machine 名称和 DCS VM URN。当前 DCS 状态快照不包含 DCS 主机名称。如果需要确认确切的主机放置位置,请从 DCS 平台或 DCS API 查询 VM 详细信息,并比较 VM 的 hostName 或 hostUrn 值。

预期结果

成功创建的集群应显示:

  • 集群状态:运行中 或 已配置
  • 所有控制平面机器:运行中
  • 所有 worker 节点(如果已部署):运行中
  • Kubernetes 节点:就绪
  • 集群模块状态:已完成
  • 对于多 NIC 集群,每个创建的 VM 都在 DCSMachine.status.additionalNic 中具有预期的额外 NIC,并且 guest OS 显示对应的 ethN 接口。

附录

完整的 KubeadmControlPlane 配置

下面是完整的 KubeadmControlPlane 配置,其中包括所有默认的审计策略、准入控制和文件内容。

嵌入式 kubelet 补丁包含 imagePullCredentialsVerificationPolicy: NeverVerify,仅从 Kubernetes 1.35 开始需要此参数。对于 Kubernetes 1.34 或更早版本,请移除此参数。

apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
  name: <cluster-name>-kcp
  namespace: cpaas-system
  annotations:
    controlplane.cluster.x-k8s.io/skip-coredns: ""
    controlplane.cluster.x-k8s.io/skip-kube-proxy: ""
spec:
  rolloutStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 0 # Required when the cluster relies on pool-managed persistent disks
  kubeadmConfigSpec:
    users:
    - name: boot
      sshAuthorizedKeys:
      - "<ssh-authorized-keys>"
    format: ignition
    files:
    - path: /etc/kubernetes/admission/psa-config.yaml
      owner: "root:root"
      permissions: "0644"
      content: |
        apiVersion: apiserver.config.k8s.io/v1
        kind: AdmissionConfiguration
        plugins:
        - name: PodSecurity
          configuration:
            apiVersion: pod-security.admission.config.k8s.io/v1
            kind: PodSecurityConfiguration
            defaults:
              enforce: "privileged"
              enforce-version: "latest"
              audit: "baseline"
              audit-version: "latest"
              warn: "baseline"
              warn-version: "latest"
            exemptions:
              usernames: []
              runtimeClasses: []
              namespaces:
              - kube-system
              - cpaas-system
    - path: /etc/kubernetes/patches/kubeletconfiguration0+strategic.json
      owner: "root:root"
      permissions: "0644"
      content: |
        {
          "apiVersion": "kubelet.config.k8s.io/v1beta1",
          "kind": "KubeletConfiguration",
          "imagePullCredentialsVerificationPolicy": "NeverVerify",
          "protectKernelDefaults": true,
          "tlsCertFile": "/etc/kubernetes/pki/kubelet.crt",
          "tlsPrivateKeyFile": "/etc/kubernetes/pki/kubelet.key",
          "streamingConnectionIdleTimeout": "5m",
          "clientCAFile": "/etc/kubernetes/pki/ca.crt"
        }
    - path: /etc/kubernetes/audit/policy.yaml
      owner: "root:root"
      append: false
      permissions: "0644"
      content: |
        apiVersion: audit.k8s.io/v1
        kind: Policy
        omitStages:
        - "RequestReceived"
        rules:
        - level: None
          users:
          - system:kube-controller-manager
          - system:kube-scheduler
          - system:serviceaccount:kube-system:endpoint-controller
          verbs: ["get", "update"]
          namespaces: ["kube-system"]
          resources:
          - group: ""
            resources: ["endpoints"]
        - level: None
          nonResourceURLs:
          - /healthz*
          - /version
          - /swagger*
        - level: None
          resources:
          - group: ""
            resources: ["events"]
        - level: None
          resources:
          - group: "devops.alauda.io"
        - level: None
          verbs: ["get", "list", "watch"]
        - level: None
          resources:
          - group: "coordination.k8s.io"
            resources: ["leases"]
        - level: None
          resources:
          - group: "authorization.k8s.io"
            resources: ["subjectaccessreviews", "selfsubjectaccessreviews"]
          - group: "authentication.k8s.io"
            resources: ["tokenreviews"]
        - level: None
          resources:
          - group: "app.alauda.io"
            resources: ["imagewhitelists"]
          - group: "k8s.io"
            resources: ["namespaceoverviews"]
        - level: Metadata
          resources:
          - group: ""
            resources: ["secrets", "configmaps"]
        - level: Metadata
          resources:
          - group: "operator.connectors.alauda.io"
            resources: ["installmanifests"]
          - group: "operators.katanomi.dev"
            resources: ["katanomis"]
        - level: RequestResponse
          resources:
          - group: ""
          - group: "aiops.alauda.io"
          - group: "apps"
          - group: "app.k8s.io"
          - group: "authentication.istio.io"
          - group: "auth.alauda.io"
          - group: "autoscaling"
          - group: "asm.alauda.io"
          - group: "clusterregistry.k8s.io"
          - group: "crd.alauda.io"
          - group: "infrastructure.alauda.io"
          - group: "monitoring.coreos.com"
          - group: "operators.coreos.com"
          - group: "networking.istio.io"
          - group: "extensions.istio.io"
          - group: "install.istio.io"
          - group: "security.istio.io"
          - group: "telemetry.istio.io"
          - group: "opentelemetry.io"
          - group: "networking.k8s.io"
          - group: "portal.alauda.io"
          - group: "rbac.authorization.k8s.io"
          - group: "storage.k8s.io"
          - group: "tke.cloud.tencent.com"
          - group: "devopsx.alauda.io"
          - group: "core.katanomi.dev"
          - group: "deliveries.katanomi.dev"
          - group: "integrations.katanomi.dev"
          - group: "artifacts.katanomi.dev"
          - group: "builds.katanomi.dev"
          - group: "versioning.katanomi.dev"
          - group: "sources.katanomi.dev"
          - group: "tekton.dev"
          - group: "operator.tekton.dev"
          - group: "eventing.knative.dev"
          - group: "flows.knative.dev"
          - group: "messaging.knative.dev"
          - group: "operator.knative.dev"
          - group: "sources.knative.dev"
          - group: "operator.devops.alauda.io"
          - group: "flagger.app"
          - group: "jaegertracing.io"
          - group: "velero.io"
            resources: ["deletebackuprequests"]
          - group: "connectors.alauda.io"
          - group: "operator.connectors.alauda.io"
            resources: ["connectorscores", "connectorsgits", "connectorsocis"]
        - level: Metadata
    preKubeadmCommands:
    - while ! ip route | grep -q "default via"; do sleep 1; done; echo "NetworkManager started"
    - mkdir -p /run/cluster-api && restorecon -Rv /run/cluster-api
    - if [ -f /etc/disk-setup.sh ]; then bash /etc/disk-setup.sh; fi
    postKubeadmCommands:
    - chmod 600 /var/lib/kubelet/config.yaml
    clusterConfiguration:
      imageRepository: cloud.alauda.io/alauda
      dns:
        imageTag: <dns-image-tag>
      etcd:
        local:
          imageTag: <etcd-image-tag>
      apiServer:
        extraArgs:
          audit-log-format: json
          audit-log-maxage: "30"
          audit-log-maxbackup: "10"
          audit-log-maxsize: "200"
          profiling: "false"
          audit-log-mode: batch
          audit-log-path: /etc/kubernetes/audit/audit.log
          audit-policy-file: /etc/kubernetes/audit/policy.yaml
          tls-cipher-suites: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
          encryption-provider-config: /etc/kubernetes/encryption-provider.conf
          admission-control-config-file: /etc/kubernetes/admission/psa-config.yaml
          tls-min-version: VersionTLS12
          kubelet-certificate-authority: /etc/kubernetes/pki/ca.crt
        extraVolumes:
        - name: vol-dir-0
          hostPath: /etc/kubernetes
          mountPath: /etc/kubernetes
          pathType: Directory
      controllerManager:
        extraArgs:
          bind-address: "::"
          profiling: "false"
          tls-min-version: VersionTLS12
          flex-volume-plugin-dir: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"
      scheduler:
        extraArgs:
          bind-address: "::"
          tls-min-version: VersionTLS12
          profiling: "false"
    initConfiguration:
      patches:
        directory: /etc/kubernetes/patches
      nodeRegistration:
        kubeletExtraArgs:
          node-labels: "kube-ovn/role=master"
          provider-id: PROVIDER_ID
          volume-plugin-dir: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"
          protect-kernel-defaults: "true"
    joinConfiguration:
      patches:
        directory: /etc/kubernetes/patches
      nodeRegistration:
        kubeletExtraArgs:
          node-ip: NODE_IP
          node-labels: "kube-ovn/role=master"
          provider-id: PROVIDER_ID
          volume-plugin-dir: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"
          protect-kernel-defaults: "true"
  machineTemplate:
    nodeDrainTimeout: 1m
    nodeDeletionTimeout: 5m
    infrastructureRef:
      apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
      kind: DCSMachineTemplate
      name: <cp-dcs-machine-template-name>
  replicas: 3
  version: <control-plane-kubernetes-version>
TIP

替代方案:引用集中管理的 Secret,而不是使用内联内容

Alauda Container Platform Infrastructure Provider for Huawei DCS 插件会在 cpaas-system 命名空间中提供一个名为 dcs-kubernetes-<kubernetes-major-minor>-files 的 Secret(例如,Kubernetes 1.33 对应的 dcs-kubernetes-1.33-files)。该 Secret 包含 psa-config.yaml、control-plane-kubelet-patch.json 和 audit-policy.yaml 的规范内容,并会随每个版本一同更新。

当该 Secret 存在时,您可以将三个内联的 files 条目替换为 contentFrom.secret 引用。内联形式和 Secret 引用形式在功能上等效;使用 Secret 可以使文件内容与已安装的插件版本保持一致,并避免在集群升级时手动更新。

files:
- contentFrom:
    secret:
      key: psa-config.yaml
      name: dcs-kubernetes-1.33-files
  owner: "root:root"
  path: /etc/kubernetes/admission/psa-config.yaml
  permissions: "0644"
- contentFrom:
    secret:
      key: control-plane-kubelet-patch.json
      name: dcs-kubernetes-1.33-files
  owner: "root:root"
  path: /etc/kubernetes/patches/kubeletconfiguration0+strategic.json
  permissions: "0644"
- contentFrom:
    secret:
      key: audit-policy.yaml
      name: dcs-kubernetes-1.33-files
  owner: "root:root"
  path: /etc/kubernetes/audit/policy.yaml
  permissions: "0644"

encryption-provider.conf 不属于此 Secret,也完全不属于 files ——它由 DCS provider 提供。请参阅加密 provider 配置的交付方式。

在决定使用哪种形式之前,可以运行以下命令检查目标集群中是否存在该 Secret:

# Replace <kubernetes-major-minor> with the value matching this cluster
# (for example, 1.33 for Kubernetes v1.33.x).
K8S_MM=<kubernetes-major-minor>
kubectl -n cpaas-system get secret "dcs-kubernetes-${K8S_MM}-files" >/dev/null 2>&1 \
  && echo "Secret present — contentFrom.secret form is supported" \
  || echo "Secret missing — use inline content form"

加密 Provider 配置的交付方式

/etc/kubernetes/encryption-provider.conf 由 DCS provider 管理,而不是由 KubeadmControlPlane 管理。provider 构建控制平面机器的 Ignition 配置时,会自行写入该文件,并移除从 kubeadmConfigSpec.files 传入的该路径的任何条目。您在其中添加的条目不会产生任何效果——它既不会被合并,也不会被应用,因此写入其中的密钥会被静默忽略。

provider 写入的内容取决于集群:

条件文件内容
已设置 DCSCluster.spec.encryptionProviderConfigRef所引用 Secret 的 encryption-provider.conf 密钥。用于灾难恢复,此时两端必须持有相同的密钥。该 Secret 必须存在于 provider 运行所在的集群中;请参阅可选的灾难恢复部署
未设置,且控制平面尚未初始化(第一台控制平面机器)新生成的随机 AES-CBC 密钥
未设置,且控制平面已在运行(后续每台控制平面机器)从正在运行的 kube-apiserver 中读回的文件,因此所有控制平面节点共享同一个密钥

worker 节点不会接收该文件。

clusterConfiguration.apiServer.extraVolumes 会将 /etc/kubernetes 挂载到 API server 容器中,因此 apiServer.extraArgs.encryption-provider-config: /etc/kubernetes/encryption-provider.conf 在每个控制平面节点上都能解析。请保留该参数(附录中已包含),以使用启用 etcd Secret 静态加密的方式运行。移除该参数会关闭静态加密;provider 仍会写入该文件,但 API server 将不再使用它。


后续步骤

创建集群后:

故障排除

如果集群达到 Provisioned,但始终未变为 Ready——例如,由于 CNI 未部署,业务节点保持在 NotReady——请参阅排查卡在 Provisioned 状态的业务集群。