1. Настройка WAL-G для работы с s3 хранилищем Yandex Cloud

Данный блок настроек необходим для работы с S3 в качестве хранилища данных. В случае, если в качестве места хранения бэкапов используется файловая система - проделайте инструкции из соответствующего раздела.

Работа с YC

  • Создаём сервисный аккаунт для работы с бакетом без дополнительных ролей в каталоге (имя произвольное):

  • Создаём бакет для хранения бэкапов:

  • Выдаём сервисному аккаунту, созданному ранее, права storage.uploader на созданный бакет:

На этом этапе работа с YC завершена.

Создаём конфигурационный файл для wal-g и выдаём права пользователю postgres на работу с ним:

wal-g s3 configuration

cat > /var/lib/postgresql/.walg.json <<EOF
{
 "PGDATA":"/var/lib/postgresql/tantor-se-server-15/main/$(cat /etc/hostname)",
 "AWS_ACCESS_KEY_ID":"xxx",
 "AWS_SECRET_ACCESS_KEY": "yyy",
 "WALE_S3_PREFIX":"s3://zzz",
 "AWS_ENDPOINT":"https://storage.yandexcloud.net",
 "AWS_S3_FORCE_PATH_STYLE":"True",
 "AWS_REGION":"ru-central1"
}
EOF

chown postgres:postgres /var/lib/postgresql/.walg.json
BASH

Объяснение параметров из блока выше:
PGDATA - каталог, содержащий данные СУБД;
AWS_ACCESS_KEY_ID - идентификатор пользователя, созданного в YC (см инструкцию выше);
AWS_SECRET_ACCESS_KEY - пароль (ключ) пользователя, созданного в YC (см инструкцию выше);
WALE_S3_PREFIX - имя бакета, созданного в YC (см инструкцию выше), для сохранения данных при помощи утилиты wal-g;
AWS_ENDPOINT - указание конечного сервиса, на базе которого создан бакет
AWS_S3_FORCE_PATH_STYLE - параметр для включения адресации в стиле пути (например, http://s3.amazonaws.com/BUCKET/KEY) при подключении к S3-совместимому сервису, который не поддерживает URL-адреса бакетов в стиле поддомена (например, http://BUCKET.s3.amazonaws.com/KEY);
AWS_REGION - регион бакета, созданного в YC (см инструкцию выше);

2. Настройка WAL-G для работы с NFS

Данный блок настроек необходим для работы с NFS в качестве хранилища данных. В случае, если в качестве места хранения бэкапов используется s3 хранилище - проделайте инструкции из соответствующего раздела.

wal-g nfs configuration

cat > /var/lib/postgresql/.walg.json <<EOF

{
    "WALG_FILE_PREFIX": "/mnt/wal-g/backups/",
    "WALG_COMPRESSION_METHOD": "brotli",
    "WALG_DELTA_MAX_STEPS": "1",
    "PGHOST": "/var/run/postgresql/.s.PGSQL.5432",
    "PGDATA": "/var/lib/postgresql/tantor-se-server-15/main/$(cat /etc/hostname)"
}
EOF

chown postgres:postgres /var/lib/postgresql/.walg.json
BASH

Объяснение параметров из блока выше:
WALG_FILE_PREFIX - каталог, примонтированный в систему из NFS сервера, в котором будут хранится бэкапы;
WALG_COMPRESSION_METHOD - определяет метод сжатия, который будет использоваться для резервных копий (поддерживаемые значения: lz4, brotli, gzip, xz, lzo)
WALG_DELTA_MAX_STEPS - определяет максимальное количество шагов (инкрементальных бэкапов) между полными резервными копиями
PGHOST - определяет хост PostgreSQL, с которым будет взаимодействовать WAL-G
PGDATA - определяет каталог, содержащий файлы данных СУБД

Особенности настройки WAL-G

В примерах выше были использованы ряд настроек (например,  "PGHOST": "/var/run/postgresql/.s.PGSQL.5432", или "AWS_REGION":"ru-central1"  и т.д.) . Это полностью настраиваемые параметры, которые необходимо выставить согласно конкретной инсталляции, типу и версии СУБД и т.д.

3. Настройка Patroni для работы с WAL-G

Для работы с кластером будет использован кастомный метод (в примере ниже - wal-g). Размещаем листинг из кода ниже в блоке bootstrap конфигурационного файла (в данном примере он располагается в каталоге /opt/tantor/etc/patroni/<имя узла>.yml) patroni:

patroni bootstrap configuration

  method: wal-g
  wal-g:
    command: "/opt/tantor/etc/patroni/tantor-wal-g.sh"
    recovery_conf:
      restore_command: '/usr/local/bin/wal-g --config /var/lib/postgresql/.walg.json wal-fetch "%f" "%p" 2>&1 | tee -a /var/lib/postgresql/walg.log'
      recovery_target_timeline: latest
      recovery_target_action: promote
      recovery_target_time: ''
YML

В блок dsc > postgresql > parameters добавляем следующие настройки:

patroni dsc postgresql parameters configuration

  parameters:
    unix_socket_directories: /var/run/postgresql
    archive_mode: 'on'
    archive_timeout: 300s
    archive_command: 'wal-g --config /var/lib/postgresql/.walg.json wal-push "%p" 2>&1 | tee -a /var/lib/postgresql/walg.log'
  recovery_conf:
    restore_command: 'wal-g --config /var/lib/postgresql/.walg.json wal-fetch "%f" "%p" 2>&1 |tee -a /var/lib/postgresql/walg.log'
YML

Создаём скрипт для кастомной загрузки c содержимым:

patroni custom bootstrap script

touch /opt/tantor/etc/patroni/tantor-wal-g.sh
chmod +x /opt/tantor/etc/patroni/tantor-wal-g.sh
chown postgres:postgres /opt/tantor/etc/patroni/tantor-wal-g.sh
BASH

Добавляем в скрипт следующее содержимое:

patroni custom bootstrap script

#!/bin/bash

# Define WAL-G configuration path
config_path="/var/lib/postgresql/.walg.json"
pgdata_dir="/var/lib/postgresql/tantor-se-server-15/main/$(cat /etc/hostname)"

# Check if the base directory exists, create if it doesn't
if [ ! -d "$pgdata_dir" ]; then
    echo "Creating base directory: $pgdata_dir"
    mkdir -p "$pgdata_dir"
fi


# Check for existing backups and catchups
backup_list=$(wal-g --config "$config_path" backup-list 2>&1)
catchup_list=$(wal-g --config "$config_path" catchup-list 2>&1)

function get_latest_date() {
   #echo "$1" | grep -v 'name' | awk '{print $2 "T" $3}' | sort | tail -n1
   echo "$1" | tail -n +2 | awk '{print $2 " " $0}' | sort | tail -n 1 | cut -d' ' -f2-
   # date -d "$1 | grep -v 'name' | awk '{print $2 "T" $4}' | sed 's/T$//')" "+%Y-%m-%d %H:%M:%S"
}

# Check for "No backups found"
if [[ "$backup_list" == *"No backups found"* && "$catchup_list" == *"No backups found"* ]]; then
    # Standard DB init if no backups are found
    echo "No backups found, initializing standard database..." >> /opt/tantor/var/log/patroni/patroni.log
    /opt/tantor/db/15/bin/initdb -D $pgdata_dir
    exit 0
else
    # Determine latest backup and perform appropriate fetch
    latest_backup_date=$(get_latest_date "$backup_list")
    latest_catchup_date=$(get_latest_date "$catchup_list")
    #latest_catchup_date=0

    if [[ "$latest_backup_date" > "$latest_catchup_date" ]]; then
        echo "Latest backup date: $latest_backup_date" >> /opt/tantor/var/log/patroni/patroni.log
        wal-g --config "$config_path" backup-fetch "$pgdata_dir" LATEST
    else
        echo "Latest catchup date: $latest_catchup_date" >> /opt/tantor/var/log/patroni/patroni.log        
        wal-g --config "$config_path" catchup-fetch "$pgdata_dir" LATEST
    fi
fi
BASH

Известные особенности, ошибки и необходимые доработки

  1. Кластер patroni всегда стартует с tl = 2;
  2. После запуска кластера wal-g начинает сразу же писать WAL-файлы , используя выбранный метод бэкапирования. В случае полного выхода из строя всего кластера (пример rm -rf по каталогу PGDATA) до создания полного бэкапа patroni не сможет собрать кластер с нуля, используя WAL файлы. Узлы будут находится в статусе ожидания лидера. В данном кейсе необходимо удаление WAL-файлов, после чего требуется повторный запуск patroni.
  3. В случае выхода из строя узла, имеющего роль "лидер" (пример выполнения функции переключения лидера при помощи patronictl switchover ) одна из реплик берёт на себя эту роль (ожидаемое поведение), в то время как другая переходит в состояние archive recovery (нестандартное поведение);
  4. Восстановление данных и наливка WAL-журналов всегда происходит при помощи wal-g (т.е. с NFS хранилища или S3 бакета), вне зависимости от доступности узла мастера.