В этой статье я расскажу, как мы выбирали и внедряли средства резервного копирования и восстановления пользовательских данных для закрытого контура заказчика в условиях ограниченности ресурсов, а также познакомлю с частью системного архитектурного стека нашего продукта. Когда речь заходит о пользовательских данных в информационных системах, всегда возникают вопросы: как хранить и как сохранить? Отвечая на первый вопрос, расскажу, что в нашем стеке под слой хранения постоянных пользовательских данных используются объектное S3 хранилище MinIO и базы данных PostgreSQL. В MinIO в основном, хранятся небольшие файлы, документы, 3D и ЦИМ модели, а в PostgreSQL — связи, облака точек моделей и другие данные. Оперативные данные хранятся в Redis/KeyDB и RabbitMQ, но они нам не так интересны. Нужно сказать, что мы имеем и поддерживаем несколько архитектур развертывания, поэтому в данной статье будет рассмотрено решение с развернутыми сервисами в среде Kubernetes (k8s) и выделенными хостами (виртуальными машинами) под кластер СУБД PostgreSQL. Ответ на второй вопрос «как сохранить?», в основном, зависит от нескольких факторов: требований к надежности ИС требований заказчика возможностей реализации Именно на него нам и предстояло ответить. Необходимо было предоставить систему резервного копирования (СРК) пользовательских данных так как текущий способ резервного копирования был не самый эффективный. Проблема заключалась в том, что у заказчика резервное копирование пользовательских данных выполнялось с помощью средств платформы виртуализации путем создания snapshot'ов. Данное решение было неудобно так как: Необходимы остановка системы и, иногда, выключение виртуальных машин для снятия snaphot'ов, чтобы быть уверенным в консистентности резервных копий Snaphot'ы занимают достаточно много места, так как их необходимо делать достаточно часто Нет возможности отката на более позднее состояние, чем последний snapshot (на время между последним и созданием нового) Не исключены проблемы с созданием некорректной копии и восстановление системы в неконсистентное состояние. Соответственно, данные недостатки должны быть устранены с помощью СРК. Возможности инкрементального копирования или создания консистентных snapshot'ов, а также резервное копирование самого кластера k8s (например, с помощью Velero) здесь рассмотрено не будет. Прежде всего необходимо руководствоваться задокументированными требованиями. В документах из требований, относящихся к СРК, у нас были только необходимые характеристики надежности RPO (Recovery Point Objective, допустимая потеря данных) и RTO (Recovery Time Objective, допустимое время восстановления данных). Этого достаточно для отправной точки, но не достаточно для составления полной картины выбора необходимого решения. Одними из основных требований большинства наших заказчиков является использование отечественного ПО (находящегося в реестре Минцифры Российского ПО), либо использование подготовленных open source решений. В связи с массовым и планомерным переходом на использование отечественного ПО, мы также адаптировали свой продукт под данные решения. Так, например, мы совместимы и поддерживаем свой продукт на отечественных операционных системах РЕД ОС и Astra Linux и, по возможности, используем ПО из официальных репозиториев данных ОС. Нужно сказать, что работа системы происходит в закрытом контуре заказчика, к которому у нас нет никакого доступа. Соответственно, все работы проводятся администраторами на стороне заказчика, поэтому необходимы подробные инструкции по установке и поддержке наших решений. Также СРК должна функционировать в уже запущенной системе без выделения дополнительных ресурсов и масштабирования. В сухом остатке имеем следующие требования: Выполнение требований по RTO и RPO Совместимость и работа СРК на отечественной ОС (РЕД ОС) Внедрение в существующую систему без масштабирования Использование отечественного ПО, либо open source решения Резервные копии должны храниться на СХД (система хранения данных) Внедрение СРК в закрытый контур Быстый и понятный процесс резервного копирования и восстановления системы Настройка и использование системы исключительно руками администраторов Необходимо резервное копирование пользовательских данных из MinIO и кластер СУБД PostgreSQL Ограничение по системным ресурсам ВМ — СРК не должно занимать много места Возможность восстановления удаленных данных Возможноть отката на определенный момент времени Итак, требования понятны, необходимо приступить к выбору. Начинаем с кластера PostgreSQL —  мы используем 14 версию. Из интересного — для хранения облаков точек и геометрии используется расширение PostGIS, поэтому таблицы в БД достаточно большие. Также необходимо брать в учет, что могут понадобиться tablespaces. Во время выбора средства для СРК, было принято решение сохранять данные кластера СУБД PostgreSQL не просто на СХД, а в объектное хранилище, желательно совместимое с S3. Такое решение достаточно практичное, так как мы получаем воможность масштабирования, удобный способ хранения и восстановления данных, возможность быстрого развертывания кластера для верификации резервных копий. Начинаем поиск и анализ средств РК. В интернете достаточно много информации на тему сравнения средств РК для PostgreSQL, а с некоторыми у нас уже был опыт работы, поэтому подробно здесь останавливаться не будем и перейдем к выбору с поясненями и выводами. Смотрим на встроенные средства и сразу отметаем идею делать dump'ы так как нет PITR, операция блокирующая, восстановление медленное, и, в целом, это не совсем резервные копии. Чистый pg_basebackup с архивацией сегментов-WAL (Write-Ahead Logging) — очень неудобно. Встроенные решения закончились. А что если посмотреть на готовые решения? Когда мы говорим про отечественные СРК, работающие с PostgreSQL, то здесь сильно не разгуляешься. Если мы зайдем в реестр Минцифры, то обнаружим, что из отечественных зарегистрированных СРК, работающих с PostgreSQL есть только RuBackup и Кибер бэкап (может, знаете другие?), которые объявили о поддержке PostgreSQL 14 совсем недавно: версия RuBackup 2.0 - 22.05.2023, а Кибер бэкап 16 - 25.04.2023. К тому же, по-хорошему, им необходим выделенный сервер для установки. Остается open source, и здесь выбора намного больше. Из решений, с которыми у нас имеется опыт работы, либо которые зарекомендовали себя, можно выделить Barman, pg_probackup, WAL-G, pgBackRest. Данные решения используются многими компаниями, они имеют хорошую документацию, поддерживаются и развиваются на текущий момент времени. Приступаем к выбору методом исключения. Первым исключаем Barman из кандидатов, так как он требует наличие выделенного сервера, не самый простой в настройке и поддержке. Далее исключаем pg_probackup, так как решение не работает с S3, а у нас уже были планы.  Выбор остается между WAL-G и pgBackRest. Оба средства достаточно похожи, надежны и быстро работают. Но в итоге выбор был сделан в пользу WAL-G, так как оно полностью удовлетворяет нашим требованиям, легковесное, совместимо с S3, поддерживает быстрые алгоритмы сжатия и имеет необходимый функционал для создания резервных копий. Хотя pgBackRest надежное решение, его функционал избыточен в нашем случае, а процесс настройки и управления немного сложнее. Кстати, в WAL-G имеется поддержка восстановления из бэкапов pgBackRest, но пока что функция в бета-тесте и не работает для инкрементальных и дифференциальных копий. После выбора средства, следует процесс настройки, разворота и тестирования решения на стендах. Так как мы решили, что для хранения резервных копий кластера PostgreSQL будем использовать объектное хранилище совместимое S3, то в качестве такого хранилища взяли легковесный MinIO, с которым у нас уже есть опыт работы.  Так как мы ограничены в ресурсах, то разворачиваем новый инстанс в уже существующем кластере k8s с выделением места под хранение на СХД. Использовать уже существующий инстанс MinIO для пользовательских данных, чтобы хранить там копии кластера, не очень хорошая практика. Данные кластера БД желательно отделять от пользовательских данных и держать отдельно, так как могут возникнуть сложности при необходимости восстановления сразу обоих.  В дальнейшем при появлении ресурсов, данное решение можно масштабировать, либо перенести на отдельный сервер. Что мы делаем? Создаем LUN на СХД и подключаем его к каждому из воркеров k8s Определяем и создаем PV и PVC для нового сервиса MinIO Разворачиваем сервис MinIO и создаем bucket для резервных копий Создаем и настраиваем скрипты, после чего разворачиваем WAL-G в связке с MinIO. WAL-G устанавливаем на хосты с СУБД. Проверяем работу СРК на РЕД ОС Не буду подробно останавливаться на установке (некоторые моменты я затрону ниже), поэтому расскажу про выбранный способ резервирования. Механизм работы достаточно прозрачный: Раз в три дня снимаются полные физические резервные копии кластера PostgreSQL при помощи утилиты WAL-G и очищаются старые полные бэкапы и WAL. Каждые 10 минут архивируются WAL-журналы с помощью archive_command (механизма PostgreSQL) и отправляются в S3 хранилище MinIO. Все происходит автоматизированно через запуск скриптов в CRON с ведением и ротацией логов.  Почему имеено раз в 3 дня?  Здесь нужно брать в расчет не только время восстановления полных копий при опредленных размерах БД, но и скорость извлечения и применения WAL-сегментов — их размер и количество также влияют на время восстановления.  При такой реализации мы получаем возможность восстановления кластера PostgreSQL на опредленный момент времени, с максимальной потерей данных в 10 минут. Данный способ экономит нам место, так как мы не делаем ежедневные полные резервные копии. И как вы уже поняли, решение отлично работает на отечественной РЕД ОС.  Хотя механизм WAL-G предоствляет возможность делать DELTA-бэкапы, и при использовании дельт время восстановления может быть меньше — их использование добавляет сложность в механизм реализации СРК. К тому же при проблемах с данными копиями, администратору, который не работал с данным механизмом и не знает внутреннее устройство СУБД PostgreSQL, будет намного тяжелее разобраться. В нашем случае мы упрощаем процесс создания и восстановления, соблюдая все необходимые требования по RTO и RPO, так как полных бэкапов и журналов WAL достаточно для восстановления кластера. В данном случае для реализации требований RTO и RPO в рассчет шли: Реальный размер кластера СУБД при различных активностях и временных интервалах (с учетом максимального размера БД за срок эксплуатации) Количество и размер и накопленных журналов WAL Время на копирование текущего неисправного кластера БД Время на восстановление кластера СУБД из резервной копии Время на применение WAL-журналов Скорость сети и I/O дисков Время, заложенное на подготовку к восстанволению и выполнение инструкций администраторами Время проверки работоспособности Но для достижения полной консистентности нашей системы, необходимо, чтобы не только кластер СУБД находился в консистентном состоянии, но и присутствовали все необходимые данные (файлы, документы, пр.) на момент времени восстановления. В кластере k8s у нас развернут сервис MinIO, который содержит необходимые пользовательские данные, которые нужно резервировать. Так как файлов в MinIO может храниться очень много (>1млн.), необходимо было выбрать средство быстрого перемещения данных на СХД.  Здесь выбирать долго не пришлось. Проверенный временем rsync справляется с данной задачей достаточно хорошо, особенно при хорошей попускной способности сети и быстрых дисках. При помощи rsync мы выполнеям инкрементальное (не путать с дифференциальным) копирование директории хранения данных (buckets) MinIO на подключенный СХД без флагов очистки удаленных файлов (обусловлено политиками хранения данных). Данную процедуру необходимо проводить раз в 10 минут, чтобы сильно не нагружать систему и обеспечить консистентность с БД PostgreSQL. Автоматизация происходит через тот же CRON.  Периодически в нерабочее системное время выполняем архивацию и сжатие директории для возможности отката к более ранним версиям (с учетом бэкапов СУБД). А как же быть с избыточностью данных, если мы их только накапливаем?Все очень просто, наша система реализует прозрачный механизм хранения в БД ссылок на объекты и позволяет решить проблему удаления избыточных данных с помощью специального скрипта. Необходимо периодически запускать данный механизм для уменьшения размера резервных копий. В зависимости от ситуации, при необходимости восстановления данных MinIO мы укладываемся в требования RTO и RPO. В конечном итоге, мы получаем связанную систему резервного копирования пользовательских данных с необходимой консистентностью при восстановлении и минимальной допустимой потерей данных. СРК интегрируется в систему, расходует минимум ее ресурсов и проста в эксплуатации.  Также нужно сказать, что мы допускаем потерю оперативных данных, кэша сервисов и пр., так как нет необходимости в этих данных при восстановлении системы, и они не влияют на консистентность и ход восстановления. В таком случае нам достаточно периодически снимать и иметь в наличии snapshot всей системы (если не использовать другие средства резервного копирования) и иметь СРК для пользовательских данных, чтобы восстановить систему по требованиям. Здесь я хочу поделиться некоторыми наблюдениями и замечаниями, возникшими в процессе реализации СРК и тестирования. При реализации HA кластеров СУБД репликация не является резервной копией, поэтому мы говорим о необходимости системы/средства для восстановления кластера. При переключении на репликацию в случае failover необходимо также переключать и СРК, причем необходимо использовать новый bucket в S3 хранилище MinIO для сохранения резервных копий. В S3 хранилище MinIO установлен часовой пояс UTC, что может не совпадать с серверным временем и часовым поясов в PostgreSQL. WAL-G при запросах списка бэкапов через wal-g backup-list отображает время MinIO. Об этом нужно помнить при восстановлении резервных копий кластера PostgreSQL из MinIO на определнный момент времени. Необходимо открыть порт в подовой сети для сервиса MinIO, чтобы у WAL-G был доступ к хранилищу, а также настроить шифрование и политики МСЭ для безопасности. Для применения настроек по реализации СРК нам необходим перезапуск кластера PostgreSQL, как минимум для параметров: archive_mode, wal_level, поэтому настройку СРК необходимо производить в сервисное окно. При использовании механизма архивирования PostgreSQL по отправке WAL журналов с помощью archive_command в случае неправильной настройки пути до S3, либо продолжительного отсутствия связи с настроенным хранилищем, процесс WAL-G wal-push может не завершиться автоматически, из-за чего WAL журналы не будут отправляться в хранилище. Необходимо следить за доступностью хранилища и убивать процесс при его зависании. В WAL-G нет команды по проверке корректного подключения к S3 хранилищам, поэтому в таком случае при автоматизированной настройке СРК можно использовать команду wal-g backup-list, которая вернет список бэкапов при правильной настройке подключения. Нужно учитывать, что копирование журналов WAL через WAL-G и файлов MinIO через rsync имеют разное время, поэтому в неокторых ситуациях время восстановления необходимо выбирать раньше, чем последнее возможное (потеря данных может составить чуть больше 10 минут). С помощью команд WAL-G wal-verify integrity и wal-verify timeline существует возможность убедиться, что хранилище сегментов WAL исправно, иногда стоит запускать данные команды для проверки. При восстановлении необходимо учитывать наложенные на файлы в директории кластера PostgreSQL права ACL или другие политики. Необходимо периодически проводить верификацию бэкапов, чтобы убедиться в их валидности. К сожалению, это делают далеко не все. Наши средства СРК предоставляют возможность верификации бэкапов путем разворота независимой от основной системы на отдельном сервере. При восстановлении данных необходимо отключать все CRON скрипты, связанные с этими данными для избежания перезаписи данных. Операции по восстановлению СУБД PostgreSQL и восстановлению пользовательских данных можно проводить параллельно для ускорения времени восстановления системы. После проведения успешных тестов на наших стендах, необходимо задокументировать процесс разворота и настройки СРК, а также детально описать процедуру резервного копирования и восстановления данных. Так как мы имеем дело с закрытым контуром заказчика, то инструкции необходимо было составить так, чтобы администратор без опыта работы с кластером PostgreSQL и объектными хранилищами мог быстро разобраться в работе СРК и восстановить систему при возникновении нештатных ситуаций. Поэтому необходимо учитывать каждый пункт и обращать внимание на мелкие детали для избежания неприятных последствий при попытках восстановления системы. Например: Прежде чем очищать директорию неисправного кластера СУБД, ее настоятельно рекомендуется скопировать (а при наличии места - переименовать) и хранить до корректного восстановления и работы системы. Параметр recovery_target_time должен быть в часовом поясе PostgreSQL, а параметр recovery_target_timeline не должен быть “LATEST” при восстановлении на конкретный момент времени (при обычном восстановлении его лучше закомментировать и использовать по умолчанию). Необходимо помнить, что после каждого восстановления СУБД timeline изменяется, и может потребоваться восстановление с учетом необходимой линии времени. При восстановлении на опредленный момент времени можно добавить параметр recovery_target_action = pause. С помощью него мы можем проверить необходимые восстановленые данные перед запуском СУБД в режиме read-write. При восстановлении на самый последний бэкап с применением всех последующих WAL через команду wal-g backup-fetch /pgdata LATEST такой возможности нет. После восстановления на опредленный момент времени следует обязательно обновить настройки конфигурации кластера БД, выполнив  команду SELECT pg_reload_conf(); без необходимости перезагрузки сервера, для возможности записи новых WAL-журналов в S3 хранилище MinIO через WAL-G. Инструкции должны содержать четкий план действий и исключать необходимость в выполнении незадокументированных действий  (особенно, когда речь идет про восстановление PostgreSQL). При обучении на тестовых стендах очень важно, чтобы администраторы выполняли действия по инструкции самостоятельно, для возможности внесения корректировок и совершенствования процессов. Передав необходимые компоненты и документацию, мы провели обучение администраторов по развороту и использованию СРК и отправили решение в тестовую среду на апробацию.  Как это обычно бывает - не все идет гладко с первого раза, но на то и проводится тестирование. Некоторые замечания я уже учел в пунктах выше, но хочу рассказать о следующем случае.  Иногда во время тестирования количество данных записываемое из MinIO на СХД через rsync за 10 минут было много больше, чем успевало передаваться rsync за то же время. Либо поток данных был постоянен и rsync не успевал отрабатывать. В итоге несколько процессов rsync могли накладываться друг на друга в результате чего мог переполняться буфернэй кэш, это могло приводить к следующим проблемам: Если буферный кэш заполняется большим объемом данных, это может привести к использованию большого количества оперативной памяти системы. В результате, доступная оперативная память для других процессов может сократиться, что может привести к увеличению использования подкачки и снижению производительности. Если несколько процессов одновременно используют буферный кэш, может возникнуть конкуренция за доступ к оперативной памяти и другими ресурсами, что может привести к снижению производительности и задержкам. Данная проблема имела негативное влияние на работу системы и для ее решения мы ввели механизм блокировки через .loc файлы для предотвращения наложения процессов друг на друга. В конечном итоге мы учли все замечания, доработали нашу документацию и скрипты, исправили имеющиеся ошибки и отправили СРК на повторное тестирование. В результате апробация прошла успешно и мы запустили СРК в рабочий режим в систему заказчика. Мы не останавливаемся на достигнутой архитектруе СРК и продолжаем развивать данное направление, пробуем интегрировать различные средства резервного копирования не только пользовательских данных, но и системы в целом и выстраиваем надежный план СРК в соответствии с требования по надежности к ИС и требованиями заказчиков.  DBA/System Architect