Всем привет! Меня зовут Валерий Хорунжин, я инженер архитектурных решений в команде Deckhouse компании «Флант». И я поставил себе виртуализацию.
Начало моего пути к виртуализации связано с использованием Obsidian, у которого не было встроенной синхронизации. У меня есть небольшой арендованный VPS, но я столкнулся с тем, что запуск готового Docker-контейнера сильно нагружал процессор сервера, из-за чего даже подключение по SSH становилось затруднительным.
В поисках решения я понял, что хочу не просто хостить одно приложение, а легко переносить и масштабировать всю инфраструктуру с помощью декларативной конфигурации. Перспектива создания собственного Kubernetes-кластера выглядела очень заманчиво.
Однако контейнеры, несмотря на свои преимущества, могут добавлять риски безопасности. Если одно из приложений будет скомпрометировано, злоумышленник может получить доступ к контейнерам и дальше, что особенно опасно, когда я не слежу за кластером.

Поэтому я пришёл к выводу, что мне необходима полноценная виртуализация. Для реализации я выбрал Open Source-платформу Deckhouse Virtualization Platform Community Edition (DVP CE). Она работает на базе Deckhouse Kubernetes Platform. Чтобы не путаться в похожих названиях, K8s-платформу и её модули далее по тексту буду называть просто Deckhouse.
В этой статье я покажу, как развернуть домашний кластер виртуализации на базе DVP CE: от выбора и подготовки оборудования до настройки сети, установки платформы и первых шагов по работе с виртуальными машинами и хранилищем данных.
Содержание:
Для тех, кто ничего не знает о Kubernetes
Нажмите, чтобы узнать подробнее о Kubernetes
Если вы никогда не использовали Kubernetes, то вот вам краткая справка, которая позволит легче усвоить материал статьи. Учитывайте, что в силу краткости здесь могут быть неточности. Также рекомендуем прочитать статью из блога Deckhouse «Кубернетес для бабушки».
Kubernetes — это система оркестрации кластеров серверов для запуска подов. В переводе на человеческий — это система, которая позволяет управлять ресурсами множества серверов без возникновения болезни, связанной с излишним загустением крови в области чуть ниже спины.
Kubernetes позволяет управлять кластером декларативно, то есть мы описываем желаемое состояние, а внутренние механизмы приводят к нему за своими слоями абстракции вне поля нашего зрения.
Взаимодействие с Kubernetes происходит через HTTP API. Стандартным клиентом для управления Kubernetes является kubectl, его мы и будем использовать в статье. При помощи kubectl мы управляем сущностями, которые и являются декларативным описанием того, что мы хотим получить от кластера. Основной язык описания сущностей — это YAML.
Сущности в Kubernetes могут располагаться в разных пространствах имён (namespaces), для указания конкретного namespace используется флаг kubectl -n
. Базовыми операциями, применяемыми из kubectl, являются apply
(применить) и delete
(удалить), флаг -f
позволяет указать этим командам файл с входными данными.
Основные сущности, встречающиеся в статье:
Контейнер — «песочница» запуска приложений со своей файловой системой, списком процессов, сетевым окружением и набором других слоёв изоляции от хост-системы.
Pod — базовая единица запуска контейнеров в Kubernetes, которая содержит один или несколько контейнеров.
Deployment (deploy) — один или несколько инстансов pod.
Service (svc) — объект, который создаёт постоянную сетевую точку доступа к группе подов внутри кластера.
Ingress — объект, который обеспечивает внешний доступ к сервисам внутри кластера, маршрутизируя HTTP- и HTTPS-трафик.
PersistentVolumeClaim (PVC) — сущность хранения данных (упрощение).
StorageClass (SC) — набор параметров, определяющих, какое физическое хранилище будет использоваться для физического размещения PVC, дисков и так далее.
Node (узел) — сервер в кластере.
Master node (master-узел, мастер-узел) — узел, на котором располагаются системные компоненты кластера (в том числе API кластера).
Worker node (worker-узел, воркер-узел) — узел, где размещаются пользовательские сущности (поды, виртуальные машины и так далее).
VirtualImage (vi) — виртуальный образ диска.
VirtualDisk (vd) — виртуальный диск.
VirtualMachine (vm) — виртуальная машина (ВМ).
Project (проект) — проект для запуска ВМ, представляет собой отдельное адресное пространство имён, набор квот по ресурсам (процессор, память, дисковое пространство), сетевые политики, и позволяет разграничить доступ для пользователей.
Подробнее о компонентах Kubernetes можно почитать в официальной документации.
Если вы хотите получше узнать о том, как концепции виртуализации в контейнерах соотносятся с виртуализацией VMware, прочитайте статью о KubeVirt — виртуализации виртуальных машин через Kubernetes.
Подготовка домашнего кластера
Параметры физических серверов
Для домашнего кластера я купил три мини-ПК (11 000 рублей за каждый) и гигабитный свитч (1 500 рублей) для связи между ними. В итоге всего заплатил 34 500 рублей.

Характеристики ПК:
Процессор: Intel N150 (4 ядра, 4 потока, 3.6 ГГц)
ОЗУ: 16 ГБ
Хранилище: 500 ГБ SSD
Предварительная настройка кластера
Чтобы развернуть кластер, необходимо провести ряд предварительных шагов для настройки сети DNS и SSH-доступа.
На кластер будет установлена платформа DVP CE.
Узлы кластера должны обладать статическими IP-адресами в подсети, в которой они взаимодействуют друг с другом. Кластер будет использовать схему «1 master- и 2 worker-узла». В качестве хранилища используем модуль Deckhouse — sds-replicated-volume.
Концептуальная схема кластера представлена на рисунке:

Для тестирования работы кластера нужно прописать DNS-записи. Я выполняю установку с ОС Windows, поэтому в моём случае нужно прописать в файл hosts
следующие домены с адресом master-узла:
api.homecluster.ru
argocd.homecluster.ru
dashboard.homecluster.ru
documentation.homecluster.ru
dex.homecluster.ru
grafana.homecluster.ru
hubble.homecluster.ru
istio.homecluster.ru
istio-api-proxy.homecluster.ru
kubeconfig.homecluster.ru
openvpn-admin.homecluster.ru
prometheus.homecluster.ru
status.homecluster.ru
upmeter.homecluster.ru
Также нужно создать SSH-ключ на компьютере, с которого будет ставиться кластер через ssh-keygen
, и прописать его в authorized_keys
на master-узле.
Установка ОС
В качестве ОС для серверов кластера я использовал Ubuntu 24.04, которую нужно установить на каждый сервер. Не буду надолго останавливаться на этом шаге, но отмечу пару моментов:
При установке рекомендую сразу настроить статичный IP для сервера, чтобы потом не писать настройки руками. После установки конфиги будут находиться в
/etc/netplan
. В моём случае сервер использует два сетевых интерфейса: Wi-Fi для подключения к интернету и гигабитный свитч для связи между серверами.

-
Для распределённого хранилища нам понадобятся блочные устройства, ими могут выступать неразмеченные устройства, либо неотформатированные, но созданные разделы. В моём случае разделы разбиты следующим образом:
/boot - 1gb
/ - ext4, 100gb
На остальном месте мы создаём раздел, у которого выбираем опцию leave unformatted, что позволит нам использовать его для распределённого хранилища sds-replicated-volume.
Развёртывание кластера
Развёртывание master-узла
Устанавливать платформу я буду с Windows. Для этого потребуется обеспечить доступ по SSH к master-узлу с использованием ключа.
Далее на втором шаге руководства homecluster по быстрому старту DVP нужно ввести шаблон доменных имён нашего кластера. У меня это %s.homecluster.ru
:

После чего жмём «Далее: установка платформы» и получаем готовый конфиг с подставленным шаблоном доменных имён.
Осталось только скопировать и изменить параметр internalNetworkCIDRs
, где указываем подсеть, в которой находится кластер. Это необходимо в случае, если на наших серверах используется более одного сетевого интерфейса. У меня это 10.0.4.0/24
, подсеть ethernet-соединения.
Обратите внимание на параметры, в которых прописаны подсети, резервируемые под нужды кластера. У вас не должно быть пересечения с другими сетями серверов. Если они есть, смените либо внешние настройки, либо данные параметры.
Конфиг получился таким, сохраним его в файл config.yml (нажмите, чтобы посмотреть)
# Общие параметры кластера.
# https://deckhouse.ru/products/virtualization-platform/reference/cr/clusterconfiguration.html
apiVersion: deckhouse.io/v1
kind: ClusterConfiguration
clusterType: Static
# Адресное пространство подов кластера.
podSubnetCIDR: 10.111.0.0/16
# Адресное пространство сети сервисов кластера.
serviceSubnetCIDR: 10.222.0.0/16
kubernetesVersion: "Automatic"
# Домен кластера.
clusterDomain: "cluster.local"
---
# Настройки первичной инициализации кластера Deckhouse.
# https://deckhouse.ru/products/virtualization-platform/reference/cr/initconfiguration.html
apiVersion: deckhouse.io/v1
kind: InitConfiguration
deckhouse:
# Адрес Docker registry с образами Deckhouse
imagesRepo: registry.deckhouse.ru/deckhouse/ce
# Строка с ключом для доступа к Docker registry (сгенерировано автоматически для вашего токена доступа)
registryDockerCfg: eyJhdXRocyI6IHsgInJlZ2lzdHJ5LmRlY2tob3VzZS5ydSI6IHt9fX0K
---
# Настройки модуля deckhouse.
# https://deckhouse.ru/products/virtualization-platform/documentation/v1/modules/deckhouse/configuration.html
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: deckhouse
spec:
version: 1
enabled: true
settings:
bundle: Default
releaseChannel: EarlyAccess
logLevel: Info
---
# Глобальные настройки Deckhouse.
# https://deckhouse.ru/products/virtualization-platform/documentation/v1/deckhouse-configure-global.html#%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%D1%8B
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: global
spec:
version: 2
settings:
modules:
# Шаблон, который будет использоваться для составления адресов системных приложений в кластере.
# Например, Grafana для %s.homecluster.ru будет доступна на домене 'grafana.homecluster.ru'.
# Домен НЕ ДОЛЖЕН совпадать с указанным в параметре clusterDomain ресурса ClusterConfiguration.
# Можете изменить на свой сразу, либо следовать шагам руководства и сменить его после установки.
publicDomainTemplate: "%s.homecluster.ru"
---
# Настройки модуля user-authn.
# https://deckhouse.ru/products/virtualization-platform/documentation/v1/modules/user-authn/configuration.html
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: user-authn
spec:
version: 2
enabled: true
settings:
controlPlaneConfigurator:
dexCAMode: DoNotNeed
# Включение доступа к API-серверу Kubernetes через Ingress.
# https://deckhouse.ru/products/virtualization-platform/documentation/v1/modules/user-authn/configuration.html#parameters-publishapi
publishAPI:
enabled: true
https:
mode: Global
global:
kubeconfigGeneratorMasterCA: ""
---
# Настройки модуля cni-cilium.
# https://deckhouse.io/products/virtualization-platform/reference/mc.html#cni-cilium
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: cni-cilium
spec:
version: 1
# Включить модуль cni-cilium
enabled: true
settings:
tunnelMode: VXLAN
---
# Настройки модуля admission-policy-engine
# https://deckhouse.io/products/virtualization-platform/reference/mc.html#admission-policy-engine
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: admission-policy-engine
spec:
enabled: true
version: 1
---
# Настройки модуля multitenancy-manager
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: multitenancy-manager
spec:
enabled: true
version: 1
---
# Параметры статического кластера.
# https://deckhouse.ru/products/virtualization-platform/reference/cr/staticclusterconfiguration.html
apiVersion: deckhouse.io/v1
kind: StaticClusterConfiguration
# Список внутренних сетей узлов кластера (например, '10.0.4.0/24'), который
# используется для связи компонентов Kubernetes (kube-apiserver, kubelet...) между собой.
# Укажите, если используете модуль virtualization или узлы кластера имеют более одного сетевого интерфейса.
# Если на узлах кластера используется только один интерфейс, ресурс StaticClusterConfiguration можно не создавать.
internalNetworkCIDRs:
- 10.0.4.0/24
И начнётся установка DVP CE на master-узел:

После того как команда выполнится, развёртывание master-узла завершится.
Установка worker-узлов
Master-узел — это прекрасно, и на нём можно крутить системные компоненты, но без worker-узлов кластер не имеет смысла, так как на них разворачиваются пользовательские нагрузки (поды, ВМ и прочее). Мне нужно сконфигурировать два дополнительных узла.
Для начала необходимо создать NodeGroup с узлами worker:
sudo -i d8 k create -f - << EOF
apiVersion: deckhouse.io/v1
kind: NodeGroup
metadata:
name: worker
spec:
nodeType: Static
staticInstances:
count: 2
labelSelector:
matchLabels:
role: worker
EOF
Обратите внимание на параметр count
— он указывает на количество узлов, входящих в группу узлов.
Далее необходимо настроить worker-узлы, что Deckhouse делает самостоятельно, нужно только обеспечить взаимодействие master- и worker-узлов по SSH. Для этого на master-узле сгенерируем SSH-ключ с пустой парольной фразой:
ssh-keygen -t rsa -f /dev/shm/caps-id -C "" -N ""
И далее создадим на кластере ресурс SSHCredentials:
sudo -i d8 k create -f - <<EOF
apiVersion: deckhouse.io/v1alpha1
kind: SSHCredentials
metadata:
name: caps
spec:
user: caps
privateSSHKey: "`cat /dev/shm/caps-id | base64 -w0`"
EOF
Публичный ключ сгенерированного ключа необходимо добавить в authorized_keys
пользователя caps
на worker-узлах. Выведём его для дальнейшего копирования:
cat /dev/shm/caps-id.pub
Дальше поочерёдно заходим по SSH на worker-узлы и под рутом выполняем следующие команды (подставьте ваше значение publickey вместо <SSH-PUBLIC-KEY>
, которые создадут пользователя caps
и пропишут ему выведённый ранее publickey):
export KEY='<SSH-PUBLIC-KEY>' # Укажите публичную часть SSH-ключа пользователя.
useradd -m -s /bin/bash caps
usermod -aG sudo caps
echo 'caps ALL=(ALL) NOPASSWD: ALL' | sudo EDITOR='tee -a' visudo
mkdir /home/caps/.ssh
echo $KEY >> /home/caps/.ssh/authorized_keys
chown -R caps:caps /home/caps
chmod 700 /home/caps/.ssh
chmod 600 /home/caps/.ssh/authorized_keys
Для добавления узла в кластер Deckhouse необходимо создать описание статического узла в кластере (StaticInstance) и дать доступ master-узлу к worker-узлам по SSH. Выполним данные шаги.
Возвращаемся на master-узел, так как далее команды выполняем снова на нём. Создадим StaticInstance по количеству worker-узлов, где укажем IP-адрес (используйте IP-адрес из внутренней сети узлов) устанавливаемого узла, а также имя создаваемой сущности (параметр name
):
export NODE=<NODE-IP-ADDRESS> # Укажите IP-адрес узла, который необходимо подключить к кластеру.
sudo -i d8 k create -f - <<EOF
apiVersion: deckhouse.io/v1alpha1
kind: StaticInstance
metadata:
name: dvp-worker
labels:
role: worker
spec:
address: "$NODE"
credentialsRef:
kind: SSHCredentials
name: caps
EOF
Введём команду получения сущностей StaticInstance с ожиданием изменения состояний:
d8 k get staticinstances.deckhouse.io -w

Когда узлы будут готовы, мы увидим следующий вывод:

Выполним d8 k get no
:

Узлы развернулись. Приступаем к следующему шагу.
Установка программно-определяемого хранилища
Одна из причин, почему я выбрал конфигурацию из трёх узлов, — желание надёжного хранения данных. Основой этого является реплицируемость данных, то есть их хранение в нескольких копиях. На данном этапе нам необходимо настроить реплицируемое хранилище данных, воспользуемся модулем Deckhouse sds-replicated-volume.
Для начала включим соответствующие модули:
sudo -i d8 k create -f - <<EOF
---
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: sds-node-configurator
spec:
version: 1
enabled: true
---
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: sds-replicated-volume
spec:
version: 1
enabled: true
EOF
Дождёмся готовности модуля sds-replicated-volume:
sudo -i d8 k wait module sds-replicated-volume --for='jsonpath={.status.phase}=Ready' --timeout=1200s
В Deckhouse развёртыванием модулей, системных образов и управлением большинством вещей в кластере занимается deployment deckhouse, который находится в namespace d8-system
. После включения модулей или изменения их конфигурации выполняется множество хуков, состояние выполнения которых можно посмотреть в очереди Deckhouse при помощи команды d8 platform queue list
. Введём watch d8 platform queue list
и дождёмся, когда в очереди не останется заданий:

Пустая очередь будет выглядеть так:

Получим список блочных устройств, который сможем использовать в хранилище. Для этого воспользуемся командой d8 k get bd
:

В sds-replicated-volume существуют понятия thin-пулы и thick-пулы для размещения данных. Thick-пулы при выделении места занимают выделенный участок сразу целиком, в то время как thin-пулы позволяют занимать только необходимую в данный момент часть дискового пространства.
Thick-пулы работают быстрее, однако выделение памяти может занять больше времени, и на thick-пулах не работают снимки. Thin-пулы позволяют экономить пространство, быстрее выделять место, но суммарно выделенное место может превышать фактическое место на хранилище. Соответственно, необходимо следить за фактическим заполнением места.
Создадим LVMVolume для каждого узла. Для этого нужно подставить в следующую команду имя узла и имя блочного устройства с этого узла:
d8 k apply -f - <<EOF
apiVersion: storage.deckhouse.io/v1alpha1
kind: LVMVolumeGroup
metadata:
name: "vg-on-worker-0"
spec:
type: Local
local:
# Замените на имя своего узла, для которого создаёте группу томов.
nodeName: "worker-0"
blockDeviceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values:
# Замените на имена своих блочных устройств узла, для которого создаёте группу томов.
- dev-ef4fb06b63d2c05fb6ee83008b55e486aa1161aa
# Имя группы томов LVM, которая будет создана из указанных выше блочных устройств на выбранном узле.
actualVGNameOnTheNode: "vg"
thinPools:
- name: thin-pool-0
size: 70%
EOF

Далее создадим thin-пул:
d8 k apply -f - <<EOF
apiVersion: storage.deckhouse.io/v1alpha1
kind: ReplicatedStoragePool
metadata:
name: thin-pool
spec:
type: LVMThin
lvmVolumeGroups:
- name: vg-1-on-homecluster0
thinPoolName: thin-pool-0
- name: vg-1-on-homecluster1
thinPoolName: thin-pool-0
- name: vg-1-on-homecluster2
thinPoolName: thin-pool-0
EOF
В sds-replicated-volume пользователь не конфигурирует StorageClass вручную, а конфигурирует сущность более высокого уровня ReplicatedStorageClass
.
Создадим ReplicatedStorageClass
(чтобы выбрать параметр replication
, ознакомьтесь с документацией):
d8 k apply -f - <<EOF
apiVersion: storage.deckhouse.io/v1alpha1
kind: ReplicatedStorageClass
metadata:
name: replicated-storage-class
spec:
# Указываем имя одного из пулов хранения, созданных ранее.
storagePool: thin-pool
# Режим поведения при удалении PVC.
# Допустимые значения: "Delete", "Retain".
# [Подробнее...](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#reclaiming)
reclaimPolicy: Delete
# Реплики смогут размещаться на любых доступных узлах: не более одной реплики определенного тома на один узел.
# В кластере нет зон (нет узлов с лейблами topology.kubernetes.io/zone).
topology: Ignored
# Режим репликации, при котором том остается доступным для чтения и записи, даже если одна из реплик тома становится недоступной.
# Данные хранятся в трех экземплярах на разных узлах.
replication: ConsistencyAndAvailability
EOF
Убедимся, что всё создалось:

Установим созданный StorageClass классом по умолчанию:
DEFAULT_STORAGE_CLASS=replicated-storage-class
sudo -i d8 k patch mc global --type='json' -p='[{"op": "replace", "path": "/spec/settings/defaultClusterStorageClass", "value": "'"$DEFAULT_STORAGE_CLASS"'"}]'
Включение модуля виртуализации
Время пришло, включим модуль виртуализации:
sudo -i d8 k create -f - <<EOF
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: virtualization
spec:
enabled: true
settings:
dvcr:
storage:
persistentVolumeClaim:
size: 50G
type: PersistentVolumeClaim
virtualMachineCIDRs:
# Укажите подсети, из которых будут назначаться IP-адреса виртуальным машинам.
- 10.66.10.0/24
- 10.66.20.0/24
- 10.66.30.0/24
version: 1
EOF
Дождёмся, пока модуль не перейдёт в состояние готовности:

После этого смотрим в очередь Deckhouse, пока она не станет пустой. Это может занять некоторое время:


Если на этапе настройки хранилища вы выбрали всё-таки thick-пул, убедитесь, что все поды в namespace d8-virtualization
находятся в состоянии Running:

Настройка Ingress и DNS
Убедимся, что под Kruise controller manager запустился и находится в статусе Running:
d8 k -n d8-ingress-nginx get po -l app=kruise
Установим Ingress-контроллер:
sudo -i d8 k apply -f - <<EOF
# Параметры контроллера NGINX Ingress.
# https://deckhouse.ru/products/virtualization-platform/reference/cr/ingressnginxcontroller.html
apiVersion: deckhouse.io/v1
kind: IngressNginxController
metadata:
name: nginx
spec:
ingressClass: nginx
# Способ поступления трафика из внешнего мира.
inlet: HostPort
hostPort:
httpPort: 80
httpsPort: 443
# Описывает, на каких узлах будет находиться Ingress-контроллер.
# Возможно, захотите изменить.
nodeSelector:
node-role.kubernetes.io/control-plane: ""
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
operator: Exists
EOF
Под контроллера должен быть в Running:
d8 k -n d8-ingress-nginx get po -l app=controller
Создание пользователя и мониторинг
Создадим пользователя для доступа к кластеру и веб-интерфейсу:
sudo -i d8 k apply -f - <<"EOF"
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
# Это хэш пароля password, сгенерированного сейчас
# Сгенерируйте свой или используйте этот, но только для тестирования
# echo "password" | htpasswd -BinC 10 "" | cut -d: -f2
# Возможно, захотите изменить
password: $2y$10$5.7NBl2MtHbQNzpc4/NOGeBU8lO73qDrc1jMjo.DQz8.X.PuZB7Ji
EOF
Зайдём на grafana.homecluster.ru:

Становится ясно, что сеть кластера работает и отдаёт нужные запрашиваемые страницы, а на странице на скриншоте мы видим метрики кластера и можем смотреть конкретные дашборды.
Примечание
Чтобы кластер был доступен из интернета, можно воспользоваться обратным пробросом порта по SSH и утилитой autossh для поддержания соединения. Это хоть и не оптимальный способ, но простой. И в таком случае вам наверняка захочется поменять доменное имя кластера, для чего нужно изменить параметр
.spec.settings.modules.publicDomainTemplate
у сущностиmc global (kubectl edit mc global)
.
Создание проекта и виртуальной машины
ВМ располагаются в проектах, поэтому пришло время создать проект и приступить к тому, для чего мы прошли столько шагов — создать, наконец, виртуальную машину.
Давайте создадим тестовый проект:
d8 k create -f - <<EOF
apiVersion: deckhouse.io/v1alpha2
kind: Project
metadata:
name: test-project
spec:
description: test-project
projectTemplateName: default
parameters:
# Квоты проекта.
resourceQuota:
requests:
cpu: 16
limits:
cpu: 16
networkPolicy: NotRestricted
# Администраторы проекта.
administrators:
- subject: User
name: admin
EOF
И образ:
d8 k apply -f - <<EOF
apiVersion: virtualization.deckhouse.io/v1alpha2
kind: VirtualImage
metadata:
name: ubuntu-22-04
namespace: test-project
spec:
# Сохраним образ в DVCR
storage: ContainerRegistry
# Источник для создания образа.
dataSource:
type: HTTP
http:
url: https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
EOF
Убедимся, что образ создался, и будем ждать его готовности:
d8 k -n test-project get vi -w

Создадим диск из этого образа:
d8 k apply -f - <<EOF
apiVersion: virtualization.deckhouse.io/v1alpha2
kind: VirtualDisk
metadata:
name: linux-vm-root
spec:
# Настройки параметров хранения диска.
persistentVolumeClaim:
# Укажем размер больше, чем значение распакованного образа.
size: 10Gi
# Подставьте ваше название StorageClass.
storageClassName: i-sds-replicated-thin-r2
# Источник, из которого создается диск.
dataSource:
type: ObjectRef
objectRef:
kind: VirtualImage
name: ubuntu-22-04
EOF
StorageClass, который мы создавали, имеет настройку WaitForFirstConsumer
, то есть диск «не поедет», пока не существует потребителя (ВМ). Эта настройка позволяет создавать диск на том же узле, что и ВМ, что сокращает задержки по работе с диском. Создадим ВМ:
d8 k apply -f - <<"EOF"
apiVersion: virtualization.deckhouse.io/v1alpha2
kind: VirtualMachine
metadata:
name: linux-vm
namespace: test-project
spec:
# Название класса ВМ.
virtualMachineClassName: generic
# Блок скриптов первичной инициализации ВМ.
provisioning:
type: UserData
# Пример cloud-init-сценария для создания пользователя cloud с паролем cloud и установки сервиса агента qemu-guest-agent и сервиса nginx.
userData: |
#cloud-config
package_update: true
packages:
- qemu-guest-agent
run_cmd:
- systemctl daemon-reload
- systemctl enable --now qemu-guest-agent.service
ssh_pwauth: True
users:
- name: cloud
passwd: '$6$rounds=4096$saltsalt$fPmUsbjAuA7mnQNTajQM6ClhesyG0.yyQhvahas02ejfMAq1ykBo1RquzS0R6GgdIDlvS.kbUwDablGZKZcTP/'
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
lock_passwd: False
final_message: "The system is finally up, after $UPTIME seconds"
# Настройки ресурсов ВМ.
cpu:
# Количество ядер ЦП.
cores: 1
# Запросить 10% процессорного времени одного физического ядра.
coreFraction: 10%
memory:
# Объём оперативной памяти.
size: 1Gi
# Список дисков и образов, используемых в ВМ.
blockDeviceRefs:
# Порядок дисков и образов в данном блоке определяет приоритет загрузки.
- kind: VirtualDisk
name: linux-vd
EOF
Остаётся только ждать старта ВМ:
d8 k -n test-project get vm -w

Теперь можно подключиться к ВМ по SSH. Я это сделаю с помощью утилиты d8 v
, которую предоставляет DVP:

Виртуальная машина доступна и готова к работе, можно пользоваться.
Сколько ресурсов в таком кластере есть для запуска ВМ
Каждый из worker-узлов кластера предоставляет примерно 10 ГБ оперативной памяти и 4 ядра CPU для запуска виртуальных машин. Таким образом, суммарно кластер имеет около 20 ГБ доступной оперативной памяти и 8 ядер CPU для ВМ.
Master-узел:

Один worker-узел (на нём была запущена, а затем остановлена ВМ):

Второй worker-узел:

Заключение
Мы получили домашний кластер виртуализации и выполнили первые шаги по его использованию — с декларативностью, мониторингом и репликацией данных. Так можно создать гибкую и отказоустойчивую среду, которую можно масштабировать и использовать для самых разных задач: тестирования, обучения, хобби-проектов или домашних сервисов. Всего на установку кластера на этой конфигурации я потратил примерно 1,5 часа, не считая установку ОС.
На данный момент конфигурация используется для личного облака Nextcloud, планирую развернуть GitLab-сервер. А ещё эксперименты с веб-приложениями и Telegram-ботами больше не причинят боль вопросом «где же мне захоститься».
P. S.
Читайте также в нашем блоге:
Комментарии (7)
dsoastro
17.07.2025 09:48Как-то перебор для синхронизации obsidian городить кубер кластер. Обычного гит хватит скорей всего. Ну или Syncthing какого-нибудь
Fearning Автор
17.07.2025 09:48Согласен, но это "заделка на будущее", чтобы один раз сделать и больше не думать где хостить тг ботов, мелкие веб приложения для себя. Но главное, конечно, это то что это домашний проект и все логические доводы падут под "мне так захотелось и я сделал")
Testinghall
17.07.2025 09:48В целом было интересно, жду продолжения, где ТС будет в контейнерах запускать серверы ла2 и Майнкрафта.
Fearning Автор
17.07.2025 09:48Мне кажется что в таких экспериментах не набёртся материала на отдельную статью, но спасибо)
fl64
А на мастере ВМ можно запускать?
Fearning Автор
Нежелательно, но можно так как нагрузка не должна делить ресурсы с управляющими компонентами. По умолчанию не будет работать, в Kubernetes реализован механизм taints который не даст запуститься нагрузке на мастер узле, нужно удалить с него taint с ключом node-role.kubernetes.io/control-plane, но не делайте так на продакшене пожалуйста)
fl64
На домашнем продакшене можно)