Создание кластера VMware vSphere в кластере global
В этом документе описано, как создать workload-кластер VMware vSphere из кластера global с использованием стандартного режима CAPV, который напрямую подключается к vCenter. Процедура охватывает минимально поддерживаемую топологию с одним datacenter, одной NIC на узел и статическим назначением IP через VSphereMachineConfigPool.
Сценарии
Используйте этот документ в следующих сценариях:
- Вы хотите создать первый базовый workload-кластер VMware vSphere в своей среде.
- Для начальной проверки вы используете один datacenter и одну NIC на узел.
- Вы хотите сохранить первое развертывание простым, прежде чем включать расширенное размещение или сетевые функции.
Этот документ применим к следующей модели развертывания:
- CAPV напрямую подключается к vCenter.
- И control plane, и worker-узлы используют
VSphereMachineConfigPool для статического назначения IP и дисков данных.
ClusterResourceSet автоматически доставляет компонент vSphere CPI.
- Первая проверка использует один datacenter и одну NIC на узел.
Этот документ не применим к следующим сценариям:
- Развертывание, зависящее от vSphere Supervisor или
vm-operator.
- Развертывание, в котором не используется
VSphereMachineConfigPool.
- Первичное развертывание, одновременно включающее несколько datacenter, несколько NIC и сложные расширения дисков.
Этот документ написан для текущей платформенной среды. Путь доставки kube-ovn зависит от контроллеров платформы, которые обрабатывают аннотации ресурса Cluster, поэтому этот workflow не предназначен для использования как универсальное автономное руководство по развертыванию CAPV вне контекста платформы.
Предварительные требования
Перед началом убедитесь, что выполнены следующие условия:
- Вы завершили сбор значений в Подготовка параметров для кластера VMware vSphere.
- Кластер
global может достигать vCenter.
- Доступны целевой шаблон, сети, datastores и resource pool vCenter.
- VIP control plane и load balancer готовы.
- Все требуемые статические IP-адреса выделены и не используются.
- Включен
ClusterResourceSet=true.
- В платформе уже настроена действующая конфигурация публичного registry.
- Платформа может обработать аннотации кластера, необходимые для установки сетевого плагина.
Ключевые объекты
ClusterResourceSet
ClusterResourceSet — это ресурс Cluster API в management cluster. После того как workload API server станет доступен, он применяет связанные ресурсы ConfigMap и Secret к workload cluster.
В этом workflow ClusterResourceSet используется для автоматической доставки ресурсов vSphere CPI.
Компонент vSphere CPI
Компонент vSphere CPI доставляется в workload cluster через ClusterResourceSet. Он подключает узлы workload к инфраструктуре vSphere, чтобы кластер мог сообщать инфраструктурные идентификаторы и завершать инициализацию cloud-provider.
machine config pool
Machine config pool — это пользовательский ресурс VSphereMachineConfigPool. В базовом workflow:
- Один machine config pool используется для узлов control plane.
- Один machine config pool используется для worker-узлов.
Каждый слот узла включает hostname, datacenter, статическое назначение IP и необязательные определения дисков данных.
Для настройки сети различайте следующие поля:
networkName — это имя сети или port group в vCenter.
deviceName — это имя NIC внутри guest operating system.
Если задан deviceName, CAPV записывает это значение в сгенерированные метаданные guest-network. Если оно опущено, текущая реализация обычно использует имена NIC, такие как eth0, eth1 и eth2, в порядке NIC.
Также различайте следующие форматы значений:
- IP-адрес узла используется вместе с длиной префикса, например
10.10.10.11/24.
- Поле gateway содержит только IP-адрес шлюза, например
10.10.10.1.
В базовом workflow:
- Один
VSphereMachineConfigPool используется для узлов control plane.
- Один
VSphereMachineConfigPool используется для worker-узлов.
Требования к VM template
VM template, используемый в этом workflow, должен соответствовать следующим минимальным требованиям:
- Он использует требуемую 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.
Локальная структура файлов
Создайте локальный рабочий каталог и сохраните манифесты со следующей структурой:
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
Шаги
Проверка среды
Выполните следующие команды из management environment, чтобы проверить минимальные предварительные требования:
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}'
Подтвердите следующие результаты:
- Management cluster доступен.
- Alauda Container Platform Kubeadm Provider и Alauda Container Platform VMware vSphere Infrastructure Provider запущены.
- Аргументы controller включают
ClusterResourceSet=true.
data.content в секрете public registry credential не пустой.
Перед продолжением также проверьте следующие пункты:
- Адрес vCenter доступен по сети.
- Имя пользователя и пароль 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 и секрета учетных данных vCenter
Создайте namespace, в котором будут храниться объекты workload cluster.
Этот workflow сохраняет объекты workload cluster в namespace cpaas-system. В манифестах и командах ниже замените каждый placeholder <namespace> на cpaas-system.
00-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: <namespace>
Создайте секрет учетных данных 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
Создайте базовый манифест кластера с настройками сети workload cluster, конечной точкой 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>
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, чтобы workload cluster автоматически получал конфигурацию и манифесты vSphere CPI после того, как workload API server станет доступен.
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>"
[Labels]
zone = "k8s-zone"
region = "k8s-region"
[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
# management-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 pools
Создайте machine config pool для control plane.
INFO
Каждый слот узла объявляет свою NIC-структуру в network.primary (обязательно) и network.additional (необязательный список). networkName для основной NIC обязателен, а provider выводит имя узла Kubernetes, DNS SAN сертификата kubelet serving certificate и node-ip kubelet из hostname и разрешенных адресов основной 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: <cp_pool_name>
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_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: <worker_pool_name>
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. Замените placeholders в следующем полном шаблоне значениями, собранными в документе checklist.
И cloneMode, и diskGiB остаются в шаблоне, потому что 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>
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: <cp_pool_name>
namespace: <namespace>
---
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
name: <cluster_name>
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 template, 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>
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: <worker_pool_name>
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
В базовом workflow обратите внимание на следующие правила, относящиеся к worker:
failureDomain по умолчанию не задается в основном worker-манифесте, поскольку базовый workflow предполагает один datacenter. Если вам нужно, чтобы worker MachineDeployment был размещен в конкретной VSphereDeploymentZone, добавьте failureDomain, как описано в Сценарии расширения.
- В некоторых средах в
KubeadmConfigTemplate добавляют дополнительные команды замены runtime image или команды перезапуска service. Эти команды намеренно не включены в базовый пример. Добавляйте их только тогда, когда требования платформы в вашей среде явно этого требуют.
Ожидание готовности кластера
После применения всех манифестов создание кластера выполняется асинхронно. Отслеживайте прогресс с помощью:
kubectl -n <namespace> get cluster,kubeadmcontrolplane,machinedeployment,machine -w
Дождитесь, пока KubeadmControlPlane не сообщит ожидаемое количество ready replicas, а все объекты Machine не перейдут в фазу Running, прежде чем переходить к проверке.
Проверка
Используйте следующие команды для проверки workflow создания кластера.
- Проверьте ресурсы доставки CPI в management cluster:
kubectl -n <namespace> get clusterresourceset
kubectl -n <namespace> get clusterresourcesetbinding
- Экспортируйте kubeconfig workload:
kubectl -n <namespace> get secret <cluster_name>-kubeconfig -o jsonpath='{.data.value}' | base64 -d > /tmp/<cluster_name>.kubeconfig
- Проверьте, создан ли daemonset vSphere CPI в workload cluster:
kubectl --kubeconfig=/tmp/<cluster_name>.kubeconfig -n kube-system get daemonset
- Проверьте объекты management cluster:
kubectl -n <namespace> get cluster,vspherecluster,kubeadmcontrolplane,machinedeployment,machine,vspheremachine,vspherevm
- Проверьте узлы workload:
kubectl --kubeconfig=/tmp/<cluster_name>.kubeconfig get nodes -o wide
Подтвердите следующие результаты:
vsphere-cloud-controller-manager появляется в workload cluster.
- Узлы control plane и worker созданы.
- Узлы в конечном итоге переходят в состояние
Ready.
Устранение неполадок
Если workflow завершается сбоем, сначала используйте следующие команды:
kubectl -n <namespace> describe cluster <cluster_name>
kubectl -n <namespace> describe vspherecluster <cluster_name>
kubectl -n <namespace> describe kubeadmcontrolplane <cluster_name>
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 не создается, проверьте, есть ли у controller требуемое право на удаление для связанных ресурсов ConfigMap и Secret.
- Если сетевой плагин не установлен, проверьте наличие требуемых аннотаций кластера и то, что их обработали контроллеры платформы.
- Если аннотация
cpaas.io/registry-address отсутствует, проверьте public registry credential и платформенный controller, который внедряет аннотацию.
- Если машина зависла в состоянии
Provisioning, проверьте условия VSphereMachine для MachineConfigPoolReady — они показывают, не произошел ли сбой выделения слота из-за привязки pool или несоответствия datacenter.
- Если VM ожидает назначение IP, проверьте VMware Tools, настройки статического IP и
VSphereVM.status.addresses.
- Если пространство datastore исчерпано, проверьте, не остались ли старые каталоги VM или файлы
.vmdk в целевом datastore.
- Если размер системного диска template не совпадает со значениями манифеста, сначала проверьте фактический режим clone. Когда VM была создана как
linkedClone, системный диск остается равным размеру template, а diskGiB игнорируется. Только fullClone использует diskGiB, и в этом случае diskGiB не должен быть меньше размера диска template.
- Если конечная точка control plane не поднимается, проверьте load balancer, VIP и порт
6443.
- Если TLS-подключение к vCenter не удается, проверьте thumbprint, адрес vCenter и наличие proxy-настроек, мешающих подключению.
При просмотре логов controller соблюдайте следующие правила:
deploy/capi-controller-manager работает в namespace cpaas-system кластера global.
- Не используйте kubeconfig workload-кластера для просмотра логов
capi-controller-manager.
- Если платформенные контроллеры обрабатывают аннотации network кластера, также проверьте логи
platform network-controller и platform cluster-lifecycle-controller.
Следующие шаги
После запуска базовой топологии продолжите с Сценарии расширения, если вам нужна вторая NIC, несколько datacenter, failure domains, дополнительные диски данных или большее число worker replicas.