В новом релизе 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.
Читайте также в нашем блоге:
ссылка на оригинал статьи https://habr.com/ru/articles/725706/
Добавить комментарий