## Настройка репликации Master-Slave - `apt install postgresql postgresql-contrib -y` - устанавливаем СУБД на обе машины - `systemctl status postgresql` ### Настройка Master ```conf # /etc/postgresql/16/main/postgresql.conf listen_addresses = 'localhost,150.241.66.94' # 150.241.66.94 адрес ВМ, на которой slave wal_level = replica # минимальный уровень необходимый для репликации max_wal_senders = 5 # максимальное количество подключений для передачи WAL, максимум 5 слейвов wal_keep_size = 1024MB hot_standby = on # разрешаем селекты с реплики, по умолчанию нельзя archive_mode = on # включаем архивирование WAL archive_command = 'find /var/lib/postgresql/16/main/archive -type f -mtime +7 -delete; gzip < %p > /var/lib/postgresql/16/main/archive/%f.gz' ``` ``` # /etc/postgresql/16/main/pg_hba.conf # файл pg_hba.conf (Host-Based Authentication) управляет доступом к PostgreSQL, определяя, какие пользователи могут подключаться # c каких IP-адресов и каким способом аутентификации host replication replicator 192.168.1.2/32 scram-sha-256 ``` Создадим роль для репликации, выполнить: ```bash sudo -u postgres psql ``` ```sql CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD ''; ``` ```sql \q ``` Открыть «MASTER» базу данных, выполнить скрипт на обновление информации о настройках доступа из файла `pg_hba.conf`: SELECT pg_reload_conf(); ??? info "Сжатый WAL" PostgreSQL не умеет автоматически разархивировать сжатые WAL-файлы. Если WAL-логи в сжатом формате (.gz), то перед восстановлением их нужно разархивировать вручную в `restore_command`. Если archive_command на master сжимает WAL при архивации, например: archive_command = 'gzip < %p > /var/lib/postgresql/archive/%f.gz' То restore_command на slave должен разархивировать WAL перед восстановлением: restore_command = 'gunzip -c /var/lib/postgresql/archive/%f.gz > %p' Как это работает? restore_command ищет запрашиваемый WAL-файл в архиве. Если он найден в сжатом виде (.gz), команда gunzip -c разархивирует его в нужное место (%p). slave применяет этот WAL-файл и продолжает репликацию. ### Настройка Slave По умолчанию реплику (slave) нельзя записывать данные, потому что она работает в режиме только для чтения (read-only), на то она и слейв реплика. ```bash sudo systemctl stop postgresql ``` ```bash sudo -u postgres rm -rf /var/lib/postgresql/16/main/* ``` ```bash sudo -u postgres pg_basebackup -h 192.109.139.92 -U replicator -D /var/lib/postgresql/16/main -P -R --wal-method=stream` # 192.109.139.92 - ip мастера ``` Если файл standby.signal присутствует в директории данных ($PGDATA) при запуске PostgreSQL, сервер не будет принимать записи и будет получать данные с Master. Он создаётся автоматически при запуске pg_basebackup с флагом -R или вручную. Если удалить standby.signal и перезапустить PostgreSQL, сервер станет обычным Master (потеряет связь с репликой). После выполнения pg_basebackup в `/var/lib/postgresql/16/main/` на Slave должен появиться файл standby.signal. Он сообщает PostgreSQL, что сервер работает как Standby (slave). ```bash sudo systemctl start postgresql ``` ```bash sudo systemctl status postgresql ``` ```sql # проверить репликацию на мастере SELECT * FROM pg_stat_replication; ``` ```sql # проверить репликацию на слейве SELECT * FROM pg_stat_wal_receiver; ``` На slave после окончания загрузки бд и запуска postgres проверить наличие файла `/var/lib/pgsql/16/data/standby.signal`, а также наличие строки подключения к серверу master в файле `/var/lib/pgsql/16/data/postgresql.auto.conf` Открыть master базу данных, выполнить скрипт на проверку состояния репликации (скрипт должен вернуть строку, в поле «state» должно быть значение «streaming») ```sql select * from pg_stat_replication; ``` Открыть slave базу данных, выполнить скрипт на проверку состояния репликации (скрипт должен вернуть строку, в поле «status» должно быть значение «streaming») ```sql select * from pg_stat_wal_receiver; ``` ## Бэкапирование По умолчанию pg_dump создает логический бэкап в формате plain (обычный SQL-скрипт) В PostgreSQL есть два типа дампов: - `Физические (pg_basebackup)` — побайтовая копия файлов БД. - `Логические (pg_dump, pg_dumpall)` — SQL-скрипты или архивные файлы. !!! info "" `pg_dumpall` создает резервную копию всех баз данных, роли (пользователи и группы), права доступа (GRANT/REVOKE), настройки tablespace, глобальные параметры (например, настройки аутентификации). Умеет отдавать только дамп в формате .sql `pg_dump` не экспортирует роли, права доступа, tablespaces, параметры кластера (например, `pg_hba.conf`) 1. plain (Текстовый SQL-скрипт) Описание: В этом формате по дефолту снимает `pg_dump`. Формат plain представляет собой обычный SQL-скрипт, содержащий скрипты `CREATE TABLE`, `INSERT INTO`, `ALTER TABLE` и другие скрипты для создания и наполнения бд . Дамп в формате `plain`: ```bash pg_dump -U postgres -d mydatabase -Fp -f backup.sql ``` Пример содержимого файла дампа: ```sql CREATE TABLE users ( id SERIAL PRIMARY KEY, name TEXT ); INSERT INTO users (id, name) VALUES (1, 'Alice'); ``` - Можно редактировать в текстовом редакторе. - Можно восстановить частично, скопировав нужные команды. - Относительно долгое восстановление, так как все данные вставляются скриптами. Как восстановить: ```bash psql -U postgres -d mydatabase -f backup.sql ``` 2. custom (Сжатый бинарный формат) Описание: Формат custom является бинарным, поддерживает сжатие и позволяет восстанавливать отдельные объекты базы данных, такие как таблицы и схемы. Как создать дамп в формате `custom`: ```bash pg_dump -U postgres -d mydatabase -Fc -Z 9 -f backup.dump ``` Параметры: - `Fc` - указывает на использование формата custom. - `Z 9` - применяет максимальное сжатие дампа. - `-a` или `--data-only`: Дамп только данных, без схемы - `-s` или `--schema-only`: Дамп только схемы, без данных - `-O` или `--no-owner`: Исключает команды SET OWNER из дампа - `-x` или `--no-privileges`: Исключает команды GRANT/REVOKE из дампа. - Поддерживает выборочное снятие отдельных объектов. - Поддерживает параллельное снятие с `-j`. Посмотреть содержимое дампа без восстановления: ```bash pg_restore -l backup.dump ``` Как восстановить базу данных: ```bash pg_restore -U postgres -d mydatabase -f backup.dump ``` 3. directory (Каталог с дампом) Описание: Формат `directory` сохраняет резервную копию в виде каталога, содержащего отдельные файлы для каждой таблицы и других объектов базы данных. Этот формат позволяет параллельно снимать бэкап. Дамп в формате `directory`: ```bash pg_dump -U postgres -d mydatabase -Fd -j4 -f backup_dir/ ``` Параметры: - `Fd` — указывает на формат directory. - `j4` — использует 4 параллельных потока для ускорения процесса. - Можно восстанавливать отдельные таблицы и объекты. Восстановление ```bash pg_restore -U postgres -d mydatabase -j 4 backup_dir/ ``` 4. tar (Архив tar) Описание: Формат tar создает архив tar, содержащий все необходимые файлы для восстановления базы данных. Он удобен для хранения и передачи, но восстановление в данном формате выполняется медленнее по сравнению с `directory` или `custom`. Дамп в формате `tar`: ```bash pg_dump -U postgres -d mydatabase -F t -f backup.tar ``` Параметры: - Ft — указывает на формат tar. - Медленное восстановление, так как данные сначала извлекаются из архива. Восстановление бд: ```bash pg_restore -U postgres -d mydatabase -Ft backup.tar ``` !!! info "" Все форматы дампов кроме `.sql` восстанавливаются через `pg_restore` ### Пример организации бэкапирования Скрипт `backup.sh` для снятия бэкапа, удаления бэкапов ```bash #!/bin/bash if [ "$#" -ne 3 ]; then echo "Необходимо передать три аргумента." echo "Пример: $0 <имя_стенда> <имя_базы_данных> <время_жизни_бэкапа_в_днях>" exit 1 fi PG_PASSWORD="postgres" PG_PORT="5432" STAND_NAME="$1" DB_NAME="$2" BACKUP_TTL="$3" BACKUP_DIR="/opt/backups/${STAND_NAME}" BACKUP_FILE="${STAND_NAME}_$(date +%Y%m%d).dump" LOG_DIR="${BACKUP_DIR}/logs" LOG_FILE="${LOG_DIR}/backup_${STAND_NAME}.log" mkdir -p "${BACKUP_DIR}" mkdir -p "${LOG_DIR}" echo "$(date +%Y%m%d_%H%M%S) Начало резервного копирования базы данных ${DB_NAME}" >> ${LOG_FILE} PGPASSWORD=${PG_PASSWORD} pg_dump -p ${PG_PORT} -h localhost -U postgres -d ${DB_NAME} -Fc -Z 9 -f "${BACKUP_DIR}/${BACKUP_FILE}" 2>> ${LOG_FILE} if [ $? -eq 0 ]; then echo "$(date +%Y%m%d_%H%M%S) Резервное копирование успешно завершено: ${BACKUP_DIR}/${BACKUP_FILE}" >> ${LOG_FILE} find ${BACKUP_DIR} -type f -name "*.dump" -mtime +${BACKUP_TTL} -exec rm {} \; if [ $? -eq 0 ]; then echo "$(date +%Y%m%d_%H%M%S) Бэкапы старше ${BACKUP_TTL} дней удалены." >> ${LOG_FILE} else echo "$(date +%Y%m%d_%H%M%S) Ошибка при удалении бэкапов." >> ${LOG_FILE} fi else echo "$(date +%Y%m%d_%H%M%S) Ошибка при резервном копировании базы данных ${DB_NAME}" >> ${LOG_FILE} fi ``` Добавить в `cron` ```bash # Для снятия бэкапа: <путь к скрипту> <имя каталога для бэкапа> <имя базы данных стенда> <кол-во хранимых бэкапов> 00 23 * * * /opt/backups/backup.sh stand1 database1 3 59 23 * * * /opt/backups/backup.sh stand2 database2 3 ```