Как сделать бэкап линукса

Бэкап Linux и восстановление его на другом железе

Я работаю в организации с маленьким штатом, деятельность тесно связана с IT и у нас возникают задачи по системному администрированию. Мне это интересно и частенько я беру на себя решение некоторых.

На прошлой неделе мы настраивали FreePBX под debian 7.8, нанимали фрилансера. В процессе настройки оказалось, что сервер (да, я так называю обычный PC) не хочет грузится с HDD при подключенных USB 3G модемах, которые мы используем для звонков на мобильные, колупание BIOSа не помогло. Непорядок. Решил, что нужно перенести его на другую железяку. Так появилось сразу две связанные задачи:

  • сделать бэкап сервера;
  • восстановить бэкап на другом железе.

Гугление не дало внятных ответов, как это сделать, пришлось собирать информацию кусками и пробовать. Всякие acronis’ы отбросил сразу, ибо не интересно.

Опыт общения с linux-системами у меня небольшой: настройка VPN сервера на open-vpn, ftp-сервера и еще пара мелочей. Сам себя я характеризую как человека умеющего читать маны и править конфиги 🙂

Ниже я описываю свой частный случай и почему я поступил именно так. Надеюсь, новичкам будет полезно, а бородатые админы улыбнутся вспомнив молодость.

Начинаем копать теорию:

Второй способ требует наличия внешнего жесткого диска объемом не меньше раздела, который архивируем. Да и что с ним потом делать, непонятно, хранить на полочке? Остановился на tar, чуть сложнее в реализации, нужно будет создать MBR, но время создания/восстановления архива существенно меньше, хранить бэкап проще, полтора гига можно закинуть в облако и скачать, когда будет нужно. Записывать его можно на ту же live-флэшку, с которой буду грузиться.

Итак, план действия:

1. Создание бэкапа

Грузимся с live-флэшки, у меня это debian-live-7.8.0-amd64-standard.

Переключаемся на root:

Монтируем раздел, который будем архивировать, у меня это sda1, чтобы случайно не наломать дров, монтируем только для чтения. Посмотреть все свои разделы можно при помощи команд ls /dev | grep sd или df -l

Наша флэшка уже примонтирована, но в режиме только чтения, нужно перемонтировать для чтения-записи, чтобы писать туда бэкап.

Все готово для создания архива

Здесь у нас параметры: c — создать архив, v — выводить информацию о процессе, z — использовать сжатие gzip, p — сохраняем данные о владельцах и правах доступа, f — пишем архив в файл, путь к файлу, —exclude — исключаем из архива каталог (я исключил каталоги с записями разговоров и каталог с бэкапами FreePBX), /mnt/ — каталог, который архивируем.

Ждем… у меня вся подготовка и создание архива заняли 10 минут. Будь флэшка быстрее, уложился бы в 7-8 минут.

Складываем архив в надежное место за пределами офиса.

Восстановление бэкапа на другом железе

2. Размечаем диск, создаем файловую систему

Грузимся с live-флэшки, у меня все та же debian-live-7.8.0.

Переключаемся на root:

Размечаем диск. Мне понравилась утилита с псевдографическим интерфейсом cfdisk. Там все просто и понятно.

Удаляем все имеющиеся разделы. Я создал два новых раздела, один на 490 Gb под / (sda1) и 10 Gb под swap (sda2) в конце диска, т.к. он практически не будет задействован. Проверим типы разделов. Который под систему должен иметь тип 83 Linux, второй — 82 Linux swap / Solaris. Помечаем системный раздел загрузочным (bootable), сохраняем изменения и выходим.

Cоздаем файловую систему на первом разделе.

3. Распаковываем архив.

Монтируем отформатированный раздел

Распаковываем архив прямо с флэшки

Параметр —same-owner — сохраняет владельцев у распаковываемых файлов, x — извлекаем из архива, v — выводить информацию о процессе, p — сохраняем права доступа, f — указываем файл, который распаковываем, C — распаковываем в категорию.

4. Создаем MBR на новом диске.

Чтобы корректно создать загрузочную запись, монтируем рабочие каталоги к нашему будущему root-каталогу, у меня это /mnt. Каталоги /dev и /proc сейчас используются live-системой, используем параметр bind, чтобы они были доступны сразу в двух местах:

Переключаемся на новую систему используя chroot:

Делаем swap-раздел для новой системы:

Подключаем его же:

Чтобы grub работал, нужно указать ему правильные UUID разделов в fstab, сейчас там прописаны разделы предыдущей системы:

Открываем второй терминал (Alt+F2) под root:

И видим текущие UUID разделов.

Вручную переписываем их в fstab переключаясь между Alt+F1 и Alt+F2. Да, муторно, но попытки копировать занимали у меня больше времени, чем переписывание. Сохраняем fstab.

Устанавливаем grub2. У меня один физический диск, поэтому ставим его на sda:

На чистый диск должно встать без ошибок. Обновляем информацию из fstab:

Возвращаемся в Live-систему:

Размонтируем все каталоги:

Если вылазят процессы, которые используют эти каталоги, убиваем их используя fuser.

Все, поехали. Грузимся с жесткого диска:

Здесь статья должна была закончиться, но у меня возникли проблемы с подключением к интернету. Сервер видит сеть, видит компьютеры в ней, но в интернет не ходит… а это как бы важно для телефонии.

5. Тестирование и устранение неполадок.

Показывет интерфейсы eth1 и lo, гугление сказало, что gateway можно прописать только подключению eth0, остальные рассчитаны только на работу внутри сети.

Похоже, отсутствие eth0 вызвано способом переноса системы. Находим файл, который отвечает за нумерацию интерфейсов, смотрим туда:

Действительно, там два активных интерфейса, определенных MAC’ами. Комментируем первый, второму прописываем eth0.

Перезапуск /etс/init.d/networking не помог, поэтому перезагружаемся:

Подключаем донглы, проверяем, все работает.
Спасибо за внимание.

Источник

Как сделать резервную копию всей системы Linux с помощью Rsync

Сделать бэкап Linux системы можно с помощью Rsync командой всего в одну строку. Хотя у rsync есть много параметров для резервного копирования, но нам данной строки достаточно для полного копирования системы. Кроме того, этот метод намного лучше, чем клонирование диска с помощью команды dd. Потому что, для rsync неважно, имеет ли ваш HDD другой размер или использует другую файловую систему. Этот метод будет работать практически на всех Linux-системах, в которых установлен rsync. В этом кратком how-to я расскажу, как создать полную резервную копию системы Linux с помощью утилиты Rsync.

Для начала, нам нужен резервный носитель (USB-флешка или внешний жесткий диск). Далее нужно узнать имя диска, используя команду «fdisk -l». В моем случае диск определился как /dev/sdb1. Теперь примонтируем наш резервный накопитель в любое удобное место, я выбрал /mnt.

Чтобы создать резервную копию всей системы, все, что вам нужно сделать, это открыть терминал. Затем запустить в нем следующую команду от имени пользователя root:

Эта команда создаст резервную копию всей корневой директории (/), исключая директории /dev, /proc, /sys, /tmp, /run, /mnt, /media, lost + found в папке / mnt.

Читайте также:  Tnt ��� mac os

Давайте разберём вышеуказанную команду и узнаем, для чего каждый параметр.

  • rsync — быстрая, универсальная, локальная и удаленная утилита копирования файлов.
  • -aAXv — файлы передаются в режиме «архив», что гарантирует сохранение символических ссылок, устройств, разрешений, владельцев, времени модификации, списков ACL и расширенных атрибутов.
  • / — Исходный каталог.
  • —exclude — исключает заданные каталоги из резервной копии.
  • /mnt — это папка назначения резервного копирования.

Обязательно исключите целевой каталог, если он существует в локальной системе, иначе будет бесконечный цикл копирования.

Чтобы восстановить систему из резервной копии, просто измените исходные и целевые пути в приведенной выше команде.

Помните, что это подходит только для локальных и автономных систем. Если ваша система активно используется некоторыми другими системами в сети, то это не лучшее решение. Потому что содержимое этих систем может постоянно обновляться каждую минуту, а некоторые файлы могут меняться в процессе rsync. Например, когда rsync достигнет файла 2, содержимое предыдущего файла (Файл 1) может быть изменено. Это приведет к ошибке с зависимостями, когда вам понадобится использовать эту резервную копию. В таких случаях резервное копирование на основе снимков является лучшим подходом. Поскольку система будет «заморожена» до того, как процесс резервного копирования запустится и будет «разморожен», когда процесс резервного копирования завершится, поэтому все файлы будут согласованы.

На этом все. Надеюсь кому-то это поможет. Если считаете наши статьи полезными, поделитесь ими в своих социальных и профессиональных сетях, чтобы другие пользователи могли также воспользоваться ими. Мы будем продолжать публиковать более полезные статьи как можно чаще. Оставайтесь с нами.

Источник

Бэкап для Linux, или как создать снапшот

Всем привет! Я работаю в Veeam над проектом Veeam Agent for Linux. С помощью этого продукта можно бэкапить машину с ОС Linux. «Agent» в названии означает, что программа позволяет бэкапить физические машины. Виртуалки тоже бэкапит, но располагается при этом на гостевой ОС.

Вдохновением для этой статьи послужил мой доклад на конференции Linux Piter, который я решил оформить в виде статьи для всех интересующихся хабражителей.

В статье я раскрою тему создания снапшота, позволяющего произвести бэкап и поведаю о проблемах, с которыми мы столкнулись при создании собственного механизма создания снапшотов блочных устройств.

Всех заинтересовавшихся прошу под кат!

Немного теории в начале

Исторически так сложилось, что есть два подхода к созданию бэкапов: File backup и Volume backup. В первом случае мы копируем каждый файл как отдельный объект, во втором – копируем всё содержимое тома в виде некоего образа.

У обоих способов есть масса своих плюсов и минусов, но мы рассмотрим их через призму восстановления после сбоя:

  • В случае File backup для полноценного восстановления сервера целиком, нам потребуется вначале установить ОС, потом — необходимые сервисы и только после этого восстановить файлы из бэкапа.
  • В случае же Volume backup для полного восстановления достаточно просто восстановить все тома машины без лишних усилий со стороны человека.

Очевидно, что в случае Volume backup восстановить систему можно быстрее, а это важная характеристика системы. Поэтому, для себя отмечаем volume backup как более предпочтительный вариант.

Как же нам взять и сохранить весь том целиком? Само собой, простым копированием мы ничего хорошего не добьёмся. Во время копирования на томе будет происходить какая-то активность с данными, в итоге в бэкапе окажутся несогласованные данные. Структура файловой системы будет нарушена, файлы баз данных повреждены, как и прочие файлы, с которыми во время копирования будут производиться операции.

Чтобы избежать всех этих проблем, прогрессивное человечество придумало технологию моментального снимка – снапшота. В теории всё просто: создаём неизменную копию – снапшот – и бэкапим данные с него. Когда бэкап окончен – снапшот уничтожаем. Звучит просто, но, как водится, есть нюансы.

Из-за них было рождено множество реализаций этой технологи. Например, решения на основе device mapper, такие, как LVM и Thin provisioning, обеспечивают полноценные снапшоты томов, но требуют специальной разметки дисков ещё на этапе установки системы, а значит, в общем случае не подходят.

BTRFS и ZFS дают возможность создавать моментальные снимки подструктур файловой системы, что очень здорово, но на данный момент их доля на серверах невелика, а мы пытаемся сделать универсальное решение.

Предположим, на нашем блочном девайсе есть банальная EXT. В этом случае мы можем использовать dm-snap (кстати, сейчас разрабатывается dm-bow), но тут — свой нюанс. Нужно иметь на готове свободное блочное устройство, чтобы было куда отбрасывать данные снапшота.
Обратив внимание на альтернативные решения для бэкапа, мы заметили что они, как правило, используют свой модуль ядра для создания снапшотов блочных устройств. Этим путём решили пойти и мы, написав свой модуль. Решено было распространять его по GPL лицензии, так что в открытом доступе он доступен на github.

How it works — в теории

Снапшот под микроскопом

Итак, теперь рассмотрим общий принцип работы модуля и более подробно остановимся на ключевых проблемах.

По сути, veeamsnap (так мы назвали свой модуль ядра) – это фильтр драйвера блочного устройства.

Его работа заключена в перехвате запросов к драйверу блочного устройства.

Перехватив запрос на запись, модуль производит копирование данных с оригинального блочного устройства в область данных снапшота. Назовём эту область снапсторой.

А что такое сам снапшот? Это виртуальное блочное устройство, копия оригинального устройства на конкретный момент времени. При обращении к блокам данных на этом устройстве они могут быть считаны либо со снапсторы, либо с оригинального устройства.

Хочу отметить, что снапшот – это именно блочное устройство, полностью идентичное оригинальному на момент снятия снапшота. Благодаря этому мы можем смонтировать файловую систему на снапшоте и произвести необходимый предпроцессинг.

Например, мы можем получить карту занятых блоков от файловой системы. Самый простой способ это сделать – воспользоваться ioctl GETFSMAP.
Данные о занятых блоках позволяют читать со снапшота только актуальные данные.

Также, можно исключить некоторые файлы. Ну, и совсем опциональное действие: проиндексировать файлы, которые попадают в бэкап, для возможности гранулярного рестора в будущем.

CoW vs RoW

Давайте немного остановимся на выборе алгоритма работы снапшота. Выбор тут не особо обширен: Copy-on-Write или Redirect-on-Write.

Redirect-on-Write при перехвате запроса на запись перенаправит его в снапстору, после чего все запросы на чтение этого блока будут уходить туда же. Замечательный алгоритм для систем хранения, построенных на базе В+ деревьев, таких, как BTRFS, ZFS и Thin Provisioning. Технология стара как мир, но особенно хорошо она проявляет себя в гипервизорах, где можно создать новый файл и писать туда новые блоки на время жизни снапшота. Производительность – отличная, по сравнению с CoW. Но есть жирный минус – структура оригинального устройства меняется, а при удалении снапшота надо скопировать все блоки из снапсторы в оригинальное место.

Copy-on-Write при перехвате запроса копирует в снапстору данные, которые должны подвергнуться изменению, после чего позволяет их перезаписать в оригинальном месте. Используется для создания снапшотов для LVM томов и теневых копий VSS. Очевидно, для создания снапшотов блочных устройств он подходит больше, т.к. не меняет структуру оригинального устройства, и при удалении (или аварии) снапшот можно просто отбросить, не рискуя данными. Минус такого подхода – снижение производительности, так как на каждую операцию записи добавляется пара операций чтение/запись.

Читайте также:  Shutdown windows from cmd

Поскольку обеспечение сохранности данных для нас основной приоритет, мы остановились именно на CoW.

Пока всё выглядит просто, поэтому давайте пройдёмся по проблемам из реальной жизни.

How it works — на практике

Согласованное состояние

Ради него всё и задумывалось.
Например, если в момент создания снапшота (в первом приближении можно считать, что создаётся он моментально) в какой-то файл будет производиться запись, то в снапшоте файл окажется недописанным, а значит — повреждённый и бессмысленный. Аналогичная ситуация и с файлам баз данных и самой файловой системой.

Но мы же в 21-м веке живём! Есть же механизмы журналирования, предохраняющие от подобных проблем! Замечание верное, правда, есть важное “но”: эта защита не от сбоя, а от его последствий. При восстановлении в согласованное состояние по журналу незавершённые операции будут отброшены, а значит — потеряны. Поэтому важно сместить приоритет на защиту от причины, а не лечить последствия.

Систему можно предупредить о том, что сейчас будет создан снапшот. Для этого в ядре есть функции freeze_bdev и thaw_bdev. Они дёргают функции файловой системы freeze_fs и unfreeze_fs. При вызове первой система должна сбросить кэш, приостановить создание новых запросов к блочному устройству и дождаться завершения всех ранее сформированных запросов. А при вызове unfreeze_fs файловая система восстанавливает своё нормальное функционирование.

Получается, что файловую систему мы можем предупредить. А что с приложениями? Тут, к сожалению, всё плохо. В то время, как в Windows существует механизм VSS, который с помощью Writer-ов обеспечивает взаимодействие с другими продуктами, в Linux каждый идёт своим путём. На данный момент это привело к ситуации, что задача администратора системы самостоятельно написать (скопировать, украсть, купить, etc) pre-freeze и post-thaw скрипты, которые будут подготавливать их приложение к снапшоту. Со своей стороны, в ближайшем релизе мы внедрим поддержку Oracle Application Processing, как наиболее часто запрашиваемую нашими клиентами функцию. Потом, возможно, будут поддержаны и другие приложения, но в целом ситуация довольно печальна.

Где расположить снапстору?

Это вторая встающая на нашем пути проблема. С первого взгляда проблема не очевидна, но, немного разобравшись, увидим, что это та ещё заноза.

Конечно же, самое простое решение — расположить снапстору в RAM. Для разработчика вариант просто отличный! Всё быстро, очень удобно делать отладку, но есть косяк: оперативка — ресурс ценный, и расположить там большую снапстору нам никто не даст.

ОК, давайте сделаем снапстору обычным файлом. Но возникает другая проблема – нельзя бэкапить том, на котором расположена снапстора. Причина проста: мы перехватываем запросы на запись, а значит, будем и перехватывать свои собственные запросы на запись в снапстору. Кони бегали по кругу, по-научному — deadlock. Следом возникает острое желание использовать для этого отдельный диск, но никто ради нас в сервера диски добавлять не будет. Работать надо уметь на том что есть.

Расположить снапстору удалённо — идея отличная, но реализуема в очень уж узких кругах сетей с большой пропускной способностью и микроскопических латенси. Иначе во время удержания снапшота на машине будет пошаговая стратегия.

Значит, надо как-то хитро расположить снапстору на локальном диске. Но, как правило, всё место на локальных дисках уже распределено между файловыми системами, и заодно надо крепко подумать, как обойти проблему deadlock’a.

Направление для раздумий, в принципе, одно: надо как-то аллоцировать у файловой системы пространство, но с блочным устройством работать напрямую. Решение этой проблемы было реализовано в user-space коде, в сервисе.

Существует системный вызов fallocate, который позволяет создать пустой файл нужного размера. При этом фактически на файловой системе создаются только метаданные, описывающие расположение файла на томе. А ioctl FIEMAP позволяет нам получить карту расположения блоков файла.

И вуаля: мы создаём файл под снапстору с помощью fallocate, FIEMAP отдаёт нам карту расположения блоков этого файла, которую мы можем передать для работы в наш модуль veeamsnap. Далее при обращении к снапсторе модуль делает запросы напрямую к блочному устройству в известные нам блоки, и никаких deadlock’ов.

Но тут есть нюанс. Системный вызов fallocate поддерживается только XFS, EXT4 и BTRFS. Для остальных файловых систем вроде EXT3 для аллокации файла его приходится полностью записывать. На функционале это сказывается увеличением времени на подготовку снапсторы, но выбирать не приходится. Опять таки, работать надо уметь на том что есть.

А что, если ioctl FIEMAP тоже не поддерживается? Это реальность NTFS и FAT32, где нет даже поддержки древнего FIBMAP. Пришлось реализовать некий generic алгоритм, работа которого не зависит от особенностей файловой системы. В двух словах алгоритм такой:

  1. Сервис создаёт файл и начинает записывать в него определённый паттерн.
  2. Модуль перехватывает запросы на запись, проверяет записываемые данные.
  3. Если данные блока соответствуют заданному паттерну, то блок помечается как относящийся к снапсторе.

Да, сложно, да, медленно, но лучше чем ничего. Применяется он в единичных случаях для файловых систем без поддержки FIEMAP и FIBMAP.

Переполнение снапшота

Вернее, заканчивается место, которые мы выделили под снапстору. Суть проблемы в том, что новые данные некуда отбрасывать, а значит, снапшот становится непригоден для использования.
Что делать?

Очевидно, надо увеличивать размер снапсторы. А насколько? Самый простой способ задания размера снапсторы – это определить процент от свободного места на томе (как сделано для VSS). Для тома в 20 TB 10% будет 2TB – что очень много для ненагруженного сервера. Для тома в 200 GB 10% составит 20GB, что может оказаться слишком мало для сервера, интенсивно обновляющего свои данные. А есть ещё тонкие тома…

В общем, заранее прикинуть оптимальный размер требуемой снапсторы может только системный администратор сервера, то есть придётся заставить человака подумать и выдать своё экспертное мнение. Это не соотвествует принципу «It just work».

Для решения этой проблемы мы разработали алгоритм stretch snapshot. Идея состоит в разбиении снапсторы на порции. При этом, новые порции создаются уже после создания снапшота по мере необходимости.

Опять же коротенько алгоритм:

  1. Перед созданием снапшота создаётся первая порция снапсторы и отдаётся модулю.
  2. Когда снапшот создан, порция начнёт заполняться.
  3. Как только половина порции оказывается заполнена, посылается запрос сервису на создание новой.
  4. Сервис создаёт её, отдаёт данные модулю.
  5. Модуль начинает заполнять следующую порцию.
  6. Алгоритм повторяется пока или бэкап не завершится, или пока не упрёмся в лимит использования свободного места на диске.

Важно отметить, что модуль должен успевать создавать новые порции снапсторы по мере необходимости, иначе — переполнение, сброс снапшота и никакого бэкапа. Поэтому, работа такого алгоритма возможна только на файловых системах с поддержкой fallocate, где можно быстро создать пустой файл.

Читайте также:  Работа с документами для mac os

Что делать в других случаях? Пытаемся угадать необходимый размер и создаём всю снапстору целиком. Но по нашей статистике, подавляющее большинство Linux серверов сейчас используют EXT4 и XFS. EXT3 встречается на старых машинах. Зато в SLES/openSUSE можно наткнуться на BTRFS.

Change Block Tracking (CBT)

Инкрементальный или дифференциальный бэкап (кстати, слаще хрен редьки или нет, предлагаю читать тут) – без него нельзя представить ни один взрослый продукт для бэкапа. А чтобы это работало, нужен CBT. Если кто-то пропустил: CBT позволяет отслеживать изменения и записывать в бэкап только изменённые с последнего бэкапа данные.

Свои наработки в этой области есть у многих. Например, в VMware vSphere эта функция доступна с 4-ой версии в 2009 году. В Hyper-V поддержка внедрена с Windows Server 2016, а для поддержки более ранних релизов был разработотан собственный драйвер VeeamFCT ещё в 2012-м. Поэтому, для нашего модуля мы не стали оригинальничать и использовали уже работающие алгоритмы.
Коротенько о том, как это работает.

Весь отслеживаемый том разбит на блоки. Модуль просто отслеживает все запросы на запись, помечая изменившиеся блоки в таблице. Фактически, таблица CBT – это массив байт, где каждый байт соответствует блоку и содержит номер снапшота, в котором он был изменён.
Во время бэкапа номер снапшота записывается в метаданные бэкапа. Таким образом, зная номера текущего снапшота и того, с которого был сделан предыдущий успешный бэкап, можно вычислить карту расположения изменившихся блоков.

Тут есть два нюанса.

Как я сказал, под номер снапшота в таблице CBT выделен один байт, значит, максимальная длина инкрементальной цепочки не может быть больше 255. При достижении этого порога таблица сбрасывается и происходит полный бэкап. Может показаться неудобным, но на самом деле цепочка в 255 инкрементов – далеко не самое лучшее решение при создании бэкап-плана.
Вторая особенность – это хранение CBT таблицы только в оперативной памяти. А значит, при перезагрузке целевой машины или выгрузке модуля она будет сброшена, и опять-таки, понадобится создавать полный бэкап. Такое решение позволяет не решать проблему старта модуля при старте системы. Кроме того, отпадает необходимость сохранять CBT таблицы при выключении системы.

Проблема производительности

Бекап — это всегда хорошая такая нагрузка на IO вашего оборудования. Если на нём и так хватает активных задач, то процесс резервного копирования может превратить вашу систему в этакого ленивца.
Давайте посмотрим, почему.

Представим, что сервер просто линейно пишет какие-то данные. Скорость записи в таком случае максимальна, все задержки минимизированы, производительность стремится к максимуму. Теперь добавим сюда процесс бэкапа, которому при каждой записи надо ещё успеть выполнить алгоритм Copy-on-Write, а это дополнительная операция чтения с последующей записью. И не забывайте, что для бэкапа надо ещё читать данные с этого же тома. Словом, ваш красивый linear access превращается в беспощадный random access со всеми вытекающими.

С этим явно надо что-то делать, и мы реализовали конвейер, чтобы обрабатывать запросы не по одному, а целыми пачками. Работает это так.

При перехвате запросов они укладываются в очередь, откуда их порциями забирает специальный поток. В это время создаются CoW-запросы, которые также обрабатываются порциями. При обработке CoW-запросов вначале производятся все операции чтения для всей порции, после чего выполняются операции записи. Только после завершения обработки всей порции CoW-запросов выполняются перехваченные запросы. Такой конвейер обеспечивает обращения к диску крупными порциями данных, что минимизирует временные потери.

Throttling

Уже на стадии отладки всплыл ещё нюанс. Во время бэкапа система становилась неотзывчивой, т.е. системные запросы ввода-вывода начинали выполняться с большими задержками. Зато, запросы на чтение данных со снапшота выполнялись на скорости, близкой к максимальной.
Пришлось немного придушить процесс бэкапа, реализовав механизм тротлинга. Для этого читающий из образа снапшота процесс переводится в состояние ожидания, если очередь перехваченных запросов не пуста. Ожидаемо, система ожила.

В результате, если нагрузка на систему ввода-вывода резко возрастает, то процесс чтения со снапшота будет ждать. Тут мы решили руководствоваться принципом, что лучше мы завершим бэкап ошибкой, чем нарушим работу сервера.

Deadlock

Я думаю, надо немного подробней объяснить, что это такое.

Уже на этапе тестирования мы стали сталкиваться с ситуациями полного повисания системы с диагнозом: семь бед – один ресет.

Стали разбираться. Выяснилось, что такую ситуацию можно наблюдать, если например создать снапшот блочного устройства, на котором расположен LVM-том, а снапстору расположить на том же LVM-томе. Напомню, что LVM использует модуль ядра device mapper.

В данной ситуации при перехвате запроса на запись, модуль, копируя данные в снапстору, отправит запрос на запись LVM-тому. Device mapper перенаправит этот запрос на блочное устройство. Запрос от device mapper-а снова будет перехвачен модулем. Но новый запрос не может быть обработан, пока не обработан предыдущий. В итоге, обработка запросов заблокирована, вас приветствует deadlock.

Чтобы не допустить подобной ситуации, в самом модуле ядра предусмотрен таймаут для операции копирования данных в снапстору. Это позволяет выявить deadlock и аварийно завершить бэкап. Логика здесь всё та же: лучше не сделать бэкап, чем подвесить сервер.

Round Robin Database

Это уже проблема, подкинутая пользователями после релиза первой версии.
Оказалось, есть такие сервисы, которые только и занимаются тем, что постоянно перезаписывают одни и те же блоки. Яркий пример – сервисы мониторинга, которые постоянно генерируют данные о состоянии системы и перезаписывают их по кругу. Для таких задач используют специализированные циклические базы данных (RRD).
Выяснилось, что при бэкапе таких баз снапшот гарантированно переполнится. При детальном изучении проблемы мы обнаружили недочёт в реализации CoW алгоритма. Если перезаписывался один и тот же блок, то в снапстору каждый раз копировались данные. Результат: дублирование данных в снапсторе.

Естественно, алгоритм мы изменили. Теперь том разбит на блоки, и данные копируются в снапстору блоками. Если блок уже был один раз скопирован, то повторно этот процесс не производится.

Выбор размера блока

Теперь, когда снапстора разбита на блоки встает вопрос: а какого, собственно, размера делать блоки для разбиения снапсторы?

Проблема двоякая. Если блок сделать большим, им легче оперировать, но при изменении хотя-бы одного сектора, придётся отправить весь блок в снапостору и, как следствие, повышаются шансы на переполнение снапсторы.

Очевидно, что чем меньше размер блока, тем больший процент полезных данных отправляется в снапстору, но как это ударит по производительности?

Правду искали эмпирическим путём и пришли в результату в 16KiB. Также отмечу, что в Windows VSS тоже используются блоки в 16 KiB.

Вместо заключения

На этом пока всё. За бортом оставлю множество других, не менее интересных проблем, таких, как зависимость от версий ядра, выбор вариантов распространения модуля, kABI совместимость, работа в условиях бекпортов и т.д. Статья и так получилась объёмной, поэтому я решил остановиться на самых интересных проблемах.

Сейчас мы готовим к релизу версию 3.0, код модуля лежит на github, и каждый желающий может использовать его в рамках GPL лицензии.

Источник

Оцените статью