Абстракт
Когда я был маленьким и глупым, в своей домашней лаборатории я развернул kuber‑barick — его возможностей хватало на баловство с нейронками и обучением крутить поды, но при попытке поднять второй (например, для стейджинга) всё упёрлось в дефицит железа и ручную пляску с самоподписными сертификатами. Решил перейти на более автоматный подход: наткнулся на Talos и Cluster API (CAPI). В статье покажу, как их связка в OpenStack позволяет быстро, повторяемо и без лишних ручных движений разворачивать мультикластер.
Что вообще за существа такие Talos и CAPI
Talos — это минималистичная и полностью упрощённая immutable операционная система, созданная специально для развёртывания и управления Kubernetes-кластером. Про неё тут уже были статьи
Cluster API — это официальный проект CNCF, предоставляющий декларативный подход к управлению жизненным циклом Kubernetes-кластеров независимо от инфраструктуры. Про этого зверя статья была лишь одна. Возмутительно, и пора исправить!
Поехали!
Из Openstack понадобится такой список
-
Keystone
-
Glance
-
Placement
-
Cinder
-
Nova
-
Neutron
-
Octavia
В случае если видеокарты не нужны можно идти к установке. А если они для проекта нужны то заострю внимание лишь на Nova. Пришлось добавить туда PCI passthrough.
В /etc/default/grub Надо подкинуть vfio-pci к всем pci-e девайсом видеокарт. Vendor и product id от карточки к карточке разный, даже у одной и той же модели, поэтому надо посмотреть вывод lspci -nn | grep NVIDIA
GRUB_CMDLINE_LINUX="... vfio-pci.ids=10de:1e02,10de:10f7,10de:1ad6,10de:1ad7 rd.driver.blacklist=nouveau,nvidiafb,snd_hda_intel,xhci_hcd,i2c_nvidia_gpu"
В /etc/nova/nova.conf на воркеры с видеокартами надо докинуть алиас
[pci] passthrough_whitelist = {\"vendor_id\":\"10de\",\"product_id\":\"1e02\"} passthrough_whitelist = {\"vendor_id\":\"10de\",\"product_id\":\"10f7\"} passthrough_whitelist = {\"vendor_id\":\"10de\",\"product_id\":\"1ad6\"} passthrough_whitelist = {\"vendor_id\":\"10de\",\"product_id\":\"1ad7\"} alias = {\"vendor_id\":\"10de\",\"product_id\":\"1e02\",\"name\":\"gpu\"} alias = {\"vendor_id\":\"10de\",\"product_id\":\"10f7\",\"name\":\"gpu_audio\"} alias = {\"vendor_id\":\"10de\",\"product_id\":\"1ad6\",\"name\":\"usb_controller\"} alias = {\"vendor_id\":\"10de\",\"product_id\":\"1ad7\",\"name\":\"ucsi_controller\"}
Создаем типы инстансов для GPU
#!/bin/bash . .secrets flavors=( # ========== GPU Optimized (G-серия) ========== "401 8 16384 40 g1.tiny" "402 16 32768 40 g1.small" "403 24 65536 40 g1.medium" "404 32 131072 40 g1.large" "405 32 196608 40 g1.xlarge" ) for flavor in "${flavors[@]}"; do openstack flavor create --id "$id" --vcpus "$vcpus" --ram "$ram" --disk "$disk" "$name" \ --property "pci_passthrough:alias"="gpu:1,gpu_audio:1,usb_controller:1,ucsi_controller:1" > /dev/null done
Установка: подготвка вспомогательного k8s
Создадим Kubernetes для создания Kubernetes-а. Для примера Kubernetes in docker на микровиртуалке ubuntu в openstack. Как депенденси KinD нужен docker. Поэтому в скрипт я вложил инструкцию отсюда.
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-linux-amd64 chmod +x ./kind sudo mv ./kind /usr/local/bin/kind sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc # Add the repository to Apt sources: echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo usermod -aG docker $USER sudo reboot #чтобы сокет докера встал с нужными правами
Стартуем KinD:
kind create cluster --name management
Устанавливаем клиент clusterctl пользуясь руководством
curl -L https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.9.6/clusterctl-linux-amd64 -o clusterctl chmod +x ./clusterctl sudo mv ./clusterctl /usr/local/bin/clusterctl
Инициализируем clusterctl и необходимый для установки openstack-resource-controller
clusterctl init --infrastructure openstack --bootstrap talos --control-plane talos --core cluster-api kubectl apply -f https://github.com/k-orc/openstack-resource-controller/releases/download/v1.0.1/install.yaml
Создаем секрет с clouds.yaml
clouds: openstack: auth: auth_url: http://controller:5000/v3/ username: "username" password: "pa$$word" project_id: "project_id" project_name: "project_name" user_domain_name: "Default" region_name: "nova" interface: "public" identity_api_version: 3
Добавляем его в темповый кубер
kubectl create secret generic cloud-config --namespace=kube-system --from-file=clouds.yaml="$HOME/.config/openstack/clouds.yaml" --from-literal=cacert="" --dry-run=client -o yaml | kubectl apply -f -
Установка: YAML‑программируем таргетный k8s
Создадим базовый набор CRD‑шек
cluster.yaml
--- apiVersion: cluster.x-k8s.io/v1beta1 kind: Cluster metadata: name: k8s-cluster namespace: kube-system spec: clusterNetwork: pods: cidrBlocks: - 192.168.0.0/16 serviceDomain: cluster.local controlPlaneRef: apiVersion: controlplane.cluster.x-k8s.io/v1alpha3 kind: TalosControlPlane name: k8s-cluster-control-plane infrastructureRef: apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: OpenStackCluster name: k8s-cluster --- apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: OpenStackCluster metadata: name: k8s-cluster namespace: kube-system spec: apiServerLoadBalancer: enabled: true externalNetwork: id: # НАДО УКАЗАТЬ ID ВНЕШНЕЙ СЕТИ OPENSTACK # identityRef: cloudName: openstack name: k8s-cluster-cloud-config managedSecurityGroups: allNodesSecurityGroupRules: - name: allow-port-talos direction: ingress etherType: IPv4 portRangeMin: 50000 portRangeMax: 50001 protocol: tcp remoteIPPrefix: 0.0.0.0/0 - name: allow-pod-traffic # Далее правила для Flannel # direction: ingress etherType: IPv4 portRangeMin: 1 portRangeMax: 65535 protocol: tcp remoteIPPrefix: 192.168.0.0/16 - name: allow-pod-traffic-udp direction: ingress etherType: IPv4 portRangeMin: 1 portRangeMax: 65535 protocol: udp remoteIPPrefix: 192.168.0.0/16 - name: allow-all-traffic-tcp direction: ingress etherType: IPv4 portRangeMin: 1 portRangeMax: 65535 protocol: tcp remoteIPPrefix: 10.10.20.0/24 - name: allow-all-traffic-udp direction: ingress etherType: IPv4 portRangeMin: 1 portRangeMax: 65535 protocol: udp remoteIPPrefix: 10.10.20.0/24 managedSubnets: - cidr: 10.10.20.0/24 dnsNameservers: - # НАДО УКАЗАТЬ DNS С КОТОРЫМ РАБОТАЕТ OPENSTACK #
Описываем воркеры
workers.yaml
--- apiVersion: cluster.x-k8s.io/v1beta1 kind: MachineDeployment metadata: name: k8s-cluster-md-0 namespace: kube-system spec: clusterName: k8s-cluster replicas: 2 selector: matchLabels: machine-deployment: k8s-cluster-md-0 template: metadata: labels: cluster.x-k8s.io/cluster-name: k8s-cluster machine-deployment: k8s-cluster-md-0 spec: bootstrap: configRef: apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3 kind: TalosConfigTemplate name: k8s-cluster-md-0 clusterName: k8s-cluster failureDomain: nova # МОЖЕТ ОТЛИЧАТЬСЯ # infrastructureRef: apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: OpenStackMachineTemplate name: k8s-cluster-md-0 version: v1.30.0 --- apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: OpenStackMachineTemplate metadata: name: k8s-cluster-md-0 namespace: kube-system spec: template: spec: flavor: m1.large image: filter: name: talos sshKeyName: pgp-card rootVolume: sizeGiB: 50 type: __SSD__ --- apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3 kind: TalosConfigTemplate metadata: name: k8s-cluster-md-0 namespace: kube-system labels: cluster.x-k8s.io/cluster-name: k8s-cluster spec: template: spec: generateType: worker talosVersion: v1.9.5 strategicPatches: - | - op: replace path: /machine/install value: disk: /dev/vda - op: add path: /machine/kubelet/extraArgs value: cloud-provider: external --- apiVersion: cluster.x-k8s.io/v1beta1 kind: MachineDeployment metadata: name: k8s-cluster-md-gpu-0 namespace: kube-system spec: clusterName: k8s-cluster replicas: 2 selector: matchLabels: machine-deployment: k8s-cluster-md-gpu-0 template: metadata: labels: cluster.x-k8s.io/cluster-name: k8s-cluster machine-deployment: k8s-cluster-md-gpu-0 spec: bootstrap: configRef: apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3 kind: TalosConfigTemplate name: k8s-cluster-md-gpu-0 clusterName: k8s-cluster failureDomain: nova # МОЖЕТ ОТЛИЧАТЬСЯ # infrastructureRef: apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: OpenStackMachineTemplate name: k8s-cluster-md-gpu-0 version: v1.30.0 --- apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: OpenStackMachineTemplate metadata: name: k8s-cluster-md-gpu-0 namespace: kube-system spec: template: spec: flavor: g1.small # МОЖЕТ ОТЛИЧАТЬСЯ # image: filter: name: talos # МОЖЕТ ОТЛИЧАТЬСЯ # sshKeyName: pgp-card # МОЖЕТ ОТЛИЧАТЬСЯ # rootVolume: sizeGiB: 50 type: __SSD__ # МОЖЕТ ОТЛИЧАТЬСЯ # --- apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3 kind: TalosConfigTemplate metadata: name: k8s-cluster-md-gpu-0 namespace: kube-system labels: cluster.x-k8s.io/cluster-name: k8s-cluster spec: template: spec: generateType: worker talosVersion: v1.9.5 strategicPatches: - | - op: replace path: /machine/install value: disk: /dev/vda # МОЖЕТ ОТЛИЧАТЬСЯ ОБРАЗ. СГЕНЕРИРУЙТЕ СВОЙ В TALOS FABRIC# image: factory.talos.dev/installer/26124abcbd408be693df9fe852c80ef1e6cc178e34d7d7d8430a28d1130b4227:v1.9.5 - op: add path: /machine/kernel value: {} - op: add path: /machine/kubelet/extraArgs value: cloud-provider: external - op: add path: /machine/kernel/modules value: - name: nvidia - name: nvidia_uvm - name: nvidia_drm - name: nvidia_modeset - op: add path: /machine/sysctls value: net.core.bpf_jit_harden: "1"
Контролплейны
controlplane.yaml
--- apiVersion: controlplane.cluster.x-k8s.io/v1alpha3 kind: TalosControlPlane metadata: name: k8s-cluster-control-plane namespace: kube-system labels: cluster.x-k8s.io/cluster-name: k8s-cluster spec: version: v1.30.0 replicas: 3 infrastructureTemplate: apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: OpenStackMachineTemplate name: k8s-cluster-control-plane namespace: kube-system controlPlaneConfig: controlplane: generateType: controlplane strategicPatches: - | - op: replace path: /machine/install value: disk: /dev/vda - op: add path: /machine/kubelet/extraArgs value: cloud-provider: external --- apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: OpenStackMachineTemplate metadata: name: k8s-cluster-control-plane namespace: kube-system spec: template: spec: flavor: m1.medium # МОЖЕТ ОТЛИЧАТЬСЯ # image: filter: name: talos # МОЖЕТ ОТЛИЧАТЬСЯ # sshKeyName: pgp-card # МОЖЕТ ОТЛИЧАТЬСЯ # rootVolume: sizeGiB: 50 type: __SSD__ # МОЖЕТ ОТЛИЧАТЬСЯ #
Применяем
kubectl apply -f .
Мигрируем контроль в таргетный кубер
В openstack уже появится новая сеть. ее надо подключить к темповому куберу чтобы он смог настроить все ноды. Чтобы получить kubeconfig нужно выполнить команду
clusterctl get kubeconfig k8s-cluster -n kube-system > k8s_kubeconfig
Дальше для таргетного кластера k8s_kubeconfig аналогично статье:
Установка Cloud Provider OpenStack
Для того, чтобы наш кластер kubernetes мог ходить в OpenStack и создавать там диски или балансировщики, необходимо поставить Cloud Provider OpenStack. Там есть совершенно разные плагины, лично меня интересовал только OpenStack Cloud Controller Manager для создания балансировщиков и Cinder CSI Plugin, чтобы при создании PV, диски сами создавались в OpenStack и монтировались к нужному узлу.
Для работоспособности необходимо создать файл cloud-config с конфигурацией и кредами от OpenStack
[Global] auth-url=OS_AUTH_URL username=OS_USERNAME password=OS_PASSWORD tenant-name=OS_PROJECT_NAME domain-name=default project-domain-name=OS_PROJECT_DOMAIN_ID user-domain-name=OS_USER_DOMAIN_ID region=OS_REGION_NAME [LoadBalancer] lb-version=v2 subnet-id=<subnet_id> # ID подсети, из которой балансировщик будет выделять IP floating-network-id=<floating_network_id> # ID сети в которой существует вышеуказанная подсеть flavor-id=<flavor_id> # ID тарифа балансировщика member-subnet-id=<member_subnet_id> # ID сети, в которой будут создаваться member. В моем случае это подсеть сети talos-k8s. create-monitor=true monitor-delay=5s monitor-timeout=3s monitor-max-retries=3 [BlockStorage] trust-device-path=false ignore-volume-az=true
OpenStack Cloud Controller Manager
Чтобы поставить этот модуль достаточно выполнить команды из документации.
-
Создать секрет из файла cloud-config описанного выше.
kubectl create secret -n kube-system generic cloud-config --from-file=cloud.conf -
Пометить namespace для необходимых прав доступа, создать RBAC ресурсы и openstack-cloud-controller-manager daemonset.
kubectl label namespace kube-system pod-security.kubernetes.io/enforce=privilegedkubectl apply -f https://raw.githubusercontent.com/kubernetes/cloud-provider-openstack/master/manifests/controller-manager/cloud-controller-manager-roles.yamlkubectl apply -f https://raw.githubusercontent.com/kubernetes/cloud-provider-openstack/master/manifests/controller-manager/cloud-controller-manager-role-bindings.yamlkubectl apply -f https://raw.githubusercontent.com/kubernetes/cloud-provider-openstack/master/manifests/controller-manager/openstack-cloud-controller-manager-ds.yaml
Проверяем статус что кластру хорошо и он готов к миграции
clusterctl describe cluster k8s-cluster -n kube-system
clusterctl move -n kube-system --to-kubeconfig ./k8s_kubeconfig
Готово!
Удаляем временую вм.
Теперь можно создавать узлы в Kubernetes просто патчами сущностей MachineDeployment и в узком пуле железа создавать другие кластеры.
Для удобства записал видео гайд.
Спасибо за внимание
ссылка на оригинал статьи https://habr.com/ru/articles/904724/
Добавить комментарий