Разработка приложений иногда достигает такого уровня, когда тестировать новые функции на статических окружениях становится неудобно. Окружений не хватает, разработчики мешают друг другу при запуске тестов. Такая проблема возникла у одного из наших клиентов.
Мы решили сделать гибридный кластер Kubernetes на базе существующего bare-metal-кластера, чтобы:
на лету заказывать узлы у облачного провайдера, когда в основном кластере заканчиваются ресурсы;
разворачивать на этих узлах нужные окружения для тестов;
сворачивать окружения после выхода изменений в production и отключать узлы.
Для этого мы воспользовались модулем cloud-provider-openstack, который входит в состав Kubernetes-платформы Deckhouse. Расскажу, как это работает.
☝️ В статье рассматривается вариант развертывания гибридного кластера в Selectel и OpenStack-облаке, которое предоставляет провайдер. Но сам подход универсален, его можно применять, к примеру, и на собственной инфраструктуре.
Настройка сетевой связности
У нас был bare-metal-кластер Kubernetes, расположенный в Selectel. Узлы в нем находились в разных зонах: SPB-3 и SPB-5. Также некоторые приложения использовали managed PostgreSQL, расположенный в облачной зоне ru-3. Чтобы была возможность дозаказывать узлы в OpenStack-облаке Selectel, нам пришлось настроить сетевое взаимодействие между уже существующими сетями и облачными.
Так выглядит L3VPN-роутер от Selectel, который позволяет связать bare-metal-узлы в зонах SPB-3 (network_SPB-3
) и SPB-5 (network_SPB-5
) с PostgreSQL (network_ru-3
):
Четвёртую сеть — k8s_l3vpn
— добавили инженеры Selectel, чтобы связать виртуальные машины (ВМ) в облаке с остальными сетями.
Облачным ВМ нужен доступ не только к внутренним сетям, но и во внешнюю сеть. Поэтому пришлось сделать две сети в облаке и попросить коллег из Selectel настроить маршруты в них.
Одна из сетей дает ВМ доступ во внешнюю сеть:
Через вторую L3VPN-роутер связывает облачные ВМ с остальными сетями:
Две этих сети объединены отдельным облачным роутером, который подключен к внешней сети:
Затем мы попросили настроить маршрутизацию таким образом, чтобы ВМ, которые заказываются в сети k8s_l3vpn
(192.168.200.0/24), получали маршруты вида:
- to: 0.0.0.0/0
via: 192.168.200.1
- to: 10.13.1.0/24
via: 192.168.200.252
- to: 192.168.204.0/24
via: 192.168.200.252
- to: 192.168.205.0/24
via: 192.168.200.252
В результате получилась такая схема:
доступ во внешнюю сеть
k8s-external-access
реализован через основной шлюз 192.168.200.1;доступ к «серым» подсетям организован через шлюз 192.168.200.252.
На этом подготовительные работы были закончены.
Подготовка и настройка модуля cloud-provider-openstack в Deckhouse
Мы создали сервисного пользователя с доступом к проекту, в котором заказываются ВМ.
Сделать это можно в личном кабинете Selectel: Профиль и настройки → Управление пользователями → Добавить пользователя → Сервисный пользователь. Там нужно указать имя, пароль и выдать права администратора необходимого проекта:
Затем мы создали keypair, чтобы при необходимости подключаться к ВМ по ключу:
openstack keypair create --public-key ~/.ssh/id-rsa.pub --type ssh deckhouse
Также можно загрузить свой образ, с которым будет бутстрапиться новая ВМ с помощью openstack image create
.
Чтобы настроить OpenStack CLI под созданного пользователя, скачали скрипт в разделе Облачная платформа → Доступ, выбрав там нужного пользователя:
И выполнили:
source rc.sh
Для работы с OpenStack мы воспользовались модулем Deckhouse cloud-provider-openstack. Вот его конфигурация:
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: cloud-provider-openstack
spec:
enabled: true
settings:
connection:
authURL: <OS_AUTH_URL из rc.sh>
domainName: <OS_PROJECT_DOMAIN_NAME из rc.sh>
password: <пароль сервисного пользователя>
region: <OS_REGION_NAME из rc.sh>
tenantID: <OS_TENANT_ID из rc.sh>
username: <имя сервисного пользователя>
instances:
sshKeyPairName: deckhouse # Имя keypair, созданного ранее.
internalNetworkNames:
- k8s_l3vpn # Имя сети, которая подключена к роутеру L3VPN и в которой будут находиться ВМ.
podNetworkMode: DirectRouting
zones:
- ru-3a
version: 1
Со всеми доступными параметрами модуля можно ознакомиться в разделе документации «OpenStack: настройки».
Сначала создали flavor с нужными параметрами:
openstack flavor create c4m8d50 --ram 8192 --disk 50 --vcpus 4 --private
Затем InstanceClass:
apiVersion: deckhouse.io/v1
kind: OpenStackInstanceClass
metadata:
name: cloud-worker
spec:
flavorName: c4m8d50
imageName: ubuntu-22-04-cloud-amd64 # Берем существующий (openstack image list) или загруженный на шаге ранее.
mainNetwork: k8s_l3vpn
И, наконец, NodeGroup:
apiVersion: deckhouse.io/v1
kind: NodeGroup
metadata:
name: cloud-worker
spec:
chaos:
mode: Disabled
cloudInstances:
classReference:
kind: OpenStackInstanceClass
name: cloud-worker # Указываем имя InstanceClass, созданного ранее.
maxPerZone: 10 # Максимальное кол-во узлов в зоне.
minPerZone: 1 # Минимальное кол-во узлов в зоне.
zones:
- ru-3a
disruptions:
approvalMode: Automatic
nodeTemplate:
labels:
node-role/cloud-worker: ""
taints:
- effect: NoExecute
key: dedicated
value: cloud-worker
nodeType: CloudEphemeral
Работа с ВМ
Через 2–3 минуты после создания NodeGroup узлы начинают заказываться с учетом параметра minPerZone
и добавляться в кластер. За процессом можно следить в панели Selectel:
И в кластере:
~ $ kubectl get no -owide | grep cloud
cloud-worker-af1ba977-7d4d4-lltnf Ready cloud-worker 28d v1.23.15 192.168.200.68 <none> Ubuntu 22.04.1 LTS 5.15.0-57-generic docker://20.10.14
Смотрим на узлы с точки зрения cloud instance manager'a:
~ $ kubectl -n d8-cloud-instance-manager get machine
NAME STATUS AGE
cloud-worker-af1ba977-7d4d4-lltnf Running 5d3h
Если при создании возникают ошибки, связанные с недостаточностью прав или некорректным подключением к API, их можно наблюдать в логах контроллера:
~ $ kubectl -n d8-cloud-instance-manager logs -l app=machine-controller-manager -c controller
Когда временные окружения сворачиваются и ресурсы в кластере освобождаются, Cluster Autoscaler удаляет облачные ВМ.
Если требуется удалить ненужную ВМ вручную, нужно выполнить два шага.
Получаем список машин:
kubectl ‑n d8-cloud‑instance‑manager get machine NAME STATUS AGE cloud‑worker‑af1ba977–7d4d4-lltnf Running 25d
Удаляем ненужную:
kubectl -n d8-cloud-instance-manager delete machine cloud-worker-af1ba977-7d4d4-lltnf
Заключение
С помощью модуля cloud-provider-openstack, который входит в состав платформы Deckhouse, мы превратили обычный bare-metal-кластер в гибридный. В нем можно создавать различные группы узлов под специфические цели с непостоянной нагрузкой и масштабировать узлы в зависимости от потребностей.
Хотя в статье рассматривается вариант развертывания гибридного кластера в Selectel, связку «Deckhouse + модуль cloud-provider-openstack» можно использовать где угодно, в том числе и на собственной инфраструктуре.
P.S.
Читайте также в нашем блоге: