Настройка потоковой реплики PostgreSQL с помощью pg_basebackup

Оглавление

Настройка источника

На источнике создаём пользователя replication с паролем при помощи команды, запущенной под пользователем postgres:

$ createuser --replication -P replication

Для генерации надёжного случайного пароля можно воспользоваться утилитой pwgen из одноимённого пакета.

Прописываем в файл /etc/postgresql/13/main/pg_hba.conf доступ для пользователя replication с IP-адреса будущей реплики:

host    replication     all             10.2.88.23/32           md5

Уже настроенные по умолчанию строчки для пользователя replication можно закомментировать, т.к. репликация будет работать и без них:

#local  replication     all                                     peer
#host   replication     all             127.0.0.1/32            md5
#host   replication     all             ::1/128                 md5

Для применения нового файла /etc/postgresql/13/main/pg_hba.conf достаточно перезагрузить PostgreSQL:

# systemctl reload postgresql

Далее нужно проверить конфигурацию сервера в файле /etc/postgresql/13/main/postgresql.conf следующим образом:

listen_addresses = '*'
wal_level = replica
max_wal_senders = 10
max_replication_slots = 10
hot_standby = on
hot_standby_feedback = on

Значение в опция listen_addresses должно допускать подключение с IP-адреса будущей реплики.

Опция wal_level должна иметь значение replica или logical, но первое значение предпочтительнее, т.к. журналы имеют меньший объём, но содержат всю информацию, необходимую для работы потоковой репликации.

Опции max_wal_senders и max_replication_slots ограничивают количество процессов, отдающих изменения в журналах репликации на реплики, и максимальное количество подключений со стороны реплик.

Опция hot_standby позволяет реплике отменять запросы, которые пытается выполнить источник, если эти запросы не могут быть выполнены достаточно быстро на реплике, что может повлечь значительное отставание реплики от источника. Подробности см. в документации: Администрирование сервера / Администрирование сервера / Отказоустойчивость, балансировка нагрузки и репликация

Опция hot_standby_feedback не позволяет реплике удалять устаревшие записи в процессе выполнения операции VACUUM, если они ещё присутствуют на источнике.

К счастью, опции wal_level, max_wal_senders, max_replication_slots, hot_standby в конфигурации по умолчанию уже имеют значения, подходящие для потоковой репликации. Если же значения опций пришлось изменить, то может потребоваться перезапуск PostgreSQL:

# systemctl restart postgresql

Настройка реплики

Файлы конфигурации реплики стоит отредактировать так, чтобы они повторяли таковые на источнике. Это будет особенно полезно, если понадобится поменять серверы ролями и переключить репликацию в обратном направлении.

Останавливаем реплику:

# systemctl stop postgresql

Удаляем на реплике имеющиеся файлы баз данных:

# rm -R /var/lib/postgresql/13/main/*

Выполняем съём резервной копии данных на источнике с помощью утилиты pg_basebackup от имени пользователя postgres:

$ pg_basebackup -h 10.2.88.16 -U replication -D /var/lib/postgresql/13/main --wal-method=stream --write-recovery-conf

После завершения съёма резервной копии запускаем PostgreSQL на реплике:

# systemctl start postgresql

Отслеживание состояния репликации

Следить за состоянием репликации на источнике можно с помощью таблицы pg_stat_replication, например, запустив от имени пользователя postgres следующую команду:

$ watch 'psql -xc "SELECT * FROM pg_stat_replication;"'

Информацию об отставании реплик можно увидеть именно на источнике.

Аналогичным образом за состоянием репликации можно следить на реплике с помощью таблицы pg_stat_wal_receiver, например, запустив от имени пользователя postgres такую команду:

$ watch 'psql -xc "SELECT * FROM pg_stat_wal_receiver;"'

Отсоединение реплики от источника

При необходимости поменять источник и реплику местами нужно сначала остановить изменение данных на источнике.

Для этого сначала нужно заблокировать в файле /etc/postgresql/13/main/pg_hba.conf возможность подключения для всех пользователей, кроме пользователя replication, закомментировав соответствующие строки. Применить изменения можно с помощью следующей команды:

# systemctl reload postgresql

Далее нужно отключить уже установленные подключения, отменив текущие выполняемые через них запросы:

SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE pid <> pg_backend_pid()
  AND usename NOT IN ('postgres', 'replication');

Далее убедиться, что реплика догнала источник, например, при помощи следующей команды, запущенной от пользователя postgres:

$ watch 'psql -xc "SELECT * FROM pg_stat_replication;"'

Теперь можно остановить источник:

# systemctl stop postgresql

Осталось только переключить реплику в режим самостоятельной работы, выполнив на ней команду от имени пользователя postgres:

$ pg_ctlcluster 13 main promote

Для настройки реплики на бывшем источники можно воспользоваться утилитой pg_basebackup так, как это было описано выше.

Решение проблем

Если PostgreSQL на реплике завершил свою работу с сообщением об ошибке "requested WAL segment has already been removed", то на источнике произошло большое количество изменений, которое реплика не успела получить и применить. Для решения проблемы нужно указать на источнике объём сегментов WAL, который должен храниться на диске, с помощью опции wal_keep_size. Значение указывается в мегабайтах. Если оно уже указано, то его следует увеличить. Для применения изменений достаточно перезагрузить PostgreSQL:

# systemctl reload postgresql

Реплику часто используют для снятия нагрузки с основного сервера, выполняя на реплике долгие аналитические запросы или резервное копирование данных. Процесс резервного копирования данных может завершиться аварийно с сообщением об ошибке следующего вида:

pg_dump: error: Dumping the contents of table "core_embedding" failed: PQgetResult() failed.
pg_dump: error: Error message from server: ERROR:  canceling statement due to conflict with recovery
DETAIL:  User was holding a relation lock for too long.
pg_dump: error: The command was: COPY public.core_embedding (id, date_created, embedding, photo_id, version_id) TO stdout;

Ошибки "canceling statement due to conflict with recovery" и "User was holding a relation lock for too long." говорят о том, что реплика слишком долго пыталась удержать от очистки на исходном сервере строки, подлежащие резервному копированию. Время удержания строк, подлежащих очистке, настраивается с помощью опций max_standby_archive_delay и max_standby_streaming_delay. По умолчанию они обе настроены на удержание в течение 30 секунд. Для успешного выполнения резервного копирования можно попробовать увеличить значения этих опций.

Использованные материалы