В мире разработки и эксплуатации ПО мажорные обновления — это всегда стресс. Независимо от того, насколько хорошо вы тестируете изменения, всегда есть риск, что что-то пойдёт не так. Особенно это касается обновлений, которые затрагивают пользовательские данные. В какой-то момент мы задумались о том, как нам минимизировать риски и сделать обновления более предсказуемыми.
Меня зовут Кристина Демидович, я DevOps‑инженер в СберТехе, занимаюсь автоматизацией в команде СУБД Pangolin — это целевая СУБД в Сбере и не только. Я расскажу о нашем подходе к обновлению СУБД Pangolin, который позволил нам превратить часть мажорных обновлений в обновление данных системного каталога — что проще, удобнее и занимает вдвое меньше времени.
Здесь приводится сокращённая версия текста, больше технических деталей — в полной версии статьи на Хабре.
Традиционно мажорное обновление предполагает полную миграцию данных. Как правило, это полный фарш: полный бэкап, инициализация новой БД и её настройка, подготовка конфигурационных файлов. Миграция с помощью pg_upgrade
, после которого необходим запуск сборки новой статистики (vacuumdb
). Перенос данных с помощью rsync
, обновление версий расширений и так далее. Всё это необходимо выполнить единовременно. В общем, у мажорных обновлений есть ряд существенных недостатков:
Полностью отказаться от мажорных обновлений мы не можем, поскольку они могут включать в себя глобальные изменения. Вот случаи, когда мы выпускаем мажорную версию:
В первых трёх случаях обойтись без мажорных обновлений нельзя, и это обоснованно. А вот на изменение системного каталога мы посмотрели под другим углом. Делать мажорное обновление в этом сценарии неоправданно долго и ресурсозатратно, даже если изменения незначительны — меняются только системные данные, но не сами физические объекты системного каталога (таблицы, столбцы, индексы, ключи и т. д.).
Нам надоело это терпеть — и мы дали жизнь новому способу обновления.
Работу разделили на два этапа:
Подробно о том, как устроен этот инструмент, расскажет мой коллега Николай Литковец в своей статье. А я расскажу вкратце и перейду к автоматизации обновления. Да, уточню, что мы ставили в приоритет совместимость с инфраструктурой. Логика решения должна была быть прозрачной и не нарушать привычные процессы.
Механизм включает в себя добавление новых объектов в системный каталог и изменение существующих объектов. Всё начинается с создания дампа pg_catalog
(это происходит до обновления), чтобы проверить согласованность исходных данных системных каталогов. С помощью дополнительной утилиты update_catalog_version
меняется версия каталога. После этого каталоги до пользовательских табличных пространств переименовываются в соответствии с новой версией системного каталога.
Далее загружаем SQL-скрипты на master-хост, для всех баз данных, включая template0 и template1. Проходит это в две итерации. Сначала скрипты запускают с функцией ROLLBACK, чтобы проверить возможность их загрузки. Если тестовая загрузка прошла успешно, то скрипты запускают повторно без ROLLBACK.
После успешной загрузки всех SQL-скриптов во все БД создаётся повторный дамп pg_catalog для проверки консистентности обновлённых данных системного каталога.
Если при первой итерации загрузки данных возникают проблемы, то вторую итерацию не запускаем. Происходит откат к исходной версии системного каталога. Для восстановления работоспособности достаточно вернуть бинарные файлы исходной версии. Если есть проблемы при второй итерации, то требуется не только восстановить бинарные файлы исходной версии, но и запустить утилиту в режиме reset.
В случае кластерной конфигурации на replica-хост данные доезжают с помощью репликации.
Следующий шаг — автоматизация запуска. Мы реализовали её через набор ролей Ansible, которые выполняют различные задачи: установку, обновление и настройку компонентов продукта. Простота управления заключается в разделении ролей по функциональным областям и компонентам продукта.
Каждая роль отвечает за выполнение определённых задач, связанных с конкретным компонентом или процессом.
При подготовке новой модели обновления нам было важно следующее:
Мы хотели не радикально поменять механизм скриптов, а интегрировать в него новые процессы. Внедрение нового процесса в существующий механизм минорного обновления позволило ускорить разработку и тестирование за счёт того, что он был успешно протестирован и внедрён ранее. Такое обновление мы назвали обновлением данных системного каталога.
Сейчас наши плейбуки работают преимущественно с монолитными системами, хотя у нас есть планы по переходу на компонентную архитектуру. Но это уже другая история. Рассмотрим исходный плейбук для минорного обновления. Он состоит из трёх основных разделов:
Сосредоточимся на обновлении СУБД Pangolin. В случае кластерной конфигурации мы обеспечиваем отсутствие простоя с помощью последовательного обновления хостов.
При обновлении данных системного каталога БД нам требуется обеспечить полную недоступность СУБД для пользователей. Во время снятия дампов системных таблиц (например, pg_class
, pg_type
, pg_proc
и других), которые содержат важную информацию о структуре данных, любые изменения этих таблиц могут привести к несоответствию между состоянием БД на момент начала обновления и по его окончании. Это может создать проблемы при восстановлении данных или привести к ошибкам в работе самой СУБД после обновления. Чтобы исключить эти риски, организован полный downtime — период, когда БД недоступна для всех операций записи и чтения со стороны пользователей.
Это ключевое отличие потребовало переработки плейбука.
Путь, по которому идёт обновление, определяется параметром pg_inplace_upgrade
. Его значение вычисляется запуском скрипта inplace_upgrade.sh в режиме info. Запуск выполняется после всех проверок готовности стенда к обновлению в рамках роли pangolin_checks
, причём одновременно на всех хостах.
Схематично последовательность действий выглядит так:
Этап, отмеченный жёлтым цветом, играет важную роль. Он включает в себя:
На этом этапе ключевой аспект — резервное копирование только системных файлов. Это отличается от полного резервного копирования всех данных, которое происходит при мажорном обновлении и может занимать длительное время на большой БД.
Создание бэкапа определено первым шагом не случайно. На этом этапе обновления могут возникнуть сценарии, требующие последующего восстановления. В зависимости от кода состояния скрипта inplace_upgrade.sh, контролируется дальнейшая работа скриптов автоматизации. Рассмотрим возможные сценарии и их обработку:
Параметр pg_inplace_upgrade
управляет тем, как будет выполняться восстановление.
Посмотрим на процесс восстановления на схеме:
Как можно заметить, по существующим исходам работы inplace_upgrade.sh может быть два пути восстановления:
Восстановление версии СУБД с запуском восстановления файлов системного каталога из бэкапа предполагает возврат бинарных файлов к предыдущей версии СУБД, а также использование ранее созданной резервной копии данных для восстановления в исходное состояние данных системного каталога. Этот метод используется только если при обновлении были затронуты данные системного каталога. Восстановление из бэкапа происходит с помощью запуска скрипта inplace_upgrade.sh в режиме reset.
И восстановление версии СУБД без запуска восстановления файлов из бэкапа. Если данные системного каталога остались нетронутыми, достаточно вернуть бинарные файлы СУБД к предыдущей версии, не нужно восстанавливать файлы из бэкапа.
Подвести итог хотелось бы числами. Напомню, что мы стремились сократить время таких обновлений. После разработки решения пришли к следующим результатам:
Мы внедрили обновление данных системных каталогов в версии 6.4.0. В результате этого текущее состояние наших версий выглядит так: 6.1.0 — 6.2.0 — 6.3.0 — 6.4.0 — 6.5.0 — 6.6.0 — 7.1.0. Изменение минорной версии означает обновление данных системных каталогов, а изменение мажорной версии — мажорное обновление. Если бы мы не внедрили этот подход, то наше состояние могло бы быть таким: 6.1.0 — 6.2.0 — 6.3.0 — 7.1.0 — 8.1.0 — 9.1.0 — 10.1.0.
Заметный прогресс, хотя и, безусловно, есть над чем работать. Если интересно узнать больше о том, как мы развиваем решение, приходите в наше сообщество.