🧠 День 14 — Миграция узла в Proxmox-кластере: безопасный вывод ноды на обслуживание
✎В небольшом домашнем Proxmox-кластере одна из нод начала вести себя нестабильно и потребовала обслуживания.
Задача была «по-взрослому SRE», даже если это homelab:
- вывести ноду из нагрузки без простоя сервисов;
- перенести VM на новый узел;
- укрепить хранилище (ZFS mirror);
- дать критичному VPN-сервису репликацию и регулярный health-check;
- сохранить план отката и пост-проверки.
Важно: материал обезличен. Нодам, VM, подсетям и внешним точкам я использую условные имена.
🗺️ Топология (как это выглядит в голове)
+---------------------+
| Proxmox Cluster |
| (3 nodes total) |
+----------+----------+
|
+----------------+----------------+
| |
+-------v-------+ +-------v-------+
| node-A | | node-B |
| (existing) | | (existing) |
| storage: ... | | storage: ... |
+-------+--------+ +-------+--------+
| \
| \
| \
+-------v--------+ +-----v------+
| node-C | <--- joined --- | NEW NODE |
| (new in cluster)| | ZFS mirror |
| storage: tank1 | | 2x1TB |
+------------------+ +------------+
Critical VM: vpn-gw -> ZFS + replication q=5m
🎯 Контекст
Один из узлов (далее node-A) стал нестабилен и его нужно было вывести на обслуживание.
Но на нём жили VM, которые нельзя «просто выключить и потом разберёмся».
Решение: завести новый узел node-C, аккуратно ввести его в кластер и перенести нагрузку.
✅ Что было сделано (по шагам)
Схема: план миграции (Before → Move → After)
Before:
node-A (unstable) : [vm1][vm2][vpn-gw][...]
node-B (stable) : [vmX][...]
node-C (new) : (empty) + ZFS mirror + storage_id=tank1
Move (batched):
backup -> migrate vm1 -> validate
-> migrate vm2 -> validate
-> migrate vpn-gw (separate window) -> validate
After:
node-A : [empty] (maintenance)
node-B : [vmX][...]
node-C : [vm1][vm2][vpn-gw][...]
1) Подготовили новый узел и безопасный админ-доступ
- добавил SSH-доступ по ключам;
- сделал алиасы для удобного администрирования;
- переключил репозитории на no-subscription;
- обновил систему и перешёл на актуальное ядро (с ребутом);
- перед join проверил совместимость версий Proxmox/пакетов.
Admin plane:
ssh-key -> aliases -> updates -> reboot -> version parity -> cluster join
2) Добавили новый узел в кластер
node-Cвошёл в существующий кластер;- проверил кворум и видимость нод;
- только после этого начал перенос нагрузок.
3) Подготовили хранилище
На новом узле поднял более надёжную схему:
- ZFS mirror (2 диска одинакового объёма);
- единый storage ID, чтобы Proxmox-репликация работала без сюрпризов.
Storage:
disk1 + disk2 => ZFS mirror => storage_id: tank1
Replication requires:
source.storage_id == target.storage_id
4) Страховка: бэкапы перед миграцией
Перед переносом целевых VM сделал страховочные бэкапы.
Это не «оверкилл», это контроль:
- если миграция пойдёт не так, есть понятный план отката;
- проще принимать инженерные решения, когда есть подушка.
5) Перенесли VM с обслуживаемой ноды
- основную массу VM перенёс в рабочем режиме;
- «сетевую appliance VM» переносил в отдельное окно и с доп.проверками.
ASCII как процесс (упрощённо):
Before:
node-A: [vm1][vm2][vpn-gw][...]
node-B: [vmX][...]
node-C: [empty]
Migrate:
backup -> move vm1 -> validate -> move vm2 -> validate -> move vpn-gw -> validate
After:
node-A: [empty / maintenance]
node-B: [vmX][...]
node-C: [vm1][vm2][vpn-gw][...]
6) Почистили «хвосты» репликации и выровняли версии
После миграций часто остаются старые replication jobs (особенно если VM меняли storage/узел).
Сделал:
- отключил/удалил устаревшие задания;
- выровнял версии пакетов и ядра между активными нодами.
7) Укрепили отказоустойчивость для VPN VM
Схема: репликация критичной VPN VM
writes
vpn-gw VM -----> tank1 (ZFS mirror) [node-C]
|
| (replication job, every 5m)
v
tank1 (same storage_id) on target node
Fail case: node-C down
-> promote replicated dataset
-> start vpn-gw VM on another node
-> restore admin access
Критичный сервис — это VPN-шлюз/точка доступа, на которую завязаны админ-доступы.
Сделал:
- перевёл диски VPN VM на ZFS (
tank1); - включил репликацию каждые 5 минут.
vpn-gw VM
storage: tank1 (ZFS)
replication: every 5m
Goal:
"если node-C умер" -> быстро поднять vpn-gw на другой ноде
8) Выровняли сеть (чтобы после миграции не ловить фантомные баги)
Сверил между узлами:
- DNS
- статические маршруты
- bridge-конфигурацию
Это важно: иногда VM «переезжает», а проблема оказывается не в VM, а в мелком различии сетевых параметров хоста.
9) Автообновления и health-check
- настроил ночные автообновления пакетов;
- добавил daily health-check с логированием и уведомлениями.
🧪 Технические нюансы (что всплыло)
-
Не все диски «просто мигрируются» между
lvmthinиzfspool.
Иногда нужен обходной сценарий (бэкап/restore или промежуточное хранилище). -
После миграции остаются «хвосты» replication jobs.
Их лучше пересоздавать, а не пытаться «починить по месту». -
Для штатной Proxmox-репликации важен единый storage ID.
Если на одной нодеtank1, а на другойtank-zfs— выстрелишь себе в ногу. -
Проверки после ребута лучше делать с паузой.
В переходном состоянии можно получить ложную картину и начать чинить то, что само бы поднялось.
✅ Итог
- Нестабильная нода выведена из нагрузки без потери VM.
- Сервисы продолжили работать на остальных узлах.
- Хранилище стало надёжнее (ZFS mirror на активной ноде).
- Критичный VPN получил репликацию и регулярный мониторинг.
- Кластер стал более предсказуемым по версиям, сети и storage.
🧠 Практический вывод
Даже в небольшом homelab имеет смысл делать такие работы как в проде:
- миграции только через бэкап + валидацию;
- storage IDs проектировать заранее;
- иметь ежедневный health-check;
- работать с окном обслуживания и планом отката.
✅ Мини-чеклист (как шаблон на будущее)
[Before]
- confirm: backups exist
- confirm: cluster quorum ok
- confirm: versions aligned
- confirm: target storage_id matches
[Move]
- migrate in batches
- validate service after each move
- move critical VM in separate window
[After]
- cleanup replication jobs
- validate routes/DNS/bridges
- enable replication for critical VM
- enable health-check + alerts