Создание кластера VMware vSphere в кластере global
В этом документе объясняется, как создать рабочий кластер VMware vSphere из кластера global с использованием стандартного режима CAPV, который напрямую подключается к vCenter. Процедура охватывает минимально поддерживаемую топологию с одним datacenter, одним NIC на узел и статическим назначением IP через VSphereMachineConfigPool.
Сценарии
Используйте этот документ в следующих сценариях:
- Вы хотите создать первый базовый рабочий кластер VMware vSphere в своей среде.
- Для первоначальной проверки вы используете один datacenter и один NIC на узел.
- Вы хотите сохранить первое развертывание простым перед включением расширенных возможностей размещения или сети.
Этот документ применим к следующей модели развертывания:
- CAPV подключается напрямую к vCenter.
- И control plane, и worker-узлы используют
VSphereMachineConfigPool для статического назначения IP и data disk.
ClusterResourceSet автоматически доставляет компонент vSphere CPI.
- Первая проверка использует один datacenter и один NIC на узел.
Этот документ не применим к следующим сценариям:
- Развертывание, которое зависит от vSphere Supervisor или
vm-operator.
- Развертывание, которое не использует
VSphereMachineConfigPool.
- Первоначальное развертывание, в котором одновременно включаются несколько datacenter, несколько NIC и сложные расширения дисков.
Этот документ написан для текущей платформенной среды. Путь доставки kube-ovn зависит от контроллеров платформы, которые обрабатывают аннотации ресурса Cluster, поэтому этот сценарий не предназначен для использования как универсальное руководство по автономному развертыванию CAPV вне контекста платформы.
Предварительные требования
Перед началом убедитесь, что выполнены следующие условия:
- Вы завершили сбор значений в Подготовка параметров для кластера VMware vSphere.
- Кластер
global может подключаться к vCenter.
- Доступны целевой template, сети, datastore и resource pool vCenter.
- Control plane VIP и load balancer готовы.
- Все требуемые статические IP-адреса выделены и не используются.
- Включено
ClusterResourceSet=true.
- В платформе уже настроена корректная конфигурация публичного registry.
- Платформа может обработать аннотации кластера, необходимые для установки сетевого плагина.
Основные объекты
ClusterResourceSet
ClusterResourceSet — это ресурс Cluster API в кластере global. После того как workload API server становится доступным, он применяет к рабочему кластеру указанные ресурсы ConfigMap и Secret.
В этом сценарии ClusterResourceSet используется для автоматической доставки ресурсов vSphere CPI.
Компонент vSphere CPI
Компонент vSphere CPI доставляется в рабочий кластер через ClusterResourceSet. Он подключает workload-узлы к инфраструктуре vSphere, чтобы кластер мог сообщать идентификаторы инфраструктуры и завершать инициализацию cloud-provider.
machine config pool
Machine config pool — это пользовательский ресурс VSphereMachineConfigPool. В базовом сценарии:
- Один machine config pool используется для узлов control plane.
- Один machine config pool используется для worker-узлов.
Каждый слот узла включает hostname, datacenter, назначение статического IP и необязательные определения data disk.
Для настройки сети различайте следующие поля:
networkName — это имя сети или port group в vCenter.
deviceName — это имя NIC внутри guest operating system.
Если deviceName задан, CAPV записывает это значение в сгенерированные метаданные guest-network. Если оно не указано, текущая реализация обычно использует имена NIC, такие как eth0, eth1 и eth2, в порядке их расположения.
Также различайте следующие форматы значений:
- IP-адрес узла используется вместе с длиной префикса, например
10.10.10.11/24.
- Поле gateway содержит только IP-адрес шлюза, например
10.10.10.1.
В базовом сценарии:
- Один
VSphereMachineConfigPool используется для узлов control plane.
- Один
VSphereMachineConfigPool используется для worker-узлов.
Требования к VM template
VM template, используемый в этом сценарии, должен соответствовать следующим минимальным требованиям:
- Он использует требуемую operating system для целевой платформенной среды.
- В него включен
cloud-init.
- В него включены VMware Tools или
open-vm-tools.
- В него включен
containerd.
- В него включены базовые компоненты, необходимые для bootstrap kubeadm.
- В него включены предварительно экспортированные container image tar-файлы в каталоге
/root/images/. Эти файлы импортируются в containerd с помощью capv-load-local-images.sh перед запуском kubeadm, поэтому bootstrap узла не зависит от загрузки образов из удаленного registry.
- Файлы
/root/images/*.tar обязательно должны включать sandbox (pause) image, ссылка на который в точности совпадает со значением sandbox_image (containerd v1) или sandbox (containerd v2), настроенным в /etc/containerd/config.toml. Например, если containerd настроен с sandbox_image = "registry.example.com/tkestack/pause:3.10", один из tar-файлов должен содержать именно такую ссылку на image. Несоответствие приводит к тому, что containerd будет загружать sandbox image из сети, что сводит на нет смысл локального предварительного загрузки и приводит к сбою в средах без доступа к сети.
Конфигурация статического IP, внедрение hostname и другие параметры инициализации зависят от cloud-init. Отчёт об IP-адресе узла зависит от guest tools.
Локальная структура файлов
Именование рабочего кластера
Рабочий cluster_name не должен быть global. Это имя зарезервировано для кластера global, и его повторное использование приводит к конфликту ресурсов рабочего кластера с ресурсами кластера global в cpaas-system. Префикс global- зарезервирован для ресурсов, которыми владеет DR-workflow кластера global; см. Общие предварительные требования. Не используйте global- для ресурсов рабочего кластера, поскольку операции failover могут выбирать эти ресурсы так, как будто они принадлежат кластеру global.
В качестве соглашения именования оставляйте ресурс CAPI Cluster и ресурс provider cluster (VSphereCluster) с точным именем <cluster_name>, а ресурсы CAPI и provider, не являющиеся корневыми (KubeadmControlPlane, KubeadmConfigTemplate, MachineDeployment, VSphereMachineTemplate, VSphereMachineConfigPool и т. д.), префиксуйте <cluster_name>- — например, в приведенных примерах манифестов используются <cluster_name>-kcp и <cluster_name>-md-0. Это рекомендация, а не правило, enforced контроллером, но она предотвращает конфликты в одной namespace, когда несколько рабочих кластеров находятся в cpaas-system, и делает владение ресурсами очевидным во время операций.
Создайте локальный рабочий каталог и сохраните манифесты со следующей структурой:
capv-cluster/
├── 00-namespace.yaml
├── 01-vsphere-credentials-secret.yaml
├── 02-vspheremachineconfigpool-control-plane.yaml
├── 03-vspheremachineconfigpool-worker.yaml
├── 10-cluster.yaml
├── 15-vsphere-cpi-clusterresourceset.yaml
├── 20-control-plane.yaml
└── 30-workers-md-0.yaml
Для создания каталога используйте следующие команды:
mkdir -p ./capv-cluster
cd ./capv-cluster
Шаги
Проверка окружения
Выполните следующие команды из кластера global, чтобы проверить минимальные предварительные требования:
kubectl get ns
kubectl get minfo -l cpaas.io/module-name=cluster-api-provider-vsphere
kubectl get minfo -l cpaas.io/module-name=cluster-api-provider-kubeadm
kubectl -n cpaas-system get deploy capi-controller-manager -o jsonpath='{.spec.template.spec.containers[0].args}'
kubectl -n cpaas-system get secret public-registry-credential -o jsonpath='{.data.content}'
Подтвердите следующие результаты:
- Кластер
global доступен.
- Запущены Alauda Container Platform Kubeadm Provider и Alauda Container Platform VMware vSphere Infrastructure Provider.
- Аргументы контроллера включают
ClusterResourceSet=true.
- Значение
data.content для учетных данных public registry не пустое.
Перед продолжением также проверьте следующие пункты:
- Доступен адрес vCenter server.
- Имя пользователя и пароль vCenter корректны.
- Thumbprint корректен.
- Имя template корректно.
- Template разрешается в целевом datacenter.
- Если VM клонируется как
fullClone, системный диск template не больше значения diskGiB, которое используется позже в манифестах. Если CAPV выполняет linkedClone, размер системного диска остается равным размеру template, а diskGiB игнорируется.
- В template установлены VMware Tools или
open-vm-tools.
- VIP существует, а порт
6443 доступен из среды выполнения.
- Ясно определена модель владения load balancer для обслуживания real-server.
Создание namespace и secret с учетными данными vCenter
Создайте namespace, в котором будут храниться объекты рабочего кластера.
В этом сценарии объекты рабочего кластера хранятся в namespace cpaas-system. В манифестах и командах ниже замените каждый шаблон <namespace> на cpaas-system.
00-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: <namespace>
Создайте secret с учетными данными vCenter, на который ссылается VSphereCluster.spec.identityRef.
01-vsphere-credentials-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: <credentials_secret_name>
namespace: <namespace>
type: Opaque
stringData:
username: "<vsphere_username>"
password: "<vsphere_password>"
Примените оба манифеста:
kubectl apply -f 00-namespace.yaml
kubectl apply -f 01-vsphere-credentials-secret.yaml
Создание объектов Cluster и VSphereCluster
Создайте базовый манифест кластера с настройками сети рабочего кластера, endpoint control plane и параметрами подключения к vCenter.
10-cluster.yaml
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: <cluster_name>
namespace: <namespace>
labels:
cluster.x-k8s.io/cluster-name: <cluster_name>
cluster-type: VSphere
addons.cluster.x-k8s.io/vsphere-cpi: "enabled"
annotations:
capi.cpaas.io/resource-group-version: infrastructure.cluster.x-k8s.io/v1beta1
capi.cpaas.io/resource-kind: VSphereCluster
cpaas.io/sentry-deploy-type: Baremetal
cpaas.io/alb-address-type: ClusterAddress
cpaas.io/network-type: kube-ovn
cpaas.io/kube-ovn-version: <kube_ovn_version>
cpaas.io/kube-ovn-join-cidr: <kube_ovn_join_cidr>
spec:
clusterNetwork:
pods:
cidrBlocks:
- <pod_cidr>
services:
cidrBlocks:
- <service_cidr>
controlPlaneRef:
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
name: <cluster_name>-kcp
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: VSphereCluster
name: <cluster_name>
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: VSphereCluster
metadata:
name: <cluster_name>
namespace: <namespace>
spec:
controlPlaneEndpoint:
host: "<vip>"
port: <api_server_port>
identityRef:
kind: Secret
name: <credentials_secret_name>
server: "<vsphere_server>"
thumbprint: "<thumbprint>"
Примените манифест:
kubectl apply -f 10-cluster.yaml
Создание ресурсов доставки vSphere CPI
Создайте ClusterResourceSet, чтобы рабочий кластер получал конфигурацию и манифесты vSphere CPI автоматически после того, как workload API server станет доступным.
INFO
В базовом сценарии VSphereCluster.spec.failureDomainSelector намеренно не задан, а в CPI vsphere.conf не включен блок [Labels]. Оба параметра требуются только после включения failure domain; настройте их вместе, как описано в Сценарии расширения. Добавление [Labels] в vsphere.conf без соответствующих объектов VSphereFailureDomain приводит к тому, что CPI ищет теги zone и region, которых не существует.
WARNING
Ресурсы CPI ConfigMap, Secret и ClusterResourceSet должны создаваться в том же namespace, что и ресурс Cluster. В этом руководстве таким namespace является cpaas-system. ClusterResourceSet может сопоставляться только с кластерами в своем namespace; если развернуть его в другом namespace, доставка ресурсов будет незаметно заблокирована.
INFO
Конфигурация kube-ovn в аннотациях Cluster обрабатывается контроллерами платформы. Этот документ не устанавливает сетевой плагин напрямую.
TIP
Этот манифест длинный и содержит вложенный YAML внутри полей data. Перед применением проверьте манифест: kubectl apply --dry-run=client -f 15-vsphere-cpi-clusterresourceset.yaml.
15-vsphere-cpi-clusterresourceset.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: <cluster_name>-vsphere-cpi-config
namespace: <namespace>
data:
data: |
apiVersion: v1
kind: ConfigMap
metadata:
name: cloud-config
namespace: kube-system
data:
vsphere.conf: |
[Global]
secret-name = "vsphere-cloud-secret"
secret-namespace = "kube-system"
service-account = "cloud-controller-manager"
port = "443"
insecure-flag = "<cpi_insecure_flag>"
datacenters = "<cpi_datacenters>"
[VirtualCenter "<vsphere_server>"]
---
apiVersion: v1
kind: Secret
metadata:
name: <cluster_name>-vsphere-cpi-secret
namespace: <namespace>
type: addons.cluster.x-k8s.io/resource-set
stringData:
data: |
apiVersion: v1
kind: Secret
metadata:
name: vsphere-cloud-secret
namespace: kube-system
type: Opaque
stringData:
<vsphere_server>.username: <vsphere_username>
<vsphere_server>.password: <vsphere_password>
---
apiVersion: v1
kind: ConfigMap
metadata:
name: <cluster_name>-vsphere-cpi-manifests
namespace: <namespace>
data:
data: |
apiVersion: v1
kind: ServiceAccount
metadata:
name: cloud-controller-manager
namespace: kube-system
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: system:cloud-controller-manager
rules:
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "patch", "update"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["*"]
- apiGroups: [""]
resources: ["nodes/status"]
verbs: ["patch"]
- apiGroups: [""]
resources: ["services"]
verbs: ["list", "patch", "update", "watch"]
- apiGroups: [""]
resources: ["services/status"]
verbs: ["patch"]
- apiGroups: [""]
resources: ["serviceaccounts"]
verbs: ["create", "get", "list", "watch", "update"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "update", "watch"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["create", "get", "list", "watch", "update"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "list", "watch", "create", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: servicecatalog.k8s.io:apiserver-authentication-reader
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: extension-apiserver-authentication-reader
subjects:
- apiGroup: ""
kind: ServiceAccount
name: cloud-controller-manager
namespace: kube-system
- apiGroup: ""
kind: User
name: cloud-controller-manager
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system:cloud-controller-manager
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:cloud-controller-manager
subjects:
- kind: ServiceAccount
name: cloud-controller-manager
namespace: kube-system
- kind: User
name: cloud-controller-manager
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
annotations:
scheduler.alpha.kubernetes.io/critical-pod: ""
labels:
component: cloud-controller-manager
tier: control-plane
k8s-app: vsphere-cloud-controller-manager
name: vsphere-cloud-controller-manager
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: vsphere-cloud-controller-manager
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
component: cloud-controller-manager
k8s-app: vsphere-cloud-controller-manager
spec:
securityContext:
runAsUser: 1001
automountServiceAccountToken: true
# Optional: required when the CPI image is stored in a private
# registry that needs authentication. The platform automatically
# syncs a dockerconfigjson secret named "global-registry-auth"
# into every namespace of the workload cluster when the
# `global` cluster secret "public-registry-credential"
# (data.content) is configured. If your environment does not
# use a private registry, remove the imagePullSecrets block.
imagePullSecrets:
- name: global-registry-auth
serviceAccountName: cloud-controller-manager
hostNetwork: true
tolerations:
- operator: Exists
- key: node.cloudprovider.kubernetes.io/uninitialized
value: "true"
effect: NoSchedule
- key: node-role.kubernetes.io/master
effect: NoSchedule
- key: node.kubernetes.io/not-ready
effect: NoSchedule
operator: Exists
containers:
- name: vsphere-cloud-controller-manager
image: <image_registry>/ait/cloud-provider-vsphere:<cpi_image_tag>
args:
- --v=2
- --cloud-provider=vsphere
- --cloud-config=/etc/cloud/vsphere.conf
volumeMounts:
- mountPath: /etc/cloud
name: vsphere-config-volume
readOnly: true
resources:
requests:
cpu: 200m
volumes:
- name: vsphere-config-volume
configMap:
name: cloud-config
---
apiVersion: v1
kind: Service
metadata:
labels:
component: cloud-controller-manager
name: vsphere-cloud-controller-manager
namespace: kube-system
spec:
type: NodePort
ports:
- port: 43001
protocol: TCP
targetPort: 43001
selector:
component: cloud-controller-manager
---
apiVersion: addons.cluster.x-k8s.io/v1beta1
kind: ClusterResourceSet
metadata:
name: <cluster_name>-vsphere-cpi
namespace: <namespace>
spec:
strategy: Reconcile
clusterSelector:
matchLabels:
addons.cluster.x-k8s.io/vsphere-cpi: "enabled"
resources:
- name: <cluster_name>-vsphere-cpi-config
kind: ConfigMap
- name: <cluster_name>-vsphere-cpi-secret
kind: Secret
- name: <cluster_name>-vsphere-cpi-manifests
kind: ConfigMap
Примените манифест:
kubectl apply -f 15-vsphere-cpi-clusterresourceset.yaml
Создание machine config pool
Создайте machine config pool для control plane.
INFO
Каждый слот узла описывает свою NIC-раскладку в network.primary (обязательно) и network.additional (необязательный список). networkName для primary NIC обязателен, а provider определяет имя Kubernetes node, DNS SAN для kubelet serving certificate и node-ip kubelet на основе hostname и вычисленных адресов primary NIC. Значение hostname должно быть допустимым DNS-1123 subdomain.
INFO
deviceName является необязательным. Если вам не нужно принудительно задавать имя NIC внутри guest, удалите строку deviceName из каждого слота узла. Provider назначает имена NIC, такие как eth0, eth1, в порядке NIC.
02-vspheremachineconfigpool-control-plane.yaml
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: VSphereMachineConfigPool
metadata:
name: <cluster_name>-cp-pool
namespace: <namespace>
spec:
clusterRef:
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
name: <cluster_name>
datacenter: "<default_datacenter>"
releaseDelayHours: <release_delay_hours>
configs:
- hostname: "<cp_node_name_1>"
datacenter: "<master_01_datacenter>"
network:
primary:
networkName: "<nic1_network_name>"
deviceName: "<nic1_device_name>"
ip: "<master_01_nic1_ip>/<nic1_prefix>"
gateway: "<nic1_gateway>"
dns:
- "<nic1_dns_1>"
persistentDisks:
- name: var-cpaas
sizeGiB: <cp_var_cpaas_size_gib>
mountPath: /var/cpaas
fsFormat: ext4
- name: var-lib-containerd
sizeGiB: <cp_var_lib_containerd_size_gib>
mountPath: /var/lib/containerd
fsFormat: ext4
- name: var-lib-etcd
sizeGiB: <cp_var_lib_etcd_size_gib>
mountPath: /var/lib/etcd
fsFormat: ext4
wipeFilesystem: true
- hostname: "<cp_node_name_2>"
datacenter: "<master_02_datacenter>"
network:
primary:
networkName: "<nic1_network_name>"
deviceName: "<nic1_device_name>"
ip: "<master_02_nic1_ip>/<nic1_prefix>"
gateway: "<nic1_gateway>"
dns:
- "<nic1_dns_1>"
persistentDisks:
- name: var-cpaas
sizeGiB: <cp_var_cpaas_size_gib>
mountPath: /var/cpaas
fsFormat: ext4
- name: var-lib-containerd
sizeGiB: <cp_var_lib_containerd_size_gib>
mountPath: /var/lib/containerd
fsFormat: ext4
- name: var-lib-etcd
sizeGiB: <cp_var_lib_etcd_size_gib>
mountPath: /var/lib/etcd
fsFormat: ext4
wipeFilesystem: true
- hostname: "<cp_node_name_3>"
datacenter: "<master_03_datacenter>"
network:
primary:
networkName: "<nic1_network_name>"
deviceName: "<nic1_device_name>"
ip: "<master_03_nic1_ip>/<nic1_prefix>"
gateway: "<nic1_gateway>"
dns:
- "<nic1_dns_1>"
persistentDisks:
- name: var-cpaas
sizeGiB: <cp_var_cpaas_size_gib>
mountPath: /var/cpaas
fsFormat: ext4
- name: var-lib-containerd
sizeGiB: <cp_var_lib_containerd_size_gib>
mountPath: /var/lib/containerd
fsFormat: ext4
- name: var-lib-etcd
sizeGiB: <cp_var_lib_etcd_size_gib>
mountPath: /var/lib/etcd
fsFormat: ext4
wipeFilesystem: true
Создайте machine config pool для worker-узлов.
03-vspheremachineconfigpool-worker.yaml
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: VSphereMachineConfigPool
metadata:
name: <cluster_name>-worker-pool
namespace: <namespace>
spec:
clusterRef:
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
name: <cluster_name>
datacenter: "<default_datacenter>"
releaseDelayHours: <release_delay_hours>
configs:
- hostname: "<worker_node_name_1>"
datacenter: "<worker_01_datacenter>"
network:
primary:
networkName: "<nic1_network_name>"
deviceName: "<nic1_device_name>"
ip: "<worker_01_nic1_ip>/<nic1_prefix>"
gateway: "<nic1_gateway>"
dns:
- "<nic1_dns_1>"
persistentDisks:
- name: var-cpaas
sizeGiB: <worker_var_cpaas_size_gib>
mountPath: /var/cpaas
fsFormat: ext4
- name: var-lib-containerd
sizeGiB: <worker_var_lib_containerd_size_gib>
mountPath: /var/lib/containerd
fsFormat: ext4
Примените оба манифеста:
kubectl apply -f 02-vspheremachineconfigpool-control-plane.yaml
kubectl apply -f 03-vspheremachineconfigpool-worker.yaml
Создание объектов control plane
Создайте объекты VSphereMachineTemplate и KubeadmControlPlane. Замените шаблоны в следующем полном template значениями, собранными в документе с контрольным списком.
cloneMode и diskGiB по-прежнему присутствуют в template, поскольку CAPV принимает оба поля. На практике diskGiB влияет на системный диск только тогда, когда фактическая операция клонирования — это fullClone. Если cloneMode равен linkedClone и template имеет пригодный snapshot, CAPV завершает linked clone, и размер системного диска остается равным размеру исходного template. Если пригодный snapshot отсутствует, CAPV переходит на fullClone, и diskGiB снова применяется.
20-control-plane.yaml
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: VSphereMachineTemplate
metadata:
name: <cluster_name>-control-plane
namespace: <namespace>
spec:
template:
spec:
server: "<vsphere_server>"
template: "<template_name>"
cloneMode: <clone_mode>
folder: "<vm_folder>"
datastore: "<cp_system_datastore>"
diskGiB: <cp_system_disk_gib>
memoryMiB: <cp_memory_mib>
numCPUs: <cp_num_cpus>
os: Linux
powerOffMode: <power_off_mode>
network:
devices:
- networkName: "<nic1_network_name>"
machineConfigPoolRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: VSphereMachineConfigPool
name: <cluster_name>-cp-pool
namespace: <namespace>
---
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
name: <cluster_name>-kcp
namespace: <namespace>
spec:
rolloutStrategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
version: "<k8s_version>"
replicas: <cp_replicas>
machineTemplate:
nodeDrainTimeout: 1m
nodeDeletionTimeout: 5m
metadata:
labels:
node-role.kubernetes.io/control-plane: ""
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: VSphereMachineTemplate
name: <cluster_name>-control-plane
kubeadmConfigSpec:
users:
- name: boot
sudo: ALL=(ALL) NOPASSWD:ALL
sshAuthorizedKeys:
- "<ssh_public_key>"
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
- <namespace>
- path: /etc/kubernetes/patches/kubeletconfiguration0+strategic.json
owner: "root:root"
permissions: "0644"
content: |
{
"apiVersion": "kubelet.config.k8s.io/v1beta1",
"kind": "KubeletConfiguration",
"protectKernelDefaults": true,
"streamingConnectionIdleTimeout": "5m",
"tlsCertFile": "/etc/kubernetes/pki/kubelet.crt",
"tlsPrivateKeyFile": "/etc/kubernetes/pki/kubelet.key"
}
# Generate the encryption key with: head -c 32 /dev/urandom | base64
- path: /etc/kubernetes/encryption-provider.conf
owner: "root:root"
append: false
permissions: "0644"
content: |
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <encryption_provider_secret>
- 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
- path: /usr/local/bin/capv-load-local-images.sh
owner: "root:root"
permissions: "0755"
content: |
#!/bin/bash
set -euo pipefail
until mountpoint -q /var/lib/containerd; do
echo "waiting for /var/lib/containerd mount"
sleep 1
done
systemctl restart containerd
until systemctl is-active --quiet containerd; do
echo "waiting for containerd"
sleep 1
done
if [ ! -d "/root/images" ]; then
echo "ERROR: /root/images directory not found" >&2
exit 1
fi
image_count=0
for image_file in /root/images/*.tar; do
if [ -f "$image_file" ]; then
echo "importing image: $image_file"
ctr -n k8s.io images import "$image_file"
image_count=$((image_count + 1))
fi
done
if [ "$image_count" -eq 0 ]; then
echo "ERROR: no tar files found in /root/images" >&2
exit 1
fi
echo "imported $image_count images"
preKubeadmCommands:
- hostnamectl set-hostname "{{ ds.meta_data.hostname }}"
- echo "::1 ipv6-localhost ipv6-loopback localhost6 localhost6.localdomain6" >/etc/hosts
- echo "127.0.0.1 {{ ds.meta_data.hostname }} {{ local_hostname }} localhost localhost.localdomain localhost4 localhost4.localdomain4" >>/etc/hosts
- while ! ip route | grep -q "default via"; do sleep 1; done; echo "NetworkManager started"
- /usr/local/bin/capv-load-local-images.sh
postKubeadmCommands:
- chmod 600 /var/lib/kubelet/config.yaml
clusterConfiguration:
imageRepository: <image_registry>/tkestack
dns:
imageTag: <dns_image_tag>
etcd:
local:
imageTag: <etcd_image_tag>
apiServer:
extraArgs:
admission-control-config-file: /etc/kubernetes/admission/psa-config.yaml
audit-log-format: json
audit-log-maxage: "30"
audit-log-maxbackup: "10"
audit-log-maxsize: "200"
audit-log-mode: batch
audit-log-path: /etc/kubernetes/audit/audit.log
audit-policy-file: /etc/kubernetes/audit/policy.yaml
encryption-provider-config: /etc/kubernetes/encryption-provider.conf
kubelet-certificate-authority: /etc/kubernetes/pki/ca.crt
profiling: "false"
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
tls-min-version: VersionTLS12
extraVolumes:
- hostPath: /etc/kubernetes
mountPath: /etc/kubernetes
name: vol-dir-0
pathType: Directory
controllerManager:
extraArgs:
bind-address: "::"
cloud-provider: external
profiling: "false"
tls-min-version: VersionTLS12
scheduler:
extraArgs:
bind-address: "::"
profiling: "false"
tls-min-version: VersionTLS12
initConfiguration:
nodeRegistration:
criSocket: /var/run/containerd/containerd.sock
ignorePreflightErrors:
- ImagePull
kubeletExtraArgs:
cloud-provider: external
node-labels: kube-ovn/role=master
name: '{{ local_hostname }}'
patches:
directory: /etc/kubernetes/patches
joinConfiguration:
nodeRegistration:
criSocket: /var/run/containerd/containerd.sock
ignorePreflightErrors:
- ImagePull
kubeletExtraArgs:
cloud-provider: external
node-labels: kube-ovn/role=master
volume-plugin-dir: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"
name: '{{ local_hostname }}'
patches:
directory: /etc/kubernetes/patches
Примените манифест:
kubectl apply -f 20-control-plane.yaml
Создание объектов worker
Создайте шаблон worker machine, bootstrap template и MachineDeployment.
30-workers-md-0.yaml
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: VSphereMachineTemplate
metadata:
name: <cluster_name>-worker
namespace: <namespace>
spec:
template:
spec:
server: "<vsphere_server>"
template: "<template_name>"
cloneMode: <clone_mode>
folder: "<vm_folder>"
datastore: "<worker_system_datastore>"
diskGiB: <worker_system_disk_gib>
memoryMiB: <worker_memory_mib>
numCPUs: <worker_num_cpus>
os: Linux
powerOffMode: <power_off_mode>
network:
devices:
- networkName: "<nic1_network_name>"
machineConfigPoolRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: VSphereMachineConfigPool
name: <cluster_name>-worker-pool
namespace: <namespace>
---
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: KubeadmConfigTemplate
metadata:
name: <cluster_name>-worker-bootstrap
namespace: <namespace>
spec:
template:
spec:
files:
- path: /etc/kubernetes/patches/kubeletconfiguration0+strategic.json
owner: "root:root"
permissions: "0644"
content: |
{
"apiVersion": "kubelet.config.k8s.io/v1beta1",
"kind": "KubeletConfiguration",
"protectKernelDefaults": true,
"staticPodPath": null,
"streamingConnectionIdleTimeout": "5m",
"tlsCertFile": "/etc/kubernetes/pki/kubelet.crt",
"tlsPrivateKeyFile": "/etc/kubernetes/pki/kubelet.key"
}
- path: /usr/local/bin/capv-load-local-images.sh
owner: "root:root"
permissions: "0755"
content: |
#!/bin/bash
set -euo pipefail
until mountpoint -q /var/lib/containerd; do
echo "waiting for /var/lib/containerd mount"
sleep 1
done
systemctl restart containerd
until systemctl is-active --quiet containerd; do
echo "waiting for containerd"
sleep 1
done
if [ ! -d "/root/images" ]; then
echo "ERROR: /root/images directory not found" >&2
exit 1
fi
image_count=0
for image_file in /root/images/*.tar; do
if [ -f "$image_file" ]; then
echo "importing image: $image_file"
ctr -n k8s.io images import "$image_file"
image_count=$((image_count + 1))
fi
done
if [ "$image_count" -eq 0 ]; then
echo "ERROR: no tar files found in /root/images" >&2
exit 1
fi
echo "imported $image_count images"
joinConfiguration:
nodeRegistration:
criSocket: /var/run/containerd/containerd.sock
ignorePreflightErrors:
- ImagePull
kubeletExtraArgs:
cloud-provider: external
volume-plugin-dir: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"
name: '{{ local_hostname }}'
patches:
directory: /etc/kubernetes/patches
preKubeadmCommands:
- hostnamectl set-hostname "{{ ds.meta_data.hostname }}"
- echo "::1 ipv6-localhost ipv6-loopback localhost6 localhost6.localdomain6" >/etc/hosts
- echo "127.0.0.1 {{ ds.meta_data.hostname }} {{ local_hostname }} localhost localhost.localdomain localhost4 localhost4.localdomain4" >>/etc/hosts
- while ! ip route | grep -q "default via"; do sleep 1; done; echo "NetworkManager started"
- /usr/local/bin/capv-load-local-images.sh
postKubeadmCommands:
- chmod 600 /var/lib/kubelet/config.yaml
users:
- name: boot
sudo: ALL=(ALL) NOPASSWD:ALL
sshAuthorizedKeys:
- "<ssh_public_key>"
---
apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
name: <cluster_name>-md-0
namespace: <namespace>
spec:
clusterName: <cluster_name>
replicas: <worker_replicas>
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
selector:
matchLabels:
nodepool: md-0
template:
metadata:
labels:
cluster.x-k8s.io/cluster-name: <cluster_name>
nodepool: md-0
spec:
clusterName: <cluster_name>
version: "<k8s_version>"
bootstrap:
configRef:
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: KubeadmConfigTemplate
name: <cluster_name>-worker-bootstrap
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: VSphereMachineTemplate
name: <cluster_name>-worker
Примените манифест:
kubectl apply -f 30-workers-md-0.yaml
В базовом сценарии обратите внимание на следующие правила для worker:
failureDomain по умолчанию не задается в основном манифесте worker, поскольку базовый сценарий предполагает один datacenter. Если вам нужно, чтобы worker MachineDeployment размещался в определенной VSphereDeploymentZone, добавьте failureDomain, как описано в Сценарии расширения.
- В некоторых средах в
KubeadmConfigTemplate добавляются дополнительные команды замены runtime-image или команды перезапуска сервисов. Эти команды намеренно не включены в базовый пример. Добавляйте их только тогда, когда требования платформы в вашей среде явно этого требуют.
Ожидание готовности кластера
После применения всех манифестов создание кластера происходит асинхронно. Отслеживайте прогресс с помощью:
kubectl -n <namespace> get cluster,kubeadmcontrolplane,machinedeployment,machine -w
Дождитесь, пока KubeadmControlPlane сообщит ожидаемое количество ready replicas, и все объекты Machine перейдут в фазу Running, прежде чем продолжать проверку.
Проверка
Используйте следующие команды для проверки процесса создания кластера.
- Проверьте ресурсы доставки CPI в кластере
global:
kubectl -n <namespace> get clusterresourceset
kubectl -n <namespace> get clusterresourcesetbinding
- Экспортируйте kubeconfig рабочего кластера:
kubectl -n <namespace> get secret <cluster_name>-kubeconfig -o jsonpath='{.data.value}' | base64 -d > /tmp/<cluster_name>.kubeconfig
- Проверьте, создан ли daemonset vSphere CPI в рабочем кластере:
kubectl --kubeconfig=/tmp/<cluster_name>.kubeconfig -n kube-system get daemonset
- Проверьте объекты кластера
global:
kubectl -n <namespace> get cluster,vspherecluster,kubeadmcontrolplane,machinedeployment,machine,vspheremachine,vspherevm
- Проверьте рабочие узлы:
kubectl --kubeconfig=/tmp/<cluster_name>.kubeconfig get nodes -o wide
Подтвердите следующие результаты:
vsphere-cloud-controller-manager присутствует в рабочем кластере.
- Созданы узлы control plane и worker.
- В конечном итоге узлы становятся
Ready.
Устранение неполадок
Если сценарий завершается сбоем, сначала используйте следующие команды:
kubectl -n <namespace> describe cluster <cluster_name>
kubectl -n <namespace> describe vspherecluster <cluster_name>
kubectl -n <namespace> describe kubeadmcontrolplane <cluster_name>-kcp
kubectl -n <namespace> describe machinedeployment <cluster_name>-md-0
kubectl -n <namespace> get cluster,vspherecluster,kubeadmcontrolplane,machinedeployment,machine,vspheremachine,vspherevm
kubectl -n cpaas-system logs deploy/capi-controller-manager
В первую очередь проверьте следующее:
- Если ресурсы CPI не доставляются, проверьте
ClusterResourceSet=true, ClusterResourceSet и ClusterResourceSetBinding.
- Если
ClusterResourceSet существует, но ClusterResourceSetBinding не создается, проверьте, есть ли у контроллера необходимое разрешение на удаление для связанных ресурсов ConfigMap и Secret.
- Если сетевой плагин не установлен, проверьте, что требуемые аннотации кластера присутствуют и что контроллеры платформы их обработали.
- Если аннотация
cpaas.io/registry-address отсутствует, проверьте public registry credential и контроллер платформы, который внедряет аннотацию.
- Если машина зависла в
Provisioning, проверьте условия VSphereMachine для MachineConfigPoolReady — они показывают, произошел ли сбой выделения слота из-за привязки к пулу или несоответствия datacenter.
- Если VM ожидает назначения IP, проверьте VMware Tools, параметры статического IP и
VSphereVM.status.addresses.
- Если объекты workload
Node остаются без spec.providerID, сначала проверьте ресурсы доставки CPI, а затем проверьте наличие дублирующихся guest hostnames в vCenter. Когда старая VM в том же datacenter по-прежнему сообщает тот же guest hostname, что и новый узел, cloud-provider-vsphere может перейти к поиску по node-name, закэшировать старую VM и отклонить новый узел, поскольку IP VM не совпадает с IP node kubelet. Проверьте логи лидера vsphere-cloud-controller-manager, SystemUUID узла, реальный UUID VM и значения guest hostname/IP в vCenter. После того как вы исправите или удалите дублирующийся hostname либо конфликт со старой VM, перезапустите Pods vsphere-cloud-controller-manager рабочего кластера, чтобы очистить некорректный in-memory cache:
kubectl --kubeconfig=/tmp/<cluster_name>.kubeconfig -n kube-system delete pod \
-l k8s-app=vsphere-cloud-controller-manager
- Если пространство datastore исчерпано, проверьте, остались ли старые каталоги VM или файлы
.vmdk в целевом datastore.
- Если размер системного диска template не соответствует значениям в манифесте, сначала проверьте фактический режим клонирования. Когда VM была создана как
linkedClone, системный диск остается равным размеру template, а diskGiB игнорируется. Только fullClone использует diskGiB, и в этом случае diskGiB не должен быть меньше размера диска template.
- Если endpoint control plane не поднимается, проверьте load balancer, VIP и порт
6443.
- Если TLS-соединение с vCenter не удается, проверьте thumbprint, адрес vCenter и не мешают ли подключению настройки proxy.
При анализе логов контроллера используйте следующие правила:
deploy/capi-controller-manager работает в namespace cpaas-system кластера global.
- Не используйте kubeconfig рабочего кластера для просмотра логов
capi-controller-manager.
- Если контроллеры платформы обрабатывают аннотации сети кластера, также просмотрите логи platform network-controller и platform cluster-lifecycle-controller.
Следующие шаги
После запуска базовой топологии продолжите с Сценариями расширения, если вам нужен второй NIC, несколько datacenter, failure domain, дополнительные data disk или больше worker replicas.