Нерушимая PostgreSQL, или Как обеспечить отказоустойчивость для «открытой» СУБД

На просторах Интернета море информации о том, как построить отказоустойчивую систему управления базами данных PostgreSQL. Но она слабо применима к задачам крупных компаний и не выдерживает строгости требований стандартов enterprise. В проекте создания ИТ-инфраструктуры для новой АИС ОСАГО нам пришлось с головой нырнуть в решение этой головоломки. И после скрупулезного отбора и тест-драйва множества решений, мы нашли оптимальный набор ИТ-инструментов и сценарии их использования, чтобы обеспечить высокую доступность базы данных. Делимся добытым рецептом.



Последнее время крупные российские компании все чаще посматривают в сторону доступных по цене решений по хранению данных. Конкурентами Oracle, SAP HANA, Sybase, Informix становятся СУБД с открытым исходным кодом: PostgreSQL, MySQL, MariaDB и т.д. Западные гиганты — Alibaba, Instagram, Skype — давно используют их в своих ИТ-ландшафтах.

В проекте для Российского союза автостраховщиков (РСА), где «Инфосистемы Джет» строила ИТ-инфраструктуру для новой АИС ОСАГО, разработчики использовали СУБД PostgreSQL. А мы продумывали, как обеспечить максимальную доступность базы данных и минимальную потерю данных при сбоях оборудования. И это «на бумаге» описание решения кажется простым, как 2+2, на деле же нашей команде пришлось серьезно потрудиться, чтобы добиться отказоустойчивости.

Существует несколько инструментов построения отказоустойчивого кластера для PostgreSQL. Это Stolon, Patroni, Repmgr, Pacemaker+Corosync и т.д.

Мы выбрали Patroni, так как этот проект активно развивается в отличие от аналогичных, имеет понятную документацию и всё чаще становится выбором администраторов баз данных.

Состав «супового набора»


Patroni — набор скриптов на python для автоматизации переключения ведущей роли сервера баз данных PostgreSQL на реплику. Также может хранить, изменять и применять параметры самой СУБД PostgreSQL. Получается, нет необходимости поддерживать актуальность конфигурационных файлов PostgreSQL на каждом сервере отдельно.

PostgreSQL — реляционная база данных с открытым исходным кодом. Хорошо зарекомендовала себя при обработке больших и сложных аналитических процессов.

Keepalived — в многонодовой конфигурации используется для включения выделенного IP-адреса именно на той ноде кластера, где на текущий момент используется роль primary-ноды PostgreSQL. IP-адрес служит точкой входа для приложений и пользователей.

DCS — распределённое хранилище конфигураций. Patroni использует его для хранения информации о составе кластера, ролей серверов кластера, а также хранения конфигурационных параметров своих и PostgreSQL. В данной статье речь пойдёт про etcd.

Эксперименты с нюансами


В поиске оптимального решения по отказоустойчивости и чтобы апробировать свои гипотезы о работе разных вариантов, мы создали несколько тестовых стендов. Изначально мы рассматривали отличные от целевой архитектуры решения: например, использовали Haproxy в качестве определителя primary-ноды PostgreSQL или DCS располагался на тех же серверах, что и PostgreSQL. Мы устраивали внутренние хакатоны, изучали, как будет вести себя Patroni в случае сбоя компонент серверов, недоступности по сети, переполнению файловых систем и т.д. То есть отрабатывали различные сценарии сбоев. В итоге этих «научных изысканий» сложилась окончательная архитектура отказоустойчивого решения.

Блюдо высокой ИТ-кухни


В PostgreSQL существуют роли серверов: primary — экземпляр с возможностью записи/чтения данных; replica — экземпляр с возможностью только чтения данных, постоянно синхронизируется с primary. Эти роли при работе PostgreSQL статичны и при сбое сервера с ролью primary необходимо, чтобы администратор БД вручную повысил роль replica до primary.

Patroni создает отказоустойчивые кластеры, то есть объединяет серверы с ролями primary и replica. Между ними происходит автоматическая смена ролей в случае какого-либо сбоя.


На иллюстрации выше показано как серверы приложений соединяются с одним из серверов, входящих в кластер Patroni. В этой конфигурации используется одна primary-нода и две реплики, одна из которых является синхронной. При синхронной репликации PostgreSQL работает так, что primary всегда ожидает записи изменений на реплике. Если синхронная реплика окажется недоступной, primary не будет писать в себя изменения, он будет доступен только на чтение. Такова архитектура PostgreSQL. Чтобы «изменить ее природу», используется вторая реплика — асинхронная (в случае, если синхронная репликация не требуется, можно ограничиться одной репликой).

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

На следующей иллюстрации показан дополнительный функционал Patroni, который жизненно необходим в промышленных корпоративных решениях — репликация данных на резервную площадку.


У Рatroni этот функционал называется standby_cluster. Он позволяет использовать кластер Рatroni на удалённой площадке в качестве асинхронной реплики. При потере основной площадки, на резервной кластер Рatroni начнет работать так, как будто это основная площадка.

Одна из нод кластера резервной площадки называется Standby Leader. Он является асинхронной репликой primary-ноды основной площадки. Две оставшиеся ноды кластера резервной площадки получают данные со Standby Leader. Так воплощается каскадная репликация, снижающая объём трафика между технологическими площадками.

Состав приложений кластера Patroni


После своего запуска Patroni создаёт отдельный TCP-порт. Сделав HTTP-запрос на этот порт, можно понять, какая нода кластера является primary, а какая — replica.


В keepalived в качестве объекта мониторинга мы указали небольшой самодельный скрипт, который опрашивает TCP-порт Patroni. Скрипт ожидает ответ HTTP GET 200. Ответившая нода кластера является Primary-нодой, на ней keepalived запускает выделенный для подключения к кластеру IP-адрес.

Если настроить второй инстанс keepalived на предмет ожидания ответа HTTP GET 200 от синхронной реплики, то keepalived на этой же ноде кластера запустит другой выделенный IP-адрес. Этот адрес может использовать приложение для считывания данных из БД. Такая опция пригодится, например, для подготовки отчетов.

Поскольку Patroni это набор скриптов, его экземпляры на каждой ноде не «общаются» между собой напрямую, а используют для этого хранилище конфигураций. В качестве него мы используем etcd, который является кворумом для самого Patroni — текущая primary-нода постоянно обновляет ключ в хранилище etcd, указывая, что она является ведущей. Остальные ноды кластера постоянно считывают этот ключ и «понимают», что они являются репликами. Сервис etcd расположен на выделенных серверах в количестве 3-х или 5-ти. Синхронизация данных в хранилище etcd между этими серверами выполняется средствами самой службы etcd.

В ходе наших экспериментов мы выяснили, что службу etcd необходимо выносить на отдельные серверы. Во-первых, etcd крайне чувствителен к сетевым задержкам и отклику дисковой подсистемы, а на выделенных серверах нагрузка создаваться не будет. Во-вторых, при возможном сетевом разделении нод кластера Patroni может случиться «brain-split» —появятся две primary-ноды, которые ничего не будут знать друг о друге, так как кластер etcd тоже «разъедется».

Проверка практикой


В масштабах проекта по построению ИТ-инфраструктуры для АИС ОСАГО добиться отказоустойчивости PostgreSQL — одна из задач по «вживлению» открытой СУБД в корпоративный ИТ-ландшафт. Рядом с ней стоят смежные вопросы интеграции кластера PostgreSQL с системами резервного копирования, средствами мониторинга инфраструктуры и информационной безопасности, надёжной сохранности данных на резервной площадке. В каждом из этих направлений свои подводные камни и пути их обхода. Об одном из них мы уже писали — рассказывали о резервировании PostgreSQL средствами enterprise-решений.


Продуманная и протестированная нами на стендах архитектура отказоустойчивости PostgreSQL доказала свою эффективность на практике. Решение готово «переносить» различные системные и логические сбои. Сейчас оно работает на 10 высоконагруженных кластерах Patroni и выдерживает нагрузки при обработке PostgreSQL в сотни гигабайт данных в час.

Автор: Дмитрий Ерыкин, инженер-проектировщик вычислительных комплексов компании «Инфосистемы Джет»
Инфосистемы Джет
Системный интегратор

Похожие публикации

Комментарии 4

    0
    Если синхронная реплика окажется недоступной, primary не будет писать в себя изменения, он будет доступен только на чтение.

    Вот это самая неприятная часть с синхронной репликацией. Везде пишут, что primary не будет писать в себя изменения, но проблема в том, что он будет это (писать и даже коммитить) делать, но не будет отправлять подтверждение клиенту о том, что он записал и закоммитил, а клиент будет ждать ответа до таймаута (или бесконечно, если таймаута нет). И даже если клиента убить pg_terminate_backend, данные, подтверждения записи которых клиент не получил, на мастере останутся и отправятся на синхронную реплику, когда она оживёт.
      0
      Да, правильно — primary будет и писать изменения, и коммитить их, будет ждать до бесконечности возврата синхронной реплики в строй, чтобы передать туда накопленные изменения. Это с точки зрения самой СУБД. Но с точки зрения приложения, все будет выглядеть так, что записать изменения в базу не получается — работа приложения прервется. Избежать потери данных, безусловно, важно, но есть и другая сторона медали — доступность информационной системы. Подозреваю, что «везде пишут» именно о доступности записи для приложения.
      0

      Джет, с разморозкой вас)

        0
        Спасибо!)

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое