Привет, 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 до версии 3.0.2 (если требуется).

  • Асинхронное выполнение ресурсоёмких операций: pg_upgrade, vacuumdb, pg_dump, бэкап.

Тестирование

Мы провели тестирование по всем возможным сценариям:

  • Все поддерживаемые мажорные версии;

  • Разные объёмы $PGDATA, LO, количество таблиц;

  • Все комбинации включённых расширений;

  • Все блоки отката.

Результаты показали надежную работу сценария.

Результаты эксплуатации

На момент публикации сценарий находится в боевой эксплуатации более полутора лет, за это время проведено свыше 1000 успешных обновлений PostgreSQL, а также добавлена поддержка обновления до версии 16.

Статистика работы сценария в ПРОД среде:

config

pg_ver

patroni_ver_leader

patroni_ver_replica

pgdata_size

count_unlogged_tables

count_all_tables

count_all_lo

duration

cluster

14.8->15.6

3.0.2->3.0.2

3.0.2->3.0.2

849 GB

0

25348

0

03:22:16

cluster

14.8->15.8

3.3.2->3.3.2

3.3.2->3.3.2

1840 GB

0

6140

0

03:00:36

cluster

14.8->15.6

3.0.2->3.0.2

3.0.2->3.0.2

898 GB

0

4419

0

02:58:19

cluster

14.8->15.6

3.0.2->3.0.2

3.0.2->3.0.2

721 GB

0

13846

0

01:39:38

cluster

13.11->15.8

3.0.2->3.0.2

3.0.2->3.0.2

150 GB

0

19834

307969

00:56:31

cluster

14.8->15.8

3.0.2->3.0.2

3.0.2->3.0.2

829 GB

0

382

0

00:50:25

cluster

14.8->15.6

3.0.2->3.0.2

3.0.2->3.0.2

922 GB

0

1454

0

00:43:47

cluster

14.11->15.8

3.3.2->3.3.2

3.3.2->3.3.2

390 GB

0

868

0

00:43:30

cluster

14.8->15.6

3.0.2->3.0.2

3.0.2->3.0.2

377 GB

0

236

0

00:40:59

cluster

13.14->15.8

2.1.5->3.0.2

2.1.5->3.0.2

644 GB

0

138

0

00:40:56

cluster

14.8->15.6

3.0.2->3.0.2

3.0.2->3.0.2

750 GB

0

778

0

00:39:47

cluster

14.11->15.8

3.3.2->3.3.2

3.3.2->3.3.2

1022 GB

0

45

0

00:35:58

cluster

14.8->15.8

2.1.5->3.0.2

2.1.5->3.0.2

224 GB

0

352

0

00:29:39

cluster

14.6->15.8

3.0.2->3.0.2

3.0.2->3.0.2

241 GB

0

150

0

00:29:04

cluster

15.6->16.4

3.3.2->3.3.2

3.3.2->3.3.2

250 GB

0

93

0

00:28:51

cluster

14.8->15.8

3.0.2->3.0.2

3.0.2->3.0.2

436 GB

0

101

0

00:26:50

cluster

14.6->15.8

3.0.2->3.0.2

3.0.2->3.0.2

230 GB

0

103

0

00:25:56

cluster

14.11->15.8

3.3.2->3.3.2

3.3.2->3.3.2

381 GB

0

257

0

00:25:16

cluster

13.11->15.8

3.0.2->3.0.2

3.0.2->3.0.2

61 GB

0

21758

180

00:24:27

cluster

14.8->15.8

2.1.5->3.0.2

2.1.5->3.0.2

210 GB

0

129

0

00:22:34

cluster

13.11->15.6

3.0.2->3.0.2

3.0.2->3.0.2

218 GB

0

197

0

00:21:33

cluster

14.11->15.8

3.3.2->3.3.2

3.3.2->3.3.2

186 GB

0

547

0

00:21:28

cluster

14.8->15.8

2.1.5->3.0.2

2.1.5->3.0.2

80 GB

0

397

0

00:21:12

cluster

14.8->15.8

3.0.2->3.0.2

3.0.2->3.0.2

184 GB

0

106

0

00:21:06

cluster

13.11->15.6

3.0.2->3.0.2

3.0.2->3.0.2

223 GB

0

197

0

00:20:34

cluster

14.8->15.8

2.1.5->3.0.2

2.1.5->3.0.2

61 GB

0

449

0

00:15:51

cluster

14.11->15.6

2.1.5->3.0.2

2.1.5->3.0.2

163 GB

0

57

0

00:15:00

cluster

14.11->15.8

2.1.5->3.0.2

2.1.5->3.0.2

96 GB

0

49

0

00:14:16

cluster

14.4->15.8

3.3.2->3.3.2

3.3.2->3.3.2

9718 MB

0

43

0

00:13:39

cluster

14.8->15.8

3.0.2->3.0.2

3.0.2->3.0.2

57 GB

0

118

0

00:11:52

cluster

14.11->15.6

2.1.5->3.0.2

2.1.5->3.0.2

56 GB

0

34

0

00:11:49

cluster

14.4->15.8

3.3.2->3.3.2

3.3.2->3.3.2

9731 MB

0

43

0

00:11:42

cluster

15.6->16.4

3.3.2->3.3.2

3.3.2->3.3.2

80 GB

0

81

0

00:11:23

cluster

14.8->15.8

3.3.2->3.3.2

3.3.2->3.3.2

16 GB

0

167

0

00:11:13

cluster

14.8->15.8

2.1.5->3.0.2

2.1.5->3.0.2

10 GB

0

408

0

00:11:11

cluster

14.8->15.8

3.0.2->3.0.2

3.0.2->3.0.2

68 GB

0

598

0

00:11:07

cluster

14.13->15.8

3.3.2->3.3.2

3.3.2->3.3.2

14 GB

0

73

0

00:10:41

cluster

15.6->16.4

3.3.2->3.3.2

3.3.2->3.3.2

43 GB

0

101

0

00:10:36

cluster

14.11->15.8

3.3.2->3.3.2

3.3.2->3.3.2

20 GB

0

129

0

00:09:50

cluster

13.14->15.6

2.1.5->3.0.2

2.1.5->3.0.2

8613 MB

0

21

0

00:09:37

cluster

14.11->15.8

3.3.2->3.3.2

3.3.2->3.3.2

9005 MB

0

74

0

00:09:30

cluster

14.11->15.8

3.3.2->3.3.2

3.3.2->3.3.2

65 GB

0

178

0

00:09:09

cluster

14.8->15.8

3.3.2->3.3.2

3.3.2->3.3.2

8787 MB

0

210

0

00:08:56

cluster

14.8->15.8

3.0.2->3.0.2

3.0.2->3.0.2

21 GB

0

324

14893

00:08:50

cluster

15.6->16.4

3.3.2->3.3.2

3.3.2->3.3.2

8642 MB

0

21

0

00:08:47

cluster

14.8->15.6

3.0.2->3.0.2

3.0.2->3.0.2

8749 MB

0

54

0

00:08:44

cluster

14.8->15.8

3.0.2->3.0.2

3.0.2->3.0.2

31 GB

0

44

0

00:08:41

cluster

14.11->15.8

3.3.2->3.3.2

3.3.2->3.3.2

9434 MB

0

76

0

00:08:33

cluster

14.11->15.6

2.1.5->3.0.2

2.1.5->3.0.2

8567 MB

0

21

0

00:08:28

cluster

15.6->16.4

3.3.2->3.3.2

3.3.2->3.3.2

22 GB

0

333

0

00:08:19

cluster

14.8->15.8

3.3.2->3.3.2

3.3.2->3.3.2

17 GB

0

165

0

00:08:07

cluster

14.8->15.8

3.3.2->3.3.2

3.3.2->3.3.2

12 GB

0

275

0

00:08:01

cluster

12.18->15.8

3.3.2->3.3.2

3.3.2->3.3.2

8627 MB

0

21

0

00:07:57

cluster

15.8->16.4

3.3.2->3.3.2

3.3.2->3.3.2

11 GB

20

1250

0

00:07:36

cluster

14.11->15.8

3.3.2->3.3.2

3.3.2->3.3.2

8807 MB

0

26

0

00:07:35

cluster

15.6->16.4

3.3.2->3.3.2

3.3.2->3.3.2

9771 MB

0

135

0

00:07:33

cluster

12.18->15.8

3.3.2->3.3.2

3.3.2->3.3.2

8628 MB

0

21

0

00:06:58

Заключение

Ключом к успешному внедрению стало соблюдение чеклистов и подготовка экземпляров — спасибо команде сопровождения PostgreSQL и сервису контроля инфраструктуры WatchDog.

Мы продолжаем развивать сценарий, и с нетерпением ждём появления в Банке версии PostgreSQL 17, чтобы добавить её поддержку в нашу «кнопку».

Что дальше

Если у вас есть опыт автоматизации обновлений PostgreSQL — поделитесь в комментариях. Какие подходы вы используете? Как решаете проблему rollback? Насколько глубоко интегрируете с CI/CD?

Мы открыты к диалогу.

Комментарии (0)