Skip to content

Instantly share code, notes, and snippets.

@vanokg
Last active June 28, 2018 11:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vanokg/7ed63a63d420ff1db8b9e79445eb7a1c to your computer and use it in GitHub Desktop.
Save vanokg/7ed63a63d420ff1db8b9e79445eb7a1c to your computer and use it in GitHub Desktop.
Replication

Настройка master

Правим /var/lib/pgsql/10/data/pg_hba.conf:


host    replication      postgres       10.0.3.0/24            md5
host    all              postgres       10.0.3.0/24            md5

Первая строчка нужна для работы утилиты pg_basebackup. Без второй не будет работать pg_rewind. Если хотим, чтобы в базу по сети мог ходить не только пользователь postgres, в последней строке можно написать вместо его имени all.


Правим /var/lib/pgsql/10/data/postgresql.conf:

listen_addresses = '*' 
wal_level = replica 
wal_log_hints = on 
max_wal_senders = 8 
wal_keep_segments = 64 
hot_standby = on

Далее открываем psql:

sudo -u postgres psql

Меняем пароль пользователя postgres:

ALTER ROLE postgres PASSWORD 'secretpass';

Перезапускаем PostgreSQL:

sudo service postgresql restart

Мастер настроен!




Настройка slave


Останавливаем PostgreSQL:

sudo service postgresql stop

Становимся пользователем postgres:

sudo -u postgres

Под этим пользователем переливаем данные с мастера:

cd /var/lib/pgsql/10/data/
rm -rf data/*
pg_basebackup -P -R -X stream -c fast -h 10.0.3.245 -U postgres \  
-D ./data

Последняя команда спросит пароль пользователя postgres, который мы меняли при настройке мастера. Используйте -c fast, чтобы синкнуться как можно быстрее, или -c spread, чтобы минимизировать нагрузку. Еще есть флаг -r, позволяющий ограничить скорость передачи данных (см man).


В /var/lib/pgsql/10/data/recovery.conf дописываем:

recovery_target_timeline = 'latest'

Когда у нас упадет мастер и мы запромоутим реплику до мастера, этот параметр позволит тянуть данные с него. Более подробна фича объяснена здесь и в официальной документации.


Также в /var/lib/pgsql/10/data/recovery.conf можно дописать:

recovery_min_apply_delay = 10min

… если вы хотите реплику, отстающую от мастера на заданное количество времени. Это позволит быстро восстановить данные в случае выполненного случайно drop database.


Файлы:

/var/lib/pgsql/10/data/pg_hba.conf
/var/lib/pgsql/10/data/postgresql.conf

… правим аналогично мастеру. Поскольку реплики могут становиться мастером, конфиги у реплик и мастера одинаковые, вся разница только в recovery.conf.


Запускаем PostgreSQL:

sudo service postgresql start

Поздравляю, репликация настроена!




Проверка репликации

На мастере говорим:

SELECT * FROM pg_stat_replication;

Должны увидеть, что реплика действительно забирает WAL:

-[ RECORD 1 ]----+------------------------------
pid              | 5544
usesysid         | 10
usename          | postgres
application_name | walreceiver
client_addr      | 10.0.3.223
client_hostname  |
client_port      | 45095
backend_start    | 2016-01-01 16:42:30.350283+03
backend_xmin     |
state            | streaming
sent_location    | 0/3000220
write_location   | 0/3000220
flush_location   | 0/3000220
replay_location  | 0/30001E8
sync_priority    | 0
sync_state       | async

На реплике:

sudo less /var/lib/pgsql/10/data/log/postgresql*.log

Должны увидеть что-то на тему «read only connections»:

LOG:  entering standby mode
LOG:  redo starts at 0/2000028
LOG:  consistent recovery state reached at 0/20000F8
LOG:  database system is ready to accept read only connections
LOG:  started streaming WAL from primary at 0/3000000 on timeline 1

Еще один способ проверить, что репликация работает — сказать на реплике:

ps wuax | grep receiver

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

ERROR:  cannot execute INSERT in a read-only transaction

Также на слейве можно смотреть, как давно было последнее обновление данных с мастера:

sudo -u postgres psql -c \
  "select now()-pg_last_xact_replay_timestamp();"

Пример вывода:

    ?column?    
-----------------
 00:00:03.639424
(1 row)

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


Промоутим реплику до мастера

Остановим мастер. Допустим, какой-то мониторинг это дело запалил и теперь нам нужно ASAP сделать реплику новым мастером.

На реплике говорим:

sudo -u postgres /usr/pgsql-10/bin/pg_ctl promote \
  -D /var/lib/pgsql/10/data

В логе увидим:

LOG:  received promote request
FATAL:  terminating walreceiver process due to administrator command
LOG:  redo done at 2/63000DC0
LOG:  last completed transaction was at log time 2016-01-01 15:35:42
LOG:  selected new timeline ID: 5
LOG:  archive recovery complete
LOG:  MultiXact member wraparound protections are now enabled
LOG:  database system is ready to accept connections
LOG:  autovacuum launcher started

При этом в каталоге /var/lib/pgsql/10/data файл recovery.conf автоматически будет переименован в recovery.done.


Легко проверить, что в бывшую реплику теперь можно писать. Конечно, если только вы не использовали синхронную репликацию с одной-единственной репликой.


Интересно, что хотя реплику и можно промоутнуть до мастера без перезапуска PostgreSQL, на практике вы, вероятно, все же захотите его перезапустить по следующей причине. Дело в том, что приложение, которое ранее подключилось к этой реплике, так и будет использовать ее в качестве реплики даже после промоута, хотя операции чтения можно было бы размазать по остальным репликам в кластере. Перезапустив PostgreSQL, вы порвете все сетевые соединения, а значит приложению придется подключиться заново, проверить, подключился ли он к мастеру или реплике (запрос SELECT pg_is_in_recovery(); вернет false на мастере и true на репликах), и использовать сетевое соединение соответствующим образом.


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

Переключение остальных реплик на новый мастер, а также восстановление бывшего мастера в качестве реплики происходит одинаково.


Чтобы было чуть меньше путаницы с новым мастером, старым мастером, старой репликой и новой репликой, условимся, что сервера мы называем в соответствии с их текущими ролями. То есть, мастером мы называем новый мастер, бывший репликой до фейловера, а репликой — тот, второй сервер.


В простом и не совсем правильном варианте нужно отредактировать, или создать, если его еще нет, файл /var/lib/pgsql/10/datarecovery.conf, указав в нем правильный IP мастера, и сделать sudo service postgresql restart (простой reload не прокатит). Кто-то для того, чтобы не править конфиги и не останавливать СУБД, использует схему с балансировщикам и DNS, но я лично так никогда не делал. В любом случае, этот способ неправильный. Для того, чтобы все хорошо работало во всяких хитрых граничных случаях, реплику следует остановить, сделать pg_rewind, затем запустить реплику.


Утилита pg_rewind находит точку в WAL, начиная с которой WAL мастера и WAL реплики начинают расходиться. Затем она «перематывает» (отсюда и название) WAL реплики на эту точку и накатывает недостающую историю с мастера. Таким образом, реплика и местер всегда приходят к консистентному состоянию. Плюс к этому pg_rewind синхронизирует файлы мастера и реплики намного быстрее, чем pg_basebackup или rsync.


Если вы считаете, что pg_rewind не требуется при использовании синхронной репликации, вот пример маловероятной, но теоретически возможной ситуации. У вас много серверов с PostgreSQL. Сервера в кластере умирают сравнительно часто, поэтому вы решили автоматизировать фейловер. Умирает мастер, запускается фейловер. Среди реплик находится та, что имеет наиболее длинный WAL, на ней делается pg_ctl promote. В этот момент с очень большой задержкой (скажем, 5 секунд — были какие-то сетевые проблемы) на другую реплику прилетает пакет от уже мертвого мастера, и WAL этой реплики становится длиннее WAL нового мастера. Вы не сможете подключить эту реплику к новому мастеру, все сломалось. Если вы хотите, чтобы фейловер работал в том числе и при таких странных граничных случаях, используйте pg_rewind.


Итак, на реплике говорим:

sudo -u postgres /usr/pgsql-10/bin/pg_rewind \
  -D /var/lib/pgsql/10/data/ \
  --source-server="host=10.0.3.223 port=5432 user=postgres password=??"

Типичный вывод:

servers diverged at WAL position 2/67002170 on timeline 5
rewinding from last common checkpoint at 2/67002100 on timeline 5
Done!

Перемещаем и правим recovery.conf:

sudo mv /var/lib/pgsql/10/data/recovery.done \
  /var/lib/pgsql/10/data/recovery.conf
sudo vim /var/lib/pgsql/10/data/recovery.conf

Проверяем IP мастера и наличие строчки:

recovery_target_timeline = 'latest'

Запускаем реплику, смотрим в логи. Там обязательно должно быть:

LOG:  database system is ready to accept read only connections

Значит PostgreSQL работает в качестве реплики.

Если вдруг видим что-то вроде:

ERROR: requested WAL segment 0000000200000005 has already been removed

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment