Capi + talos в openstack? Не вопрос

от автора

Абстракт

Когда я был маленьким и глупым, в своей домашней лаборатории я развернул 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

Чтобы поставить этот модуль достаточно выполнить команды из документации.

  1. Создать секрет из файла cloud-config описанного выше.

    kubectl create secret -n kube-system generic cloud-config --from-file=cloud.conf
  2. Пометить 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *