Как сделать облако дома с Proxmox
Однажды я понял, что хочу домашний сервер для экспериментов с деплоем и инфраструктурой. В значительной степени этому поспособствовали посты на r/homelab и r/selfhosted.
Изначально я хотел пойти по пути bare-metal и собрать k3s кластер на нескольких RaspberryPi, но сравнив стоимость, ограничения и возможности, я решил купить пару мини-ПК Lenovo ThinkCentre и установить на них Proxmox Virtual Environment (PVE).
В рабочих целях я активно использовал Selectel и Yandex.Cloud, поэтому для меня было важно удобство работы с виртуальными серверами, аналогичное тому, что предоставляет compute cloud.
Основные требования к инфраструктуре виртуализации:
- VM могут находиться в изолированной сети, недоступной из домашней;
- простое создание сервера с Debian и cloud-init;
- управление серверами через Terraform.
После нескольких дней, проведённых в гугле, конфигах, а также пары переустановок всего с нуля, я сформировал рабочее решение и нашёл подводные камни.
Подготовка приватной сети
Я решил обеспечить минимальную безопасность, выделив виртуальные серверы в отдельную от других устройств сеть.
Я не стал тратить время на VLAN, сложную маршрутизацию и межсетевые экраны, а использовал отдельный Linux Bridge на хосте PVE.
На диаграмме ниже отражена концептуальная схема сети. Все VM подключаются к интерфейсу приватной сети vmbr1. VM, которые должны быть доступны из домашней сети, дополнительно подключаются к интерфейсу vmbr0. Для обеспечения доступа в интернет приватных VM реализуется NAT из vmbr1 через vmbr0.
Интерфейс для приватной сети можно добавить через панель управления Proxmox, либо стандартными средствами Debian.
Рассмотрим второй вариант и добавим следующую конфигурацию в файл /etc/network/interfaces:
auto vmbr1
iface vmbr1 inet static
address 10.0.2.1/24
bridge-ports none
bridge-stp off
bridge-fd 0Новый интерфейс необходимо активировать командной:
ifup vmbr1Созданный интерфейс должен отобразиться в панели управления Proxmox.
Для доступа VM в интернет необходимо настроить NAT.
В файле /etc/sysctl.conf нужно включить IP-форвардинг, добавив следующую строку:
net.ipv4.ip_forward=1Сразу же применить эту настройку, выполнив команду:
sysctl -w net.ipv4.ip_forward=1Затем необходимо включить NAT в iptables, выполнив команду:
iptables -t nat -A POSTROUTING -s 10.0.2.0/24 -o vmbr0 -j MASQUERADEЗдесь vmbr0 — имя интерфейса, имеющего доступ в интернет.
Для сохранения настроек iptables между перезагрузками, необходимо установить пакет iptables-persistent:
apt install iptables-persistentСеть сконфигурирована и можно переходить к следующему этапу.
Подготовка образа
Для создания VM аналогично облаку, нужен специальный облачный образ Debian.
Также понадобятся инструменты для модификации образов виртуальных машин:
apt install libguestfs-toolsКоманда для скачивания и подготовки образа к использованию в шаблоне:
wget https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2 \
&& virt-customize -a debian-12-generic-amd64.qcow2 --install qemu-guest-agent,net-tools \
&& virt-customize -a debian-12-generic-amd64.qcow2 --run-command "echo 'en_US.UTF-8 UTF-8' >> /etc/locale.gen" \
&& virt-customize -a debian-12-generic-amd64.qcow2 --run-command "locale-gen" \
&& virt-customize -a debian-12-generic-amd64.qcow2 --run-command "update-locale LANGUAGE=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 LANG=en_US.UTF-8" \
&& virt-customize -a debian-12-generic-amd64.qcow2 --truncate /etc/machine-idВ аргументе --install можно указать дополнительные пакеты, которые будут автоматически установлены на всех серверах. Пакет qemu-guest-agent необходим для работы агента PVE.
Также важно вызвать --truncate /etc/machine-id, чтобы уникальный идентификатор генерировался для каждой копируемой VM.
Манипуляции с локалью не обязательны, но помогают избежать мусорных сообщений об ошибках локали при работе по SSH.
После того как образ подготовлен, необходимо создать непосредственно шаблон VM в Proxmox. Для этого можно выполнить следующую команду:
QM_ID=9001 \
&& qm create $QM_ID --name "debian12-cloudinit" --memory 512 --cores 1 --net0 virtio,bridge=vmbr1 --machine q35 \
&& qm importdisk $QM_ID debian-12-generic-amd64.qcow2 local-lvm \
&& qm set $QM_ID --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-$QM_ID-disk-0 \
&& qm set $QM_ID --boot c --bootdisk scsi0 \
&& qm set $QM_ID --ide2 local-lvm:cloudinit \
&& qm set $QM_ID --serial0 socket --vga serial0 \
&& qm set $QM_ID --agent enabled=1 \
&& qm template $QM_IDЗначения идентификатора шаблона 9001 и его названия "debian12-cloudinit" могут быть изменены на любые произвольные. Ресурсы и настройки сети будут переопределяться для каждого копируемого инстанса.
Все предварительные настройки сделаны и теперь можно использовать Terraform для управления виртуальными серверами.
Terraform
Для работы с Proxmox через Terraform или OpenTofu можно использовать провайдер telmate/proxmox.
Из соображений безопасности рекомендуется создать отдельного пользователя с ограниченным набором прав. Однако в контексте домашнего использования с доступом только из локальной сети этот шаг можно пропустить.
Содержимое файла provider.tf:
terraform {
required_providers {
proxmox = {
source = "telmate/proxmox"
version = "3.0.2-rc03" # последняя версия на момент написания статьи: 3.0.2-rc07
}
}
}
provider "proxmox" {
pm_api_url = "https://${var.pm_host}:8006/api2/json"
pm_user = "${var.pm_user}@pam"
pm_password = var.pm_password
pm_tls_insecure = true
}Содержимое файла variables.tf:
variable "pm_user" {}
variable "pm_password" { sensitive = true }
variable "pm_host" {}
variable "ssh_key" { default = "~/.ssh/id_rsa.pub" }После установки провайдера командой terraform init можно описывать инфраструктуру также как при работе с облачными поставщиками.
Ссылаться на созданный ранее облачный шаблон можно как по названию через clone, так и по его идентификатору через clone_id.
Пример создания виртуального сервера, доступного только в приватной сети:
locals {
debian_template = "debian12-cloudinit"
debian_template_id = 9001
}
resource "proxmox_vm_qemu" "internal-vm" {
vmid = 310
name = "internal-vm"
target_node = "pve-1"
clone = local.debian_template
onboot = true
cpu {
cores = 1
}
memory = 512
boot = "order=scsi0"
scsihw = "virtio-scsi-single"
agent = 1
vm_state = "running"
automatic_reboot = true
ciupgrade = false
ipconfig0 = "ip=10.0.2.11/24,gw=10.0.2.1"
skip_ipv6 = true
ciuser = "cloud"
sshkeys = file(var.ssh_key)
serial {
id = 0
}
disks {
scsi {
scsi0 {
disk {
storage = "local-lvm"
size = "8G"
}
}
}
ide {
ide1 {
cloudinit {
storage = "local-lvm"
}
}
}
}
network {
id = 0
bridge = "vmbr1"
model = "virtio"
}
}Для создания сервера, доступного в локальной сети, необходимо указать дополнительный bridge и его конфигурацию:
# ...
resource "proxmox_vm_qemu" "jump-host" {
vmid = 110
name = "jump-host"
target_node = "pve-1"
clone_id = local.debian_template_id
# ...
ipconfig0 = "ip=10.0.2.10/24,gw=10.0.2.1"
ipconfig1 = "ip=dhcp"
# ...
network {
id = 0
bridge = "vmbr1"
model = "virtio"
}
network {
id = 1
bridge = "vmbr0"
model = "virtio"
macaddr = "AA:24:11:75:01:10"
}
}MAC-адрес VM фиксируется для создания статических IP на стороне DHCP сервера. Созданные выше VM доступны по SSH через jump-host под пользователем cloud и SSH-ключу, указанному в переменных Terraform.
Особенности работы с кластером PVE
Для управления инфраструктурой кластера можно подключаться к API любой его ноды. В PVE нет «ведущего» хоста. Через подключение к API одной ноды можно создавать и модифицировать ресурсы на других нодах.
Однако если в состоянии Terraform есть ресурсы, которые находятся на выключенной ноде, TF будет падать с ошибкой. Поэтому при использовании Terraform, должны быть доступны все ноды кластера с управляемыми ресурсами.
Ошибка кворума
Для тестовой имитации распределённых систем я использую кластер из двух хостов. Большую часть времени мне не нужны оба хоста PVE, и включен только один.
По умолчанию такой подход приводит к ошибке «cluster not ready - no quorum?» и полной неработоспособности Proxmox.
Чтобы кластер мог работать даже с одной нодой, необходимо выполнить команду:
pvecm expect 1Сделать настройку персистентной можно, добавив в файл /etc/pve/corosync.conf три строчки:
# ...
quorum {
# ... defaults
expected_votes: 1
two_node: 0
wait_for_all: 0
}
# ...Работа с шаблонами
Так как образ шаблона VM хранится в локальной файловой системе, необходимо создать его на каждом хосте.
Proxmox ожидает уникальные идентификаторы серверов и шаблонов на уровне кластера, поэтому:
- при подготовке шаблона на каждом хосте необходимо изменять его идентификатор;
- при использовании
clone_idв Terraform необходимо указывать идентификатор шаблона, который находится на указаннойtarget_node.
