在 Huawei DCS 上创建集群

本文档介绍如何在 Huawei DCS 平台上创建 Kubernetes 集群。可通过 manifests 使用基于 YAML 的集群创建方式。如果已安装 Fleet Essentials 且 Alauda Container Platform DCS Infrastructure Provider 版本为 1.0.13 或更高,还可以通过 Web UI 创建集群。如果工作流依赖由池管理的持久化磁盘,请使用 DCS provider v1.0.16 或更高版本。在 v1.0.16 中,DCSIpHostnamePool 上的 persistentDisk 声明仅可通过 YAML 使用,Web UI 中不提供该字段。

在选择 VM 模板和 provider 包之前,请先查看 Alauda OS 和 Provider 兼容性

INFO

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

前提条件

在创建集群之前,请确保满足以下所有前提条件:

1. 基础设施资源

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

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

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

2. 必需的 Plugin 安装

请在 global 集群上安装以下插件:

  • Alauda Container Platform Kubeadm Provider
  • Alauda Container Platform DCS Infrastructure Provider

有关详细的安装说明,请参考 安装指南

3. 虚拟机模板准备

要安装 Kubernetes,必须:

  • 将 Alauda OS 镜像上传到 DCS 平台
  • 基于该镜像创建虚拟机模板
  • 确保模板包含所有必要的 Kubernetes 组件
  • 如果计划使用持久化磁盘,请使用 DCS VM 模板 4.2.1 或更高版本,因为安全关机和磁盘卸载依赖 guest tools
  • 对于任何将依赖由池管理的持久化磁盘的集群,请使用逐个替换方式。请将控制平面和 worker 节点池的 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 平台返回一个指向特定物理主机管理 IP 和端口的 URL;随后 provider 将文件流式传输到该 URL。创建集群之前,两个目标都必须端到端可达。

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

要求:必须同时连通两个目标,才能进行集群创建和管理。

5. LoadBalancer 配置

在创建集群之前,请先配置稳定的 Kubernetes API 端点。对于 DCS Provider v1.0.21,高可用集群需要一个由你在集群外部提供并维护的 external LoadBalancer。

模式版本管理方适用场景
External LoadBalancer所有文档中列出的 DCS provider 版本,包括 v1.0.21你在创建集群之前负责提供并维护 listener 和 control-plane 后端。用于生产环境和其他高可用集群。
单 control plane 直接端点所有受支持版本不创建负载均衡器。API 端点就是单个 control plane 节点 IP。仅用于开发或只有一个 control plane 副本的 PoC 集群。

有关 external LoadBalancer 合同,请遵循 规划 Control Plane 端点。DCS Provider v1.0.21 不支持 type: internal;不要仅依据 ACP 版本来推断 provider 能力。对于 API server 前方没有 load balancer 的单 control-plane 部署,请参见 单 control-plane(无 External LB)布局

6. 公共 Registry 配置

请配置公共 registry 凭证。这包括:

  • Registry 仓库地址配置
  • 正确的身份验证凭证设置

使用 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 和 Alauda Container Platform DCS Infrastructure Provider 版本为 1.0.13 或更高。如果 provider 版本早于 1.0.13,请使用 YAML manifests。如果使用由池管理的持久化磁盘,请使用 DCS provider v1.0.16 或更高版本。在 v1.0.16 中,请通过 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

导航路径:Clusters → Clusters → Create Cluster → Select Huawei DCS

步骤 1:基本信息

字段类型必填描述
Infrastructure Credential下拉框选择现有的 Cloud Credential
Name文本集群唯一标识符(小写字母、数字、连字符)
Display Name文本用于便于识别的自定义描述
Distribution Version只读-ACP 版本(与 global 集群匹配)
Kubernetes Version只读-由 Distribution Version 决定
Cluster API Address文本格式:https://<load-balancer-address>:6443。创建集群之前请先提供并验证 external LoadBalancer。

前提检查

在创建集群之前,请确保:

  • DCS VM Templates 已存在于 DCS 平台中,且 Alauda OS 版本与 Kubernetes 版本匹配
  • Kubernetes API 端点和 external LoadBalancer 在创建集群之前已就绪

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

步骤 2:Control Plane 节点池

control plane 节点池的副本数固定为 3,以满足高可用要求。

字段类型必填描述
Machine Template下拉框按 Type: Control Plane 和兼容的 Kubernetes 版本过滤模板
Replicas只读-固定为 3
SSH Authorized Keys文本添加多个 SSH 公钥用于节点访问

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

步骤 3:Worker 节点池

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

字段类型必填描述
Pool Name文本该节点池的唯一标识符
Machine Template下拉框按 Type: Worker Node 和兼容的 Kubernetes 版本过滤模板
Replicas数字默认值:3
Max Surge数字默认值:0,必须 ≥ 0。如果该节点池将使用由池管理的持久化磁盘,请将此值保持为 0
Max Unavailable数字默认值:1,必须 ≥ 0。当 maxSurge = 0 时,maxUnavailable 必须 > 0 且 ≤ Replicas
SSH Authorized Keys文本添加多个 SSH 公钥

校验规则

  • 节点池名称在集群内必须唯一
  • IP Pool 必须具有足够的可用 IP 地址(≥ Replicas)
  • maxSurge 和 maxUnavailable 必须满足约束:如果 maxSurge = 0,则 maxUnavailable > 0
  • 如果集群将依赖由池管理的持久化磁盘,请将 maxSurge = 0,以便在后续升级时节点按顺序逐个替换

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

步骤 4:网络

字段类型必填描述
Pods CIDRCIDRPod 网络地址范围
Services CIDRCIDRService 网络地址范围
Join CIDRCIDRKube-OVN join CIDR 参数

校验:Pods CIDR 和 Services CIDR 不能重叠。

步骤 5:审查

在创建集群之前,请审查所有配置设置:

基本信息

  • Name、Display Name、Infrastructure Credential
  • Distribution Version、Kubernetes Version
  • Cluster API Address

Control Plane 节点池

  • Machine Template,包含 VM Template Name、OS Version、Kubernetes Version
  • CPU、Memory、Replicas、SSH Keys

Worker 节点池(列表视图):

  • Pool Name、Machine Template、Replicas
  • Max Surge、Max Unavailable、SSH Keys

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

网络

  • Pods CIDR、Services CIDR、Join CIDR

点击 Create 即可开始集群创建流程。


使用 YAML

集群创建流程

使用 YAML 时,你需要在 global 集群中创建 Cluster API 资源,以便为基础设施提供支持并引导一个可用的 Kubernetes 集群。

WARNING

重要的命名空间要求

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

WARNING

Workload 集群命名

workload cluster-name 不能global。该名称保留给 global 集群使用,重复使用会导致 workload 集群的资源与 cpaas-system 中的 global 集群资源发生冲突。global- 前缀保留给 global 集群 DR 工作流所拥有的资源;请参见 常见前提条件。不要将 global- 用于 workload 集群资源,因为故障切换操作可能会将这些资源视为属于 global 集群。

作为约定,请将 CAPI Cluster 和 provider cluster 资源(DCSCluster)命名为完全相同的 <cluster-name>,并将非根级 CAPI 和 provider 资源(KubeadmControlPlaneKubeadmConfigTemplateMachineDeployment、machine templates、IP/hostname pools 等)以前缀 <cluster-name>- 命名——例如,示例 manifests 使用 <cluster-name>-kcp。这属于建议而非 controller 强制规则,但它可以避免多个 workload 集群共存于 cpaas-system 时发生同命名空间冲突,并让资源归属在运维过程中更加清晰。

配置流程

请按以下顺序操作,以便创建一个可用的集群(control plane worker 节点):

  1. 配置 KubeadmControlPlane(control-plane 规格和 kubeadm bootstrap)。
  2. 配置 DCSCluster(基础设施绑定和 load balancer 引用)。
  3. 创建 Cluster 资源(连接以上两者的顶层 CAPI 对象)。
  4. 配置 worker 资源:KubeadmConfigTemplate(worker bootstrap)、worker DCSMachineTemplate、worker DCSIpHostnamePoolMachineDeployment仅有 control plane 并不能构成可用集群。 有关四个 worker YAML 资源,请参见 Huawei DCS 上的节点管理

注意:基础设施资源(Secret、control-plane DCSIpHostnamePool、control-plane DCSMachineTemplate)应单独配置。有关说明请参见 Huawei DCS 的基础设施资源

如果你需要某个磁盘在滚动替换过程中保留,请在匹配的 DCSIpHostnamePool.spec.pool[].persistentDisk 条目中声明它。这包括平台要求的 /var/cpaas 磁盘。provider 会创建新的持久化卷,作为独立的 persistent normal volumes。当 DCS 环境要求显式指定 thin-provisioning 设置时,请设置 persistentDisk[].isThin;如果省略该字段,provider 不会发送 isThin,而 DCS 将使用平台默认值。

如果集群需要额外的 NIC,请在创建对应 Machine 之前,将它们声明在 DCSIpHostnamePool.spec.pool[].additionNic[] 中。provider 仅在新 VM 创建期间应用额外 NIC。后续编辑 Pool 时,已有 VM 不会获得热添加的 NIC。

解析占位符值

下面的示例 manifests 使用 <placeholder> 语法表示与环境相关的值。其中有些值具有权威来源,建议查询而不是手工指定:

占位符权威来源获取方式
<control-plane-kubernetes-version> / <worker-kubernetes-version>位于 cpaas-system 命名空间中的 cpaas.io/dcs-vm-template ConfigMap,每个 Distribution Version 对应一个。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 label 值(必须与 DCS 平台中注册的 VM 模板名称匹配)。同样使用 kubectl get cm 查询 — 读取 label。
encryption-provider.conf 中的 <base64-encoded-secret>本地生成;请将其视为真实的集群 Secret。head -c 32 /dev/urandom | base64 — 安全存储,并在同一集群的所有 control-plane 副本之间复用。
<ssh-authorized-keys>由操作者提供。每一项都是单行 OpenSSH 格式的公钥(ssh-ed25519 AAAA… / ssh-rsa AAAA…)。该字段是 ignition 校验器所需,且不能为空。对于不需要交互式 SSH 的测试或 PoC 集群,可提供任意语法正确的公钥(无需持有对应私钥);对于生产环境,请使用运维团队的签名密钥。n/a — 由操作者生成或提供。
<auth-secret-name>你在 Infrastructure → Cloud Credentials 中创建的 Secret。kubectl -n cpaas-system get secret <name>
<cluster-name>由操作者选择;必须满足 DNS-1123,并且 不能global(该名称保留给 global 集群)。该名称会复用在 ClusterDCSCluster 以及每个 Machine 上的 cluster.x-k8s.io/cluster-name label 中。KubeadmControlPlane 使用带前缀的形式 <cluster-name>-kcp。有关完整约定,请参见 Workload 集群命名n/a
<load-balancer-ip-or-domain-name>由操作者提供的端点,客户端通过它访问集群 API server。对于 type: external,可以是 external load balancer 的 IP 地址或域名。对于没有 external LB 的单 control-plane 集群,这是唯一 control-plane 节点的 IP(参见 单 control-plane 布局)。n/a
<pods-cidr> / <services-cidr> / <kube-ovn-join-cidr>由操作者提供。不得与主机网络、global 集群的 CIDR 或任何你打算互联的其他 CAPI 集群 CIDR 重叠。留空时将回退到 global 集群自带的 kube-ovn 默认值,这在生产环境中不推荐:显式指定 CIDR 可避免在多个 workload 集群位于同一个 global 集群时发生无声冲突。n/a

Magic Token 占位符

示例 manifests 中有几个值看起来像占位符,但实际上是 DCS Provider 在机器加入时替换的字面 token。请保持其原样:

字面 token含义由谁替换
PROVIDER_ID每个 Machine 的 provider ID(例如 dcs://<dcsmachine-name>)。DCS Provider — 在生成的 kubelet 配置中被覆盖,早于 kubeadm init / kubeadm join 执行之前。
NODE_IP从该 Machine 关联的 DCSIpHostnamePool 条目中分配的节点 IP。DCS Provider — 用 DCSIpHostnamePool.spec.pool[].ip 的值覆盖。

手动替换或给这些 token 加引号都会破坏加入流程,并导致节点无法注册到 control plane。

网络规划和 Load Balancer

在创建 control plane 资源之前,请先规划网络架构和 external control plane 端点。

要求

  • 网络分段:规划 control plane 节点的 IP 地址范围
  • 额外 NIC:如果节点需要存储、管理或隔离的应用网络,请为每个 IP slot 规划 DCSIpHostnamePool.spec.pool[].additionNic[] 值,包括 DVS 和 Port Group 名称
  • API 端点模式:对高可用 DCS 集群使用 external LoadBalancer
  • API server 地址:准备一个稳定的 load balancer VIP 或 FQDN 作为 Kubernetes API server
  • 连通性:确保所有组件之间的网络连通

请在 规划 Control Plane 端点 中验证 Layer 4 listener、TCP 6443 backends、HTTPS /healthz 检查、backend 所有权以及端点可达性。

配置 KubeadmControlPlane

KubeadmControlPlane 资源定义了 control plane 配置,包括 Kubernetes 版本、节点规格和 bootstrap 设置。

Kubernetes 1.35 kubelet 设置

imagePullCredentialsVerificationPolicy: NeverVerify 仅从 Kubernetes 1.35 开始必需。在使用 Kubernetes 1.34 或更早版本创建集群时,请省略该参数。如果 contentFrom.secret 提供了该 patch,请确认引用的 Secret key 在 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.replicasintcontrol plane 副本数。必须满足 1 ≤ replicas ≤ IP Pool size。对于开发 / PoC 的单 control-plane 部署,请设为 1(参见 单 control-plane 布局)。生产环境通常使用 3 以实现 HA。
.spec.versionstringKubernetes 版本(必须与 VM 模板匹配 — 参见 解析占位符值

有关组件版本(例如 <dns-image-tag><etcd-image-tag>),请参见 OS 支持矩阵

配置 DCSCluster

DCSCluster 是基础设施集群声明,用于引用 load balancer 和 DCS 平台凭证。

对于 external LoadBalancer,请使用 type: external。这是 DCS Provider v1.0.21 以及所有已文档化更早版本支持的模式。

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

参数说明

参数类型描述必填
.spec.controlPlaneLoadBalancerobjectcontrol plane API server 的暴露方式
.spec.controlPlaneLoadBalancer.typestring设置为 external。DCS Provider v1.0.21 不支持 internal
.spec.controlPlaneLoadBalancer.hoststringexternal load balancer 的 IP 地址或域名。
.spec.controlPlaneLoadBalancer.portintKubernetes API server 端口。有效范围为 1-65535
.spec.credentialSecretRef.namestringDCS 身份验证 Secret 名称。该 Secret 决定 DCS Provider 采用接口互联用户(默认)还是域用户进行认证 — 参见 凭证用户类型
.spec.controlPlaneEndpointobjectCAPI 和 kubeadm 使用的 Kubernetes API 端点。请将其设置为与 controlPlaneLoadBalancer 相同的 host 和 port。
.spec.controlPlaneHA.enabledbool为 control-plane VM 启用由 provider 管理的 DCS DRS 互斥规则。仅当目标 DCS 计算集群已启用 DRS,且具备足够的主机和容量以支持跨主机放置时,才将其设为 true
.spec.networkTypestring当前仅支持 "kube-ovn"
.spec.sitestringDCS 平台 site ID

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

单 control-plane(无 External LB)布局

对于开发、PoC 或任何 control plane 仅有一个副本(KubeadmControlPlane.spec.replicas: 1)的部署,你在 API server 前方并没有真正的 load balancer。尽管如此,仍然有两个字段需要设置值:

  • .spec.controlPlaneLoadBalancer.host.spec.controlPlaneEndpoint.host — 两者都设置为 唯一 control plane 节点的 IP(即该节点在 control-plane 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——一旦唯一的 control plane 节点丢失,集群 API 将无法访问,直到该节点恢复。对于生产环境,请使用 replicas: 3 并配合 external LoadBalancer。

配置 Cluster

Cluster 资源用于声明集群,并引用 control plane 和基础设施资源。

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
  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.controlPlaneRefobjectcontrol plane 引用
.spec.infrastructureRefobject基础设施集群引用

Cluster 注解

上面的示例展示了三个注解,但完整的 Cluster 资源还会包含更多。下表列出了由操作者填写的注解(其他一些由 ACP controllers 写入,你不应预先设置它们):

注解必填值来源目的
capi.cpaas.io/resource-group-version字面量 infrastructure.cluster.x-k8s.io/v1beta1告知 CAPI infrastructure 绑定应使用哪个 API group。
capi.cpaas.io/resource-kind字面量 DCSCluster告知 CAPI infrastructure 绑定应绑定到哪个 kind。
capi.cpaas.io/kubernetesKubeadmControlPlane.spec.versionMachineDeployment.spec.template.spec.version 相同的值。显示标签,同时也被某些升级和 inventory 工具使用。权威来源是 cpaas.io/dcs-vm-template ConfigMap 的 kubernetesVersion
cpaas.io/kube-ovn-join-cidr由操作者选择的 /16 CIDR,不能与 pod / service CIDR 或任何其他集群的 join CIDR 重叠。Kube-OVN 节点间隧道网络。
cpaas.io/kube-ovn-versionglobal 集群随附的 kube-ovn release。可从 global CAPI Cluster 上的 cpaas.io/kube-ovn-version 注解读取(kubectl get cluster global -n cpaas-system -o jsonpath='{.metadata.annotations.cpaas\.io/kube-ovn-version}')。同一 global 集群上的每个健康 workload 集群都会带有相同值,因此如果存在,也可以从这些集群中的任意一个读取。将安装到 workload 集群中的 kube-ovn 版本固定下来。
cpaas.io/registry-addressglobal 集群使用的镜像 registry(通常为 <registry-host>:11443)。可从 global CAPI Cluster 上的 cpaas.io/registry-address 注解读取(kubectl get cluster global -n cpaas-system -o jsonpath='{.metadata.annotations.cpaas\.io/registry-address}')。workload 集群从该 registry 拉取平台镜像(CoreDNS、kube-proxy、kube-ovn)。
cpaas.io/nodes-mode对于由 DCS Provider 负责创建的集群,字面量 self-managed标记该集群的节点生命周期由 CAPI + 此 provider 管理。

ACP controllers 可能会在集群启动后写入其他只读注解(如 cpaas.io/cpu-cores-numbercpaas.io/memoriescpaas.io/nodes-number 等)——这些值是计算得出的,不得 在你应用的 YAML 中预先设置。

部署节点

有关部署 worker 节点的说明,请参见 Huawei DCS 上的节点管理


集群验证

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

使用控制台

  1. 导航到 ClustersClusters
  2. 在集群列表中找到新创建的集群
  3. 验证集群状态是否显示为 Running
  4. 检查所有 control plane 和 worker 节点是否为 Ready

使用 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 对象中:

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

验证 Control Plane HA

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

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

按如下方式解读该 condition:

状态和原因含义下一步
True ControlPlaneHAReadyprovider 管理的 DRS 规则已存在,并且 provider 已观察到当前 control-plane 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[] 列表包含规则中每个 control-plane VM 对应的 CAPI Machine 名称和 DCS VM URN。当前 DCS 状态快照不包含 DCS 主机名称。如果你需要确认准确的主机放置位置,请从 DCS 平台或 DCS API 查询 VM 详情,并对比 VM 的 hostNamehostUrn 值。

预期结果

成功创建的集群应显示:

  • Cluster 状态:RunningProvisioned
  • 所有 control plane Machines:Running
  • 所有 worker 节点(如果已部署):Running
  • Kubernetes 节点:Ready
  • Cluster Module 状态:Completed
  • 对于多 NIC 集群,每个已创建的 VM 都会在 DCSMachine.status.additionalNic 中包含预期的额外 NIC,并且 guest OS 会显示对应的 ethN 接口。

附录

完整 KubeadmControlPlane 配置

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

嵌入的 kubelet patch 包含 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/encryption-provider.conf
      owner: "root:root"
      append: false
      permissions: "0644"
      content: |
        apiVersion: apiserver.config.k8s.io/v1
        kind: EncryptionConfiguration
        resources:
        - resources:
          - secrets
          providers:
          - aescbc:
              keys:
              - name: key1
                secret: <base64-encoded-secret>
    - 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 DCS Infrastructure Provider plugin 会在 cpaas-system 命名空间中提供一个名为 dcs-kubernetes-<kubernetes-major-minor>-files 的 Secret(例如,Kubernetes 1.33 对应 dcs-kubernetes-1.33-files)。它包含 psa-config.yamlcontrol-plane-kubelet-patch.jsonaudit-policy.yaml 的权威内容,并会随每个 release 一同更新。

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

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"
- path: /etc/kubernetes/encryption-provider.conf
  owner: "root:root"
  append: false
  permissions: "0644"
  content: |
    apiVersion: apiserver.config.k8s.io/v1
    kind: EncryptionConfiguration
    resources:
    - resources:
      - secrets
      providers:
      - aescbc:
          keys:
          - name: key1
            secret: <base64-encoded-secret>

encryption-provider.conf 不由该 Secret 提供。你可以像上面所示那样将其保留为内联内容(并提供你自己的 <base64-encoded-secret>),也可以完全省略该内联文件,并依赖 DCS VM 模板镜像中已内置的版本——这两种方式都有效;当 VM 模板的默认 key 适合你的环境时,后者更简单。

最小 plugin 版本:该 Secret 从 DCS Provider plugin v1.0.13 开始提供。在更早的 plugin 版本中,该 Secret 不存在;此时请保留内联 content: 形式。要在决定采用哪种形式之前检查目标集群上是否存在该 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"

后续步骤

创建集群后:

故障排查

如果集群已经达到 Provisioned 但始终无法变为 Ready——例如,由于未部署 CNI 导致 workload 节点一直处于 NotReady——请参见 排查卡在 Provisioned 状态的 Workload 集群