Бекап Для Linux, Или Как Создать Снапшот

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

Виртуальные машины также резервируются, но располагаются в гостевой ОС.

Эта статья была вдохновлена моим выступлением на конференции.

Линукс Питер , которую я решил оформить в виде статьи для всех заинтересованных хакеров.

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

Прошу всех, кому интересно, следить за разрезом!

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



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

Исторически сложилось так, что существует два подхода к созданию резервных копий: резервное копирование файлов и резервное копирование томов.

В первом случае мы копируем каждый файл как отдельный объект, во втором — копируем все содержимое тома в виде определенного изображения.

Оба метода имеют массу плюсов и минусов, но мы посмотрим на них через призму аварийного восстановления:

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

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

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

характеристики системы .

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

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

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

Чтобы избежать всех этих проблем, прогрессивное человечество придумало технологию моментальных снимков.

В теории все просто: создаем неизменяемую копию — снапшот — и делаем резервную копию данных из него.

Когда резервное копирование завершено, мы уничтожаем снимок.

Звучит просто, но, как обычно, есть нюансы.

Благодаря им на свет появилось множество реализаций этой технологии.

Например, решения на основе устройство сопоставления устройств , такие как LVM и Thin Provisioning, предоставляют полноценные снимки томов, но требуют специального разбиения диска на этапе установки системы, а значит, в целом не подходят. BTRFS и ZFS предоставляют возможность создавать снимки подструктур файловой системы, что очень здорово, но на данный момент их доля в серверах невелика, и мы пытаемся сделать универсальное решение.

Предположим, что наше блочное устройство имеет банальный EXT. В этом случае мы можем использовать ДМ-оснастка (кстати, он сейчас в разработке дм-лук ), но здесь есть нюанс.

Вам необходимо иметь наготове свободное блочное устройство, чтобы было куда сбросить данные моментального снимка.

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

Мы решили пойти по этому пути и написали свой модуль.

Было решено распространять его под лицензией GPL, поэтому он доступен в свободном доступе на github .



Как это работает — в теории



Снимок под микроскопом

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

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



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

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

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

Назовем эту область Snapstore.

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

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

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

Хочу отметить, что снапшот — это блочное устройство, полностью идентичное оригинальному на момент создания снапшота.

Благодаря этому мы можем смонтировать файловую систему на снимке и выполнить необходимую предварительную обработку.

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

Самый простой способ сделать это — использовать ioctl ГЕТФСМАП .

Данные о занятых блоках позволяют читать из снимка только текущие данные.

Вы также можете исключить некоторые файлы.

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



CoW против RoW

Остановимся немного на выборе алгоритма моментального снимка.

Выбор здесь не особо обширен: Копирование при записи или перенаправление при записи .

Redirect-on-Write при перехвате запроса на запись перенаправит его в snapstore, после чего все запросы на чтение этого блока пойдут туда.

Отличный алгоритм для систем хранения данных, построенных на деревьях B+, таких как BTRFS, ZFS и Thin Provisioning. Технология стара как мир, но особенно хорошо она работает в гипервизорах, где можно создать новый файл и записать туда новые блоки на время жизни снимка.

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

Copy-on-Write при перехвате запроса копирует данные, которые необходимо изменить, в Snapstore, а затем позволяет перезаписать их в исходное место.

Используется для создания снимков томов LVM и теневых копий VSS. Очевидно, что он больше подходит для создания снимков блочных устройств, поскольку не меняет структуру исходного устройства, а в случае его удаления (или сбоя) снимок можно просто удалить, не рискуя данными.

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

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



Как это работает – на практике



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

Ради него все и планировалось.

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

.

Аналогичная ситуация и с файлами базы данных и самой файловой системой.

Но мы живем в 21 веке! Существуют механизмы логирования, защищающие от таких проблем! Замечание верное, однако есть важное «но»: это защита не от сбоя, а от его последствий.

При восстановлении до согласованного состояния по журналу незавершенные операции будут отброшены и, следовательно, потеряны.

Поэтому важно сместить приоритет на защиту от причины, а не на лечение последствий.

Систему можно предупредить о том, что сейчас будет создан снимок.

Для этого в ядре есть функции замораживание_bdev И thaw_bdev .

Они используют функции файловой системыFreeze_fs и unfreeze_fs. При первом вызове система должна сбросить кеш, приостановить создание новых запросов к блочному устройству и дождаться завершения всех ранее сгенерированных запросов.

А при вызове unfreeze_fs файловая система восстанавливает нормальное функционирование.

Оказывается, мы можем предупредить файловую систему.

А что насчет приложений? К сожалению, здесь все плохо.

Хотя в Windows есть механизм ВСС , который с помощью Writers обеспечивает взаимодействие с другими продуктами, в Linux каждый идет своим путем.

На данный момент это привело к тому, что задача системного администратора состоит в самостоятельном написании (копировании, краже, покупке и т.д.) скриптов до заморозки и после разморозки, которые подготовят свое приложение к снапшоту.

Со своей стороны, в следующем релизе мы представим поддержку Oracle Application Processing, как наиболее часто запрашиваемой функции нашими клиентами.

Потом, возможно, будут поддерживаться и другие приложения, но в целом ситуация достаточно печальная.



Где мне разместить Snapstore?

Это вторая проблема, с которой мы столкнулись.

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

Конечно, самое простое решение — разместить snapstore в оперативной памяти.

Это отличный вариант для застройщика! Все быстро, отладку делать очень удобно, но есть проблема: оперативная память — ценный ресурс, и разместить там большой снапстор нам никто не позволит. Хорошо, давайте сделаем snapstore обычным файлом.

Но возникает другая проблема — нельзя сделать резервную копию тома, на котором находится снапстор.

Причина проста: мы перехватываем запросы на запись, а это значит, что мы будем перехватывать собственные запросы на запись в Snapstore. Лошади бежали по кругу, который по-научному называется тупиком.

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

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

В противном случае, пока держите снапшот на машине, будет пошаговая стратегия.

Это значит, что нужно как-то грамотно разместить snapstore на локальном диске.

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

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

Решение этой проблемы было реализовано в коде пользовательского пространства, в сервисе.

Есть системный вызов выпадение , что позволяет создать пустой файл нужного размера.

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

И ioctl ФИЕМАП позволяет нам получить карту расположения блоков файла.

И вуаля: создаем файл для snapstore с помощью Fallocate, FIEMAP выдает нам карту расположения блоков этого файла, которую мы можем передать в наш модуль veeamsnap для работы.

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

Но здесь есть нюанс.

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

Это влияет на функционал, увеличивая время подготовки снапстора, но выбирать не приходится.

Опять же, вам нужно уметь работать с тем, что у вас есть.

Что делать, если ioctl FIEMAP также не поддерживается? Это реальность NTFS и FAT32, где нет даже поддержки древнего FIBMAP. Нам пришлось реализовать некий универсальный алгоритм, работа которого не зависит от особенностей файловой системы.

Вкратце алгоритм такой:

  1. Сервис создает файл и начинает записывать в него определенный шаблон.

  2. Модуль перехватывает запросы на запись и проверяет записываемые данные.

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

Да, это сложно, да, это медленно, но это лучше, чем ничего.

Используется в единичных случаях для файловых систем без поддержки FIEMAP и FIBMAP.

Переполнение снимка

Вернее, место, которое мы выделили для Snapstore, заканчивается.

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

Что делать? Очевидно, нам нужно увеличить размер Snapstore. Сколько? Самый простой способ установить размер Snapstore — определить процент свободного места на томе (как это делается для VSS).

Для тома в 20 ТБ 10% будут составлять 2 ТБ — это очень много для незагруженного сервера.

Для тома размером 200 ГБ 10% будут составлять 20 ГБ, что может быть слишком мало для сервера, который интенсивно обновляет свои данные.

А есть еще тоненькие томики.

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

Это не соответствует принципу «Это просто работает».

Чтобы решить эту проблему, мы разработали алгоритм растянутого снимка.

Идея состоит в том, чтобы разделить снимки на части.

В то же время новые части создаются после создания снимка по мере необходимости.



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

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

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

  2. Когда снимок будет создан, его часть начнет заполняться.

  3. Как только половина порции заполнена, в сервис отправляется запрос на создание новой.

  4. Сервис создает его и передает данные модулю.

  5. Модуль начинает заполнять следующую порцию.

  6. Алгоритм повторяется до тех пор, пока либо резервная копия не будет завершена, либо пока мы не достигнем лимита использования свободного дискового пространства.

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

Что делать в остальных случаях? Пытаемся угадать необходимый размер и создаём весь снапстор.

Но по нашей статистике, подавляющее большинство Linux-серверов сейчас используют EXT4 и XFS. EXT3 встречается на старых машинах.

Но в SLES/openSUSE вы можете наткнуться на BTRFS.

Отслеживание блоков изменений (CBT)

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

И чтобы это работало, вам нужна КПТ.

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



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

Многие люди имеют собственный опыт в этой области.

Например, в VMware vSphere эта возможность доступна с 4 версии 2009 года.

Поддержка Hyper-V появилась с Windows Server 2016, а для поддержки более ранних выпусков еще в 2012 году был разработан собственный драйвер VeeamFCT. Поэтому для нашего модуля мы не стал оригинальным и использовал уже работающие алгоритмы.

Коротко о том, как это работает.

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

Весь контролируемый объем разделен на блоки.

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

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

Во время резервного копирования номер моментального снимка записывается в метаданные резервной копии.

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

Здесь есть два нюанса.

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

Это может показаться неудобным, но на самом деле цепочка из 255 инкрементов — далеко не лучшее решение при создании плана резервного копирования.

Вторая особенность — хранение таблицы CBT только в оперативной памяти.

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

Данное решение позволяет избежать решения проблемы запуска модуля при старте системы.

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



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

Резервное копирование — это всегда хорошая нагрузка на IO вашего оборудования.

Если на нем уже достаточно активных задач, то процесс резервного копирования может превратить вашу систему в своего рода лень .

Давайте посмотрим, почему.

Давайте представим, что сервер просто линейно записывает какие-то данные.

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

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

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

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

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

Это работает следующим образом.



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

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

В это время создаются запросы CoW, которые также обрабатываются порционно.

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

Только после завершения обработки всей порции запросов CoW перехваченные запросы выполняются.

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



Регулирование

Уже на этапе отладки выявился еще один нюанс.

Во время резервного копирования система перестала отвечать на запросы, т. е.

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

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

Пришлось немного подавить процесс резервного копирования, внедрив механизм регулирования.

Для этого процесс чтения из образа снапшота переводится в состояние ожидания, если очередь перехваченных запросов не пуста.

Как и ожидалось, система ожила.



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

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

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



Тупик

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

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

Они начали разбираться.

Оказалось, что такую ситуацию можно наблюдать, если, например, создать снапшот блочного устройства, на котором расположен том LVM, и разместить snapstore на этом же томе LVM. Напомню, что LVM использует модуль ядра устройства сопоставления.



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

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

Запрос от сопоставителя устройств снова будет перехвачен модулем.

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

В результате обработка запроса блокируется, вас встречает взаимоблокировка.

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

Логика здесь все та же: лучше не делать бэкап, чем крашить сервер.



База данных кругового турнира

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

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

Яркий пример — сервисы мониторинга, которые постоянно генерируют данные о состоянии системы и переписывают их по кругу.

Для таких задач используются специализированные циклические базы данных ( РРД ).

Оказалось, что при резервном копировании таких баз снапшот гарантированно переполняется.

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

Результат: дублирование данных в снапсторе.



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

Естественно, мы изменили алгоритм.

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

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



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

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

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



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

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

Также отмечу, что Windows VSS также использует блоки по 16 КиБ.



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

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

Сейчас мы готовим к выпуску версию 3.0, код модуля находится в github , и любой может использовать его по лицензии GPL. Теги: #linux #разработка Linux #Резервное копирование #открытый исходный код #C++ #модуль ядра #veeam #агент veeam для linux #снимок #veeamsnap #модуль ядра

Вместе с данным постом часто просматривают: