В новом релизе Kubernetes-платформы Deckhouse v1.43 появилась система виртуализации, основанная на современных технологиях: KubeVirt, Cilium, LINSTOR. Она позволяет в удобном и привычном для пользователя платформы режиме запускать виртуальные машины и управлять их жизненным циклом.
В статье мы рассмотрим это на практике: развернем Deckhouse на bare-metal-сервере, запустим в нем виртуальную машину и протестируем, как обеспечивается связь между компонентами кластера с помощью магии Cilium.
Виртуализация в кластере
С новой системной виртуализации появилась возможность запускать привычные виртуальные машины (ВМ) прямо в Kubernetes и работать с ними как с ресурсами кластера.
Это может быть полезно, если для работы приложений требуются какие-то специфические инструменты, работа которых невозможна в K8s. А также в случае перехода от старой инфраструктуры с ВМ к контейнеризации приложений. В этом случае можно развернуть одну часть приложения в кластере, а другую поднять рядом в отдельной ВМ, созданной средствами Deckhouse и объединенной с кластером одной сетью.
Подготовка сервера
Для установки нам понадобится сервер, соответствующий минимальным требованиям:
не менее 4 ядер CPU;
не менее 8 Гб RAM;
не менее 40 Гб дискового пространства;
HTTPS-доступ к хранилищу образов контейнеров registry.deckhouse.io;
на сервере не должно быть установлено пакетов container runtime, например containerd или Docker.
Также понадобится персональный компьютер, с которого будет производиться установка. Он должен соответствовать следующим требованиям:
ОС: Windows 10+, macOS 10.15+, Linux (Ubuntu 18.04+, Fedora 35+);
установленный Docker для запуска инсталлятора Deckhouse (инструкции по установке для Ubuntu, macOS, Windows);
HTTPS-доступ к хранилищу образов контейнеров registry.deckhouse.io;
SSH-доступ по ключу к серверу, который будет master-узлом будущего кластера.
В качестве сервера для тестов у нас будет 1U-сервер Supermicro со следующими характеристиками:
2 процессора Intel(R) Xeon(R) CPU E5620 @ 2.40GHz, 16 ядер;
40 Гб оперативной памяти;
256 Гб SSD под корень ОС;
320 Гб на отдельном диске для хранения данных виртуальных машин.
В первую очередь установим ОС, выбрав подходящую из списка поддерживаемых:
РЕД ОС 7.3;
AlterOS 7;
Astra Linux Special Edition 1.7.2;
CentOS 7, 8, 9;
Debian 9, 10, 11;
Rocky Linux 8, 9;
Ubuntu 18.04, 20.04, 22.04.
В нашем случае это будет Ubuntu Server 22.04.2 LTS. Устанавливаем ее в обычном режиме, не разбивая диск на разделы и не выбирая в конце установки никакого дополнительного софта, кроме OpenSSH-сервера.
Загружаемся в свежую ОС и настраиваем доступ по ключу с основного ПК:
$ ssh-copy-id <IP-адрес сервера>
Команда не сработает, если ключ не был заранее сгенерирован. Сгенерировать его можно командой ssh-keygen -t rsa
.
Подключимся к серверу, чтобы убедиться, что все настроено как нужно:
$ ssh 192.168.2.38
Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.0-60-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Wed Mar 1 11:23:13 AM UTC 2023
System load: 0.0 Temperature: 46.0 C
Usage of /: 2.7% of 292.35GB Processes: 135
Memory usage: 2% Users logged in: 0
Swap usage: 0% IPv4 address for enp3s0: 192.168.2.38
* Introducing Expanded Security Maintenance for Applications.
Receive updates to over 25,000 software packages with your
Ubuntu Pro subscription. Free for personal use.
https://ubuntu.com/pro
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
Last login: Wed Mar 1 10:34:01 2023 from 192.168.2.35
Все работает. Отключаемся от сервера, выполнив команду exit
или нажав сочетание клавиш Ctrl + D.
Установка Deckhouse на сервер
Конфигурация будущего кластера
Перейдем на страницу конфигурации будущего кластера в разделе Getting Started официального сайта Deckhouse. Здесь нужно указать шаблон доменных имен для веб-интерфейсов будущих компонентов кластера, таких как Grafana или Kubernetes Dashboard:
Шаблон имеет формат %s.example.com
, где %s
— имена доменов системных приложений кластера.
Нажмем кнопку Далее: Установка.
На следующей странице отобразится сгенерированное содержимое для файла конфигурации config.yml
. Введенный ранее шаблон доменных имен появится в секции publicDomainTemplate
:
# Cекция с общими параметрами кластера (ClusterConfiguration).
# Используемая версия API Deckhouse Platform.
apiVersion: deckhouse.io/v1
# Тип секции конфигурации.
kind: ClusterConfiguration
# Тип инфраструктуры: bare metal (Static) или облако (Cloud).
clusterType: Static
# Адресное пространство Pod'ов кластера.
podSubnetCIDR: 10.111.0.0/16
# Адресное пространство для Service'ов кластера.
serviceSubnetCIDR: 10.222.0.0/16
# Устанавливаемая версия Kubernetes.
kubernetesVersion: "1.23"
# Домен кластера.
clusterDomain: "cluster.local"
---
# Секция первичной инициализации кластера Deckhouse (InitConfiguration).
# Используемая версия API Deckhouse.
apiVersion: deckhouse.io/v1
# Тип секции конфигурации.
kind: InitConfiguration
# Секция с параметрами Deckhouse.
deckhouse:
# Используемый канал обновлений.
releaseChannel: Stable
configOverrides:
global:
modules:
# Шаблон, который будет использоваться для составления адресов системных приложений в кластере.
# Например, Grafana для %s.example.com будет доступна на домене grafana.example.com.
publicDomainTemplate: "%s.example.com"
# Включить модуль cni-cilium.
cniCiliumEnabled: true
# Конфигурация модуля
cniCilium:
# Режим работы туннеля.
tunnelMode: VXLAN
---
# Cекция с параметрами bare metal кластера (StaticClusterConfiguration).
# Используемая версия API Deckhouse.
apiVersion: deckhouse.io/v1
# Тип секции конфигурации.
kind: StaticClusterConfiguration
# Список внутренних сетей узлов кластера (например, '10.0.4.0/24'), который
# используется для связи компонентов Kubernetes (kube-apiserver, kubelet...) между собой.
# Если каждый узел в кластере имеет только один сетевой интерфейс,
# ресурс StaticClusterConfiguration можно не создавать.
internalNetworkCIDRs:
- '192.168.2.0/24'
Здесь нужно обратить внимание на два момента:
В последней секции сгенерированного конфигурационного файла —
StaticClusterConfiguration
— нужно указать сеть, в которую смотрит основной сетевой интерфейс сервера, т. к. на борту их несколько.
☝️ Если на вашей машине один сетевой интерфейс — эту секцию можно удалить.Модуль, реализующий CNI, заменен с cni-flannel на cni-cilium. Это необходимо для работы виртуальных машин — их сетевое взаимодействие организовано на базе Cilium. Также у него указан параметр работы туннелей
tunnelMode: VXLAN
.
Сохраним полученное содержимое в файле config.yml
, положив его в любой отдельный каталог.
Установка Deckhouse
Для установки платформы используется специальный подготовленный Docker-образ, в который необходимо передать созданный конфигурационный файл и SSH-ключи доступа на master-узел.
Перейдем в каталог с созданным файлом и выполним команду:
docker run --pull=always -it -v "$PWD/config.yml:/config.yml" -v "$HOME/.ssh/:/tmp/.ssh/" registry.deckhouse.io/deckhouse/ce/install:stable bash
Через некоторое время образ скачается из хранилища, запустится, и в терминале будет отображено приглашение командной строки внутри созданного контейнера:
[deckhouse] root@9134e1cd790b / #
Выполним команду:
dhctl bootstrap --ssh-user=<username> --ssh-host=<master_ip> --ssh-agent-private-keys=/tmp/.ssh/id_rsa \
--config=/config.yml \
--ask-become-pass
На запрос пароля sudo введем пароль пользователя на сервере.
Если sudo на сервере настроен таким образом, чтобы не запрашивать пароль, не вводите параметр --ask-become-pass
.
Установка может занять от 15 до 30 минут, в процессе будет отображаться подробный лог. По завершении мы снова увидим приглашение командной строки контейнера.
Финальная настройка кластера
Осталось выполнить несколько шагов, чтобы кластер был готов к работе.
Добавим конфигурацию kubectl для обычного пользователя, чтобы не вызывать каждый раз sudo:
$ mkdir ~/.kube
$ sudo cat /etc/kubernetes/admin.conf >> ~/.kube/config
Убедимся, что теперь вызов команды доступен без повышенных привилегий:
$ kubectl get no
NAME STATUS ROLES AGE VERSION
virtualization-habr Ready control-plane,master 3d20h v1.23.15
Так как наш кластер состоит из одного узла, нужно снять с него taint. Для этого выполним на сервере команду:
kubectl patch nodegroup master --type json -p '[{"op": "remove", "path": "/spec/nodeTemplate/taints"}]'
Если ваш кластер будет состоять из нескольких узлов, вместо снятия taint'а добавьте в кластер дополнительные узлы.
После этого нужно подождать несколько минут, пока Deckhouse переконфигурируется. Проверить статус можно командой:
kubectl -n d8-system exec deploy/deckhouse -- deckhouse-controller queue list
В ответ будет отображено много текста с описанием модулей, но нас интересует последняя часть:
Summary:
- 'main' queue: empty.
- 68 other queues (1 active, 67 empty): 1 task.
- total 1 task to handle.
Если в строке queue
значение не empty
, значит процесс еще не завершен.
Установка Ingress-контроллера
Создадим на master-узле файл ingress-nginx-controller.yml
в отдельном каталоге:
$ mkdir templates
$ cd templates
~/templates$ vim ingress-nginx-controller.yml
Скопируем в файл конфигурацию контроллера:
# Cекция, описывающая параметры nginx ingress controller.
# Используемая версия API Deckhouse.
apiVersion: deckhouse.io/v1
kind: IngressNginxController
metadata:
name: nginx
spec:
# Имя Ingress-класса для обслуживания Ingress NGINX controller.
ingressClass: nginx
# Версия Ingress-контроллера (используйте версию 1.1 с Kubernetes 1.23+).
controllerVersion: "1.1"
# Способ поступления трафика из внешнего мира.
inlet: HostPort
hostPort:
httpPort: 80
httpsPort: 443
# Описывает, на каких узлах будет находиться компонент.
# Возможно, захотите изменить.
nodeSelector:
node-role.kubernetes.io/control-plane: ""
tolerations:
- operator: Exists
Применим его, выполнив команду:
kubectl create -f ingress-nginx-controller.yml
Если все прошло успешно, в ответ отобразится сообщение:
ingressnginxcontroller.deckhouse.io/nginx created
Создание пользователя для доступа в веб-интерфейсы кластера
Чтобы получить доступ в веб-интерфейсы кластера, необходимо создать пользователя, под которым будет выполняться вход. Скопируем содержимое в файл user.yml
:
apiVersion: deckhouse.io/v1
kind: ClusterAuthorizationRule
metadata:
name: admin
spec:
# Список учётных записей Kubernetes RBAC.
subjects:
- kind: User
name: admin@deckhouse.io
# Предустановленный шаблон уровня доступа.
accessLevel: SuperAdmin
# Разрешить пользователю делать kubectl port-forward.
portForwarding: true
---
# Секция, описывающая параметры статического пользователя.
# Используемая версия API Deckhouse.
apiVersion: deckhouse.io/v1
kind: User
metadata:
name: admin
spec:
# E-mail пользователя
email: admin@deckhouse.io
# Это хэш пароля byul2sbqfb, сгенерированного сейчас.
# Сгенерируйте свой или используйте этот, но только для тестирования.
# echo "byul2sbqfb" | htpasswd -BinC 10 "" | cut -d: -f2
# Возможно, захотите изменить.
password: '$2a$10$pnQvwsl.gibQ1oM/z3D9g.g6o8v7dkbyqT0wFh7MsA7sUu3GhRwl6'
Обратите внимание на раздел password
: в комментариях показан пример генерации нового пароля. Обязательно измените пароль, если готовите кластер не для ознакомительных целей!
Применим созданный файл в кластере:
kubectl create -f user.yml
В случае успешного создания пользователя отобразится сообщение:
clusterauthorizationrule.deckhouse.io/admin created
user.deckhouse.io/admin created
Подготовка DNS-записей
Для доступа к веб-интерфейсам кластера необходимо настроить DNS-записи, ведущие на IP-адрес master-узла кластера. Сделать это можно различными способами: настроить DNS-сервер, прописать соответствие в админ-панели хостера, предоставляющего доменное имя, или же указать нужные записи в файле hosts
.
Добавим следующие строки в /etc/hosts
на рабочей машине, с которой велась установка:
api.example.com
argocd.example.com
dashboard.example.com
deckhouse.example.com
dex.example.com
grafana.example.com
hubble.example.com
istio.example.com
istio-api-proxy.example.com
kubeconfig.example.com
openvpn-admin.example.com
prometheus.example.com
status.example.com
upmeter.example.com
Не забудьте поменять example.com
на адрес, указанный в config.yml
.
Проверка работоспособности
Теперь убедимся, что кластер развернут и готов к работе. Зайдем в веб-интерфейс Grafana по адресу grafana.example.com. Нас перебросит на страницу входа Dex, где нужно ввести данные созданного ранее пользователя:
После ввода учетных данных откроется главный дашборд Grafana:
Здесь отображается основная информация о компонентах кластера, а также его нагрузка.
Кластер работает. Настало время развернуть в нем виртуальные машины!
Развертывание виртуальных машин
Включение LINSTOR
Для работы с виртуальными жесткими дисками используется модуль linstor, реализующий в кластере поддержку LINSTOR — системы для оркестрации блочных устройств хранения данных в Kubernetes.
Создадим файл linstor.yml
:
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: linstor
spec:
enabled: true
И применим его в кластере:
$ kubectl create -f linstor.yml
moduleconfig.deckhouse.io/linstor created
Подождем пару минут и убедимся, что модуль запущен и работает:
$ kubectl get moduleconfigs
NAME STATE VERSION AGE STATUS
cni-cilium Enabled 1 3d22h
deckhouse Enabled 1 3d22h
global Enabled 1 3d22h
linstor Enabled 1 96s
Конфигурация хранилища
Теперь нужно сконфигурировать хранилище.
Добавим alias для удобного управления LINSTOR:
alias linstor='kubectl exec -n d8-linstor deploy/linstor-controller -- linstor'
Для сохранения настроек пропишите эту команду в ~/.bashrc — и в новом сеансе SSH команда станет доступна сразу после входа.
Посмотрим список всех доступных для организации хранилища дисковых устройств:
$ linstor physical-storage list
+----------------------------------------------------+
| Size | Rotational | Nodes |
|====================================================|
| 500107862016 | True | officeserver[/dev/sda] |
+----------------------------------------------------+
Обратите внимание, что здесь отображаются только пустые диски!
Для некоторых действий с хранилищем может понадобиться использование мастер-пароля, поэтому создадим Secret с ним (файл pass.yml
):
apiVersion: v1
kind: Secret
metadata:
name: linstor-passphrase
namespace: d8-system
immutable: true
stringData:
MASTER_PASSPHRASE: *!пароль* # Мастер-пароль для LINSTOR
И применим его в кластере:
$ kubectl create -f pass.yml
secret/linstor-passphrase created
Теперь создадим новый пул LVM на отдельном диске /dev/sda
, найденном выше:
$ sudo vgcreate vg0 /dev/sda --add-tag linstor-data
Physical volume "/dev/sda" successfully created.
Volume group "vg0" successfully created
Подождем несколько минут пока пул будет создан, и убедимся, что следом за ним создался и StorageClass, в котором будут храниться диски будущих виртуальных машин:
$ kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
linstor-data-r1 linstor.csi.linbit.com Delete Immediate true 2m46s
Хранилище готово! Можно создавать виртуальные машины.
Включение модуля виртуализации
По умолчанию модуль виртуализации отключен.
Создадим файл virtualization.yml
со следующим содержимым:
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: virtualization
spec:
version: 1
enabled: true
settings:
vmCIDRs:
- 10.10.10.0/24
В параметре vmCIDRs
мы указали подсеть, из которой будут выделяться IP-адреса виртуальным машинам.
Применим модуль на master-узле:
$ kubectl create -f virtualization.yml
moduleconfig.deckhouse.io/virtualization created
Подождем пару минут и проверим, что модуль запустился:
$ kubectl get moduleconfigs
NAME STATE VERSION AGE STATUS
cni-cilium Enabled 1 80m
deckhouse Enabled 1 80m
global Enabled 1 80m
linstor Enabled 1 40m
virtualization Enabled 1 3m26s
В последней строчке состояние модуля должно быть Enabled
.
Создание виртуальных машин
Посмотрим перечень ОС, доступных для развертывания на создаваемых виртуальных машинах:
$ kubectl get cvmi
NAME AGE
ubuntu-18.04 80s
ubuntu-20.04 80s
ubuntu-22.04 80s
При первом вызове команда может показать ошибку error: the server doesn't have a resource type "cvmi"
— нужно просто подождать и еще раз выполнить запрос.
Создадим файл habr-vm.yml
с описанием будущей виртуальной машины:
apiVersion: deckhouse.io/v1alpha1
kind: VirtualMachine
metadata:
name: habr-vm
namespace: default
spec:
running: true
resources:
memory: 8192M
cpu: "4"
userName: ubuntu
sshPublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADA ..."
bootDisk:
source:
kind: ClusterVirtualMachineImage
name: ubuntu-22.04
size: 50Gi
storageClassName: linstor-data-r1
autoDelete: true
Здесь нужно указать параметры будущей виртуальной машины: количество оперативной памяти, имя пользователя, количество ядер CPU и ключ SSH для аутентификации. А также в разделе storageClassName
указать созданный ранее StorageClass.
Применим файл на master-узле:
$ kubectl create -f habr-vm.yml
virtualmachine.deckhouse.io/habr-vm created
Подождем некоторое время, пока машина развернется, и убедимся, что она запущена:
$ kubectl get virtualmachines
NAME IPADDRESS STATUS AGE
habr-vm 10.10.10.0 Provisioning 40s
Статус Provisioning
говорит о том, что машина успешно создана и в данный момент разворачивается. Как только процесс завершится, статус изменится на Running
.
Если вы обнаружили ошибку в конфиге и хотите пересоздать ВМ, сначала удалите развернутую машину, а затем исправьте конфиг и создайте ВМ заново:
$ kubectl delete virtualmachines master-vm
virtualmachine.deckhouse.io "master-vm" deleted
Проверка работоспособности
Итак, виртуальная машина развернута и успешно работает. Cilium обеспечивает сетевую связность элементов кластера таким образом, что получить доступ к созданной ВМ можно из любого места, например из Pod'а, развернутого в кластере непосредственно рядом с ВМ.
Создадим демо-Pod для проверки:
$ kubectl run demo --image=ubuntu --rm -it --command -- bash
If you don't see a command prompt, try pressing enter.
root@demo:/#
И попробуем «достучаться» до развернутой рядом виртуальной машины. Для этого сначала установим утилиту ping, которой по умолчанию нет в образе Ubuntu, а затем «постучимся» по IP-адресу ВМ:
/# apt update
/# apt install iputils-ping
/# ping 10.10.10.0
PING 10.10.10.0 (10.10.10.0) 56(84) bytes of data.
64 bytes from 10.10.10.0: icmp_seq=1 ttl=63 time=0.310 ms
64 bytes from 10.10.10.0: icmp_seq=2 ttl=63 time=0.356 ms
64 bytes from 10.10.10.0: icmp_seq=3 ttl=63 time=0.410 ms
64 bytes from 10.10.10.0: icmp_seq=4 ttl=63 time=0.384 ms
^C
--- 10.10.10.0 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3063ms
Как видно из лога, прямой доступ из Pod'а в кластере к ВМ есть.
Получение доступа к виртуальной машине извне
Сейчас виртуальная машина доступна только внутри сервера. В таком режиме можно работать, если она требуется только для развертывания отдельного сервиса. Но иногда нужен доступ к ней или ее сервисам снаружи. Предоставить его можно с помощью Service с настроенными externalIPs
:
apiVersion: v1
kind: Service
metadata:
labels:
run: master-service
name: master-service
namespace: default
spec:
ports:
- port: 15022
protocol: TCP
targetPort: 22
selector:
vm.kubevirt.io/name: habr-vm
externalIPs:
- 192.168.2.45
Здесь мы пробрасываем порт 15022
снаружи кластера на порт 22
ВМ с именем habr-vm
, чтобы получить SSH-доступ к машине извне кластера.
Сохраним его в файл external-ip.yml
и применим:
$ kubectl create -f external-ip.yml
service/master-service created
Убедимся, что Service создан и работает:
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.222.0.1 <none> 443/TCP 3h50m
master-service ClusterIP 10.222.158.22 192.168.2.45 15022/TCP 6s
Здесь мы воспользовались простым пробросом порта снаружи на ресурс внутри кластера. Такой подход не очень удобен, т. к. требует явного указания порта для соединения по SSH с внутренней ВМ. Если обратиться на порт по умолчанию (22), то мы попадем на master-узел основного кластера.
Для элегантного решения этой проблемы можно воспользоваться модулем metallb Deckhouse, который доступен в Enterprise-версии платформы. С его помощью можно настроить доступ так, чтобы из сети казалось, что у master-узла кластера есть несколько IP-адресов. Это позволит получить доступ к ВМ напрямую по ее IP без проброса разных портов.
Проверим, что теперь мы можем попасть на внутреннюю ВМ снаружи:
$ ssh 192.168.2.45 -p 15022
Welcome to Ubuntu 22.04.1 LTS (GNU/Linux 5.15.0-57-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Tue Mar 21 13:24:57 UTC 2023
System load: 0.0 Processes: 128
Usage of /: 2.9% of 48.28GB Users logged in: 0
Memory usage: 3% IPv4 address for enp1s0: 10.10.10.0
Swap usage: 0%
0 updates can be applied immediately.
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Last login: Tue Mar 21 13:16:49 2023 from 192.168.2.35
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
zhbert@habr-vm:~$
Заключение
В статье мы рассмотрели пример использования нового модуля виртуализации Deckhouse. С его помощью можно развернуть полноценную виртуальную машину и работать с ней как с ресурсом Kubernetes, используя привычные команды kubectl.
Магия Cilium, основанного на eBPF проекта для реализации сетевого взаимодействия, позволяет обеспечить сетевую связность всех компонентов таким образом, чтобы доступ к виртуальной машине был возможен из любой точки кластера. Благодаря этому ВМ можно использовать для развертывания сервисов, которые по каким-либо причинам невозможно развернуть внутри кластера привычным способом. Более того, на созданных ВМ можно развернуть еще один кластер под управлением Deckhouse, а затем получить доступ к отдельной ВМ уже из него.
С любыми вопросами и предложениями ждем вас в комментариях к статье, а также в Telegram-чате deckhouse_ru, где всегда готовы помочь. Будем рады issues (и, конечно, звёздам) в GitHub-репозитории Deckhouse.
P.S.
Читайте также в нашем блоге: