Отказоустойчивость PostgreSQL

Кластер из нескольких экземпляров Mitigator´а использует единую базу данных PostgreSQL. По умолчанию база данных физически располагается на сервере одного из экземпляров. При выключении этого сервера теряется возможность управления кластером, а при отказе диска — теряются настройки.

В целях отказоустойчивости синхронизированные копии БД должны физически храниться на разных сервера (реплицироваться). Предлагается схема, в которой реплики БД хранятся на тех же серверах, где работают экземпляры Mitigator´а. Это позволяет сэкономить ресурсы и не требует знаний по настройке PostgreSQL.

Отказоустойчивая БД на двух узлах

Экземпляры PostgreSQL работают в схеме потоковой репликации active — hot standby. Вместо подключения к PostgreSQL напрямую каждый Mitigator подключается к локальной работающий программе pgfailover, которая перенаправляет подключения к PostgreSQL-лидеру репликации. Если лидер недоступен, pgfailover делает лидером одну из ведомых реплик в заданной очередности.

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

Настройка

Процесс настройки описан для двух стендов, большая часть шагов идентична для Active и Standby стендов.

  1. В файле .env задать переменную POSTGRES_PORT=15432;

  2. В файле .env задать переменную MITIGATOR_OWN_ADDRESS=192.0.2.1. Здесь 192.0.2.1 – адрес хоста для данного экземпляра;

  3. docker-compose.override.yml дополнить:

    services:
      pgfailover:
        image: docker.mitigator.ru/vendor/pgfailover:${VERSION:-latest}
        networks:
          default:
            aliases:
            - pgfailover.mitigator
        ports:
        - "5432:5432"
        volumes:
        - failover:/trigger:rw
        command:
        - "/pgfailover/pgfailover"
        - "-index"
        - "0" # порядковый идентификатор, в котором сервер postgresql становится Active, уникальные
        - "-server"
        - "postgres://repuser@pg01.test:15432/mitigator?sslmode=disable" # имя хоста текущего стенда (в примере pg01.test) заменить на реальное, разрешаемое с хоста
        - "-server"
        - "postgres://repuser@pg02.test:15432/mitigator?sslmode=disable" # имя хоста второго стенда (в примере pg02.test) заменить на реальное, разрешаемое с хоста
        - "-trigger"
        - "/trigger/failover"
    
      postgres:
        volumes:
        - failover:/failover:rw
        depends_on:
        - pgfailover
    
      backend:
        environment:
          BACKEND_DATABASE_URI: "postgres://backend@pgfailover.mitigator/mitigator?sslmode=disable"
    
    
    volumes:
      failover:
    

Использование

  • Стенд с Active базой запускается как обычно:

    systemctl start mitigator

  • Стенд с Standby инициализируется репликой:

    docker-compose run --rm postgres standby

    после чего запускается как обычно:

    systemctl start mitigator

Если произойдет разрыв соединения между Active и Standby, либо в случае падения Active, Standby будет переведен в Active.

Механизма переключения бывшего Active в Standby не предусмотрено штатной репликацией PostgreSQL. Для схемы из двух баз данных это означает прекращение репликации на “другой” сервер до ручной перенастройки схемы.

Поднятие Standby из бывшего Active

Для переключения бывшего Active и возвращения его в схему репликации необходимо остановить сервис PostgreSQL, а также удалить локальные данные базы.

  1. Остановка сервиса PostgreSQL:

    docker-compose rm -fsv postgres

  2. Удаление локальных данных базы:

    docker volume rm mitigator_postgres

  3. Инициализация Standby аналогично первой инициализации:

    docker-compose run --rm postgres standby

    после чего запускается как обычно:

    systemctl start mitigator

Документация pgfailover

pgfailover наблюдает за состоянием кластера PostgreSQL и выступает для клиентов как TCP-прокси к текущему лидеру репликации (primary). При смене лидера pgfailover разрывает соединения с клиентами, и они должны переподключиться.

Параметры подключения задаются в виде DSN:

./pgfailover -bind :5432 \
    -server postgres://repuser@pg0.example.com/database \
    -server postgres://repuser@pg1.example.com/database

Роль сервера (primary/standby) проверяется pg_is_in_recovery() по умолчанию раз в 5 секунд, регулируется параметром -interval.

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

Индекс текущего сервера задается параметром -index, начиная с 0. Лидерство активируется путем создания файла-триггера по заданному пути. Экземпляры pgfailover должны иметь одинаковый список серверов PostgreSQL и уникальные номера без пропусков.

Пример для первого сервера (индекс 0):

./pgfailover -index 0 -trigger /var/lib/postgresql/11/database/trigger \
    -server postgres://repuser@pg0.example.com/database \
    -server postgres://repuser@pg1.example.com/database

Пример для второго сервера (индекс 1):

./pgfailover -index 1 -trigger /srv/postgres/database/promote \
    -server postgres://repuser@pg0.example.com/database \
    -server postgres://repuser@pg1.example.com/database