Назад к статьям

PostgreSQL 10 — Потоковая репликация с отработкой фейловера

PostgreSQL 10 — Потоковая репликация с отработкой фейловера. Данная запись посвящается исключительно «Потоковой репликации», не архивированию, не логической репликации, не восстановлению из бэкапа. Тут описывается потоковая репликация, настройка одного мастера, одного слейва и переключение между ними. Репликация — не бэкап.

  • В случае если вы на мастере убили базу, это изменение переедет в реплику.
  • Так же если у вас нету бэкапа убитой базы — вероятно вас повесят.
  • Если у вас в процессе работы упал мастер, например сдох диск, сгорел сервер, на часть здания в которой располагался мастер — упал метеорит. Вас спасет репликация.

Репликация PostgreSQL 10 — это когда вы превращаете слейв в текущий мастер и продолжаете радаснаработать с минимально возможным оставанием текущего мастера от старого мастера на момент выхода из строя и минимальным временем простоя.

Даже если вы просто не знаете что произошло с мастером но уверены что в ближайшие секунды он не заработает — вы можете превратить слейв в мастер для восстановления работы. А уже потом вы будете разбираться с тем что случилось с прошлым мастером. Главное не допустить существования в вашей сети двух рассинхронизированных мастеров. Тогда вас тоже повесят 🙂

Не смотрите на то что запись получилось достаточно объемной. Она приняла такие масштабы исключительно по причине желания сделать её более понятной и рассмотреть все ситуации работы данной схемы. Сам процесс — это пара десятков команд на обоих серверах и пара минут времени.

Исходные данные:

  • Два хоста настроенные по этой инструкции: Ubuntu 18.04 — Установка PostgreSQL 10 для 1С
  • 2 Ядра + 16GB Ram + 32GB SSD у каждого (Не потому что так надо, а потому что это стенд, параметры продакшена надо смотреть по фактической нагрузке)
  • Имя мастера: ag-psql10-s1 / IP 192.168.1.231
  • Имя слейва: ag-psql10-s2 / IP 192.168.1.232
  • Пароль пользователя postgres h4GiQn
  • Пароль пользователя replica 4UtKbw
  • Все данные которые есть на слейве — будут удалены и заменены данными мастера. Включая и пользователей. Это важно! Не убейте свои ценные данные.
  • Данные pg_hba.conf и postgresql.conf не реплицируются, их придется настраивать ручками

PostgreSQL 10 — Потоковая репликация — Настраиваем мастер

Создаем пользователя репликации

bash
sudo su postgres #В этот момент мы авторизовываемся под пользователем postgres
psql
postgres=# CREATE USER replica REPLICATION LOGIN CONNECTION LIMIT 2 ENCRYPTED PASSWORD '4UtKbw';
\q

У данного пользователя в postgresql будет пароль 4UtKbw и лимит в 2 одновременных подключения. Помним что если в pg_hba.conf у нас настроено следующим образом:

bash
# Database administrative login by Unix domain socket
local   all             postgres                                md5

То при вводе команды psql система попросит вас ввести пароль вашего пользователя postgres в postgresql. У меня он h4GiQn. Если у вас просит пароль, но вы не помните его, надо изменить md5 на trust, перезапустить постгрес и вас пустит без пароля.

Бэкапим pg_hba.conf

bash
#будучи всё еще авторизованными под пользователем postgres
cp /etc/postgresql/10/main/pg_hba.conf /etc/postgresql/10/main/pg_hba.bkp

Редактируем pg_hba.conf

bash
#будучи всё еще авторизованными под пользователем postgres
nano /etc/postgresql/10/main/pg_hba.conf

Приводим раздел репликации к такому виду:

bash
#Настройки pg_hba.conf для ag-psql10-s1
# Allow replication connections from localhost, by a user with the
# replication privilege.
#local   replication     all                                     peer
#host    replication     all             127.0.0.1/32            md5
#host    replication     all             ::1/128                 md5
host    replication     replica         192.168.1.232/32        md5

Мы указываем что репликация разрешена пользователю с именем replica, подключающемуся с адреса 192.168.1.232 с авторизацией по паролю
Сохраняем внесенные изменения командой Ctrl+O, закрываем nano командой Ctrl+X

Бэкапим postgresql.conf

bash
#будучи всё еще авторизованными под пользователем postgres
cp /etc/postgresql/10/main/postgresql.conf /etc/postgresql/10/main/postgresql.bkp

Редактируем postgresql.conf

bash
#будучи всё еще авторизованными под пользователем postgres
nano /etc/postgresql/10/main/postgresql.conf

Для того чтобы быстро находить нужные нам параметры, можно использовать поиск, активируемый комбинацией Ctrl+W

bash
listen_addresses = '*'
hot_standby = on
wal_level = replica
max_wal_senders = 10
wal_keep_segments = 32

Приводим параметры к указанному выше виду.
Немного комментариев:

  • listen_addresses Этот параметр отвечает за то на каких интерфейсах PostgresSQL будет принимать клиентские подключения. Я оставлю ‘*’, что значит ВСЕ
  • hot_standby Если очень поверхностно — этот параметр отвечает за то, можно ли читать данные слейва. Например для какой-нибудь умной аналитической нагрузки которой не надо записывать данные в базу. В случае с 1Ской можно сделать off. В случае каскадной репликации придется оставить on. Я оставлю on.
  • wal_level Отвечает за количество информации записываемой в WAL. Для репликации необходимо значение replica. Возможны значения minimal и для логической репликации logical. По объему данных оно выглядит как: minimal < replica < logical. Нам надо replica
  • max_wal_senders Отвечает за количество одновременно подключенных слейвов у мастера. По умолчанию 10, если 0 — репликация отключается. У нас будет один слейв. Можно либо оставить 10, либо указать 1. Я оставлю 10
  • wal_keep_segments 1 сегмент = 16 метров. Сколько сегментов будет указано хранить здесь, столько postgres сохранит прежде чем при добавлении следующего удалить самый старый сегмент. Если к моменту удаления слейв не успеет всосать всё еще нужный ему WAL — реплика будет остановлена и по сути её нужно будет настраивать заново закачивая данные через pg_backup. Сколько ставить зависит от того сколько у вас свободного места, но ставить больше 64 я бы не рекомендовал (это 1 гиг). Я поставлю 32. Стандартное значение = 0.

Перезапускаем postgres

bash
#будучи всё еще авторизованными под пользователем postgres
service postgresql restart #система попросит ввести пароль вашей админской учетки
exit
Postgresql 10 - Перезапускаем мастер после настройки

Перезапускаем мастер после настройки

PostgreSQL 10 — Потоковая репликация — Настраиваем слейв

Останавливаем postgresql

bash
sudo su postgres
service postgresql stop #система попросит ввести пароль вашей админской учетки

Удаляем текущие данные

bash
#будучи всё еще авторизованными под пользователем postgres
rm -R /var/lib/postgresql/10/main/

Для того чтобы инициализировать репликацию, необходимо скачать с сервера свежую копию данных. Иначе если разрыв данных будет слишком велик (больше чем доступно данных согласно wal_keep_segments), репликация попросту не запустится.

Закачиваем данные с мастера

bash
#будучи всё еще авторизованными под пользователем postgres
pg_basebackup -P -R -X stream -c fast -h 192.168.1.231 -U replica -D /var/lib/postgresql/10/main/
service postgresql start #система попросит ввести пароль вашей админской учетки

Система запросит пароль от пользователя replica. Когда мы изначально создавали этого пользователя на мастере, мы назначили ему пароль 4UtKbw.
Дожидаемся окончания загрузки.

Настраиваем revocery.conf

После использования команды pg_basebackup на слейве автоматом был создан файл /var/lib/postgresql/10/main/recovery.conf с настройками репликации

bash
nano /var/lib/postgresql/10/main/recovery.conf
#В файле мы увидим две строки:
standby_mode = 'on'
primary_conninfo = 'user=replica password=4UtKbw host=192.168.1.231 port=5432 sslmode=prefer sslcompression=1 krbsrvname=postgres target_session_attrs=any'
#Добавляем триггер файл
trigger_file = '/tmp/to_master'
  • standby_mode = ‘on’ — значит что сервер является резервным и после скачивания последнего WAL файла продолжит пытаться скачивать их используя данные из primary_conninfo
  • primary_conninfo — строка подключения слейва к мастеру
  • trigger_file — путь к файлу, наличие которого, в случае работы сервера в режиме репликации тут же заставит его превратиться в мастер и выйти из режима READONLY.

Приводим раздел репликации СЛЕЙВА к такому виду:

bash
#будучи всё еще авторизованными под пользователем postgres
nano /etc/postgresql/10/main/pg_hba.conf
#Настройки pg_hba.conf для ag-psql10-s2
# Allow replication connections from localhost, by a user with the
# replication privilege.
#local   replication     all                                     peer
#host    replication     all             127.0.0.1/32            md5
#host    replication     all             ::1/128                 md5
host    replication     replica         192.168.1.231/32        md5

На слейве в pg_hba.conf мы указываем IP адрес ag-psql10-s1, чтобы когда он станет слейвом он смог инициализировать подключение пользователя replica

Запускаем слейв

bash
#будучи всё еще авторизованными под пользователем postgres
service postgresql start

PostgreSQL 10 — Потоковая репликация — Проверяем репликацию

Создаем на мастере тестовую базу данных

bash
sudo su postgres #Если вдруг мы авторизованы под другим пользователем
psql #Запускаем psql, вводим пароль если авторизация стоит md5
CREATE DATABASE replica_test_ok;

Смотрим результат на слейве

bash
sudo su postgres #Если вдруг мы авторизованы под другим пользователем
psql #Запускаем psql, вводим пароль если авторизация стоит md5
postgres=# \l 
Список баз данных
Имя       | Владелец | Кодировка | LC_COLLATE  |  LC_CTYPE   |     Права доступа
-----------------+----------+-----------+-------------+-------------+-----------------------
postgres        | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 |
replica_test_ok | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 |
template0       | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 | =c/postgres          +
|          |           |             |             | postgres=CTc/postgres
template1       | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 | =c/postgres          +
|          |           |             |             | postgres=CTc/postgres
(4 строки)

Всё ок, можно создать или удалить еще какую-нибудь базу или таблицу на мастере и данные тут же приедут на реплику (к слову о том что будет если вы убьете в продакшене базу на мастере не имея её бекапа)

PostgreSQL 10 — Потоковая репликация — Отрабатываем фейловер ag-psql10-s1 => ag-psql10-s2

Итак, мы настроили репликацию. Наш мастер: ag-psql10-s1, наш слейв: ag-psql10-s2. Теперь мы отработаем фейловер в процессе которого ag-psql10-s1 умрёт и роль мастера уедет на ag-psql10-s2.

Для наглядности создадим на мастере новую базу

bash
sudo su postgres
psql
CREATE DATABASE failover_s1_to_s2_test_ok;
\q
exit

Выключаем мастер

bash
sudo service postgresql stop
#или если хочется прям ваще по жести то вместо остановки постгреса убьем весь сервер
sudo shutdown -h now

Проверяем слейв ag-psql10-s2

bash
sudo su postgres
psql
postgres=# \l
Список баз данных
Имя            | Владелец | Кодировка | LC_COLLATE  |  LC_CTYPE   |     Права доступа
---------------------------+----------+-----------+-------------+-------------+-----------------------
failover_s1_to_s2_test_ok | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 |
postgres                  | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 |
replica_test_ok           | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 |
template0                 | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 | =c/postgres          +
|          |           |             |             | postgres=CTc/postgres
template1                 | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 | =c/postgres          +
|          |           |             |             | postgres=CTc/postgres

На нем должна быть созданная нами на мастере база failover_s1_to_s2_test_ok
Пытаемся создать в нем базу данных

bash
postgres=# CREATE DATABASE test;
ОШИБКА:  в транзакции в режиме "только чтение" нельзя выполнить CREATE DATABASE
\q #выходим из psql

Видим отказ, т.к. слейв еще не знает что мастер умер

Создаем триггер файл и превращаем ag-psql10-s2 в новый мастер

Помним что в recovery.conf триггер файлом мы указали файл «/tmp/to_master»
Создаем этот файл

bash
# Не имеет значения авторизованы вы под postgres или под какой либо другой учеткой.
# В данном случае я выполняю команду из под postgres
touch /tmp/to_master

Пытаемся снова создать базу

bash
postgres=# CREATE DATABASE failover_s1_to_s2_complete;
CREATE DATABASE

Видим что база успешно создана. Мастер умер! Даздравствует новый Мастер! В этот момент клиенты уже могут набигать на новый мастер

Переключаем клиентские приложения на новый мастер

  1. Я предпочитаю менять DNS запись. У меня есть отдельное имя psql_master где прописан айпишник текущего мастера
  2. Можно поменять айпиадрес мастера
  3. Или поменять в приложениях настройки подключений со старого мастера на новый
  4. А лучше поднять специальный балансировщик и настраивать все подключения на него, а он уже автоматом будет перенаправлять запросы на действующего мастера на вкус и цвет, я ленивый пользуюсь первым способом но ищу в себе силы поднять 4й способ. Способы 2 и 3 для тех кто любит знатно подрочить.

PostgreSQL 10 — Потоковая репликация — Превращаем старый мастер ag-psql10-s1 в слейв

После того как мы перенесли роль мастера на ag-psql10-s2, наш почивший мастер ag-psql10-s1 не в курсе последних событий, и как только он стартанет — он начнет активно принимать подключения и писать в себя данные. Ну а клиентам плевать куда писать, им куда дадут они туда и пишут. Поэтому если часть клиентов уже пишет на s2, а часть например еще не в курсе что в сети новый хозяин, в случае пробуждения пребывающего в таком же неведнии s1, мы рискуем получить в сети 2 мастера. Этим и хорош способ со сменой адреса DNS имени, что клиент сам не подозревая переключится на новый мастер. В любом случае сразу же после включения нам надо вырубить постгрес на старом мастере sudo service postgresql stop , а дальше тихо мирно продолжать конфигурировать его в новый слейв.

Стераем директорию main и закачиваем с нового мастера.

Это та же команда что и ранее только IP изменен на новый. На ag-psql10-s2 сейчас полная копия данных с ag-psql10-s1 до его выхода из строя. Включая пользователей и пароли.

bash
sudo su postgres
rm -R /var/lib/postgresql/10/main/
pg_basebackup -P -R -X stream -c fast -h 192.168.1.232 -U replica -D /var/lib/postgresql/10/main/

На новом мастере при отработке фейловера файл recovery.conf переименовался в recovery.done, он так же будет закачан, его нужно удалить

bash
rm /var/lib/postgresql/10/main/recovery.done

При синхронизации pg_basebackup снова создала нам recovery.conf с актуальными параметрами подключения. Прописываем туда trigger_file

bash
nano /var/lib/postgresql/10/main/recovery.conf
standby_mode = 'on'
primary_conninfo = 'user=replica password=4UtKbw host=192.168.1.231 port=5432 sslmode=prefer sslcompression=1 krbsrvname=postgres target_session_attrs=any'
#Добавляем триггер файл
trigger_file = '/tmp/to_master'

Запускаем новый слейв ag-psql10-s1

bash
service postgresql start

Проверяем что он в режиме READONLY

bash
psql
postgres=# create database qwe;
ОШИБКА:  в транзакции в режиме "только чтение" нельзя выполнить CREATE DATABASE

На текущем мастере ag-psql10-s2 создаем тестовую БД

bash
postgres=# CREATE DATABASE master_to_slave_config_ok;
CREATE DATABASE

Смотрим результат на слейве

bash
postgres-# \l
Список баз данных
Имя             | Владелец | Кодировка | LC_COLLATE  |  LC_CTYPE   |     Права доступа
----------------------------+----------+-----------+-------------+-------------+-----------------------
failover_s1_to_s2_complete | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 |
failover_s1_to_s2_test_ok  | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 |
master_to_slave_config_ok  | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 |
postgres                   | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 |
replica_test_ok            | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 |
template0                  | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 | =c/postgres          +
|          |           |             |             | postgres=CTc/postgres
template1                  | postgres | UTF8      | ru_RU.UTF-8 | ru_RU.UTF-8 | =c/postgres          +
|          |           |             |             | postgres=CTc/postgres
(7 строк)

База приехала! Yaaaay!

PostgreSQL 10 — Потоковая репликация — Отрабатываем фейловер ag-psql10-s2 => ag-psql10-s1

Собсно действия абсолютно такие же как в случае ag-psql10-s1 => ag-psql10-s2.

Убиваем мастер s2 в случае если он сам не умер

Создаём на слейве s1 триггер файл:

touch /tmp/to_master

. Слейв становится мастером. Можно проверить что он начал принимать команды записи.

Переподключаем клиенты на новый мастер s1

Запускаем старый мастер s2 и выключаем на нем postgresql если запущена

sudo service postgresql stop

Удаляем старую базу

bash
sudo su postgres
rm -R /var/lib/postgresql/10/main/

Закачиваем новую базу

bash
pg_basebackup -P -R -X stream -c fast -h 192.168.1.231 -U replica -D /var/lib/postgresql/10/main/

Внимательно проверяем что указан IP адрес текущего мастера, теперь это 192.168.1.231 (ag-psql10-s1)

Удаляем recovery.done

bash
rm /var/lib/postgresql/10/main/recovery.done

Добавляем в recovery.conf триггер

bash
nano /var/lib/postgresql/10/main/recovery.conf
standby_mode = 'on'
primary_conninfo = 'user=replica password=4UtKbw host=192.168.1.231 port=5432 sslmode=prefer sslcompression=1 krbsrvname=postgres target_session_attrs=any'
#Добавляем триггер файл
trigger_file = '/tmp/to_master'

Запускаем текущий слев

bash
service postgresql start

По окончании имеем исходный вариант работы.

Собственно вот и все дела. Если есть какие-то вопросы — не стесняйтесь задавать их в комментариях.

P.S. Лучше обкатать 2 фейловера, с мастера на слейв с полной передачей ролей и обратно. Делается это за тем, чтобы проверить все конфиги в контролируемой среде. До того, как что-то реально сломается.