Резервне копіювання віртуальних машин в середовищі гіпервізора QEMU/KVM

Як відомо, бекапи потрібно робити, мало того, потрібно робити їх так, щоб потім з них можна було розгорнуться. Особливо це стосується віртуальних машин (ВМ). Розглянемо, як можна зробити бекап віртуальних дисків машини в середовищі QCOW/KVM. Основних проблем тут дві: по-перше, потрібно отримати консистентый (цілісний) бекап, тобто якщо у нас є СУБД або інше, яке активно використовує власний кеш на запис, то перед бекапом його потрібно попросити скинути кеш і заморозити запис на диск, інакше дані в снепшот потраплять, але не ті, і при відновленні СУБД може не зрозуміти такий фінт. Друге питання — продуктивність ВМ в режимі снэпшота, непогано було б, що б ВМ не дуже гальмувала, коли ми знімаємо копію, і не зависала б, коли ми видаляємо снепшот.

Відразу дам відповідь на перше питання — щоб отримати консистентный бекап, потрібно перед створенням бекапа вимкнути ВМ засобами гостьової ОС, тоді бекап точно вийде цілісним. Якщо вас влаштовує така ситуація — статтю можна далі не читати. Якщо ж ні — прошу під кат.

Отже, щоб отримати консистентный бекап, не вимикаючи ВМ, потрібно використовувати гостьовий агент для QEMU (не плутати з гостьовим агентом для QEMU SPICE і паравиртуальными драйверами для QEMU взагалі). В репозиторії Debian Jessie це пакет qemu-guest-agent, в Wheezy пакет доступний тільки через wheezy-backports. QEMU guest agent — це невелика утиліта, яка приймає команди від хоста через virio-канал з ім'ям org.qemu.guest_agent.0 і виконує їх контексту гостя. На стороні гіпервізора канал закінчується unix-сокетом, який можна писати текстові команди за допомогою утиліти socat. Libvirt, правда, любить сама займає цей канал, так що у випадку, якщо ви для управління гіпервізором використовуйте Libvirt, спілкуватися з гостем доведеться через команду «virsh qemu-agent-command». Команди QEMU guest agent вміє різні, ось, наприклад, мій список:

  • guest-set-vcpus
  • guest-get-vcpus
  • guest-network-get-interfaces
  • guest-suspend-hybrid
  • guest-suspend-ram
  • guest-suspend-disk
  • guest-fstrim
  • guest-fsfreeze-thaw
  • guest-fsfreeze-freeze
  • guest-fsfreeze-status
  • guest-file-flush
  • guest-file-seek
  • guest-file-write
  • guest-file-read
  • guest-file-close
  • guest-file-open
  • guest-shutdown
  • guest-info
  • guest-set-time
  • guest-get-time
  • guest-ping
  • guest-sync
  • guest-sync-delimited
Короткий опис команд є у файлі qga/qapi-schema.josn в исходниках QEMU, а повне можна отримати шляхом аналізу файлів qga/commands-posix.c і qga/commands-win32.c. З аналізу можна, наприклад, дізнатися, що команди guest-set-vcpus, guest-get-vcpus, guest-network-get-interfaces, guest-suspend-hybrid, guest-suspend-ram, guest-suspend-disk під Windows не підтримуються, а команди guest-fsfreeze-freeze/guest-fsfreeze-thaw намагаються під Windows використовувати тіньове копіювання тому — VSS. Однак, оскільки в статті мова піде про Linux в якості гостя, нас ці тонкощі стосуватися не будуть.

Серед усього списку команд нас цікавлять guest-fsfreeze-freeze та guest-fsfreeze-thaw. Як випливає з назви, перша з них «заморожує» файлову систему гостя, а друга «розморожує» її. Команда (точніше IOCTL) fsfreeze — це не фіча QEMU, а здатність віртуальної файлової системи гостя, яка появиласть в ядрі Linux досить давно. Тобто, заморожувати ФС можна не тільки у віртуальному середовищі, але і на реальному залозі, досить скористатися утилітою fsfreeze з пакету util-linux. Man-е fsfreeze сказано, що підтримуються Ext3/4, ReiserFS, JFS, XFS, але у мене fsfreeze «заморозив» і Btrfs. Перед власне «заморожуванням», але вже після того, як відпрацювали всі потоки записи, кодом ядра вызвается sync() (файл fs/super.c, рядок 1329), так що за цілісність даних можна не турбуватися. Взагалі, «заморожування» ФС потрібна ядра для отримання цілісних снэпшотов LVM-томів, а не для сумнівних забав з системами віртуалізації.

Отже, ми знаємо, що для отримання цілісного снэпшота нам потрібно з гостя з допомогою QEMU guest agent викликати функцію guest-fsfreeze-freeze. Однак, бути може, ми даремно хвилюємося і ця функція вызвается при створенні снэпшота? На жаль, і для Libvirt (2.9), і для Proxmox (гілка pvetest), і для Openstack це не так, а значити, щоб автоматизувати виклик функції guest-fsfreeze-freeze треба правити вихідні коди відповідних продуктів, що виходить за рамки цієї статті.

Припустимо, ми знайшли спосіб (наприклад, самописным скриптом), «заморожувати» ФС гостя перед зняттям снэпшота. Тепер перед нами стоїть наступне завдання — оповістити гостьове ЗА безпосередньо перед заморожуванням. QEMU guest agent підтримує параметр-F, який говорить про те, що перед «заморожуванням» і після «розморожування» потрібно викликати скрипт /etc/qemu/fsfreeze-hook і параметрами freeze і thaw відповідно. Тому в Debian скрипт запуску агента (/etc/init.d/qemu-guest-agent) доведеться поправити: DAEMON_ARGS="-F". Майте на увазі, що якщо скрипт завершиться з помилкою, «заморозки» ФС не відбудеться.

Для MySQL сервера перший прийшов на розум, але непрацюючий скрипт може виглядати приблизно так:

#!/bin/bash

USER="<Користувач>"
PASSWORD="<Пароль>"


case "$1" in
freeze ) 
/usr/bin/mysql-u $USER-p$PASSWORD-e "FLUSH TABLES READ WITH LOCK;"
exit $?
;;

thaw ) 
/usr/bin/mysql-u $USER-p$PASSWORD-e "UNLOCK TABLES;"
exit $?
;;

* ) 
logger Fsfreeze script has activated with unknown parameter: $1
exit 1
;;
esac

exit 1


Насправді блокування з бази буде знята відразу по завершенні команди
mysql-u $USER-p$PASSWORD-e "FLUSH TABLES READ WITH LOCK"
з-за того, що всі блокування в MySQL працюють лише поки користувач, поставив їх, є в системі. Для правильного бекапа доведеться писати невеликий додатковий сервіс (наприклад, на Python), який буде відкривати базу MySQL і ставити блокування по команді freeze, а потім не закривати базу і чекати команду thaw.

А як йдуть справи з Windows в ролі гостя?Треба сказати, що для Windows і MS SQL ця ж процедура не вимагає ніяких теложвижений — QEMU guest agent автоматично викликає відповідну функцію служби тіньового копіювання тому VSS, VSS інформує всіх передплатників про те, що ось-ось почнеться бекап і непогано було б «скинутися» на диск і т. п.

Отже, ми заблокували MySQL-таблиці та «заморозили» ФС гостя, прийшла пора знімати бекап. Припустимо, що ми зберігаємо образи дисків ВМ у файлах формату qcow2, а не, наприклад, у вигляді LVM-томів. Навіть у цьому випадку нам пропонується безліч варіантів, непогано б у них розібратися.

Internal QEMU snapshot External QEMU snapshot QEMU backup Снепшот LVM-тома з файлами qcow2 Снепшот ФС Brtfs з файлами qcow2
Засіб QEMU QEMU QEMU ОС ОС
Команда QEMU savevm/snapshot_blkdev_internal snapshot_blkdev drive_backup
Команда libvirt/virsh snapshot-create/snapshot-create-as snapshot-create/snapshot-create-as
Команда ОС lvcreate btrfs subvolume snapshot
Вигляд Записи всередині образу диска Окремий файл — образ диска Окремий файл — образ диска Блочний пристрій ФС (каталог) з образами дисків
Область Конкретна ВМ Конкретна ВМ Конкретна ВМ Всі сховище Всі сховище
Технологія Перенаправлення запису в іншу область того ж файлу Перенаправлення запису в інший файл Повне копіювання дисків машини в інший файл Копіювання оригінальних даних на пристрій снэпшота при їх зміні Перенаправлення запису в іншу область файлової системи
Копіювання снэпшота в сховище резервних копій qemu-nbd/нбд-client Копіювання файлу Копіювання файлу Монтування снэпшота, копіювання файлу Копіювання файлу
Швидкодія дисків ВМ на запис, коли снепшот створений Середнє (на кожну запис потрібно зробити два sync(), опція qcow2:lazy refcounts покращує ситуацію) Висока Висока Нижче звичайного приблизно в 2 рази Висока
Навантаження на сховище при видаленні (commit) снэпшота Нижче середньої (потрібно перезаписати метадані) Висока (потрібно скопіювати дані в вихідний образ і записати метадані) Низька (потрібно видалити файл) Низька (потрібно видалити блочний пристрій) Нижче середньої (потрібно перезаписати метадані)
Навантаження на сховище при відкаті (rollback) на снепшот Нижче середньої (потрібно перезаписати метадані) Низька (потрібно видалити файл) Низька для Libvirt (потрібно замінити файл), висока для Proxmox (потрібно розпакувати файл з архіву) Висока (потрібно скопіювати дані на вихідне блочний пристрій) Нижче середньої (потрібно перезаписати метадані)
У кожного способу є свої полюси і мінуси. Так, спосіб «Internal» є, по-суті, стандартом в утиліті Virt-Manager і середовищі Proxmox, і отримання снэпшотов такого формату автоматизовано. Однак для того, щоб «витягнути» снепшот з файлу, потрібно підняти NBD-сервер на базі qemu-nbd і підключити файл образу до нього. У способі «External» готовий скопіювати файл з резервної копалень виходить в процесі створення снэпшота, але процес видалення снэпшота непростий і передбачає «повернення» (block-commit) записаних даних з файлу снэпшота в базовий образ, що супроводжується кратним збільшенням навантаження на запис в процесі видалення снэпшота. Приміром, VMWare ESXi в такій же ситуації «просідає» по продуктивності на запис в 5 разів.. Треба сказати, що є ще й інший спосіб видалення снэпшота типу «External» — копіювання всіх блоків з вихідний образу в снепшот. Спосіб цей називається block-stream, про доцільність застосування його в продакшені судити не беруся, але, очевидно, для сховища це буде непоганий бенчмарк.

Снепшот LVM-тома викликає падіння продуктивності основного тома на запис, так що його краще використовувати тоді, коли ми впевнені, що під час існування снэпшота на диск не будуть інтенсивно писати.

Великі перспективи відкриває використання в якості файлової системи для сховища образів дисків файлової системи BTRFS, оскільки в цьому випадку снэпшоты, стиснення і дедупликация забезпечуються самою архітектурою ФС. Мінуси — Btrfs не можна використовувати в якості поділюваної ФС в кластерному середовищі, крім того, Btrfs — відносно нова ФС і, можливо, вона менш надійна, ніж зв'язка LVM і ext4.

Метод отримання бекапів командою drive_backup хороший тим, що можна відразу створити резервну копію на примонтированном віддаленому сховищі, проте в цьому випадку він створює велике навантаження на мережу. Для інших способів можна передбачити передачу тільки змінених блоків з допомогою rsync. На жаль, QEMU backup не підтримує передачу тільки «брудних» (змінені з моменту останнього бекапа) блоків, як це реалізовано, наприклад, у механізмі VMWare CBT. Обидві спроби реалізувати такий механізм -livebackup і in-memory dirty bitmap не були прийняті в основну гілку QEMU, судячи з усього, перший — через архітектури (додається зайвий демон і окремий мережний протокол тільки для цієї операції), другий — з-за очевидних обмежень у застосуванні: карта «брудних» блоків може зберігатися тільки в оперативній пам'яті.

На закінчення розглянемо ситуацію, при якій ВМ має кілька підключених образів дисків. Очевидно, для такої ВМ потрібно створювати снэпшоты всіх дисків одночасно. Якщо ви використовуйте Libvirt, то вам нема чого хвилюватися — бібліотека бере всі турботи по синхронізації снэпшотов на себе. Але, якщо ви хочете виконати цю операцію на «чистому» QEMU, то існує два способи зробити це: зупинити ВМ командою stop, отримати снэпшоты, а потім продовжити виконання ВМ командою cont або ж використовувати механізм транзакційного виконання команд, наявний у QEMU. Можна використовувати для цієї мети тільки гостьовий агент QEMU і команди guest-fsfreeze-freeze/guest-fsfreeze-thaw, оскільки хоч агент і «заморожує» всі змонтовані ФС за одну команду, проте робить це не одночасно, а послідовно, так що можлива асинхронність між томами.

Якщо ви знайшли у статті помилку, або ж вам є, що сказати — прошу в коментарі.

Робіть бекапи, панове!

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.