Бэкап OpenWrt-хостов и их виртуалок в Proxmox Backup Server
Часть моих коробок на OpenWrt — x86-машины, на которых под голым QEMU крутится ещё одна-две виртуалки. Всё остальное в хоумлабе бэкапится в Proxmox Backup Server, значит, и они должны. Этим занимаются два пакета из моего фида для OpenWrt.
Клиент
Для бэкапов в PBS нужен proxmox-backup-client, а он на Rust, и в OpenWrt
его нет. Пакет proxmox-backup
в фиде собирает из исходников upstream только клиент, с несколькими патчами:
сборка cargo только клиента, без libsystemd и с системным zstd. Пока я
проверял его только на x86_64.
pbs-helper: бэкап самого хоста
pbs-helper
добавляет pbs-backup, который бэкапит корневую файловую систему роутера
как архив .pxar. Настройка — через UCI:
# /etc/config/pbs
config proxmox_backup_server 'pbs_server'
option enabled '1'
option repository 'backup@pbs@pbs.example.org:datastore'
option password '...'
option backup_name 'rootfs'
option prefix '/'
list backup_arg '--exclude=/tmp'
option hc_ping_url 'https://hc-ping.com/<uuid>'
option gotify_url 'https://gotify.example.org'
option gotify_app_token '...'
и строчка в cron вроде 0 1 * * * /usr/bin/pbs-backup.
Несколько деталей, которые мне важны:
- Пароль не попадает в командную строку.
proxmox-backup-clientчитает его черезPBS_PASSWORD_CMD, который вызываетuci get, так что он не виден ни вps, ни во временных файлах. - Два независимых отчёта. Healthchecks
получает
/startперед бэкапом и/<код выхода>после; Gotify — сообщение в Markdown. Любой можно не настраивать, и сбой одного никак не влияет на другой. В отчёт идут начало и конец лога — что бэкапилось и чем закончилось, — а тысячи строк про загрузку чанков посередине выбрасываются, так что отчёт укладывается в лимит Healthchecks в 10 кБ. - Логи идут в syslog под cron и в терминал при ручном запуске.
За ним стоит pbs.helper — небольшой ucode-модуль, который запускает задачи
и общий с бэкапами виртуалок ниже. Все побочные эффекты — запуск команд,
HTTP, логирование — внедряются снаружи, так что он покрыт юнит-тестами без
всякого роутера.
qemu-instance-init: виртуалки как сервисы
qemu-instance-init вырос из init-скрипта из вики OpenWrt, переписанного под procd. Каждая VM — секция UCI:
# /etc/config/qemu
config instance 'vm1'
option vcpus '2'
option ram '2G'
option root_disk '/dev/vg0/vm1'
option root_disk_type 'lvm'
option net0_mac '52:54:00:12:34:56'
option net0_bridge 'br-lan'
option guest_agent '1'
option backup '1'
option backup_server 'pbs_server'
procd запускает гостей и перезапускает при падении. При остановке скрипт
отправляет ACPI system_powerdown через QMP и даёт гостю время спокойно
выключиться, прежде чем procd его убьёт. UEFI-гости работают с OVMF, а сокет
гостевого агента включается одной опцией.
Пакет есть в двух вариантах. qemu-instance-init — только init-скрипт.
qemu-instance-init-pbs добавляет /etc/init.d/qemu backup [instance],
который работает через тот же pbs.helper с теми же отчётами в Healthchecks
и Gotify.
Консистентные бэкапы работающих VM
Если читать диск работающей виртуалки, получится бэкап диска, который
менялся, пока его читали. Вариант -pbs держит его неподвижным:
- Заморозить файловые системы гостя через гостевой агент: гость сбрасывает кэши и перестаёт писать.
- Снять снапшот диска:
- для дисков на LVM —
lvcreate -s.kmod-dmв OpenWrt собран без поддержки снапшотов, поэтому в фиде есть небольшой пакетkmod-dm-snapshot; - для qcow2 — временный overlay через QMP
blockdev-snapshot-sync: запись гостя идёт в overlay, а базовый файл перестаёт меняться.
- для дисков на LVM —
- Разморозить сразу же — гость заморожен только на время снапшота.
- Забэкапить снапшот или базовый файл.
- Прибраться: удалить LVM-снапшот или влить overlay обратно через
block-commitи удалить его.
Если чего-то не хватает, бэкап не падает, а деградирует с предупреждением в отчёте: без гостевого агента получится снапшот, консистентный как после внезапного отключения питания, без снапшота — сброс кэшей и потом живой диск. Остатки тоже подбираются: устаревший LVM-снапшот удаляет следующий бэкап, а overlay, брошенный убитым бэкапом, вливается обратно следующим бэкапом или, если VM была остановлена, при следующем старте.