Я всегда считал, что одна из основных проблем при использовании систем резервного копирования — свойственный этому процессу консерватизм. Причин для него огромное количество, начиная с того факта, что первый бэкап был сделан ещё в 1951 году. При этом наличие бэкапов, которые можно восстановить, остаётся показателем зрелости компании и её опыта в эксплуатации.  В этой статье я расскажу, как подружить тренды проектирования систем с резервным копированием и при этом не потерять уверенность в том, что нужные данные можно достать в любом, даже самом критическом, случае. Я не буду говорить о себе, а расскажу чужую историю. Все имена в ней выдуманные, а совпадения — случайны. Её главный герой — человек по имени Савелий. И, по случайному совпадению, он админ, как и я. Савелий очень любил читать книжки о Linux. Он изучал всё, что мог купить и скачать бесплатно. В конце концов это увлечение зашло настолько далеко, что он устроился работать админом. Инфраструктура, которую Савелий построил на работе, поначалу была очень простой: один сервер баз данных, одна машина с нагрузкой под фронтенд и некий бэкенд. Он просто брал каждую машину, сливал всё, что на ней было, в хранилище — и получался бэкап.  Из этой статьи вы узнаете об эволюции взглядов Савелия: как с усложнением инфраструктуры менялся его подход к бэкапам и к каким выводам он пришёл. Я рассказывал об этом на прошлогоднем HighLoad++, а сегодня делюсь текстом по мотивам доклада. Наш Савелий жил-поживал и продолжал изучать материалы по Linux — даже на работе. И однажды он узнал, что существуют системы управления конфигурациями: Ansible, Puppet, Chef и им подобные. Они избавляют от ручной работы при подготовке сервера ко вводу в эксплуатацию.  «Какая классная штука, надо использовать!» — подумал Савелий. И тут же внедрил такую систему. После этого он решил, что бэкапить машину целиком уже не имеет смысла, и стал забирать в бэкап только часть необходимых данных. Всем остальным занималась система управления конфигурациями.  Со временем инфраструктура проекта усложнилась.  Разрослись серверы с фронтендами. Да и с бэкендами, честно говоря, тоже. Появились связи между компонентами, связи внутри каждого компонента.  Самое интересное изменение касалось баз данных: появилось шардирование. У каждого шарда было по одной или несколько реплик. Помимо того, что данные расползлись по инфраструктуре, произошло дублирование: данные одного шарда могли располагаться на пяти-шести репликах. Данных стало много. Сервисы также изменились: их стало больше, появились связи и зависимости.  Савелий долго наблюдал за ситуацией, и ему всё нравилось. Всё работало хорошо и без сбоев. В конце концов он подумал: а зачем вообще бэкапы? Ведь теперь всё просто: машина упала — поднял другую реплику, считал с мастера — и всё поедет дальше.  Так Савелий отказался от бэкапов. Савелий работал в компании не один, с ним было много программистов. Один из них — Аристофан — был его добрым другом.  Однажды, работая с базой данных, Аристофан случайно сделал дроп.  Савелий — хороший админ. Когда он настраивал репликацию, то следил за отставанием от мастера. Точнее он его не допускал.  Поэтому, как только дроп приехал на мастер, он разъехался по всем репликам. Данных больше не осталось.  Что тут сказать: такое бывает не только в сказках.  После этого Савелий задумался. Нет, не о дружбе с Аристофаном, а о том, что бэкапы всё же нужны. И о том, что такое правильный бэкап.  Савелий пришёл к выводу, что бэкап — это неизменяемые данные на определённый момент времени. У бэкапа должно быть несколько обязательных свойств:  Он должен находиться в стороне и не должен быть подвержен изменению.  Он обязательно должен восстанавливаться.  Всегда нужно знать, сколько времени требуется на восстановление бэкапа.  После того как Савелий сформулировал эти правила, появился другой вопрос. Как правильно поступать: по старинке бэкапить сервер целиком или же делать именно сервисные бэкапы? В то время у Савелия на одном сервере уже могло располагаться более одного сервиса, и сервисы могли быть совершенно разными. Ясное дело, что информацию о том, где и какой сервис расположен, в голове удержать сложно. Но всему своё время, не будем забегать вперёд. Попробовав оба варианта, он пришёл к выводу, что с точки зрения формирования бэкапа большой разницы нет. В обоих случаях приходилось использовать карту расположения сервисов.  Сложность заключалась в другом: после восстановления бэкапа сервера рядом с нужными данными всегда лежали данные, которые были не нужны. Например, когда Савелию нужно было восстановить все данные сервиса А, но при этом на сервере жили ещё и сервисы Б и В, то в бэкапе сервера оказывались все файлы всех трёх сервисов (А, Б и В).  Процесс восстановления в таком случае мог пойти по двум сценариям: Восстановить весь бэкап сервера и потом взять нужные данные. Минусы: увеличение нагрузки на сеть; увеличение нагрузки на устройство хранения; увеличение времени на выборку нужных данных из общей кучи. Выбрать вручную только те данные, которые нужно восстановить. Минусы: увеличение времени на выборку нужных данных из общей кучи; высокая вероятность того, что будут выбраны не все данные и процедуру придётся повторить (опять-таки потраченное время). Как видно, оба варианта имеют существенные недостатки. Кроме того, иногда данные с одного сервера мигрировали на другой. В этом случае приходилось копаться, выяснять, куда они делись и какой сервер действительно нужен.  Савелий попробовал оба варианта и пришёл к выводу, что с точки зрения удобства и времени восстановления лучше бэкапить именно сервис. Мысль о том, что бэкапы не нужны, Савелию в голову больше не приходила. Перед ним стоял вопрос: как сообщить системе бэкапа, где какой сервис живёт?  В итоге Савелий решил, что ему подойдёт таблица в базе данных, где он будет описывать, какой сервис какой версии на каком сервере должен располагаться. Карта расположения сервисов стала своего рода системой оркестрации в инфраструктуре Савелия. Вывод, к которому пришёл Савелий, прост: современным бэкапам — современную оркестрацию.  Жил теперь Савелий хорошо: у него была чёткая стратегия, которой он придерживался. Но однажды к нему пришёл Аристофан и заявил, что бэкап мешает работе его сервиса.  Савелий сперва не поверил. Но, внимательно выслушав друга, понял, что бэкап действительно нужно ограничивать в потреблении ресурсов: диска, сети, процессора и памяти. Пригорюнился Савелий: опять нужно что-то менять.  Решение пришло быстро. Один из способов уменьшения потребления сети и места в хранилище — инкрементальные бэкапы. Наученный опытом Савелий решил не рубить с плеча и сначала разобраться в плюсах и минусах такого подхода. Минусы: формирование инкрементального бэкапа на клиенте в некоторых случаях более затратно с точки зрения ресурсов, так как нужно вычислять разницу «было-стало»;  потеря инкрементального бэкапа может привести к невозможности восстановления данных на нужный момент времени. Плюсы: экономия ресурсов сети, так как по ней передаётся не весь объём данных, а лишь те, которые изменились; экономия места в хранилище. Савелий подозревал, что есть какая-то золотая середина между полным и инкрементальным бэкапом. Он, конечно, использовал оба подхода и всегда очень внимательно следил за их правильным чередованием.  Помимо всего прочего, он настроил лимиты потребления ресурсов сети и диска на серверах, которые бэкапил, — без фанатизма и только там, где требовалось. В общем, Савелий — молодец. Жили серверы Савелия долго и счастливо. Но пришла новая беда: стало невозможно выкатывать изменения сразу в боевое окружение. Было создано devel-окружение, которое нужно было поддерживать и бэкапить. Оно было точной копией продакшен-окружения, но располагалось на виртуальных машинах.  Проблема была в том, что devel — это территория разработчика, вотчина Аристофана. Но Савелий понимал, что рано или поздно Аристофан придёт к нему с вопросом, а есть ли у него бэкап данных за какой-то день.  Выходит, только Аристофан может знать, что нужно бэкапить, а что можно выкинуть. И несёт за это ответственность.  Поэтому Савелий решил действовать хитро — сыграть на опережение: раз в сутки брать из карты сервисов список виртуальных машин, вычёркивать из него машины с базами данных и давать Аристофану возможность влиять на это — по большей части включать и выключать определённые виртуалки из бэкапа.  Как Савелий организовал процесс бэкапа виртуальных машин:  На момент начала бэкапа виртуальной машины на ней не должно быть снапшотов. Если на виртуальной машине есть снапшот, сделанный вручную Аристофаном, то: не удалять снапшот; не выполнять бэкап; сообщать об этом Аристофану и уточнять, нужен ли ему ещё снапшот, и действовать в зависимости от его ответа. По окончании бэкапа удалять временный снапшот. Также Савелий придумал оповещалку, которая сообщала Аристофану о том, что машины в инфраструктуре уже нет, а запись о её бэкапе или не-бэкапе присутствует.  Почему Савелий не бэкапил виртуалки с базами данных? На самом деле всё просто: теперь он всегда бэкапит все инстансы с базами данных. По умолчанию. Потому что он помнит прекрасную историю про дроп. А на машинах с базами данных ничего ценного, кроме самих данных в базе (для всего остального есть Puppet!), нет. База данных тоже сервис. Бэкап сервиса тоже бэкап. В общем случае при выполнении бэкапа файлы берутся из локальной файловой системы. Сложность в том, что очень часто на сервере с базой данных датасет настолько большой, что локально положить выгрузку (дамп), а потом поместить её в систему резервного копирования невозможно — на это просто не хватает места.  С этим столкнулся и наш герой. Он начал использовать pipe: подключался к базе — и на лету сливал данные в удалённое хранилище. Причём даже в тех случаях, когда места на сервере хватало. Савелий принял решение бэкапить везде одинаково, чтобы не было энтропии. Админы довольно быстро понимают плюсы такого подхода, а кто не понимает, тот рано или поздно тонет в «костылях» («костыль» в данном случае — это использование разных подходов к решению одной и той же задачи).  Разрабатывая своё решение по бэкапу баз данных, Савелий решил, что нужно дать Аристофану несколько рычагов управления. Многие вещи в инфраструктуре Аристофан знал хорошо, даже лучше админа. Например, он знал, в какой момент времени какая база более нагружена, какую базу можно бэкапить, а какую — нет. Поэтому Савелий разрешил Аристофану: включать и выключать бэкап; менять параметры утилит бэкапа; менять расписание бэкапа. К реализации Савелий подошёл просто: он периодически формировал список со всеми машинами с базами данных и делал приоритизацию. Для чего в данном случае понадобилась приоритизация? Всё просто -- давайте представим себе ситуацию, когда нужно выполнить бэкап большого объема данных в условиях того, что этот большой объем расположен на большом количестве серверов? Очевидные проблемы, которые могут возникнуть при данных условиях: устройство хранения может стать узким местом, так как сеть не резиновая (источников много, а устройство хранения -- одно) как только возникает ограничение пропускной способности -- происходит увеличение времени бэкапа для тех серверов, которые бэкапятся При бэкапе базы данных важным моментом является то, что снять его (бэкап) желательно за оптимальный срок (чем быстрее - тем лучше, но не стоит забывать о том, что сервис не должен страдать во время бэкапа). Взяв на вооружение данные факты, Савелий пришел ко мнению о том, что необходимо выстраивать очередь бэкапа. И вот какую приоритизацию он придумал: сначала всегда бэкапились мелкие базы, потом — крупные, чтобы при появлении окна для бэкапа за это время проходила как можно большая часть работы. Длительные процессы оставались на потом и не держали в очереди всё остальное.  Долго ли, коротко ли, но бэкапы Савелий запустил. И больше они сервисам не мешали. Можно расходиться, всё работает.  Но всё оказалось не так просто.  Савелий решил проверить по метрикам, как ведёт себя сервис. Когда приходит бэкап, не начинает ли он, например, отвечать медленнее? Изучив графики, он увидел, что есть сервисы, которые в процессе работы дампят свои данные на локальный диск. В то время, когда данные дампятся, иногда приходит бэкап — и начинает забирать данные, которые на диск ещё не записаны.  Появляется сразу несколько проблем. Главная — в бэкап уезжает мусор, так как данные не записались на диск целиком. Последствия этого: тратится место в хранилище; потребляются ресурсы сети; расходуются дисковый и остальные ресурсы на машине, откуда снимается бэкап. Отрицательные значения на графике — это запись дампа. Зелёные линии — бэкап. Как видно, они пересекаются по времени. Помимо того что довольно сильно нагружается дисковая система, ещё и не всегда получается консистентный бэкап.  Тогда Савелий задумался над расписанием и изменил его. На следующем графике видно, что локальные записи дампа сервиса и бэкапа не пересекаются по времени. Никто никому не мешает. Всё хорошо.  Всё сделано, все бэкапится. Савелий научился выявлять пересечения и составлять расписание.  Однако наш герой не мог спокойно спать, потому что не был уверен в том, что с тем, что он забэкапил, всё нормально: можно это достать и что-то с этим сделать.  Как об этом узнать?  Савелий нашёл простое решение. Он разработал чек-лист из четырёх шагов — от простого к сложному. В случае если по какой-то причине нельзя было пройти один из шагов, происходил откат на шаг назад и предпринималась попытка устранить проблему.  Шаг № 1 Проверяется статус: бэкап завершился со статусом «ОК» или произошла ошибка. Если ошибка, то разбираемся почему. Если «ОК», переходим к следующему шагу.  Шаг № 2 Проверка цепочки. Проверяем на каждый момент времени состояние, необходимое для восстановления того или иного бэкапа: присутствуют ли нужные для восстановления данные на сервере хранения, есть ли успешный полный бэкап, потом — цепочка живых инкременталов до даты восстановления. Если всё есть, отлично. Если нет, то это проблема. В этом случае происходит возврат на предыдущий шаг. Бэкап сервиса считается невалидным.  Шаг № 3 Восстановление. Данные необходимо взять непосредственно из системы хранения, залить их на некий сервер и убедиться в том, что можно достать их из хранилища. Только на этом этапе можно выяснить, сколько данных (мегабайтов/гигабайтов/терабайтов) в час прокачивается при восстановлении из бэкапа.  Этот шаг нужен, чтобы всегда быть уверенным в том, что цифры, которые ты называешь для восстановления, соответствуют действительности. Если на шаге восстановления верификация не укладывается в некий таймлайн, то нужно либо сообщать о том, что бэкап невалиден, либо делать предупреждение и просить посмотреть, что там в связке между хранилищем и целевой машиной.  Шаг № 4 Запуск сервиса: либо он запускается, либо нет. Если сервис не запустился, это самая большая проблема. Значит, надо копать глубже.  Когда однажды это произошло, Савелий в очередной раз убедился: недостаточно просто положить данные в бэкап — всегда нужно гарантировать их восстановление. Как в каждой правильной сказке, в конце Савелий должен был изобрести свою систему бэкапов, которая удовлетворяла бы всем его требованиям. На этом можно было бы закончить историю словами «И жили они долго и счастливо!», но не тут-то было. Жизнь не сказка: работа сама себя не сделает, как и система бэкапов по щелчку из воздуха не появится. На момент выбора решений было не то чтобы очень много: коммерческие системы резервного копирования (чаще всего на них не хотят тратиться); AMANDA; Bacula; возможно, было что-то ещё, но про это уже никто не помнит. Савелий решил действовать по хардкору: начал использовать Bacula, а позже —  Bareos (форк Bacula). Он никогда не искал лёгких путей.  Скоро сказка сказывается, да не скоро дело делается. Вдоволь повозившись с этой системой, Савелий сформулировал для себя и коллег девять практических советов.  Совет № 1 Используйте virtual changer (VC). Это не какой-то конкретный инструмент, а целый класс утилит. Для примера представьте набор сервисов, который мы бэкапим в два пула. Один пул называется SOMETHING-full, второй — SOMETHING-incremental. В этом случае можно бэкапить данные в два пула и не переживать о том, что пачка заданий, ожидающих полного бэкапа, встанет в очередь, пока не закончатся инкрементальные бэкапы. Это довольно часто бывает нужно.  Когда идёт набор бэкап-заданий и есть необходимость сделать восстановление, преимущества VC становятся понятны. Без него пришлось бы останавливать все бэкап-задания, монтировать нужные кассеты для восстановления и пытаться восстановиться. Virtual changer позволяет довольно просто осуществлять удаление и добавление кассет, маркировки, очистки, транкейта и пр.  Совет № 2 Рассматривайте файловый бэкап как бэкап полноценной ленточной библиотеки и используйте блочные устройства как «магазин». Это позволит строго ограничивать место под те или иные пулы, удалять их независимо друг от друга, добавлять, делать ресайзы, добавлять в них кассеты и т. д. Если вы имеете дело с отдельным блочным устройством, экспериментируйте с параметром read ahead. Конечно, всё зависит от того, хватит ли памяти на машине, но в случае восстановления это достаточно сильно помогало Савелию.  Совет № 3 Кассеты в Bareos подвержены фрагментации. Если у нас есть некое хранилище и пять—десять клиентов, которые в один момент времени решили писаться в кассеты одного пула, то получится такая ситуация: сначала запишется кусок первого задания, потом — кусок второго, потом, возможно, опять кусок первого, потом третий, четвёртый — вразнобой.  На момент записи это не является проблемой, но можно столкнуться с проблемой при восстановлении. Для того чтобы демон хранилища получил нужный кусок данных, помимо блоков, которые планируется восстановить, ему придётся вычитывать все промежуточное и выкидывать за ненадобностью. Второй вариант — писать меньше заданий на одну кассету: например, не больше одного. В конечном итоге занимаемое бэкапом место совершенно одинаково при любом подходе.  Совет № 4 В Bareos много собственных параметров и крутилок. Крутить можно всё что угодно. Основное, на что Савелий обращает внимание: параметры, которые относятся к Maximum Volume Jobs и Concurrent Jobs, — как раз для настройки количества заданий на кассете;  параметры, которые касаются block size и file size, влияют на скорость записи бэкапа, если их можно откуда-то линейно быстро читать и быстро писать;  параметры, которые дадут чуть больше гарантий того, что будет корректная цепочка бэкапов Max Full Interval; параметры, которые остановят выполнение задачи, если нет подходящей кассеты Max Wait Time; параметры Spool Attributes и Spool Data относятся больше к ленточному бэкапу, чем к файловому, но Савелий считает, что имеет смысл их тоже покрутить.  Всё очень сильно зависит от задачи, которая решается. Поэтому прежде чем изменять параметры, нужно понимать, зачем это делается, и измерять результаты до изменения и после него.  Совет № 5 Когда Савелий решил бэкапить все базы по умолчанию, возникла проблема. Раз он бэкапит всё, то правила хранения одинаковые. У Савелия стандартные условия хранения бэкапа баз такие: Full: два последних успешных бэкапа (делается один раз в неделю); Incremental: всё до самого старого Full (делается один раз в сутки). Но бывают ситуации, когда нужно хранить данные либо больше, либо меньше. На желание хранить бэкап меньше можно не обращать внимания: лучше  потому что если бэкап данных хранится больше, чем требуется — это хорошо.  Если нужно хранить дольше, то с этим нужно что-то делать, например бэкапить базы или сервисы дополнительно в другие пулы. Но более правильным решением будет использование copy job (migration, если не хочется занимать в два раза больше места), которая заберёт то, что уже лежит в системе хранения, положит в другой пул — и оно будет там жить столько времени, сколько нужно.  Совет № 6 Если необходимо забэкапить кусок файловой системы с кучей директорий, миллионами мелких файлов и т. п., то простой вариант Bareos-бэкапа не подходит. Файловый демон будет тратить всё время на процесс построения дерева того, что необходимо забэкапить, а не заниматься копированием бэкапа в хранилище.  Нужно искать другие варианты решения, например: бэкапиться снапшотом; делать бэкап через pipe. Или рассмотреть вариант не бэкапить Bareos по классической схеме File daemon -> Storage daemon, а придумать другую систему, которая будет заливать необходимые файлы в хранилище. Если выбран такой вариант, то его стоит обозначить как задачу с пустым fileset в Bareos. Нужно это для того, чтобы сведения обо всех бэкапах находились в одном месте. Совет № 7 Те, кто работал с Bareos и Bacula, прекрасно знают, что настройки пула, хранилища, кассеты, ретеншена можно раскидать по таким разным местам, что другой человек не сможет во всём этом разобраться. Поэтому Савелий рекомендует договориться с коллегами о том, где и как будут располагаться те или иные настройки, где и в каких местах какие секции конфигов будут находиться, и задокументировать это.  Совет № 8  Проверяйте базу Bareos: bareos-dbcheck в помощь. Обратите внимание на DB backend: MySQL, PostgreSQL. Как минимум стоит принять во внимание мнение разработчиков по данному вопросу, чтобы потом не удивляться. Совет № 9  Собирайте логи и метрики: в Elasticsearch, InfluxDB, Prometheus — на ваш вкус. Они помогут ответить на кучу вопросов, а при построении бэкапа они возникнут абсолютно точно.  Приблизительно такие графики можно строить, собирая различные метрики: Данный график показывает, как в течение определённого отрезка времени происходит запись в разные пулы.  Если посмотреть на большой отрезок времени, можно увидеть окна, где бэкап совсем не идёт и где выполняется много процессов, и поменять их местами.  Также будут видны попытки что-то ресторить.  Там, где график идёт вверх и скорость увеличивается, меняли параметр read ahead. В данном случае это изменение стандартного read ahead для блочного устройства.  Можно увидеть, какое количество данных поминутно, по часам сливается в бэкап-систему.  Также важно знать, в какие пулы сливаются данные и когда заканчиваются задания. Это даёт возможность найти окна в расписании, что-то подвинуть, сделать жизнь и работу бэкапа чуть лучше.  Начало работ: Окончание и результаты работ: Работы, завершённые с ошибками: Вот и подошла к концу история Савелия. Как известно, сказка — ложь, да в ней всегда есть урок для добрых молодцев и девиц. А умение делать выводы по результатам проделанной работы — одно из ключевых качеств хорошего инженера. При этом вывод — это не обязательно жирная точка, это может быть и промежуточный результат. В нашей истории легко заметить, что Савелий делать выводы умеет. Он не боится трудностей и всегда продолжает совершенствовать свою систему. Как бы ни менялся мир вокруг, какие бы технологии ни появлялись, данные — это основная ценность проекта. Поэтому кто бы когда бы и что бы ни говорил, бэкапы нужны всегда. Бэкапы важны! Савелий умеет расставлять приоритеты и точно знает, что недостаточно сделать бэкап. Тут-то всё очень просто — скопировал и забыл. Важно уметь бэкапы восстанавливать. За восстановлением данных не приходят каждый день/неделю/месяц, но, если пришли, необходимо эти данные предоставить. Восстановление данных из бэкапа — это очень важная (если не важнейшая) составляющая системы резервного копирования. Пожалуйста, уделяйте этому процессу достаточно внимания! Не надо бояться экспериментировать. Хотя Bareos по своей сути довольно консервативен, всегда можно прикрутить что-то сбоку, чтобы сделать возможным и эффективным его использование в современных инфраструктурах. Рано или поздно всё точно получится. Савелий смог — и вы сможете! Внимательный читатель наверняка обратил внимание на то, что в истории не сказано ни слова о необходимости бэкапа базы самого Bareos. Давайте отнесёмся к этому философски и примем за аксиому: бэкап базы системы резервного копирования — это само собой разумеющееся условие, без каких бы то ни было исключений.