Прикладная демонология, практические рецепты

Поточная репликация PostgreSQL

Теория подробно изложена в руководстве.
Ниже приведен пошаговый пример настройки репликации между двумя серверами (db01 и db02) в терминах конфигурации Ansible.

Добавляем оба сервера в группу rsyncd:

[rsyncd:children]
dbXX

Включаем в rsyncd.conf модуль для доставки WAL-файлов:

[wal]
    path = {{ postgresql.wal_dir }}
    uid = {{ postgresql.user }}
    gid = {{ postgresql.group }}
    read only = no

Не забываем открыть файрволл для rsync:

# rsync
pass in proto tcp from <dbXX> to {{ net.primary.address }} port {{ rsyncd.port }}

Открываем файрволл для доступа к PostgreSQL:

# postgresql replication
{% for x in groups['dbXX'] %}
pass in proto tcp from {{ hostvars[ x ].net.primary.address }} to {{ net.primary.address }} port {{ postgresql.port }} # {{ hostvars[ x ].fqdn }}
{% endfor %}

Создаем каталоги на обеих серверах:

mkdir -m700 -p /data/wal && chown pgsql:pgsql /data/wal

В параметрах группы (group_vars/dbXX) указываем trigger_file:

postgresql:
    trigger_file: trigger

В параметрах мастера (host_vars/db01) указываем куда отправлять WAL в файлах:

postgresql:
    wal_send: ['db02.example.com']

В параметрах слейва (host_vars/db02) указываем откуда получать WAL по TCP:

postgresql:
    wal_recv:
        host: db01.example.com
        port: 5432
        user: replica

Создаем выделенного пользователя для репликации (только на мастере):

createuser -U pgsql -SDR --replication replica

И разрешаем ему ходить с хостов в группе репликации:

###################
# replication
###################
{% for x in groups['dbXX'] %}
host    replication    replica    {{ hostvars[ x ].net.primary.address }}/32    trust
{% endfor %}

После этого выкатываем конфигурацию мастера:

ansible-playbook postgresql.yml -l db01 -D

Перезапускаем мастера, смотрим в логи и убеждаемся, что вызовами rsync он успешно отправляет WAL-файлы слейву:

service postgresql restart
fgrep 'WAL send' /var/log/pgsql/postgresql-2013-09-22.log
2013-09-22 22:20:08 MSK LOG: WAL sending 000000010000001E00000045 to db02.example.com
2013-09-22 22:20:57 MSK LOG: WAL sending 000000010000001E00000046 to db02.example.com
2013-09-22 22:21:03 MSK LOG: WAL sending 000000010000001E00000047 to db02.example.com

Останавливаем потенциального слейва, откладываем в сторону его каталог базы, создаем новый из копии мастера:

/usr/local/etc/rc.d/postgresql stop
mv /var/db/pgsql /var/db/pgsql.bak
pg_basebackup -Pv -D /var/db/pgsql -h db01.example.com -U replica

На этом шаге придется подождать (примерно половину от checkpoint_timeout), когда начнется и завершится принудительный checkpoint мастера, его можно увидеть в логах мастера:

fgrep -i checkpoint /var/log/pgsql/postgresql-2013-10-24.log
2013-10-24 14:24:18 MSK [2076]: [2175] db= LOG:  checkpoint starting: force wait
2013-10-24 14:39:18 MSK [2076]: [2176] db= LOG:  checkpoint complete: wrote 80720 buffers (1.0%); 0 transaction log file(s) added, 0 removed, 9 recycled; write=899.860 s, sync=0.025 s, total=899.909 s; sync files=187, longest=0.002 s, average=0.000 s

После того как данные приедут с мастера, исправим атрибуты каталога базы данных и (возможно) удалим старый каталог:

chmod 700 /var/db/pgsql && chown -R pgsql:pgsql /var/db/pgsql
rm -Rf /var/db/pgsql.bak

На предыдущем шаге вместе с базой приехали конфиги мастера, поэтому перевыкатываем конфиги на слейв и стартуем его:

ansible-playbook postgresql.yml -l db02 -D

Посмотреть на состояние репликации можно на мастере:

psql -U pgsql template1
select * from pg_stat_replication;

При необходимости аварийного превращения слейва в standalone мастер:

touch /var/db/pgsql/trigger