В этой статье я хочу поделиться опытом развертывания кластера Master-slave на СУБД PostgreSQL. Отказоустойчивость достигается с помощью возможностей pgpool-II (failover, online recovery).
pgpool — это прекрасное средство для масштабирования и распределения нагрузки между серверами и, думаю, немногие знают о возможностях автоматического создания failover на ведомом сервере при отказе ведущего и как добавить новые мощности в уже работающий кластер без отключения всего кластера.
Схема кластера и требования к машинам
На рисунке представлена типичная схема кластера Master-slave.
Кластер должен содержать 1 ведущий сервер (Master), хотя бы 1 ведомый (Slave), 1 узел масштабирования (Balancer).
При на каждый из серверов должен быть установлен Linux-дистрибутив (у меня поставлен Red Hat 6.1), на узле масштабирования должен установлен компилятор gcc.
Версия PostgreSQL — 9.0.1, pgpool-II 3.0.5. Можно использовать и другие версии СУБД и pgpool. При этом обратитесь к документации.
Настройка удаленного соединения между серверами кластера
Online recovery и failover требуют настройки удаленного соединения по протоколу SSH без пароля. Для этого нужно создать SSH-ключи пользователя postgres
и разослать их пользователям postgres
каждому из серверов.
Важный момент! Для online recovery необходимо, чтобы при открытии удаленной сессии можно было перейти в еще одну удаленную сессию (т.е. можно было реализовать следующий механизм перехода по SSH без пароля: узел масштабирования — ведущий сервер — ведомый сервер и узел масштабирования — ведомый сервер — ведущий сервер).
Для failover необходимо создать SSH-ключ пользователя root
на узле масштабирования и переслать пользователям postgres
ведущего и ведомого сервера.
Этот шаг является важным при настройке, поэтому убедитесь, что возможно подключение из удаленной сессии одного из серверов к другому.
Настройка потоковой репликации
Предварительно нужно открыть прием/передачу данных по порту 5432 (стандартный порт PostgreSQL) в iptables.
Отредактируйте конфигурационный файл $PGDATA/postgresql.conf
ведущего сервера следующим образом:
listen_addresses = '*' wal_level = hot_standby max_wal_senders = 2 wal_keep_segments = 32 #hot_standby = on
Отмечу важность последней строки. Дело в том, что она будет использована в скрипте восстановления ведомого узла, поэтому ее нужно обязательно изменить так, как написано выше.
Далее добавляем строчки для репликации в $PGDATA/pg_hba.conf
:
host replication postgres 192.168.100.2/32 trust host replication postgres 192.168.100.3/32 trust
postgres — это администратор базы, который будет проводить репликацию и прочие админские хитрости. С помощью этих строк мы разрешили производить репликацию как ведомого, так и ведущего сервера.
После чего перегружаем ведущий сервер:
# service postgresql restart
Останавливаем ведомый сервер (если был запущен ранее):
# service postgresql stop
Теперь можно приступать к репликации.
На ведущем сервере пользователем postgres
создаем backup базы пересылаем ведомому серверу:
$ psql -c "SELECT pg_start_backup('stream');" $ rsync -a /var/lib/pgsql/data/ 192.168.100.3:/var/lib/pgsql/data/ --exclude postmaster.pid $ psql -c "SELECT pg_stop_backup();"
После чего на ведомом создаем конфиг репликации $PGDATA/recovery.conf
:
recovery_mode = 'on' primary_conninfo = 'host=192.168.100.2 port=5432 user=postgres' trigger_file = 'failover'
Параметр trigger_file
отвечает за путь, по которому PostgreSQL ищет файл, чтобы переключиться в режим ведущего. В данном случае PostgreSQL ищет файл по пути $PGDATA/failover
.
Далее нужно включить режим «горячего резерва» на ведомом сервере:
$ sed -i 's/#hot_stadnby = on/hot_standby = on/' /var/lib/pgsql/data/postgresql.conf
После чего нужно запустить ведомый сервер:
# service postgresql start
Активность репликации можно проверить следующим образом:
На ведущем сервере выполнить команду
$ ps aux | grep sender
Она должна вывести приблизительно следующее:
2561 ? Ss 0:00 postgres: wal sender process postgres 192.168.100.3(33341) streaming 0/2031D28
Аналогично на ведомом сервере:
$ ps aux | grep receiver
Она выдаст следующее:
1524 ? Ss 0:00 postgres: wal reciever process streaming 0/2031D28
Общая настройка узла масштабирования
Меняем конфигурационный файл /etc/pgpool-II/pgpool.conf
:
# Устанавливаем весь диапазон прослушиваемых адресов listen_addresses = '*' # Параметры подключения к базе на ведомом сервере backend_hostname0 = '192.168.100.3' backend_port0 = 5432 backend_weight0 = 1 backend_data_directory0 = '/var/lib/pgsql/data' # Параметры подключения к базе на ведущем сервере backend_hostname1 = '192.168.100.2' backend_port1 = 5432 backend_weight1 = 1 backend_data_directory1 = '/var/lib/pgsql/data' # Используем pool_hba.conf для авторизации клиентов enable_pool_hba = true
Далее в /etc/pgpool-II/pool_hba.conf
добавляем информацию об авторизации клиентов:
host all all 127.0.0.1/32 trust host all all 192.168.100.2/32 trust host all all 192.168.100.3/32 trust
Перезагружаем pgpool:
# service pgpool restart
Настройка автоматического failover
Механизм создания автоматического failover следующий:
- На рабочих (ведущем и ведомом) серверах выполняется процедура
pgpool-walrecrunning()
, определяющая, какой из серверов является ведущим, какой ведомый. - pgpool удаленно подключается к рабочим серверам и проверяет активность процессов СУБД. Если нет, то pgpool вызывает скрипт, который создает failover на ведомом узле в случае отказа ведущего сервера.
- После чего pgpool отключается от упавшего узла и перезапускает все клиентские приложения, подключенные к нему.
А теперь настройка:
На узле масштабирования меняем конфиг pgpool /etc/pgpool-II/pgpool.conf
:
# Скрипт, вызываемый при падении сервера failover_command = '/etc/pgpool-II/failover.sh %d %H /var/lib/pgsql/data/failover' # Пользователь, проверяющий статус сервера health_check_user = 'postgres' # Ставим активность режима "Ведущий-ведомый" master_slave_mode = true # У нас настроена потоковая репликация, ставим режим работы 'потоковая' master_slave_sub_mode = 'stream' # Не разрешаем pgpool отправлять копию запроса на узлы кластера, у нас это произойдет автоматически благодаря репликации replication_mode = false # Включаем балансировку серверов, чтобы запросы могли равномерно распределяться между серверами load_balance_mode = true
Расскажу немного подробнее про параметр failover_command
. Скрипту, указанному в этой строке, передаются параметры %d
— идентификатор упавшего узла (согласно backend_hostname
в pgpool.conf
), %H
— IP нового ведущего сервера.
Собственно сам скрипт failover.sh
:
#! /bin/bash # ID упавшего узла FAILED_NODE=$1 # IP нового мастера NEW_MASTER=$2 # Путь к триггерному файлу TRIGGER_FILE=$3 if [ $FAILED_NODE = 1 ]; then echo "Ведомый сервер вышел из строя" exit 1 fi echo "Ведущий сервер вышел из строя" echo "Новый ведущий сервер: $NEW_MASTER" ssh -T postgres$NEW_MASTER touch $TRIGGER_FILE exit 0
Этот скрипт нужно создать в каталоге pgpool /etc/pgpool-II/
и выдать права 755.
Теперь нужно скомпилировать процедуры pgpool. В src пакета pgpool в каталоге sql/pgpool-walrecrunning
содержится исходный код нужной нам процедуры. Для ее компилирования нужны заголовочные файлы PostgreSQL, после чего можно воспользоваться командой make
и получить pgpool-walrecrunning.so
и SQL-запрос загрузки этой процедуры pgpool-walrecrunning.sql
.
Процедуру нужно скопировать в каталог на каждом рабочем сервере /usr/lib64/pgsql/
, который именуется $libdir
, sql-файл в /usr/share/pgsql/
.
Загружаем в базу на ведущем сервере:
psql -f /usr/share/pgsql/pgpool-walrecrunning.sql -d postgres
Загрузку в базу этой процедуры на ведомом сервере нет необходимости: она будет доступна благодаря настроенной ранее репликации.
Вот и все.
Статус серверов можно определить с помощью запроса
SHOW pool_nodes;
предварительно зайдя в клиент psql
на узле масштабирования.
Пример вывода запроса:
hostname | port | status | lb_weight ----------------------------------------------------- 192.168.100.3 | 5432 | 2 | 0.500000 192.168.100.2 | 5432 | 2 | 0.500000 (2 rows)
Статус сервера 2 означает, что сервер активен и доступен для запросов. В случае отказа одного из серверов статус изменится на 3.
Протестировать механизм автоматического failover можно следующим образом:
- Отключить ведущий сервер
- Выполнить запрос SHOW pool_nodes; на узле масштабирования
- Смотреть логи pgpool на предмет выполнения скрипта
- Убедиться в том, что ведомый сервер после выполнения скрипта может принимать запросы на запись
Online recovery
Наверное, этот механизм является наиболее сложным в плане дебага, но при этом является мощным инструментом при администрировании базы. Работа этого механизма заключается в следующем: есть рабочий кластер, мы хотим включить упавший ранее ведомый сервер, но данные, хранящиеся на нем, не соответствуют данным в кластере. Этот механизм позволяет нам добавить в реальном времени еще один ведомый сервер без остановки кластера и проведении каких-либо дополнительных действий при его настройке.
Online recovery работает следующим образом:
- На узле масштабирования запускается процедура восстановления ведомого сервера
- Эта процедура на ведущем сервере запускает скрипт, выполняющий автоматическую репликацию между ведущим и ведомым сервером
- После успешного выполнения репликации, база на ведущем сервере удаленно запускается с помощью стандартной утилиты PostgreSQL
PGCTL
- pgpool перезапускается, обнаруживает ведомый сервер и включает его в кластер
Переходим к настройке.
Добавить следующие строчки в /etc/pgpool-II/pgpool.conf
:
# Пользователь, проводящий восстановление recovery_user = 'postgres' # Пароль этого пользователя recovery_password = '123456' # Скрипт, запускающийся на ведущем скрипте из каталога $PGDATA recovery_1st_stage_command = 'basebackup.sh'
Добавить хэш пароля postgres
:
# pg_md5 123456 >> /etc/pgpool-II/pcp.conf
123456 — это пароль postgres
в открытом виде. При этом дополнительно перед хэшем пароля нужно указать имя пользователя, кому этот хэш принадлежит, т.е. в файле должно быть строка postgres:enrypted_password
.
На ведущем узле создать скрипт basebackup.sh
следующего содержания:
#!/bin/bash # Путь к каталогу $PGDATA на ведущем сервере PRIMARY_DATA=$1 # IP-адрес ведомого сервера, который включается в кластер SLAVE_IP=$2 # Путь к каталогу $PGDATA на ведомом сервере SLAVE_DATA=$3 # Определяем IP ведущего сервера для включения в конфиг recovery.conf PRIMARY_IP=$(ifconfig eth0| sed -n '2 {s/^.*inet addr:\([0-9.]*\) .*/\1/;p}') # Директория для хранения конфигов для ведомого сервера TMP_DIR=/var/lib/pgsql/tmp # На ведущем сервере удаляем старые конфиги от репликации (если ведущий сервер был когда-то ведомым) cd $PRIMARY_DATA rm -f recovery.* failover # Проверяем, активен ли режим горячего резерва на ведущем сервере cat postgresql.conf | grep '#hot_standby = on' # Если активен, то выключаем его if [ $? = 1 ] then sed -i 's/hot_standby = on/#hot_standby = on/' postgresql.conf # Перезапускаем ведущий сервер /usr/bin/pg_ctl restart -D $PGDIR fi #Удаленно останавливаем ведомый сервер ssh -T postgres@$SLAVE_IP "/usr/bin/pg_ctl stop -D $SLAVE_DATA" # Создаем backup базы на ведущем сервере psql -c "SELECT pg_start_backup('Streaming Replication', true)" postgres # Пересылаем его ведомому rsync -a $PRIMARY_DATA/ $SLAVE_IP:$SLAVE_DATA/ --exclude postmaster.pid --exclude postmaster.opts # Создаем временную папку конфигов для ведомого сервера mkdir $TMP_DIR cd $TMP_DIR # Копируем конфиг postgresql.conf и включаем hot_standby cp $PRIMARY_DATA/postgresql.conf $TMP_DIR/ sed -i 's/#hot_standby = on/hot_standby = on/' postgresql.conf # Создаем конфиг recovery.conf echo "standby_mode = 'on'" > recovery.conf echo "primary_conninfo = 'host=$PRIMARY_IP port=5432 user=postgres'" >> recovery.conf echo "trigger_file = 'failover'" >> recovery.conf # Удаляем с ведомого сервера старые конфиги репликации ssh -T postgres@$SLAVE_IP rm -f $SLAVE_DATA/recovery.* # Копируем новые конфиги scp postgresql.conf postgres@$SLAVE_IP:$SLAVE_DATA/postgresql.conf scp recovery.conf postgres@$SLAVE_IP:$SLAVE_DATA/recovery.conf #Завершаем процесс backup psql -c "SELECT pg_stop_backup()" postgres # Удаляем временную папку с конифгами cd .. rm -fr $TMP_DIR
Подчеркиваю, этот скрипт должен быть в каталоге $PGDATA
. Скрипту назначить права 755.
На ведомом и ведущем сервере в каталоге $PGDATA
создать скрипт pgpool_remote_start
(Именно под таким именем!) со следующим содержанием:
#! /bin/bash if [ $# -ne 2 ] then echo "Мало аргументов, переданных скрипту" exit 1 fi SLAVE_IP=$1 SLAVE_DIR=$2 PGCTL=/usr/bin/pg_ctl ssh -T $SLAVE_IP $PGCTL -w -D $SLAVE_DIR start 2>/dev/null 1>/dev/null < /dev/null &
Он позволит удаленно запускать процессы СУБД.
Далее на узле масштабирования нужно скомпилировать хранимую процедуру pgpool-recovery.so
, расположенную по пути sql/pgpool-recovery
src пакета pgpool. Аналогичным образом переслать ее рабочим серверам и выполнить загрузку процедуру в базу:
$ psql -f /usr/share/pgsql/pgpool-recovery.sql -d template1
На этом настройка online recovery закончена.
Для включения нового ведомого сервера в кластер необходимо выполнить следующие действия:
- Запустить базу на новом ведущем узле
- На узле масштабирования выполнить команду по восстановлению сервера:
pcp_recovery_node 20 192.168.100.4 9898 postgres 123456 1
Подробнее о pcp_recovery_node
. Эта команда реализует восстановление сервера в кластер. 20
— это число попыток подключения к ведомому серверу, 192.168.100.4
— IP узла масштабирования, 9898
— порт pcp команд узла масштабирования, postgres
— имя пользователя, производящего восстановления, 123456
— его пароль, 1
— ID восстанавливаемого узла.
На этом закончена настройка online recovery.
Можно произвести тестирование этих двух механизмов по следующему плану:
- Создать тестовую базу данных на ведущем сервере. Убедиться, что она реплицировалась на ведомый
- Имитировать отказ ведущего сервера, отключив его
- Убедиться, что сработал failover и ведомый сервер стал новым ведущим
- Внести изменения в базу данных на ведомом сервере
- Запустить упавший ведущий сервер и сделать его ведомым, проведя online recovery
Таким образом, описанные выше механизмы позволяют обезопасить кластер «Ведущий-ведомый» и упростить работу администратора базы данных при ее восстановлении.
P.S. Надеюсь, данный пост помог кому-нибудь. Комментарии и дополнения приветствуются! Благодарю за внимание.
ссылка на оригинал статьи http://habrahabr.ru/post/188096/
Добавить комментарий