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

Как настроить репликацию в PostgreSQL

Настраиваем репликацию данных в PostgreSQL на двух виртуальных серверах.

Введение

В статье мы расскажем, что такое репликация и где она применяется. Также на примере двух виртуальных серверов настроим репликацию данных в PostgreSQL и проверим, что она работает.

Что такое репликация

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

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

Виды репликации в PostgreSQL

Существует несколько видов репликаций, у каждого из них свои особенности. Но прежде чем рассказывать о видах, нужно хотя бы поверхностно познакомиться с WAL — журналом предзаписи транзакций.

Когда PostgreSQL получает команду на изменение данных, она не сразу изменяет их на диске. Сначала она записывает изменения в WAL. Этот журнал нужен для того, чтобы в случае сбоя сервера можно было восстановить незафиксированные данные. Также WAL используется и для репликации данных.

Итак, в PostgreSQL есть несколько видов репликации:

Потоковая репликация (Streaming Replication). Это репликация, при которой от основного сервера PostgreSQL на реплики передается WAL. И каждая реплика затем по этому журналу изменяет свои данные. Для настройки такой репликации все серверы должны быть одной версии, работать на одной ОС и архитектуре. Потоковая репликация в Postgres бывает двух видов — асинхронная и синхронная.

  • Асинхронная репликация. В этом случае PostgreSQL сначала применит изменения на основном узле и только потом отправит записи из WAL на реплики. Преимущество такого способа — быстрое подтверждение транзакции, т.к. не нужно ждать пока все реплики применят изменения. Недостаток в том, что при падении основного сервера часть данных на репликах может потеряться, так как изменения не успели продублироваться.
  • Синхронная репликация. В этом случае изменения сначала записываются в WAL хотя бы одной реплики и только после этого фиксируются на основном сервере. Преимущество — более надежный способ, при котором сложнее потерять данные. Недостаток — операции выполняются медленнее, потому что прежде чем подтвердить транзакцию, нужно сначала продублировать ее на реплике.

Логическая репликация (Logical Replication). Логическая репликация оперирует записями в таблицах PostgreSQL. Этим она отличается от потоковой репликации, которая оперирует физическим уровнем данных: биты, байты, и адреса блоков на диске. Возможность настройки логической репликации появилась в PostgreSQL 10.
Этот вид репликации построен на механизме публикации/подписки: один сервер публикует изменения, другой подписывается на них. При этом подписываться можно не на все изменения, а выборочно. Например, на основном сервере 50 таблиц: 25 из них могут копироваться на одну реплику, а 25 — на другую.
Также есть несколько ограничений, главное из которых — нельзя реплицировать изменения структуры БД. То есть если на основном сервере добавится новая таблица или столбец — эти изменения не попадут в реплики автоматически, их нужно применять отдельно.
В отличие от потоковой репликации, логическая может работать между разными версиями PostgreSQL, ОС и архитектурами.

Установить PostgreSQL

Перейдем к практике: настроим потоковую асинхронную репликацию в режиме Master-Replica: один сервер — основной, в него можно писать данные, другой — реплика, из него можно только читать.

На примере платформы Selectel создадим два отдельных сервера PostgreSQL, один из которых будет основным (Master), а второй — репликой (Replica).

В панели управления платформой заходим в раздел Облачная платформа — Серверы, нажимаем кнопку Создать сервер.

Укажем имя сервера — Master. Так нам будет проще ориентироваться в серверах. Выберем ОС — Ubuntu 22.04, конфигурацию — 2 vCPU, 8 ГБ оперативной памяти и 10 ГБ диска.

В разделе Сеть нужно выбрать подсеть с публичным адресом, чтобы к виртуальной машине можно было подключаться из интернета. В разделе Доступ нужно проверить, что вы либо записали пароль root-пользователя, либо указали правильный SSH-ключ для подключения к машине.

По такому же принципу создаем второй сервер, только укажем другое имя — Replica. Остальные параметры оставим такими же.
В итоге у нас получилось два сервера. Обратите внимание, что у них есть публичные и приватные IP-адреса.

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

Теперь нужно подключиться к каждому серверу по SSH. Рекомендуем открыть 2 окна терминала и держать их открытыми, потому что мы будем часто переключаться между серверами.

Итак, подключаемся к серверам и устанавливаем PostgreSQL на каждом из них: 


    apt-get update
apt-get install postgresql

Все, сервера готовы к настройке репликации. Сейчас они ничем не отличаются друг от друга, кроме названия. Перейдем к настройке каждого из них.

Настроить Master-сервер

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


    su - postgres
psql -c "ALTER ROLE postgres PASSWORD 'ВАШ_ПАРОЛЬ'"
exit

Далее нужно разрешить этому пользователю подключаться из Replica-сервера в Master. Для этого мы отредактируем файл /etc/postgresql/12/main/pg_hba.conf.
Обратите внимание, что мы показываем настройку репликации на примере PostgreSQL 12, поэтому в пути к файлу есть номер — 12. Если у вас другая версия PostgreSQL, то вам нужно поменять путь к файлу.

Итак, откроем файл:


    nano /etc/postgresql/12/main/pg_hba.conf

Найдем в нем строчку «If you want to allow non-local connections, you need to add more» и впишем после нее такую строчку:

host    replication    postgres    REPLICA_ВНУТРЕННИЙ_IP/32    md5

Так мы разрешаем пользователю postgres подключаться к этому серверу из Replica.


    # If you want to allow non-local connections, you need to add more
# “host” records. In that case you will also need to make PostgreSQL
# listen on a non-local interface via the listen_address
# configuration parameter, or via the -i or -h command line switches.
host    replication    postgres    192.168.0.3/32    md5 

Обратите внимание, что мы используем приватные адреса, потому что виртуальные машины находятся в одной сети. При этом нам не нужно открывать порты, настраивать Firewall и так далее. Если ваши машины будут находиться в разных сетях или вы хотите, чтобы они общались друг с другом по публичным адресам — скорее всего вам придется настроить Firewall.
Далее нужно указать настройки репликации. Открываем конфигурационный файл PostgreSQL:


    nano /etc/postgresql/12/main/postgresql.conf

Находим в нем эти параметры, раскомментируем их и подставляем указанные значения.


    listen_addresses = 'localhost, MASTER_ВНУТРЕННИЙ_IP'
wal_level = hot_standby
archive_mode = on
archive_command = 'cd .'
max_wal_senders = 8
hot_standby = on

Все, Master настроен. Чтобы применить настройки, перезапускаем сервер:


    service postgresql restart

Настроить Replica-сервер

Переключаемся в окно терминала Replica-сервера. Перед началом настройки нужно остановить PostgreSQL-сервер:


    service postgresql stop

Аналогично Master-серверу отредактируем файл pg_hba.conf. В то же самое место вставим ту же самую строчку, но только теперь укажем IP-адрес мастера.

Открываем файл:


    nano /etc/postgresql/12/main/pg_hba.conf

Добавляем в него строчку:


    host    replication    postgres    MASTER_ВНУТРЕННИЙ_IP/32    md5

Затем правим файл postgresql.conf. Настройки те же самые, как и у Master, только нужно поменять IP-адрес. Открываем файл на редактирование:


    nano /etc/postgresql/12/main/postgresql.conf

Находим в нем эти параметры, раскомментируем их и подставляем указанные значения:


    listen_addresses = 'localhost, REPLICA_ВНУТРЕННИЙ_IP'
wal_level = hot_standby
archive_mode = on
archive_command = 'cd .'
max_wal_senders = 8
hot_standby = on

Сейчас настройки обоих серверов одинаковые, отличаются только IP-адреса. Это потому, что при необходимости реплики могут становиться мастером, а вся разница будет в наличии одного лишь файла. О нем расскажем далее.
Прежде чем Replica-сервер сможет начать реплицировать данные, нужно создать новую БД, идентичную Master-серверу. Для этого воспользуемся утилитой pg_basebackup. Она создаст бэкап с Master-сервера и скачает его на Replica-сервер. Эту операцию нужно выполнять от имени пользователя postgres, поэтому логинимся от него:


    su - postgres

Далее переходим в каталог с базой данных:


    cd /var/lib/postgresql/12/

Удалим каталог с дефолтной БД и снова его создадим, но уже пустой:


    rm -rf main; mkdir main; chmod go-rwx main

Теперь выгрузим БД с мастера. Для выполнения этой команды нужно будет ввести пароль от пользователя postgres, который мы задавали в самом начале настройки Master-сервера.


    pg_basebackup -P -R -X stream -c fast -h MASTER_ВНУТРЕННИЙ_IP -U postgres -D ./main

В этой команде есть важный параметр -R. Он означает, что PostgreSQL-сервер также создаст пустой файл standby.signal. Несмотря на то, что файл пустой, само наличие этого файла означает, что этот сервер — реплика.

Файл standby.signal появился только в PostgreSQL версии 12. Раньше вместо него создавался файл recovery.conf, в котором хранились настройки для подключения к Master-серверу. Имейте это ввиду, если вы используете более ранние версии PostgreSQL.

Возвращаемся в root-пользователя и запускаем PostgreSQL-сервер:


    exit
service postgresql start

Проверить репликацию

Теперь нужно протестировать репликацию и убедиться, что мы все правильно настроили. На Master-сервере создадим таблицу и вставим в нее одну строчку:


    su - postgres
psql -c "CREATE TABLE test_table (id INT, name TEXT);"
psql -c "INSERT INTO test_table (id, name) VALUES (1, 'test');"


Переключимся в терминал Replica-сервера и проверим, что таблица с данными появилась:


    su - postgres
psql -c "SELECT * FROM test_table;"

Результат:


     id | name  
----+------
 1 | test
(1 row)

Еще одна проверка — попробуем создать новую таблицу на сервере Replica. Если мы все сделали правильно, то сервер не должен позволить нам этого сделать, потому что он настроен только на репликацию с основной БД.
На Replica-сервере выполняем команду:


    psql -c "CREATE TABLE test_table2 (id INT, name TEXT);"

Результат:


    ERROR: cannot execute CREATE TABLE in a read-only transaction

Значит репликация настроена правильно.
Теперь покажем, как можно из Replica-сервера сделать Master. Сымитируем ситуацию, что основной сервер вышел из строя. Для этого в консоли управления платформой Selectel просто выключим сервер Master.

Чтобы перевести Replica-сервер в режим записи, выполните команду:


    /usr/lib/postgresql/12/bin/pg_ctl promote -D /var/lib/postgresql/12/main


Проверяем, снова пробуем создать новую таблицу:


    psql -c "CREATE TABLE test_table2 (id INT, name TEXT);"  

На этот раз таблица создастся. Значит, мы перевели сервер из режима чтения в режим записи.
Но нужно понимать, что запросы от приложений не начнут автоматически направляться на этот сервер. Если сервисы и приложения подключались к «старому» Master-серверу напрямую, они так и будут пытаться подключаться к нему.

Обычно в такой ситуации используется балансировщик нагрузки. Он сам следит за состоянием серверов и распределяет нагрузку между всеми рабочими инстансами. При этом приложения отправляют запросы не напрямую в СУБД, а в балансировщик нагрузки.

Заключение

В статье мы показали, как настроить репликацию в PostgreSQL и проверить ее. Это не сложно, но требует некоторой подготовки. Есть и другой способ — использовать managed-решения.
На нашей платформе есть управляемая PostgreSQL, которая создается в несколько кликов мыши. Вам не придется настраивать и сопровождать СУБД, а репликация и балансировщик нагрузки есть «из коробки». Если Master-сервер выйдет из строя, платформа автоматически переключит одну из реплик в режим записи и перенаправить на нее все запросы.