Кластер PostgreSQL

В этой инструкции рассматривается развёртывание отказоустойчивого кластера PostgreSQL средствами, включёнными в состав дистрибутива Геном.

Пропустите этот шаг, если отказоустойчивость СУБД не требуется или будет использоваться внешний сервер PostgreSQL.

Доступ к мастеру PostgreSQL выполняется через VIP. Для автоматического переназначения VIP на узлах кластера развёртываются службы vip-manager и etcd.

Для отслеживания состояния узлов кластера и автоматического выбора нового мастера используется Patroni.

В Patroni мастер PostgreSQL называется лидером.

В этой инструкции в качестве примера рассматривается развёртывание кластера на узлах со следующими параметрами:

IP-адрес Доменное имя Описание

192.168.0.21

pg-1.example.com

Узлы кластера

192.168.0.22

pg-2.example.com

192.168.0.23

pg-3.example.com

192.168.0.24

pg.example.com

VIP, всегда указывающий на мастер кластера PostgreSQL

Сеть

Для корректной работы кластера должны быть открыты соответствующие сетевые порты. Список портов и протоколов приводятся в описании компонентов подсистемы мониторинга:

Подготовка узлов кластера

Чтобы подготовить узлы будущего кластера к развёртыванию:

  1. Убедитесь, что настройки sudo разрешают группе wheel выполнение любых команд.

    Как правило, достаточно создать в директории /etc/sudoers.d/ файл wheel-group со следующим содержимым:

    %wheel ALL=(ALL:ALL) ALL

    Для создания файла рекомендуется использовать утилиту visudo. Она автоматически проверит синтаксис созданного файла и предотвратит сохранение изменений, которые могут нарушить работу sudo.

    visudo -f /etc/sudoers.d/wheel-group
  2. Убедитесь, что пользователь root состоит в группе wheel.

    Чтобы добавить пользователя root в группу wheel, используйте команду usermod:

    usermod -a wheel -G root

Установка

После подготовительных работ переходите к установке:

  1. Выберите один из узлов будущего кластера и используйте в качестве установочного. Все описанные далее действия выполняйте на нём.

  2. Создайте и активируйте виртуальное окружение Python.

  3. Загрузите на установочный узел архив ansible-ha-pg-cluster-2025-12-12.tar.gz.

  4. Распакуйте архив ansible-ha-pg-cluster-2025-12-12.tar.gz:

    tar xvfz ansible-ha-pg-cluster-2025-12-12.tar.gz
  5. Перейдите в директорию с файлами, извлечёнными из архива ansible-ha-pg-cluster-2025-12-12.tar.gz:

    cd ansible-pg-cluster/
  6. Заполните файл инвентаря inventory.yml.

    Пример заполнения файла inventory.yml
    all:
      vars:
        ansible_user: root
        ansible_ssh_extra_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
        ansible_ssh_pass: "<password>"
    
        secrets: /root/patroni_secrets
        cluster_name: patroni
    
        pgport: 5432
        pgdata: /pgdata
    
        ip_master: 192.168.0.24/24
    
        service_users:
          - name: admin
            password: "<admin_password>"
            flags: SUPERUSER
    
      hosts:
        pg-1.example.com:
          ansible_host: 192.168.0.21
          hostname: pg-1.example.com
        pg-2.example.com:
          ansible_host: 192.168.0.22
          hostname: pg-2.example.com
        pg-3.example.com:
          ansible_host: 192.168.0.23
          hostname: pg-3.example.com

    Здесь:

    • cluster_name — название кластера Patroni;

    • ip_master — Virtual IP, который будет использоваться для доступа к мастеру PostgreSQL;

    • pgdata — путь к директории для хранения данных PostgreSQL;

    • pgport — номер порта, который будет слушать кластер PostgreSQL;

    • secrets — путь к директории, в которую плейбук должен сохранить секреты, используемые для доступа к кластеру Patroni;

    • service_users — список пользователей, используемых для управления кластером PostgreSQL.

  7. Запустите выполнение плейбука:

    ansible-playbook -i inventory.yml cluster.yml
  8. Деактивируйте виртуальное окружение:

    deactivate
  9. Сохраните в надёжном месте содержимое директории, путь к которой указали в файле inventory.yml в значении параметра all.vars.secrets.

    При повторном запуске плейбук проверяет наличие секретов в указанной директории. Если секреты не будут найдены, плейбук сгенерирует их заново. При этом старые секреты станут недействительными и использующие их клиенты потеряют доступ к кластеру.

Инициализация

  1. Используя VIP, подключитесь к мастеру кластера PostgreSQL через SSH.

  2. Запустите интерпретатор PostgreSQL:

    sudo -u postgres psql
  3. Создайте роли:

    CREATE ROLE <role> WITH LOGIN PASSWORD '<password>';

    Здесь:

    • <role> — название роли.

      Таблица 1. Рекомендуемые названия ролей
      Роль Компонент

      auth

      Платформа

      authz

      genomebr

      Геном.БР

      genome

      Сервер подсистемы управления

      vision

      Сервер подсистемы мониторинга и Grafana

    • <password> — пароль роли.

  4. Создайте базы данных:

    CREATE DATABASE <name>
      OWNER <owner>
      ENCODING 'UTF8'
      LC_COLLATE 'ru_RU.UTF-8'
      LC_CTYPE 'ru_RU.UTF-8'
      TEMPLATE template0;

    Здесь:

    • <name> — название БД;

    • <owner> — название роли владельца БД.

    Таблица 2. Рекомендуемые названия и владельцы БД
    БД Владелец Компонент

    auth_db

    auth

    Платформа

    authz_db

    authz

    genome_br_db

    genome_br

    Геном.БР

    genome_db

    genome

    Сервер подсистемы управления

    grafana_db

    vision

    Grafana

    vision_db

    Сервер подсистемы мониторинга

  5. Завершите работу с интерпретатором:

    \q
  6. Отключитесь от мастера PostgreSQL.

Проверка корректности

Проверьте корректность развёртывания кластера:

  1. На любом узле кластера выполните команду:

    patronictl -c /etc/patroni/config.yml topology

    Ожидаемый результат выполнения команды:

    • Один узел с ролью Leader.

    • Один узел с ролью Sync Standby.

    • Один узел с ролью Replica.

  2. На лидере выполните команду:

    ip -br a

    Ожидаемый результат выполнения команды: к одному из сетевых интерфейсов привязан VIP, указанный в файле inventory.yml в значении параметра all.vars.ip_master.

  3. Убедитесь в возможности подключения к кластеру с не входящих в него узлов:

    psql -h <vip> -U <admin> -p 5432 -d postgres

    Здесь:

    • <vip> — VIP лидера;

    • <admin> — название учётной записи администратора СУБД.

    Подключение должно быть успешным.

  4. Выполните дополнительные проверки:

    • Убедитесь, что на лидере запрос возвращает значение f:

      Запрос
      SELECT pg_is_in_recovery();
      Ожидаемый результат
       pg_is_in_recovery
      -------------------
       f
      (1 строка)
    • Получите список БД:

      \l+
    • Убедитесь, что на лидере выводится информация о репликах:

      Запрос
      SELECT * FROM pg_stat_replication \gx
      Пример ответа
      -[ RECORD 1 ]----+------------------------------
      pid              | 36671
      usesysid         | 16384
      usename          | repl
      application_name | pg-2.example.com
      client_addr      | 192.168.0.22
      client_hostname  |
      client_port      | 44264
      backend_start    | 2026-09-02 12:24:38.271025+03
      backend_xmin     |
      state            | streaming
      sent_lsn         | 0/48E3F68
      write_lsn        | 0/48E3F68
      flush_lsn        | 0/48E3F68
      replay_lsn       | 0/48E3F68
      write_lag        |
      flush_lag        |
      replay_lag       |
      sync_priority    | 1
      sync_state       | sync
      reply_time       | 2026-09-02 13:14:12.321205+03
      -[ RECORD 2 ]----+------------------------------
      pid              | 36672
      usesysid         | 16384
      usename          | repl
      application_name | pg-3.example.com
      client_addr      | 192.168.0.23
      client_hostname  |
      client_port      | 41578
      backend_start    | 2026-09-02 12:24:38.291781+03
      backend_xmin     |
      state            | streaming
      sent_lsn         | 0/48E3F68
      write_lsn        | 0/48E3F68
      flush_lsn        | 0/48E3F68
      replay_lsn       | 0/48E3F68
      write_lag        |
      flush_lag        |
      replay_lag       |
      sync_priority    | 0
      sync_state       | async
      reply_time       | 2026-09-02 13:14:12.321089+03
    • Проверьте значение настройки archive_mode, по умолчанию оно должно быть равно off.

      Запрос
      SHOW archive_mode;
      Пример ответа
       archive_mode
      --------------
       off
      (1 строка)