Стадия жизненного цикла модуля: General Availability
У модуля есть требования для установки
Руководство описывает, как настроить модуль virtualization и управлять его кластерными ресурсами.
Права администратора включают и управление проектными ресурсами, которые описаны в руководстве пользователя.
Параметры модуля
Конфигурация модуля virtualization задаётся в ресурсе ModuleConfig. Ниже приведён пример базовой настройки, в которой указаны класс Ingress-контроллера, хранилище образов и подсеть для виртуальных машин:
- В командной строке
- В веб-интерфейсе
Примените манифест с нужными параметрами:
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: virtualization
spec:
enabled: true
version: 1
settings:
ingressClass: nginx # опциональный параметр
dvcr:
storage:
persistentVolumeClaim:
size: 50G
storageClassName: rv-thin-r1
type: PersistentVolumeClaim
virtualMachineCIDRs:
- 10.66.10.0/24- Перейдите на вкладку «Система», далее в раздел «Deckhouse» → «Модули».
- Из списка выберите модуль
virtualization. - В открывшемся окне выберите вкладку «Конфигурация».
- Чтобы отобразить настройки, нажмите переключатель «Дополнительные настройки».
- Задайте параметры. Названия полей формы соответствуют названиям параметров в YAML.
- Нажмите кнопку «Сохранить».
Включение и выключение модуля
За состояние модуля отвечает параметр .spec.enabled. Значение true включает модуль, значение false выключает его.
Выключение модуля останавливает все системные компоненты, которые создают и запускают виртуальные машины (ВМ), поэтому по умолчанию модуль выключить нельзя.
Чтобы это стало возможным, добавьте на ModuleConfig virtualization аннотацию modules.deckhouse.io/allow-disabling со значением true.
Перед выключением подготовьте кластер:
-
Удалите все ресурсы модуля, включая виртуальные машины, диски и образы.
-
Убедитесь, что в кластере не осталось активных ресурсов:
d8 k get virtualization -A d8 k get virtualization-cluster
После этого отредактируйте ModuleConfig virtualization:
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: virtualization
annotations:
modules.deckhouse.io/allow-disabling: "true"
spec:
enabled: false
version: 1
settings:
# Укажите существующие настройки.Если ресурсы модуля не удалены, выключение может привести к потере данных.
Версия конфигурации
Параметр .spec.version определяет версию схемы настроек. Структура параметров может меняться между версиями, актуальные значения приведены в настройках модуля.
Настройки Ingress
Образы виртуальных машин загружаются в кластер через Ingress-контроллер, класс которого определяет параметр .spec.settings.ingressClass.
Указывать его необязательно, и если параметр не задан, модуль использует глобальное значение из конфигурации Deckhouse Platform (DP).
Задавайте его только тогда, когда для загрузки образов нужен отдельный Ingress-контроллер.
Пример:
spec:
settings:
ingressClass: nginxБольшие образы виртуальных машин по медленному каналу связи загружаются долго, и перезапуск или обновление Ingress-контроллера прерывает такую загрузку. Чтобы этого избежать, увеличьте тайм-аут завершения рабочих процессов в ресурсе IngressNginxController.
Пример:
apiVersion: deckhouse.io/v1
kind: IngressNginxController
metadata:
name: nginx
spec:
config:
worker-shutdown-timeout: 1800s # 30 минут или более при необходимостиСетевые настройки
В блоке .spec.settings.virtualMachineCIDRs перечисляются подсети в формате CIDR, из которых DP выдаёт IP-адреса виртуальным машинам автоматически или по запросу.
Указывайте начальный адрес подсети, выровненный по маске, например 192.168.1.192/27, а не произвольный адрес из диапазона.
Пример:
spec:
settings:
virtualMachineCIDRs:
- 10.66.10.0/24
- 10.66.20.0/24
- 10.77.20.0/16Первый и последний адреса каждой подсети зарезервированы и виртуальным машинам не выдаются. Например, в подсети 10.66.10.0/24 недоступны адреса 10.66.10.0 и 10.66.10.255.
Блок можно не задавать. Модуль в этом случае включится, но работать с адресами виртуальных машин уже нельзя, а именно:
- создать или использовать ресурс VirtualMachineIPAddress нельзя;
- виртуальная машина не может запросить сеть
Mainв параметре.spec.networks; - параметр
.spec.networksвиртуальной машины не может быть пустым.
Подсети блока .spec.settings.virtualMachineCIDRs не должны пересекаться с подсетями узлов кластера, подсетью сервисов или подсетью подов (podCIDR).
Удалить подсеть, из которой уже выданы адреса виртуальным машинам, нельзя. Заданный блок также нельзя очистить полностью.
Хранилище образов виртуальных машин
Образы виртуальных машин DP хранит во внутреннем хранилище образов контейнеров (DVCR), которое размещается на постоянном томе кластера. Оттуда образы попадают на диски виртуальных машин, поэтому от размера тома зависит, сколько образов поместится в кластер.
Размер и класс хранения
Размер тома и класс хранения задаются в блоке .spec.settings.dvcr.storage. Чтобы расширить хранилище, увеличьте размер тома.
После того как том создан, уменьшить его размер и сменить класс хранения нельзя.
Классы хранения для образов и дисков
Класс хранения для образа или диска выбирает владелец проекта. Вы можете ограничить этот выбор и задать класс, который применяется по умолчанию. За образы отвечает блок .spec.settings.virtualImages, за диски — блок .spec.settings.virtualDisks.
Пример:
spec:
settings:
virtualImages:
allowedStorageClassSelector:
matchNames:
- sc-1
- sc-2
defaultStorageClassName: sc-1
virtualDisks:
allowedStorageClassSelector:
matchNames:
- sc-3
defaultStorageClassName: sc-3Оба блока устроены одинаково и оба необязательны. Параметр allowedStorageClassSelector.matchNames перечисляет классы, которые разрешено выбирать в спецификации VirtualImage и VirtualDisk, а defaultStorageClassName задаёт класс для тех ресурсов, где параметр .spec.persistentVolumeClaim.storageClassName не задан.
Очистка хранилища образов
Когда образы и диски удаляются из кластера, их данные какое-то время остаются в DVCR. Чтобы хранилище не заполнялось неактуальными данными, DP запускает сборку мусора по расписанию.
По умолчанию она выполняется ежедневно в 02:00. Задать своё расписание можно параметром .spec.settings.dvcr.gc.schedule в ModuleConfig virtualization.
Пока идёт сборка мусора, хранилище работает в режиме «только чтение», поэтому создание образов и дисков в это время откладывается до её завершения.
- В командной строке
- В веб-интерфейсе
Пример конфигурации с изменённым расписанием:
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: virtualization
spec:
# ...
settings:
dvcr:
gc:
schedule: "0 20 * * *"
# ...Посмотреть, сколько места занято и какие данные будут удалены при следующей сборке, можно командой:
d8 k -n d8-virtualization exec deploy/dvcr -- dvcr-cleaner gc checkПример вывода:
Found 2 cvi, 5 vi, 1 vd manifests in registry
Found 1 cvi, 5 vi, 11 vd resources in cluster
Total Used Avail Use%
36.3GiB 13.1GiB 22.4GiB 39%
Images eligible for cleanup:
KIND NAMESPACE NAME
ClusterVirtualImage debian-12
VirtualDisk default debian-10-root
VirtualImage default ubuntu-2404
- Перейдите на вкладку «Система», далее в раздел «Deckhouse» → «Модули».
- Из списка выберите модуль
virtualization. - В открывшемся окне на вкладке «Конфигурация» включите переключатель «Дополнительные настройки».
- В блоке «Хранилище образов дисков и ISO» в поле «Расписание очистки в формате Cron» задайте расписание.
- Нажмите кнопку «Сохранить».
Образы
Образ хранит содержимое диска, из которого владельцы проектов создают диски виртуальных машин. Кластерный образ ClusterVirtualImage доступен во всех неймспейсах и проектах кластера, поэтому загруженный однажды образ используют сразу все проекты.
Образ появляется в кластере в три шага:
- Администратор создаёт ресурс ClusterVirtualImage и указывает в нём источник данных.
- DP загружает образ из этого источника во внутреннее хранилище (DVCR).
- Загруженный образ становится доступен для создания дисков.
Источником образа может быть HTTP-сервер с файлом образа, хранилище образов контейнеров или файл на вашем компьютере, который вы загружаете из командной строки. Кроме того, образ можно создать из другого образа, из диска виртуальной машины или из снимка диска.
Ход создания образа показывает колонка PHASE в выводе d8 k get cvi, её значения описаны в поле .status.phase. Следить за созданием в реальном времени помогает ключ -w, а если образ надолго остаётся не готов, причину подскажет блок .status.conditions и команда d8 k describe cvi.
Пока образ не перешёл в фазу Ready, блок .spec можно менять, и после изменения загрузка начнётся заново. У готового образа блок .spec изменить уже нельзя. Все параметры образа описаны в ClusterVirtualImage.
Типы и форматы образов
Образы бывают двух видов:
- ISO-образ — установочный образ для первоначальной установки операционной системы (ОС). Такие образы выпускают производители ОС и применяют их для установки на физические и виртуальные серверы.
- Образ диска с предустановленной системой — содержит уже установленную и настроенную ОС, готовую к работе сразу после создания виртуальной машины (ВМ). Такие образы публикуют разработчики дистрибутивов, либо вы готовите их самостоятельно.
Готовые образы с предустановленной системой публикуют разработчики дистрибутивов. В таблице приведены страницы загрузки и имена пользователей, которые заданы в этих образах по умолчанию:
| Дистрибутив | Пользователь по умолчанию |
|---|---|
| AlmaLinux | almalinux |
| AlpineLinux | alpine |
| AltLinux | altlinux |
| AstraLinux | astra |
| CentOS | cloud-user |
| Debian | debian |
| Rocky | rocky |
| Ubuntu | ubuntu |
DP принимает файл образа в следующих форматах:
qcow2;raw;vmdk;vdi;vhd;vhdx.
Образ можно передать сжатым алгоритмом gz, xz или zst, DP распакует его при загрузке.
Тип и размер образа DP определяет сам и записывает их в статус ресурса. Размеров два, и оба видны в выводе команды d8 k get cvi -o wide:
STOREDSIZE— объём, который образ занимает в хранилище. Для образа, загруженного в сжатом виде, он меньше распакованного размера. По этой колонке удобно оценивать, сколько места образы занимают в DVCR.UNPACKEDSIZE— размер образа после распаковки. Он задаёт минимальный размер диска, который получится создать из этого образа.
Создавая диск из образа, указывайте размер не меньше значения UNPACKEDSIZE.
Если размер не задан, диск создаётся ровно по распакованному размеру образа.
Создание кластерного образа с HTTP-сервера
Проще всего создать образ, указав ссылку на файл, который лежит на HTTP-сервере.
- В командной строке
- В веб-интерфейсе
-
Создайте ресурс ClusterVirtualImage:
d8 k apply -f - <<EOF apiVersion: virtualization.deckhouse.io/v1alpha2 kind: ClusterVirtualImage metadata: name: ubuntu-24-04 spec: # Источник для создания образа. dataSource: type: HTTP http: url: https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img EOF -
Проверьте, что образ создан:
d8 k get clustervirtualimage ubuntu-24-04 # Короткий вариант команды. d8 k get cvi ubuntu-24-04Пример вывода:
NAME PHASE CDROM PROGRESS AGE ubuntu-24-04 Ready false 100% 23h
Чтобы DP сверил скачанный файл с контрольной суммой, добавьте в источник блок checksum. Если файл не совпал ни с одной из указанных сумм, образ перейдёт в фазу Failed.
- Перейдите на вкладку «Система», далее в раздел «Виртуализация» → «Кластерные образы».
- Нажмите кнопку «Создать», затем в блоке «Источник» выберите «По ссылке».
- В поле «Имя образа» введите имя образа.
- В поле «URL» укажите ссылку на образ.
- Нажмите кнопку «Создать».
- Дождитесь, когда образ перейдёт в состояние «Готов».
Создание кластерного образа из хранилища образов контейнеров
DP умеет забирать образ из внешнего хранилища образов контейнеров, но файл диска должен лежать в образе контейнера по пути /disk. Ниже показано, как подготовить такой образ контейнера и создать из него кластерный образ.
- В командной строке
- В веб-интерфейсе
-
Скачайте файл образа на локальную машину:
curl -L https://cloud-images.ubuntu.com/minimal/releases/noble/release/ubuntu-24.04-minimal-cloudimg-amd64.img -o ubuntu2404.img -
Создайте
Dockerfileсо следующим содержимым:FROM scratch COPY ubuntu2404.img /disk/ubuntu2404.img -
Соберите образ контейнера. В примере используется хранилище docker.com, для работы с которым нужны учётная запись и настроенное окружение:
docker build -t docker.io/<USERNAME>/ubuntu2404:latestЗдесь
<USERNAME>— имя пользователя, указанное при регистрации в хранилище. -
Загрузите собранный образ контейнера в хранилище:
docker push docker.io/<USERNAME>/ubuntu2404:latest -
Создайте ресурс ClusterVirtualImage, указав путь к образу контейнера:
d8 k apply -f - <<EOF apiVersion: virtualization.deckhouse.io/v1alpha2 kind: ClusterVirtualImage metadata: name: ubuntu-2404 spec: dataSource: type: ContainerImage containerImage: image: docker.io/<USERNAME>/ubuntu2404:latest EOF
DP работает только с теми хранилищами, где включён TLS. Если хранилище использует собственный центр сертификации, передайте цепочку сертификатов в параметре caBundle, а учётные данные для доступа к закрытому хранилищу возьмите из секрета, указанного в параметре imagePullSecret.
- Перейдите на вкладку «Система», далее в раздел «Виртуализация» → «Кластерные образы».
- Нажмите кнопку «Создать», затем в блоке «Источник» выберите «Из реестра».
- В поле «Имя образа» введите имя образа.
- В поле «Образ в реестре контейнеров» укажите ссылку на образ.
- Нажмите кнопку «Создать».
- Дождитесь, когда образ перейдёт в состояние «Готов».
Загрузка кластерного образа из командной строки
Если файл образа лежит на вашем компьютере, загрузите его напрямую. DP создаёт для этого временную точку приёма данных и ждёт загрузки.
- В командной строке
- В веб-интерфейсе
-
Создайте ресурс ClusterVirtualImage с источником
Upload:d8 k apply -f - <<EOF apiVersion: virtualization.deckhouse.io/v1alpha2 kind: ClusterVirtualImage metadata: name: some-image spec: # Настройки источника образа. dataSource: type: Upload EOFРесурс перейдёт в фазу
WaitForUserUploadи будет готов принять файл. Начните загрузку в течение 10 минут, иначе ресурс перейдёт в фазуFailedи его придётся создать заново. -
Получите адреса, по которым принимается файл:
d8 k get cvi some-image -o jsonpath="{.status.imageUploadURLs}" | jqПример вывода:
{ "external":"https://virtualization.example.com/upload/<SECRET_URL>", "inCluster":"http://10.222.165.239/upload" }Адрес
inClusterиспользуйте, если загружаете файл с одного из узлов кластера, аexternal— во всех остальных случаях. -
Загрузите файл по выбранному адресу. В примере сначала скачивается образ Cirros, а затем отправляется в кластер:
curl -L http://download.cirros-cloud.net/0.5.1/cirros-0.5.1-x86_64-disk.img -o cirros.img curl https://virtualization.example.com/upload/<SECRET_URL> --progress-bar -T cirros.img | catЗдесь
<SECRET_URL>— последняя часть адреса из предыдущего шага. -
Убедитесь, что образ перешёл в фазу
Ready:d8 k get cvi some-imageПример вывода:
NAME PHASE CDROM PROGRESS AGE some-image Ready false 100% 1m
Загруженный файл тоже можно сверить с контрольной суммой, для этого задайте блок checksum в источнике данных.
- Перейдите на вкладку «Система», далее в раздел «Виртуализация» → «Кластерные образы».
- Нажмите кнопку «Создать», затем в блоке «Источник» выберите «Загрузить».
- В поле «Имя образа» введите имя образа.
- В блоке «Загрузить файл» перетащите файл в выделенную область или нажмите «выберите на вашем компьютере».
- Выберите файл в открывшемся файловом менеджере.
- Нажмите кнопку «Создать».
- Дождитесь, когда образ перейдёт в состояние «Готов».
Классы виртуальных машин
Класс виртуальной машины (ВМ) задаёт то, что владелец проекта не настраивает сам, а именно модель виртуального процессора, допустимые сочетания ядер и памяти, а также узлы, на которых ВМ может работать. Описывает эти правила ресурс VirtualMachineClass, и через него вы управляете тем, как рабочие нагрузки проектов распределяются по узлам кластера.
При первичной установке модуль создаёт класс generic с моделью процессора Nehalem. Эта модель старая, но поддерживается любым современным процессором, поэтому ВМ такого класса запускаются на любом узле кластера и мигрируют между узлами без ограничений.
Класс generic соответствует процессору с наименьшим набором инструкций, поэтому для рабочих нагрузок в production он не подходит.
Когда все узлы добавлены в кластер и настроены, создайте хотя бы один класс с типом процессора Discovery. DP подберёт для него набор инструкций, доступный на всех узлах сразу, и виртуальные машины смогут использовать возможности процессоров полнее, сохранив способность мигрировать между узлами. Набор инструкций фиксируется в момент создания ресурса и не меняется, когда узлы добавляются или удаляются.
Как настроить такой класс, показано в разделе «Пример конфигурации vCPU Discovery».
Классы существуют на уровне кластера. Чтобы вывести их список, выполните команду:
d8 k get virtualmachineclassПример вывода:
NAME PHASE ISDEFAULT AGE
generic Ready 6d1h
Изменить у любого класса можно всё, кроме блока .spec.cpu, потому что модель процессора фиксируется при создании ресурса. Класс generic разрешено и менять, и удалять, но заново он не создастся, потому что модуль добавляет его только при первичной установке.
Владелец проекта указывает класс в параметре .spec.virtualMachineClassName виртуальной машины:
apiVersion: virtualization.deckhouse.io/v1alpha2
kind: VirtualMachine
metadata:
name: linux-vm
spec:
virtualMachineClassName: generic # Название ресурса VirtualMachineClass.
# ...VirtualMachineClass по умолчанию
Один из классов можно назначить классом по умолчанию. DP подставит его имя в параметр .spec.virtualMachineClassName, если владелец проекта не указал класс сам.
Класс по умолчанию помечается аннотацией virtualmachineclass.virtualization.deckhouse.io/is-default-class со значением true. Такой класс в кластере может быть только один, поэтому, чтобы назначить новый, сначала снимите аннотацию с текущего.
Не ставьте аннотацию на класс generic, потому что при обновлении модуля она может пропасть. Создайте собственный класс и назначьте по умолчанию его.
-
Посмотрите, какие классы есть в кластере:
d8 k get vmclassПример вывода, в котором класса по умолчанию нет:
NAME PHASE ISDEFAULT AGE generic Ready 1d host-passthrough-custom Ready 1d -
Назначьте класс по умолчанию:
d8 k annotate vmclass host-passthrough-custom virtualmachineclass.virtualization.deckhouse.io/is-default-class=true -
Убедитесь, что аннотация проставлена:
d8 k get vmclassПример вывода:
NAME PHASE ISDEFAULT AGE generic Ready 1d host-passthrough-custom Ready true 1d
Теперь виртуальные машины, созданные без указания класса, получат класс host-passthrough-custom.
Настройки VirtualMachineClass
Класс состоит из трёх блоков, каждый из которых отвечает за свою группу настроек:
- В командной строке
- В веб-интерфейсе
Опишите класс в ресурсе VirtualMachineClass:
apiVersion: virtualization.deckhouse.io/v1alpha2
kind: VirtualMachineClass
metadata:
name: <VMCLASS_NAME>
# Аннотация назначает класс классом по умолчанию, её можно не указывать.
# annotations:
# virtualmachineclass.virtualization.deckhouse.io/is-default-class: "true"
spec:
# Параметры виртуального процессора. Блок обязателен и после создания ресурса не меняется.
cpu: ...
# Правила размещения виртуальных машин по узлам. Блок необязателен.
# Изменения применяются ко всем машинам этого класса.
nodeSelector: ...
# Политика подбора ресурсов для виртуальных машин. Блок необязателен.
# Изменения применяются ко всем машинам этого класса.
sizingPolicies: ...Здесь <VMCLASS_NAME> — имя создаваемого класса.
- Перейдите на вкладку «Система», далее в раздел «Виртуализация» → «Классы ВМ».
- Нажмите кнопку «Создать».
- В открывшейся форме в поле «Имя» введите имя класса ВМ.
Дальше блоки разобраны по отдельности.
Виртуальный процессор
Блок .spec.cpu определяет, какой процессор увидит гостевая ОС. От него зависит и то, между какими узлами ВМ сможет мигрировать.
Блок .spec.cpu после создания ресурса изменить нельзя. Чтобы задать другой процессор, создайте новый класс.
Ниже приведены примеры для каждого типа процессора.
-
Набор процессорных инструкций, обязательных для ВМ. Задаётся типом
Features:spec: cpu: features: - vmx type: FeaturesКак настроить vCPU в веб-интерфейсе в форме создания классов ВМ:
- В блоке «Настройки ЦП» в поле «Тип» выберите
Features. - В поле «Обязательный набор поддерживаемых инструкций» выберите нужные инструкции.
- Нажмите кнопку «Создать».
- В блоке «Настройки ЦП» в поле «Тип» выберите
-
Универсальный процессор для заданного набора узлов. Задаётся типом
Discovery:spec: cpu: discovery: nodeSelector: matchExpressions: - key: node-role.kubernetes.io/control-plane operator: DoesNotExist type: DiscoveryКак выполнить операцию в веб-интерфейсе в форме создания классов ВМ:
- В блоке «Настройки ЦП» в поле «Тип» выберите
Discovery. - Нажмите кнопку «Добавить» в блоке «Условия для создания универсального процессора» → «Лейблы и выражения».
- Задайте «Ключ», «Оператор» и «Значение», они соответствуют параметру
.spec.cpu.discovery.nodeSelector. - Нажмите клавишу «Enter», чтобы подтвердить параметры ключа.
- Нажмите кнопку «Создать».
- В блоке «Настройки ЦП» в поле «Тип» выберите
-
Процессор, близкий к процессору узла. Задаётся типом
Host. Гостевая ОС получает почти полный набор инструкций узла, поэтому производительность выше, чем у фиксированной модели. ВМ такого класса мигрирует только между узлами со схожими процессорами. Например, между узлами с процессорами Intel и AMD миграция невозможна, как и между процессорами разных поколений, если их наборы инструкций различаются.spec: cpu: type: HostКак выполнить операцию в веб-интерфейсе в форме создания классов ВМ:
- В блоке «Настройки ЦП» в поле «Тип» выберите
Host. - Нажмите кнопку «Создать».
- В блоке «Настройки ЦП» в поле «Тип» выберите
-
Процессор узла без изменений. Задаётся типом
HostPassthrough. ВМ такого класса мигрирует только на узел, процессор которого в точности совпадает с процессором исходного узла.spec: cpu: type: HostPassthroughКак выполнить операцию в веб-интерфейсе в форме создания классов ВМ:
- В блоке «Настройки ЦП» в поле «Тип» выберите
HostPassthrough. - Нажмите кнопку «Создать».
- В блоке «Настройки ЦП» в поле «Тип» выберите
-
Конкретная модель процессора с заранее известным набором инструкций. Задаётся типом
Model. Сначала посмотрите, какие модели поддерживает нужный узел:d8 k get nodes <NODE_NAME> -o json | jq '.metadata.labels | to_entries[] | select(.key | test("cpu-model.node.virtualization.deckhouse.io")) | .key | split("/")[1]' -rЗдесь
<NODE_NAME>— имя узла кластера.Пример вывода:
Broadwell-noTSX Broadwell-noTSX-IBRS Haswell-noTSX Haswell-noTSX-IBRS IvyBridge IvyBridge-IBRS Nehalem Nehalem-IBRS Penryn SandyBridge SandyBridge-IBRS Skylake-Client-noTSX-IBRS Westmere Westmere-IBRSЗатем укажите выбранную модель в спецификации класса:
spec: cpu: model: IvyBridge type: ModelКак выполнить операцию в веб-интерфейсе в форме создания классов ВМ:
- В блоке «Настройки ЦП» в поле «Тип» выберите
Model. - В поле «Модель» выберите модель процессора.
- Нажмите кнопку «Создать».
- В блоке «Настройки ЦП» в поле «Тип» выберите
Пример конфигурации vCPU Discovery
Ниже показано, как подобрать типы процессора в кластере с разнородными узлами.

Ниже разобран кластер из четырёх узлов. Два узла с лейблом group=blue оснащены процессором «CPU X» с тремя наборами инструкций, два других с лейблом group=green — более новым процессором «CPU Y» с четырьмя наборами.
Набор инструкций процессора — это все команды, которые он умеет выполнять, от сложения до работы с памятью. От набора зависит, какие программы запустятся и насколько быстро, а у разных поколений процессоров наборы различаются.
Такому кластеру подойдут три класса:
universal— ВМ запускаются на любом узле и мигрируют между всеми четырьмя. DP возьмёт набор инструкций, общий для обоих процессоров, поэтому совместимость максимальная, а часть возможностей «CPU Y» останется неиспользованной;cpuX— ВМ запускаются только на узлах с «CPU X» и мигрируют между ними, используя все инструкции этого процессора;cpuY— то же самое для узлов с «CPU Y».
Классы для такого кластера выглядят так:
---
apiVersion: virtualization.deckhouse.io/v1alpha2
kind: VirtualMachineClass
metadata:
name: universal
spec:
cpu:
# Пустой discovery означает, что учитываются все узлы кластера.
discovery: {}
type: Discovery
---
apiVersion: virtualization.deckhouse.io/v1alpha2
kind: VirtualMachineClass
metadata:
name: cpuX
spec:
cpu:
discovery:
nodeSelector:
matchExpressions:
- key: group
operator: In
values: ["blue"]
type: Discovery
---
apiVersion: virtualization.deckhouse.io/v1alpha2
kind: VirtualMachineClass
metadata:
name: cpuY
spec:
cpu:
discovery:
nodeSelector:
matchExpressions:
- key: group
operator: In
values: ["green"]
type: DiscoveryРазмещение по узлам
Необязательный блок .spec.nodeSelector ограничивает набор узлов, на которых работают виртуальные машины (ВМ) этого класса. Узлы отбираются по лейблам:
spec:
nodeSelector:
matchExpressions:
- key: node.deckhouse.io/group
operator: In
values:
- greenИзменение блока .spec.nodeSelector затрагивает все виртуальные машины класса сразу. Те из них, что работают на узлах, переставших подходить под новые условия, придётся переместить:
- в коммерческих редакциях DP мигрирует такие ВМ на подходящие узлы;
- в DP Open ВМ перезапускаются, а момент перезапуска зависит от параметра
.spec.disruptions.restartApprovalModeвиртуальной машины, который по умолчанию равенManualи требует подтверждения владельца проекта.
Как выполнить операцию в веб-интерфейсе в форме создания классов ВМ:
- Нажмите кнопку «Добавить» в блоке «Условия планирования ВМ на узлах» → «Лейблы и выражения».
- Задайте «Ключ», «Оператор» и «Значение», они соответствуют параметру
.spec.nodeSelector. - Нажмите клавишу «Enter», чтобы подтвердить параметры ключа.
- Нажмите кнопку «Создать».
Политика сайзинга
Блок .spec.sizingPolicies задаёт, какие сочетания ядер, доли ядра и памяти разрешены виртуальным машинам (ВМ) этого класса.
Изменения в блоке .spec.sizingPolicies затрагивают уже существующие виртуальные машины.
У виртуальных машин, которые перестали соответствовать новым требованиям, условие SizingPolicyMatched в блоке .status.conditions принимает статус False.
Задавая политики, учитывайте топологию CPU виртуальных машин.
Политика состоит из списка правил, каждое из которых действует на свой диапазон ядер. Диапазон задаётся обязательным блоком cores, и диапазоны разных правил пересекаться не могут, иначе DP отклонит такой класс.
Правильная структура, где диапазоны идут друг за другом без пересечений:
- cores:
min: 1
max: 4
# ...
- cores:
min: 5 # Начало следующего диапазона на единицу больше предыдущего max.
max: 8Недопустимая структура, где значение 4 попадает сразу в два диапазона:
- cores:
min: 1
max: 4
# ...
- cores:
min: 4
max: 8Оставлять разрывы между диапазонами DP не запрещает, но виртуальная машина, число ядер которой не попало ни в один диапазон, останется без политики. Поэтому начинайте очередной диапазон со значения, следующего за max предыдущего.
Внутри диапазона задаются требования к памяти и к доле ядра:
memory— минимум и максимум памяти. Указывается либо на весь диапазон, либо на одно ядро через вложенный блокmemory.perCore.coreFractions— список разрешённых долей ядра, например[25, 50, 100]для 25%, 50% и 100%. Если владелец проекта задал параметрcoreFractionвиртуальной машины явно, значение должно быть из этого списка.defaultCoreFraction— доля ядра, которую получит виртуальная машина, еслиcoreFractionв ней не задан. Значение должно входить в списокcoreFractions. Когда параметр не указан, применяется 100%.
Правило, в котором нет ни memory, ни coreFractions, ничего не ограничивает, поэтому задавайте хотя бы одно из них.
В коммерческих редакциях DP параметру defaultCoreFraction можно задать значение Auto. Тогда долю ядра для ВМ без явного coreFraction подбирает вертикальное автомасштабирование. Auto — это режим, а не доля ядра, поэтому в списке coreFractions его быть не должно.
spec:
sizingPolicies:
- cores:
min: 1
max: 8
coreFractions: [10, 25, 50, 100]
defaultCoreFraction: AutoЗначение Auto принимается, только когда доступны обе возможности:
- вертикальное автомасштабирование виртуальных машин, которое включается само в коммерческих редакциях DP при включённом модуле
vertical-pod-autoscaler; - изменение числа ядер и объёма памяти без перезапуска, которое включается функцией
HotplugCPUAndMemoryWithInPlaceResizeв параметре.spec.settings.featureGatesмодуля.
Если хотя бы одна из них недоступна, DP отклонит создание такого класса.
Новое значение по умолчанию применяется только к виртуальным машинам, созданным после его изменения. У существующей машины прежнее значение уже записано в её спецификации и остаётся там, пока владелец проекта не задаст другое.
Примеры зависимости объёма памяти от числа ядер:
-
Параметр
memoryзадаёт границы, одинаковые для всего диапазона ядер:- cores: min: 1 max: 4 memory: min: 2Gi max: 8GiВиртуальная машина с любым числом ядер от 1 до 4 получает от 2 до 8 ГиБ памяти, и число ядер на эти границы не влияет.
-
Параметр
memory.perCoreзадаёт границы в расчёте на одно ядро, а итоговые границы получаются умножением на число ядер:- cores: min: 1 max: 4 memory: perCore: min: 1Gi max: 2GiДля такой политики допустимый объём памяти растёт вместе с числом ядер:
- 1 ядро — от 1 до 2 ГиБ;
- 2 ядра — от 2 до 4 ГиБ;
- 3 ядра — от 3 до 6 ГиБ;
- 4 ядра — от 4 до 8 ГиБ.
-
Параметр
memory.stepограничивает набор допустимых значений памяти шагом сетки, чтобы владелец проекта не выбирал произвольные объёмы.Вместе с
memory.minиmemory.maxшаг отсчитывается от минимума:- cores: min: 1 max: 4 memory: min: 2Gi max: 8Gi step: 1GiДопустимы только значения 2, 3, 4, 5, 6, 7 и 8 ГиБ, а 2,5 или 7,5 ГиБ задать нельзя.
Вместе с
memory.perCoreшаг отсчитывается от памяти на одно ядро, и уже полученное значение умножается на число ядер:- cores: min: 1 max: 4 memory: perCore: min: 1Gi max: 2Gi step: 512MiНа одно ядро допустимы 1, 1,5 и 2 ГиБ, поэтому итоговый объём зависит от числа ядер:
- 1 ядро — 1, 1,5 или 2 ГиБ;
- 2 ядра — 2, 3 или 4 ГиБ;
- 3 ядра — 3, 4,5 или 6 ГиБ;
- 4 ядра — 4, 6 или 8 ГиБ.
Пример политики, которая покрывает диапазоны от 1 до 248 ядер:
spec:
sizingPolicies:
# Для 1-4 ядер доступно от 1 до 8 ГиБ памяти с шагом 512 МиБ,
# то есть 1 ГиБ, 1,5 ГиБ, 2 ГиБ, 2,5 ГиБ и так далее.
# Доступны все доли ядра.
- cores:
min: 1
max: 4
memory:
min: 1Gi
max: 8Gi
step: 512Mi
coreFractions: [5, 10, 20, 50, 100]
defaultCoreFraction: 50 # Доля ядра по умолчанию для диапазона 1-4 ядра.
# Для 5-8 ядер доступно от 5 до 16 ГиБ памяти с шагом 1 ГиБ,
# то есть 5 ГиБ, 6 ГиБ, 7 ГиБ и так далее.
# Доли ядра ограничены тремя значениями.
- cores:
min: 5
max: 8
memory:
min: 5Gi
max: 16Gi
step: 1Gi
coreFractions: [20, 50, 100]
defaultCoreFraction: 100 # Доля ядра по умолчанию для диапазона 5-8 ядер.
# Для 9-16 ядер доступно от 9 до 32 ГиБ памяти с шагом 1 ГиБ.
# Доли ядра ограничены двумя значениями.
- cores:
min: 9
max: 16
memory:
min: 9Gi
max: 32Gi
step: 1Gi
coreFractions: [50, 100]
# Для 17-248 ядер доступно от 1 до 2 ГиБ памяти на каждое ядро.
# Доля ядра только 100%.
- cores:
min: 17
max: 248
memory:
perCore:
min: 1Gi
max: 2Gi
coreFractions: [100]Как настроить политики сайзинга в веб-интерфейсе в форме создания классов ВМ:
- Нажмите кнопку «Добавить» в блоке «Правила выделения ресурсов для виртуальных машин».
- В блоке «ЦП» в поле «Мин» укажите
1, а в поле «Макс» —4. - В блоке «ЦП» в поле «Разрешить задать доли ядра» выберите по порядку значения
5%,10%,20%,50%,100%. - В блоке «Память» установите переключатель в положение «Объём на 1 ядро».
- В блоке «Память» в поле «Мин» укажите
1, а в поле «Макс» —8. - В блоке «Память» в поле «Шаг дискретизации» укажите
1. - При необходимости добавьте другие диапазоны кнопкой «Добавить».
- Нажмите кнопку «Создать».
Управление переподпиской на CPU
Переподписка позволяет выдать виртуальным машинам (ВМ) узла больше виртуальных ядер, чем на нём есть физических. Это оправданно, потому что ВМ редко нагружают процессор одновременно и на полную мощность.
Степенью переподписки управляет параметр coreFraction виртуальной машины, а допустимые его значения вы задаёте в политике сайзинга класса. Параметр определяет долю мощности ядра, которая ВМ гарантирована. Например, при coreFraction: 20% ВМ всегда получит пятую часть ядра, а при наличии свободных ресурсов на узле сможет занять и всё ядро целиком.
Если в классе список coreFractions не задан или содержит несколько значений, степень переподписки выбирает владелец проекта, указывая coreFraction при создании ВМ.
Размещая ВМ на узле, DP складывает гарантированные доли всех ВМ узла по формуле Σ(cores × coreFraction / 100). Если сумма превысит число физических ядер, на этом узле ВМ не запустится.
Ниже разобран узел с 4 физическими ядрами и 5 ВМ, у каждой по 2 ядра и coreFraction: 20%. Гарантированная нагрузка составит 5 × 2 × 0,2 = 2 ядра при 10 виртуальных ядрах на 4 физических, то есть переподписка 2,5 к 1. Все пять ВМ разместятся на узле, потому что 2 ядра меньше доступных 4.
Переподписка, заданная жёстко
Список из одного значения не оставляет владельцу проекта выбора, и степень переподписки для всех ВМ класса определяете вы:
apiVersion: virtualization.deckhouse.io/v1alpha2
kind: VirtualMachineClass
metadata:
name: oversubscribed
spec:
sizingPolicies:
- cores:
min: 1
max: 8
memory:
perCore:
min: 1Gi
max: 8Gi
coreFractions: [20] # Единственное разрешённое значение.
defaultCoreFraction: 20Все ВМ этого класса получают по 20% ядра, что даёт переподписку 5 к 1.
Переподписка на выбор владельца проекта
Список из нескольких значений оставляет выбор за владельцем проекта:
apiVersion: virtualization.deckhouse.io/v1alpha2
kind: VirtualMachineClass
metadata:
name: standard
spec:
sizingPolicies:
- cores:
min: 1
max: 4
memory:
perCore:
min: 1Gi
max: 8Gi
coreFractions: [5, 10, 20, 50, 100]
defaultCoreFraction: 20Владелец проекта выбирает coreFraction из списка, а если не выбрал, ВМ получает 20%.
Обслуживание узлов и отказоустойчивость ВМ
В этом разделе собраны средства, которые помогают виртуальным машинам (ВМ) пережить обслуживание и отказ узла. Часть из них работает сама, часть требует вашего вмешательства.
Миграция ВМ и обслуживание узлов
Живая миграция перемещает работающую виртуальную машину с одного узла на другой, не выключая её. Она нужна в трёх ситуациях:
- при балансировке нагрузки, чтобы равномерно распределить ВМ по узлам;
- при выводе узла на обслуживание или обновление, чтобы освободить его от ВМ;
- при обновлении прошивки виртуальных машин, которое иначе потребовало бы их перезапуска.
Живая миграция ограничена по скорости и по числу одновременных перемещений:
- узел готовит и передаёт память только одной ВМ за раз, и одновременно принимает только одну входящую миграцию;
- отсюда и предел для кластера, где одновременных миграций не больше, чем узлов, на которых разрешён запуск виртуальных машин;
- скорость передачи одной миграции ограничена 640 МиБ/с, это примерно 5 Гбит/с.
Перемещение выбранной машины на другой узел
Ниже показано, как переместить выбранную ВМ на другой узел.
- В командной строке
- В веб-интерфейсе
-
Посмотрите, на каком узле ВМ работает сейчас:
d8 k get vmПример вывода:
NAME PHASE UPTIME NODE IPADDRESS AGE linux-vm Running 79m virtlab-pt-1 10.66.10.14 79mВМ запущена на узле
virtlab-pt-1. -
Создайте ресурс VirtualMachineOperation с типом
Evict. DP подберёт для ВМ новый узел, соблюдая требования к её размещению:d8 k create -f - <<EOF apiVersion: virtualization.deckhouse.io/v1alpha2 kind: VirtualMachineOperation metadata: generateName: evict-linux-vm- spec: # Имя виртуальной машины. virtualMachineName: linux-vm # Операция для миграции. type: Evict EOF -
Сразу после создания ресурса проследите за ходом миграции:
d8 k get vm -wПример вывода:
NAME PHASE UPTIME NODE IPADDRESS AGE linux-vm Running 79m virtlab-pt-1 10.66.10.14 79m linux-vm Migrating 79m virtlab-pt-1 10.66.10.14 79m linux-vm Migrating 79m virtlab-pt-1 10.66.10.14 79m linux-vm Running 79m virtlab-pt-2 10.66.10.14 79mIP-адрес ВМ при переезде сохраняется, меняется только узел в колонке
NODE. -
Чтобы прервать миграцию, удалите созданный ресурс, пока он находится в фазе
PendingилиInProgress.
- Перейдите на вкладку «Проекты» и выберите нужный проект.
- Перейдите в раздел «Виртуализация» → «Виртуальные машины».
- Из списка выберите нужную виртуальную машину и нажмите кнопку с многоточием.
- В открывшемся меню выберите «Мигрировать».
- В окне «Миграция виртуальной машины» выберите «Мигрировать на произвольный узел» либо «Мигрировать на выбранный узел» и укажите узел в поле «Доступные для миграции узлы».
- При необходимости включите «Мигрировать диски», чтобы перенести вместе с ВМ её диски, и «Принудительно (замедлить CPU гостя)», чтобы миграция гарантированно завершилась на активно работающей ВМ.
- Нажмите кнопку «Мигрировать» либо откажитесь от операции кнопкой «Отмена».
Выделенная сеть для миграции
По умолчанию трафик живой миграции идёт по основной сети узла и конкурирует за полосу пропускания с рабочими нагрузками. Его можно направить через выделенный VLAN, предоставляемый модулем sdn.
Для этого нужно, чтобы модуль sdn был включён, а ресурс SystemNetwork создан и находился в состоянии Ready.
Чтобы направить трафик в выделенную сеть, задайте блок .spec.settings.liveMigration.network в ModuleConfig virtualization. Укажите в нём type: SystemNetwork и имя подготовленной сети в поле systemNetwork.name. После этого все миграции в кластере пойдут по VLAN указанной сети.
spec:
settings:
liveMigration:
network:
type: SystemNetwork
systemNetwork:
name: migration-netЧтобы вернуть трафик миграции на основную сеть узла, удалите блок network (по умолчанию, если он не задан, используется сеть узла).
Как создать системную сеть в веб-интерфейсе:
- Перейдите на вкладку «Система», далее в раздел «Сеть» → «SDN» → «Системные сети».
- Нажмите кнопку «Создать».
- В открывшемся окне «Создать ресурс» в поле «Имя» введите имя сети.
- На вкладке «Конфигурация» в поле «Type» выберите тип (
VLAN,AccessилиSRIOVVirtualFunction), в поле «Underlay-сеть» — underlay-сеть, в блоке «VLAN» — идентификатор VLAN. При необходимости задайте параметры блока «IPAM». - Нажмите кнопку «Применить».
- Созданные сети отображаются в списке с колонками «Статус», «Тип», «Underlay-сеть», «VLAN ID» и «IP-пул».
Предварительно в разделах «Сеть» → «SDN» → «Network-классы» и «Underlay-сети» создаются Network-класс (диапазоны VLAN ID и родительские сетевые интерфейсы узлов) и underlay-сеть (участвующие сетевые интерфейсы узлов и режим «Dedicated» или «Shared»).
Проверка ВМ перед обслуживанием узла
ВМ, которые не смогут мигрировать с узла, лучше найти перед началом обслуживания, до того, как узел будет выведен из планирования:
d8 k get vm -o wide | grep <NODE_NAME>ВМ со значением False в колонке MIGRATABLE при выводе узла придётся остановить. Живая миграция для них невозможна, и эвакуация завершится ошибкой.
Одного значения в колонке недостаточно. ВМ со значением True и причиной VirtualMachineWaitingForMigrationTarget тоже никуда не поедет, пока подходящий узел не вернётся в планирование, поэтому перед обслуживанием посмотрите причины всех ВМ на узле:
d8 k get vm -o json | jq -r '.items[] | [.metadata.name, (.status.conditions[] | select(.type=="Migratable") | .reason)] | @tsv'Режим обслуживания
Работы на узле, где запущены виртуальные машины, могут нарушить их работу. Чтобы этого не произошло, переведите узел в режим обслуживания, и DP перенесёт ВМ на другие узлы.
- В командной строке
- В веб-интерфейсе
Освободить узел от всех ресурсов, включая системные, можно командой:
d8 k drain <NODE_NAME> --ignore-daemonsets --delete-emptydir-dataЧтобы вытеснить с узла только виртуальные машины, добавьте отбор по лейблу:
d8 k drain <NODE_NAME> --pod-selector vm.kubevirt.internal.virtualization.deckhouse.io/name --delete-emptydir-dataЗдесь <NODE_NAME> — имя узла, на котором предстоят работы.
После выполнения команды узел переходит в режим обслуживания, и запускать на нём виртуальные машины нельзя.
Чтобы вернуть узел в работу, остановите команду drain сочетанием клавиш Ctrl+C, а затем выполните:
d8 k uncordon <NODE_NAME>
- Перейдите на вкладку «Система», далее в раздел «Узлы».
- Из списка выберите нужный узел, нажмите кнопку с многоточием и в открывшемся меню выберите «Cordon + Drain».
- Чтобы вывести узел из режима обслуживания, в том же меню выберите «Uncordon».
Перезапуск виртуальных машин при обслуживании узла
Виртуальную машину не всегда можно перенести на другой узел живой миграцией. Она может быть закреплена за узлом правилами размещения или использовать проброшенное с узла устройство. Причину показывает условие Migratable в статусе ВМ. Такая ВМ продолжает работать и удерживает узел, поэтому обслуживание не завершится, пока её не перезапустят.
Обнаружив такую ВМ при переводе узла в режим обслуживания, DP добавляет на узел аннотацию virtualization.deckhouse.io/virtualmachines-restart-required. Чтобы разрешить перезапуск, добавьте на узел ответную аннотацию:
d8 k annotate node <NODE_NAME> virtualization.deckhouse.io/virtualmachines-restart-approved=""Здесь <NODE_NAME> — имя узла, который переводится в режим обслуживания.
Перезапускаются только те ВМ, которые нельзя перенести живой миграцией. Гостевая ОС завершает работу штатно, после чего ВМ запускается в соответствии с политикой запуска .spec.runPolicy. Для каждого перезапуска создаётся ресурс VirtualMachineOperation с именем вида node-maintenance-restart-*. Перезапуск прерывает работу приложений внутри ВМ, поэтому согласуйте его с владельцами проектов.
Разрешение не распространяется на ВМ, которые можно перенести живой миграцией (в том числе на те, для которых сейчас нет подходящего узла). Такие ВМ будут перенесены живой миграцией, как только подходящий узел появится.
Разрешение можно дать заранее, при планировании работ. Пока узел не переводят в режим обслуживания, аннотация ни на что не влияет. Обе аннотации DP снимает после освобождения узла, поэтому одно разрешение действует только на одно обслуживание одного узла.
DP реагирует на вытеснение ВМ с узла. Если вытеснение прекратилось по тайм-ауту (параметр .spec.nodeDrainTimeoutSecond ресурса NodeGroup, по умолчанию 10 минут), повторное вытеснение не выполняется. Разрешение, данное после этого, перезапуска не вызовет, и узел придётся освобождать вручную.
Перезапуск освобождает узел, но не гарантирует, что ВМ сразу запустится на другом. Ограничение, из-за которого её нельзя перенести живой миграцией, чаще всего мешает и запуску на другом узле. В этом случае ВМ остаётся в фазе Pending, а в условии Running указывается причина, полученная от планировщика. Обслуживание при этом можно продолжать. ВМ запустится, как только появится подходящий узел, в том числе после возвращения узла в работу командой d8 k uncordon.
Владелец ВМ видит то же самое в условии EvictionRequired в статусе ВМ. Пока узел только готовят к обслуживанию, условие носит предупреждающий характер. После начала вытеснения условие показывает, что произойдёт с ВМ, а именно перенос живой миграцией, перезапуск силами DP или ожидание, если перезапуск не разрешён.
Выключение и перезагрузка узла с виртуальными машинами
Работающие виртуальные машины откладывают выключение и перезагрузку своего узла. DP сам помечает их рабочие нагрузки лейблом pod.deckhouse.io/inhibit-node-shutdown и по нему задерживает выключение узла. Механизм доступен в коммерческих редакциях DP, описан в документации модуля node-manager и включения не требует.
Если на узле запрошено выключение или перезагрузка, а на нём ещё работают виртуальные машины:
- выключение узла откладывается на срок до трёх суток;
- в консоль узла периодически выводится сообщение о том, какие рабочие нагрузки удерживают выключение.
На узлах, где работает механизм задержки, условие GracefulShutdownPostpone присутствует постоянно. Пока механизм ждёт сигнала на выключение или удерживает узел, условие имеет статус True, а когда удерживать больше нечего, переходит в False. Что именно происходит с узлом, показывает причина в поле reason этого условия:
WaitingForShutdownSignal— механизм активен и ожидает запроса на выключение узла;PodsWithLabelAreRunningOnNode— выключение узла запрошено и отложено, поскольку на узле ещё работают виртуальные машины;NoRunningPodsWithLabel— виртуальных машин на узле не осталось, удерживать выключение больше нечем, и оно продолжается.
Чтобы узнать причину, выполните следующую команду:
d8 k get node <NODE_NAME> -o jsonpath='{range .status.conditions[?(@.type=="GracefulShutdownPostpone")]}{.reason}{"\n"}{end}'Задержка выключения не переносит виртуальные машины на другие узлы, она лишь не даёт узлу выключиться. Поэтому перед работами, требующими выключения или перезагрузки узла, освободите его от виртуальных машин:
-
если ВМ можно мигрировать, то есть условие
Migratableимеет статусTrue, переведите узел в режим обслуживания командойd8 k drain; -
если ВМ мигрировать нельзя, то есть условие
Migratableимеет статусFalseиз-за локальных дисков или проброшенных с узла устройств, остановите её командойd8 v stop <VM_NAME>, а после завершения работ запустите командойd8 v start <VM_NAME>.Остановка доступна только для политик запуска
ManualиAlwaysOnUnlessStoppedManually. Проверьте политику ВМ:d8 k -n <NAMESPACE> get vm <VM_NAME> -o jsonpath='{.spec.runPolicy}'При политике
AlwaysOnкоманда остановки будет отклонена с причинойNotApplicableForVirtualMachineRunPolicy. В этом случае сначала измените политику, а после завершения работ верните прежнее значение:d8 k -n <NAMESPACE> patch vm <VM_NAME> --type merge -p '{"spec":{"runPolicy":"AlwaysOnUnlessStoppedManually"}}'Здесь
<NAMESPACE>— неймспейс проекта, а<VM_NAME>— имя виртуальной машины.
Вместо ручной остановки можно разрешить DP перезапустить такие ВМ на время обслуживания узла. Менять политику запуска при этом не требуется.
Если ничего из этого не сделать, узел не выключится. О такой ситуации сообщают два алерта. Алерт D8VirtualizationVirtualMachineHoldsNodeMaintenance перечисляет ВМ, которые удерживают узел и ждут решения администратора. Алерт D8VirtualizationNodeEvacuationStuck срабатывает, если ВМ вытеснили с узла, но в течение 15 минут она не мигрировала и не перезапустилась.
Перебалансировка ВМ
Со временем распределение виртуальных машин по узлам перестаёт быть равномерным. Вернуть баланс умеет модуль descheduler, который переносит ВМ живой миграцией, не прерывая их работу. Включите этот модуль, и распределение будет поддерживаться без вашего участия.
Перебалансировка решает две задачи:
- выравнивает нагрузку. DP следит за тем, сколько процессорных ресурсов зарезервировано на каждом узле, и, когда узел резервирует больше 80%, переносит часть ВМ на узлы, где зарезервировано меньше 50%;
- восстанавливает корректное размещение. DP проверяет, отвечает ли текущий узел требованиям ВМ и правилам взаимного расположения ВМ. Например, если правила запрещают держать определённые ВМ на одном узле, лишние будут перенесены.
Настройки перебалансировки DP задаёт сам: при включённом модуле descheduler он создаёт ресурс Descheduler с именем virtualization и отбирает в нём только те машины, которые можно перенести живой миграцией.
- В командной строке
- В веб-интерфейсе
Посмотреть настройки перебалансировки можно командой:
d8 k get descheduler virtualization -o yaml- Перейдите на вкладку «Система», далее в раздел «Конфигурация» → «Deschedulers».
- Нажмите кнопку «Создать».
- В открывшемся окне «Создать ресурс» в поле «Имя» введите имя ресурса.
- На вкладке «Конфигурация» в блоке «Стратегии» включите нужные. Стратегия «Низкая загрузка узлов (балансировка)» переносит ВМ с перегруженных узлов, а «Нарушения Inter-Pod Anti-Affinity» и «Нарушения Node Affinity» восстанавливают корректное размещение.
- Нажмите кнопку «Применить».
Созданные ресурсы и включённые в них стратегии отображаются в списке раздела.
Перебалансировка касается только тех ВМ, которые могут уехать с узла живой миграцией. ВМ, которую мигрировать нельзя, например с проброшенным устройством, перебалансировка не трогает, потому что увезти её с узла можно только перезапуском. Такая ВМ перезапускается лишь при обслуживании узла и только с разрешения администратора.
ColdStandby
Механизм ColdStandby возвращает виртуальную машину в работу после отказа узла, на котором она была запущена.
Чтобы механизм работал, выполните два требования:
- политика запуска виртуальной машины
.spec.runPolicyдолжна иметь значениеAlwaysOnUnlessStoppedManuallyилиAlwaysOn; - на узлах, где запущены виртуальные машины, должен быть включён механизм Fencing.
Без Fencing механизм не работает. Недоступная ВМ в этом случае не переезжает, а остаётся на отказавшем узле и возобновляет работу вместе с ним.
Порядок восстановления на примере кластера из трёх узлов master, workerA и workerB, где Fencing включён на обоих worker-узлах, а ВМ linux-vm запущена на workerA:
- Узел
workerAотказывает, например из-за потери питания или сети. - Контроллер проверяет доступность узлов и обнаруживает, что
workerAне отвечает. - Контроллер удаляет
workerAиз кластера. - ВМ
linux-vmзапускается на другом подходящем узле, в примере этоworkerB.

USB-устройства
Проброс USB-устройств доступен в коммерческих редакциях DP.
За проброс USB-устройств к виртуальным машинам (ВМ) отвечает системный компонент virtualization-dra, которому на узле нужны три модуля ядра:
usbip_core;usbip_host;vhci_hcd.
DP загружает их на узлах сам. Узел, где доступны все три модуля, получает лейбл virtualization.deckhouse.io/usbip=true, и только на таких узлах запускается компонент virtualization-dra. Если модули ядра перестают быть доступны, лейбл снимается, а компонент с узла удаляется.
Чтобы посмотреть, какие узлы готовы к пробросу USB-устройств, выполните команду:
d8 k get nodes -l virtualization.deckhouse.io/usbip=trueПример вывода:
NAME STATUS ROLES AGE VERSION
node-1 Ready worker 10d v1.34.1
Чтобы убедиться, что компонент действительно работает на этих узлах, выполните команду:
d8 k -n d8-virtualization get pods -l app=virtualization-dra -o wideУзел, которого нет в выводе, загрузить модули ядра не смог, и USB-устройства этого узла не обнаруживаются. Установите модули ядра самостоятельно из пакета вашей операционной системы или соберите их для используемого ядра. DP обнаружит их сам и в течение нескольких минут назначит узлу лейбл.
Путь USB-устройства от узла до машины
Путь USB-устройства от узла до виртуальной машины состоит из четырёх шагов:
-
DRA-драйвер обнаруживает USB-устройства на узлах и публикует сведения о них в API Kubernetes как ResourceSlice. DP создаёт ресурсы NodeUSBDevice по этим данным.
-
Администратор назначает неймспейс ресурсу NodeUSBDevice, задав параметр
.spec.assignedNamespace. Это делает устройство доступным в этом неймспейсе. -
После назначения неймспейса DP создаёт в нём ресурс USBDevice.
-
Владелец проекта подключает устройство USBDevice к виртуальной машине, добавив его в параметр
.spec.usbDevicesресурса VirtualMachine.
Обнаруженные устройства (NodeUSBDevice)
Ресурс NodeUSBDevice описывает физическое USB-устройство, обнаруженное на узле. Ресурс существует на уровне кластера, поэтому все обнаруженные устройства видны вам в одном списке:
d8 k get nodeusbdeviceПример вывода:
NAME NODE READY ASSIGNED ATTACHED NAMESPACE AGE
usb-flash-drive node-1 True False False 10m
logitech-webcam node-2 True True True my-project 15m
Готовность устройства и его состояние отражают условия в блоке .status.conditions. Условия Ready и Attached совпадают с условиями USBDevice, а условие Assigned показывает, назначен ли устройству неймспейс:
Available— неймспейс не назначен;InProgress— неймспейс назначен, и ресурс USBDevice создаётся;Assigned— ресурс USBDevice создан, устройство доступно в неймспейсе.
Назначение неймспейса USB-устройству
Пока устройству не назначен неймспейс, владелец проекта его не видит. Чтобы сделать устройство доступным в проекте, выполните следующие шаги.
-
Подключите USB-устройство к узлу, готовому к пробросу, и дождитесь появления ресурса NodeUSBDevice.
-
Назначьте неймспейс параметром
.spec.assignedNamespace:d8 k apply -f - <<EOF apiVersion: virtualization.deckhouse.io/v1alpha2 kind: NodeUSBDevice metadata: name: logitech-webcam spec: assignedNamespace: my-project EOF -
Убедитесь, что в неймспейсе появился ресурс USBDevice:
d8 k get usbdevice -n my-project
После этого владелец проекта подключает устройство к виртуальной машине.
Просмотр информации об USB-устройстве
Полные сведения об устройстве и его текущее состояние доступны в статусе ресурса.
Когда устройство физически отключают от узла, условие Attached принимает значение False, а условие Ready получает причину NotFound. То же самое отражается в статусе ресурса USBDevice в проектном неймспейсе.
- В командной строке
- В веб-интерфейсе
Идентификаторы устройства, его расположение и текущие условия хранятся в статусе ресурса:
d8 k get nodeusbdevice <DEVICE_NAME> -o yamlЗдесь <DEVICE_NAME> — имя ресурса NodeUSBDevice.
Чтобы получить только атрибуты устройства, обратитесь к нужным полям напрямую:
d8 k get nodeusbdevice <DEVICE_NAME> \
-o jsonpath='{.status.attributes.manufacturer}{" "}{.status.attributes.product}{" ("}{.status.attributes.vendorID}{":"}{.status.attributes.productID}{")\n"}'Пример вывода:
Logitech Webcam C920 (046d:082d)
- Перейдите на вкладку «Система», далее в раздел «Виртуализация» → «USB-устройства узлов».
- Посмотрите список, в котором показаны статус устройства, производитель, продукт, серийный номер, узел, шина, номер устройства и назначенный неймспейс.
Устройства, назначенные проекту, его владелец видит в разделе «Виртуализация» → «USB-устройства» своего проекта.
Требования и ограничения
Планируя проброс USB-устройств, учитывайте следующие требования и ограничения:
- узел, на котором нужно обнаруживать USB-устройства, должен нести лейбл
virtualization.deckhouse.io/usbip=trueи работать на containerd версии 2, иначе компонентvirtualization-draтам не запустится; - устройство передаётся виртуальной машине по сети средствами USBIP, поэтому ВМ может работать на другом узле, а не на том, куда устройство подключено физически;
- пробросить можно только устройство, которое сообщает о себе скорость USB 2.0 (480 Мбит/с) или USB 3.x (от 5 Гбит/с). Устройство с меньшей скоростью, например мышь или клавиатуру на 1,5 или 12 Мбит/с, DP подключить к ВМ не даст;
- узел подключает не более 16 устройств, по 8 на концентратор USB 2.0 и USB 3.0;
- концентратор выбирается по скорости устройства, и вручную его не изменить. Устройство со скоростью USB 2.0 к концентратору USB 3.0 не подключится, как и наоборот;
- устройство можно подключать к работающей ВМ и отключать от неё, не останавливая ВМ.
GPU-устройства
Проброс GPU-устройств — экспериментальная возможность, доступная в коммерческих редакциях DP.
DP подключает физические GPU-устройства к виртуальным машинам через DRA (Dynamic Resource Allocation). Владелец проекта запрашивает устройство по ссылке на GPUClass в блоке .spec.gpus своей машины, а кластер к этому готовите вы.
Чтобы проброс заработал, обеспечьте следующее:
- Kubernetes версии не ниже 1.34 с feature gates DRA, которые нужны конфигурации вашего кластера.
- Feature gate
GPUв настройках модуляvirtualization. - Включённый модуль
gpuв режиме DRA, который задаёт параметрdra.enabled. - Ресурс
GPUClass, отбирающий устройства нужной модели. Модульgpuсоздаёт по нему ресурс DeviceClass с таким же именем, через который устройство и выделяется машине.
Чтобы включить feature gate, добавьте его в настройки модуля:
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: virtualization
spec:
settings:
featureGates:
- GPUПосле этого сообщите владельцам проектов имена доступных ресурсов GPUClass. К одной машине подключается не более 16 устройств, а изменение блока .spec.gpus применяется только после её перезапуска.
PCI-устройства
Проброс PCI-устройств доступен в коммерческих редакциях DP.
Проброс PCI-устройств позволяет подключить к виртуальной машине (ВМ) физическое устройство узла, например промышленный контроллер, аппаратный модуль безопасности, плату видеозахвата, ПЛИС или сетевую карту целиком. В гостевой операционной системе такое устройство работает под её собственным драйвером, поэтому в ВМ можно использовать оборудование, которое DP не поддерживает напрямую.
Устройство попадает в машину в два шага. Сначала вы назначаете его неймспейсу проекта, а затем владелец проекта указывает устройство в спецификации своей машины. Устройство предоставляется в монопольное использование, поэтому доступно только в одном неймспейсе и только одной машине.
Драйверы устройства DP переключает сам, вручную настраивать их на узле не требуется. При запуске машины DP отвязывает устройство от штатного драйвера ядра и привязывает его к драйверу vfio-pci, а после остановки машины возвращает штатному драйверу. Пока машина работает, узел это устройство не использует.
Требования к узлам
За проброс PCI-устройств отвечает системный компонент virtualization-dra-pci. Он запускается только на узлах с containerd версии 2 и включённой аппаратной виртуализацией ввода-вывода. Узлы DP проверяет сам и назначает подходящим лейбл virtualization.deckhouse.io/vfio=true.
Чтобы включить аппаратную виртуализацию ввода-вывода, задайте в BIOS узла VT-d у Intel или AMD-Vi у AMD и добавьте в параметры ядра intel_iommu=on или amd_iommu=on.
Чтобы посмотреть, какие узлы готовы к пробросу PCI-устройств, выполните команду:
d8 k get nodes -l virtualization.deckhouse.io/vfio=true,virtualization.deckhouse.io/containerd-version=v2Пример вывода:
NAME STATUS ROLES AGE VERSION
node-1 Ready worker 10d v1.34.1
Узел, отсутствующий в выводе, лейбл не получил. Проверьте на нём каталог /sys/kernel/iommu_groups. Пустой каталог означает, что аппаратная виртуализация ввода-вывода выключена. Включите её и перезагрузите узел, и в течение нескольких минут лейбл будет назначен автоматически.
Чтобы убедиться, что компонент действительно работает на этих узлах, выполните команду:
d8 k -n d8-virtualization get pods -l app=virtualization-dra-pci -o wideНазначение неймспейса PCI-устройству
DP обнаруживает устройства на подходящих узлах и создаёт для каждого из них ресурс NodePCIDevice. Шину PCI компонент сканирует при запуске и далее каждые пять минут, поэтому только что установленное устройство появляется в списке через несколько минут.
Чтобы предоставить устройство проекту, выполните следующие шаги.
-
Найдите устройство среди обнаруженных:
d8 k get nodepcideviceПример вывода:
NAME NODE ADDRESS READY ASSIGNED ATTACHED NAMESPACE AGE pci-4f2c0b1e8d9a3c5b7e1f0a2d4c6b8e0f1a3c5d7e node-1 0000:3b:00.0 True False False 10m pci-9a1b3c5d7e9f0a2b4c6d8e0f1a3b5c7d9e1f0a2b node-2 0000:65:00.0 True True False my-project 15mИмя ресурса формируется как хеш от параметров устройства и имени узла, поэтому ищите нужное устройство по колонкам
NODEиADDRESS. Проверить выбор помогает блок.status.attributes, где указаны адрес на шине PCI и идентификаторы производителя и модели. По ним то же устройство находится в выводе командыlspci -nnна узле. -
Назначьте неймспейс параметром
.spec.assignedNamespace:d8 k apply -f - <<EOF apiVersion: virtualization.deckhouse.io/v1alpha2 kind: NodePCIDevice metadata: name: pci-4f2c0b1e8d9a3c5b7e1f0a2d4c6b8e0f1a3c5d7e spec: assignedNamespace: my-project EOF -
Убедитесь, что в неймспейсе появился ресурс PCIDevice:
d8 k get pcidevice -n my-project
После этого владелец проекта подключает устройство к своей машине, указав имя ресурса PCIDevice в параметре .spec.pciDevices ресурса VirtualMachine.
Чтобы сделать устройство недоступным проекту, очистите параметр .spec.assignedNamespace или укажите другой неймспейс. Ресурс PCIDevice в прежнем неймспейсе удаляется.
Пока устройство указано в спецификации машины, ресурс PCIDevice не удаляется, а сама машина продолжает работать. Попросите владельца проекта убрать устройство из спецификации. Если вместо очистки параметра вы указали другой неймспейс, ресурс появится в нём сразу, но машина нового проекта останется в фазе Pending, пока устройство занято прежней машиной.
Требования и ограничения PCI-устройств
Планируя проброс PCI-устройств, учитывайте следующие требования и ограничения:
- проброс работает в кластере с Kubernetes версии не ниже 1.34 и containerd версии 2 на узлах, а в kube-apiserver должны быть включены feature gates
DRAResourceClaimDeviceStatus,DRADeviceBindingConditionsиDRAConsumableCapacity; - DP обнаруживает не все устройства узла, потому что оборудование, от которого зависит работа самого узла, остаётся в его распоряжении. В списке не появятся:
- видеоадаптеры, которые пробрасываются как GPU-устройства;
- устройства, интегрированные в чипсет, мосты, контроллеры памяти и системная периферия;
- сетевые контроллеры с активными интерфейсами;
- контроллеры накопителей, диски которых использует узел;
- устройства, попавшие с ними в одну IOMMU-группу, потому что в ВМ группа передаётся целиком;
- ВМ запускается только на том узле, где находится подключённое к ней устройство, поэтому все её PCI-устройства должны быть на одном узле, иначе спецификация будет отклонена;
- ВМ с PCI-устройством живой миграцией не переносится, а условие
Migratableполучает причинуVirtualMachineHostDevicesNotMigratable, поэтому перед выводом узла на обслуживание такие машины нужно остановить; - устройства подключаются при запуске ВМ, поэтому изменение параметра
.spec.pciDevicesтребует её перезапуска; - устройство подключается только к одной ВМ, а к каждой ВМ подключается не более восьми устройств;
- проброшенная сетевая карта работает в обход сетевой подсистемы кластера. Она не отражается в параметре
.spec.networks, а адреса IP и MAC на ней DP не выдаёт и не учитывает.
Аудит событий безопасности
Аудит фиксирует действия с виртуальными машинами (ВМ) и с самим модулем, чтобы вы могли разобрать инцидент и восстановить последовательность событий.
Доступно в коммерческих редакциях DP.
Включение аудита
Чтобы включить аудит событий безопасности, выполните следующие шаги:
-
Включите модули
log-shipperиruntime-audit-engine. -
Включите аудит API Kubernetes, задав
.spec.settings.apiserver.auditPolicyEnabledв значениеtrueв модулеcontrol-plane-manager. -
Задайте
.spec.settings.audit.enabledв значениеtrueв модулеvirtualization:spec: settings: audit: enabled: true
Пока все три условия не выполнены, компонент аудита в кластере не запускается. Остальные параметры описаны в настройках модуля.
Состав событий
Тип события записан в поле type. Аудит различает следующие типы:
Access to VM— подключение к ВМ по консоли, VNC или через проброс портов, фиксируются начало и завершение сеанса.Manage VM— создание, изменение или удаление ресурса VirtualMachine.Control VM— изменение состояния ВМ, в том числе запуск, остановка, перезапуск, миграция и вытеснение через ресурс VirtualMachineOperation, а также остановка или перезапуск из гостевой ОС и аварийное завершение работы.Module control— создание, изменение, выключение или удаление ModuleConfig.Virtualization control— создание или удаление системного компонента модуля в неймспейсеd8-virtualization.Integrity check— несовпадение контрольной суммы конфигурации ВМ с эталонной.Forbidden operation— попытка выполнить запрещённую операцию.
Независимо от типа каждое событие содержит одни и те же поля:
name— описание произошедшего;datetime— время события;request_subject— имя пользователя или ServiceAccount, от имени которого выполнено действие;operation_result— результат операции;uid— идентификатор записи в аудите Kubernetes.
К ним добавляются уточняющие поля, состав которых зависит от типа события. Например, события с ВМ содержат поля virtual_machine_name и virtual_machine_namespace, а запрещённые операции описывают источник запроса в поле source_ip и причину отказа в поле forbid_reason.
Просмотр событий
События собирает системный компонент virtualization-audit в неймспейсе d8-virtualization. Чтобы перенаправить их в систему логирования кластера, например в Loki, создайте ClusterLoggingConfig:
apiVersion: deckhouse.io/v1alpha1
kind: ClusterLoggingConfig
metadata:
name: virtualization-audit-logs
spec:
destinationRefs:
- d8-loki
kubernetesPods:
namespaceSelector:
matchNames:
- d8-virtualization
labelSelector:
matchLabels:
app: virtualization-audit
type: KubernetesPodsЧтобы посмотреть события в Grafana, используйте запрос к Loki:
{namespace="d8-virtualization", pod=~"virtualization-audit-.*"}