
Привет, Habr! На связи эксперты команды сервиса WatchDog — Дмитрий Коновалов и Геннадий Переломов.
В ВТБ, у нашего основного заказчика, мы развиваем сервисы автоматизации сопровождения баз данных. Одной из ключевых СУБД в инфраструктуре является PostgreSQL. Поддержка её в актуальном состоянии требует периодических мажорных обновлений, которые остаются одной из самых трудоёмких задач для DBA, особенно в ночные или выходные технологические окна.
В этой статье мы расскажем, как разработали внутренний сервис, позволяющий администраторам прикладных систем запускать мажорное обновление PostgreSQL в один клик и без участия DBA.
Зачем автоматизировать мажорные обновления PostgreSQL
Жизненный цикл мажорной версии PostgreSQL — 5 лет. Чтобы оставаться в зоне официальной поддержки, получать новые возможности и улучшения, и при этом соответствовать внутренним нормативным документам (ВНД), необходимо регулярно обновлять инсталляции. На момент старта проекта в ВТБ было несколько тысяч экземпляров PostgreSQL, и ручное обновление каждого из них было бы затратным и рискованным.
Постановка задачи
В мае 2023 года перед нами поставили задачу:
Создать сервис (условно — «кнопку»), который позволит администраторам АБС выполнять мажорное обновление PostgreSQL без участия DBA.
Требования:
Поддержка standalone и cluster конфигураций.
Надёжность — на входе и выходе должен быть рабочий экземпляр PostgreSQL.
Минимизация человеческого фактора.
Поддержка расширений pg_stat_statements и pgaudit.
Обязательное выполнение резервного копирования.
Полное соответствие корпоративным чеклистам.
Анализ и подготовка
Мы изучили:
статистику используемых версий PostgreSQL (от 11 до начинающейся 15),
типовые расширения и модули,
текущие процессы обновления,
особенности конфигураций (Patroni, standalone),
ограничения по времени обновлений (ночное окно).
В результате мы сузили фокус:
поддержка обновлений до версий 14 и 15,
анализ расширений и автоматическая проверка их наличия,
обязательные предварительные проверки, чтобы гарантировать запуск PostgreSQL после обновления.
Реализация: архитектура «кнопки»
Сценарий был разбит на две части:
Frontend: веб-интерфейс с выбором сервера и мажорной версии;
Backend: масштабный Ansible Playbook (более 3000 строк и ~100 шагов).
Форма пользователя:
Выбор сервера из доступных.
Выбор версии обновления (14 или 15).
Отображение ограничений сценария.
Чекбокс «Я понимаю, что делаю» — без него запуск невозможен.
Структура playbook
Шесть основных блоков + блоки отката:
1. Предварительные проверки:
Доступность экземпляра,
Конфигурация,
Наличие расширений,
Соответствие чеклисту.
2. Установка новой версии PostgreSQL.
3. Подготовка к pg_upgrade:
Dump-проверка,
Тестовое выполнение pg_upgrade.
4. Запуск pg_upgrade.
5. Бэкап:
Для cluster — бэкапом является сама реплика, которую сценарий не трогает пока успешно не обновим лидер,
Для standalone — до выполнения pg_upgrade.
6. Финальные проверки и правки конфигурации.
Механизмы отката
Механизмы отката предусмотрены для всех этапов до и после неуспешного выполнения pg_upgrade. В случае ошибки сценарий восстанавливает PostgreSQL в изначальное рабочее состояние.
Дополнительно:
Обновление Patroni до целевой версии (если требуется).
Асинхронное выполнение ресурсоёмких операций: pg_upgrade, vacuumdb, pg_dump, бэкап.
Тестирование
Мы провели тестирование по всем возможным сценариям:
Все поддерживаемые мажорные версии;
Разные объёмы $PGDATA, LO, количество таблиц;
Все комбинации включённых расширений;
Все блоки отката.
Результаты показали надежную работу сценария.
Результаты эксплуатации
На момент публикации сценарий находится в боевой эксплуатации более полутора лет, за это время проведено свыше 1000 успешных обновлений PostgreSQL, а также добавлена поддержка обновления до версии 16.
Статистика работы сценария в ПРОД среде:
config | pg_ver | pgdata_size | count_unlogged_tables | count_all_tables | count_all_lo | duration |
cluster | 14 -> 15 | 849 GB | 0 | 25348 | 0 | 03:22:16 |
cluster | 14 -> 15 | 1840 GB | 0 | 6140 | 0 | 03:00:36 |
cluster | 14 -> 15 | 898 GB | 0 | 4419 | 0 | 02:58:19 |
cluster | 14 -> 15 | 721 GB | 0 | 13846 | 0 | 01:39:38 |
cluster | 13 -> 15 | 150 GB | 0 | 19834 | 307969 | 00:56:31 |
cluster | 14 -> 15 | 829 GB | 0 | 382 | 0 | 00:50:25 |
cluster | 14 -> 15 | 922 GB | 0 | 1454 | 0 | 00:43:47 |
cluster | 14 -> 15 | 390 GB | 0 | 868 | 0 | 00:43:30 |
cluster | 14 -> 15 | 377 GB | 0 | 236 | 0 | 00:40:59 |
cluster | 13 -> 15 | 644 GB | 0 | 138 | 0 | 00:40:56 |
cluster | 14 -> 15 | 750 GB | 0 | 778 | 0 | 00:39:47 |
cluster | 14 -> 15 | 1022 GB | 0 | 45 | 0 | 00:35:58 |
cluster | 14 -> 15 | 224 GB | 0 | 352 | 0 | 00:29:39 |
cluster | 14 -> 15 | 241 GB | 0 | 150 | 0 | 00:29:04 |
cluster | 15 -> 16 | 250 GB | 0 | 93 | 0 | 00:28:51 |
cluster | 14 -> 15 | 436 GB | 0 | 101 | 0 | 00:26:50 |
cluster | 14 -> 15 | 230 GB | 0 | 103 | 0 | 00:25:56 |
cluster | 14 -> 15 | 381 GB | 0 | 257 | 0 | 00:25:16 |
cluster | 13 -> 15 | 61 GB | 0 | 21758 | 180 | 00:24:27 |
cluster | 14 -> 15 | 210 GB | 0 | 129 | 0 | 00:22:34 |
cluster | 13 -> 15 | 218 GB | 0 | 197 | 0 | 00:21:33 |
cluster | 14 -> 15 | 186 GB | 0 | 547 | 0 | 00:21:28 |
cluster | 14 -> 15 | 80 GB | 0 | 397 | 0 | 00:21:12 |
cluster | 14 -> 15 | 184 GB | 0 | 106 | 0 | 00:21:06 |
cluster | 13 -> 15 | 223 GB | 0 | 197 | 0 | 00:20:34 |
cluster | 14 -> 15 | 61 GB | 0 | 449 | 0 | 00:15:51 |
cluster | 14 -> 15 | 163 GB | 0 | 57 | 0 | 00:15:00 |
cluster | 14 -> 15 | 96 GB | 0 | 49 | 0 | 00:14:16 |
cluster | 14 -> 15 | 9718 MB | 0 | 43 | 0 | 00:13:39 |
cluster | 14 -> 15 | 57 GB | 0 | 118 | 0 | 00:11:52 |
cluster | 14 -> 15 | 56 GB | 0 | 34 | 0 | 00:11:49 |
cluster | 14 -> 15 | 9731 MB | 0 | 43 | 0 | 00:11:42 |
cluster | 15 -> 16 | 80 GB | 0 | 81 | 0 | 00:11:23 |
cluster | 14 -> 15 | 16 GB | 0 | 167 | 0 | 00:11:13 |
cluster | 14 -> 15 | 10 GB | 0 | 408 | 0 | 00:11:11 |
cluster | 14 -> 15 | 68 GB | 0 | 598 | 0 | 00:11:07 |
cluster | 14 -> 15 | 14 GB | 0 | 73 | 0 | 00:10:41 |
cluster | 15 -> 16 | 43 GB | 0 | 101 | 0 | 00:10:36 |
cluster | 14 -> 15 | 20 GB | 0 | 129 | 0 | 00:09:50 |
cluster | 13 -> 15 | 8613 MB | 0 | 21 | 0 | 00:09:37 |
cluster | 14 -> 15 | 9005 MB | 0 | 74 | 0 | 00:09:30 |
cluster | 14 -> 15 | 65 GB | 0 | 178 | 0 | 00:09:09 |
cluster | 14 -> 15 | 8787 MB | 0 | 210 | 0 | 00:08:56 |
cluster | 14 -> 15 | 21 GB | 0 | 324 | 14893 | 00:08:50 |
cluster | 15 -> 16 | 8642 MB | 0 | 21 | 0 | 00:08:47 |
cluster | 14 -> 15 | 8749 MB | 0 | 54 | 0 | 00:08:44 |
cluster | 14 -> 15 | 31 GB | 0 | 44 | 0 | 00:08:41 |
cluster | 14 -> 15 | 9434 MB | 0 | 76 | 0 | 00:08:33 |
cluster | 14 -> 15 | 8567 MB | 0 | 21 | 0 | 00:08:28 |
cluster | 15 -> 16 | 22 GB | 0 | 333 | 0 | 00:08:19 |
cluster | 14 -> 15 | 17 GB | 0 | 165 | 0 | 00:08:07 |
cluster | 14 -> 15 | 12 GB | 0 | 275 | 0 | 00:08:01 |
cluster | 12 -> 15 | 8627 MB | 0 | 21 | 0 | 00:07:57 |
cluster | 15 -> 16 | 11 GB | 20 | 1250 | 0 | 00:07:36 |
cluster | 14 -> 15 | 8807 MB | 0 | 26 | 0 | 00:07:35 |
cluster | 15 -> 16 | 9771 MB | 0 | 135 | 0 | 00:07:33 |
cluster | 12 -> 15 | 8628 MB | 0 | 21 | 0 | 00:06:58 |
Заключение
Ключом к успешному внедрению стало соблюдение чеклистов и подготовка экземпляров — спасибо команде сопровождения PostgreSQL и сервису контроля инфраструктуры WatchDog.
Мы продолжаем развивать сценарий, и с нетерпением ждём появления в Банке версии PostgreSQL 17, чтобы добавить её поддержку в нашу «кнопку».
Что дальше
Если у вас есть опыт автоматизации обновлений PostgreSQL — поделитесь в комментариях. Какие подходы вы используете? Как решаете проблему rollback? Насколько глубоко интегрируете с CI/CD?
Мы открыты к диалогу.
