- PVSM.RU - https://www.pvsm.ru -

Неубиваемый Postgresql cluster внутри Kubernetes cluster

Если вы когда-нибудь задумывались о доверии и надежде, то скорее всего, не испытывали этого ни к чему так же сильно, как к системам управления базами данных. Ну и действительно, это же База Данных! В названии содержится весь смысл — место, где хранятся данные, основная задача ХРАНИТЬ. И что самое печальное, как всегда, однажды, эти убеждения разбиваются об останки такой одной умершей БД на 'проде'.

Неубиваемый Postgresql cluster внутри Kubernetes cluster - 1

И что же делать? — спросите вы. Не деплоить на сервера ничего, — отвечаем мы. Ничего, что не умеет само себя чинить, хотя бы временно, однако надежно и быстро!

В этой статье я попробую рассказать о своем опыте настройки почти бессмертного Postgresql кластера внутри другого отказоустойчивого решения от Google — Kubernates (aka k8s)

Содержание

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

Задача

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

Неубиваемый Postgresql cluster внутри Kubernetes cluster - 2

Итого получаем проблемы для решения:

  • Физически распределенный сервис
  • Балансировка
  • Не ограниченное масштабирование путем добавление новых узлов
  • Автоматическое восстановление при сбоях, уничтожении и потери связи узлами
  • Отсутствие единой точки отказа

Дополнительные пункты, обусловленные спецификой религиозных убеждений автора:

  • Postgres (наиболее академическое и консистентное решение для РСУБД среди бесплатных доступных)
  • Docker упаковка
  • Kubernetes описание инфраструктуры

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

master (primary node1)  --
|- slave1 (node2)       ---     / balancer  
|  |- slave2 (node3)    ----|---|            |----client
|- slave3 (node4)       ---/      balancer /
   |- slave4 (node5)    --/

При условии входных данных:

  • Большее число запросов на чтение (по отношению к записи)
  • Линейный рост нагрузки при пиках до x2 от среднего

Решение методом "Гугления"

Будучи, искушенным в решении IT проблем, человеком, я решил спросить у коллективного разума: "postgres cluster kubernetes" — куча мусора, "postgres cluster docker" — куча мусора, "postgres cluster" — несколько вариантов, из которых пришлось воять.

Неубиваемый Postgresql cluster внутри Kubernetes cluster - 3

Что меня расстроило, так это отсутствие вменяемых Docker сборок и описание любого варианта для кластеризации. Не говоря уже о Kubernetes. Кстати говоря, для Mysql вариантов было не много, но все же были. Как минимум понравился пример в официальном репозитории k8s для Galera(Mysql cluster) [10]

Гугл дал ясно понять, что проблемы придется решать самому и в ручном режиме..."но хоть с помощью разрозненных советов и статей" — выдохнул я.

Плохие и непреемлимые решения

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

Неубиваемый Postgresql cluster внутри Kubernetes cluster - 4

Pgpool. Почему Pgpool не всегда хорош?

Когда кто-то делает универсальное решение (для чего бы то ни было), мне всегда кажется, что такие вещи громоздки, неповоротливы и тяжелы в обслуживании. Так же вышло и с Pgpool, который умеет почти все:

  • Балансировка
  • Хранение пачки коннектов для оптимизации соединения и скорости доступа к БД
  • Поддержка разных вариантов репликации (stream, slony)
  • Авто определение Primary сервера для записи, что важно при реорганизации ролей в кластере
  • Поддержка failover/failback
  • Cобственная репликация master-master
  • Согласованная работа нескольких узлов Pgpool-ов для искоренения единой точки отказа.

Первые четыре пункта я нашел полезными, и остановился на том, поняв и обдумав проблемы остальных:

  • Восстановление с помощью Pgpool2 не предлагает никакой системы принятия решения о следующем мастере — вся логика должна быть описана в командах failover/failback
  • Время записи, при репликации master-master, сводится к удвоенному по отношению варианта без него, вне зависимости от количества узлов… ну хоть не растет линейно
  • Как построить каскадный кластер (когда один slave читает с предыдущего slave) — вообще не понятно
  • Конечно же хорошо, что Pgpool знает о своих братьях и может оперативно становиться активным звеном при проблемах на соседнем узле, но эту проблему для меня решает Kubernetes, который гарантирует аналогичное поведение для, вообще, любого сервиса установленного в него.

Slony. Как слон оставил нас

Собственно, тоже почитав и сравнив найденное [11] с уже знакомой и работающей "из коробки" поточной репликацией (Streaming Replication [12]), легко далось решение даже не думать о Слонах.

Да и ко всему прочему, на первой же странице сайта проекта ребята пишут что, с postgres 9.0+ вам не нужны Slony при условии отсутствия неких специфичных требований к системе:

  • частичная репликация
  • интеграция с другими решениями ("Londiste and Bucardo")
  • дополнительное поведение при репликации

Вообщем, мне кажется Slony не торт… как минимум если у вас нет этих самых трех специфичных задач.

Master-master replication. Не все репликации одинаково полезны

Осмотревшись вокруг [13] и разобравшись в вариантах подхода идеальной двусторонней репликации, оказалось, что жертвы не совместимы с жизнью некоторых приложений. Не говоря о скорости, есть ограничения в работе с транзакциями, сложными запросами (SELECT FOR UPDATE и другие).
Вполне вероятно, я не так искушен именно в этом вопросе, но увиденного мне хватило, чтобы тоже оставить эту идею. И еще раскинув мозгами, мне показалось, что для системы с усиленной операцией записи нужны совершенно другие технологии, а не реляционные базы данных.

Консолидация и сборка решения

В примерах я буду говорить о том, как принципиально должно выглядеть решение, а в коде — как это вышло у меня. Для создания кластера вовсе не обязательно иметь Kubernetes (есть пример docker-compose) или Docker в принципе. Просто тогда, все описанное будет полезно, не как решение типа CPM (Copy-Paste-Modify), а как руководство по установке cо снипетами.

Primary и Standby вместо Master и Slave

Почему же коллеги из Postgresql отказались от терминов "Master" и "Slave"?.. хм, могу ошибаться, но ходил слух, что из-за не-полит-корректности, мол, что рабство это плохо [14]. Ну и правильно.

Неубиваемый Postgresql cluster внутри Kubernetes cluster - 5

Первое что нужно сделать — включить Primary сервер, за ним первый слой Standby, а за тем второй — все согласно поставленной задачи. Отсюда получаем простую процедуру по включению обычного Postgresql сервера в режиме Primary/Standby с конфигурацией для включения Streaming Replication [15]

На что стоит обратить внимание в файле конфигурации

wal_level = hot_standby 
max_wal_senders = 5
wal_keep_segments = 5001
hot_standby = on

Все параметры в комментариях имеют краткое описание, но если вкратце, эта конфигурация дает серверу понять, что он отныне часть кластер и, в случае чего, нужно позволить читать WAL логи [16] другим клиентам. Плюс разрешить запросы во время восстановления. Отличное описание по подробной настройке такого рода репликации можно найти на Postgresql Wiki [12].

Как только мы получили первый сервер кластера, можем включать Stanby, который знает, где находится его Primary.

Моя задача здесь свелась к сборке универсального образа Docker Image [17], который включается в работу, в зависимости от режима, как то так:

  • Для Primary [18]:
    • Конфигурирует Repmgr (о нем немного позже)
    • Создает базу и пользователя для приложения
    • Создает базу и пользователя для мониторинга и поддержки репликации
    • Обновляет конфиг(postgresql.conf) и открывает доступ пользователям извне(pg_hba.conf)
    • Запускает Postgresql сервис в фоне
    • Регистрируется как Master в Repmgr
    • Запускает repmgrd — демон от Repmgr для мониторинга репликации (о нем тоже позже)
  • Для Standby [19]:
    • Клонирует Primary сервер с помощью Repmgr (между прочим со всеми конфигами, так как просто копирует $PGDATA директорию)
    • Конфигурирует Repmgr
    • Запускает Postgresql сервис в фоне — после клонирования сервис вменяемо осознает, что является standby и покорно следует за Primary
    • Регистрируется как Slave в Repmgr
    • Запускает repmgrd

Для всех этих операций важна последовательность, и посему в коде [20] напиханы sleep. Знаю — не хорошо, но так удобно конфигурировать задержки через ENV переменные, когда нужно разом стартануть все контейнеры (например через docker-compose up )
Все переменные к этому образу описаны в docker-compose файле [21].

Вся разница первого и второго слоя Standby сервисов в том, что для второго мастером является любой сервис из первого слоя, а не Primary. Не забываем, что второй эшелон должен стартовать после первого с задержкой во времени.

Split-brain и выборы нового лидера в кластере

Split brain [22] — ситуация, в которой разные сегменты кластера могут создать/избрать нового Master-a и думать, что проблема решена.

Неубиваемый Postgresql cluster внутри Kubernetes cluster - 6

Это одна, но далеко не единственная проблема, которую мне помог решить Repmgr [23].

По сути это менеджер, который умеет делать следующее:

  • Клонировать Master (master — в терминах Repmgr) и автоматически настоить вновьрожденный Slave
  • Помочь реанимировать кластер при смерти Master-а.
  • В автоматическом или ручном режиме Repmgr может избрать нового Master-а и перенастроить все Slave сервисы следовать за новым лидером.
  • Выводить из кластера узлы
  • Мониторить здоровье кластера
  • Выполнять команды при событиях внутри кластера [24]

В нашем случае на помощь приходит repmgrd который запускается основным процессом в контейнере и следит за целостностью кластера. При ситуации, когда пропадает доступ к Master серверу, Repmgr пытается проанализировать текущую структуру кластера и принять решение о том, кто станет следующим Master-ом. Естественно Repmgr достаточно умен чтобы не создать ситуацию Split Brain и выбрать единственно правильного Master-а.

Pgpool-II — swiming pool of connections

Последняя часть системы — Pgpool. Как я писал в разделе о плохих решениях, сервис все же делает свою работу:

  • Балансирует нагрузку между всеми уздами кластера
  • Храннит дескрипторы соединений для оптимизации скорости доступа к БД
  • При нашем случае Streaming Replication — автоматически находит Master и использует его для запросов на запись.

Неубиваемый Postgresql cluster внутри Kubernetes cluster - 7

Как исход, у меня получился довольно простой Docker Image [25], который при старте конфигурирует себя на работу с набором узлов и пользователей, у которых будет возможность проходить md5 авторизацию сквозь Pgpool (c этим тоже, как оказалось, не все просто [26])

Очень часто возникает задача избавиться от единой точки отказа, и в нашем случае — этой точкой является pgpool сервис, который проксирует все запросы и может стать самым слабым звеном на пути доступа к данным.
К счастью, в этом случае нашу проблему решает k8s и позволяет сделать столько репликаций сервиса сколько нужно.

В примере для Kubernetes [27] к сожалению этого нет, но если вы знакомы c тем как работает Replication Controller [28] и/или Deployment [29], то провернуть вышеописанное вам не составит труда.

Результат

Эта статья — не пересказ скриптов для решения задачи, но описание структуры решения этой самой задачи. Что означает — для более глубокого понимания и оптимизации решения придется почитать код, как минимум README.md в github [30], который пошагово и дотошно рассказывает как запустить кластер для docker-compose и Kubernetes. Ко всему прочему, для тех, кто проникнется и решится с этим двигаться дальше, я готов протянуть виртуальную руку помощи [31].

Неубиваемый Postgresql cluster внутри Kubernetes cluster - 8

Документация и использованный материал

PS:

Надеюсь, что изложенный материал будет полезен и подарит немного позитива перед началом лета! Удачи и хорошего настроения, коллеги ;)

Автор: ZmeeeD

Источник [35]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/postgresql/124340

Ссылки в тексте:

[1] Задача: #task

[2] Решение методом "Гугления": #solution_google

[3] Плохие и непреемлимые решения: #solution_bad

[4] Консолидация и сборка решения: #solution_my

[5] Primary и Standby вместо Master и Slave: #solution_my_streaming

[6] Split-brain и выборы нового лидера в кластере: #solution_my_repmgr

[7] Pgpool-II — swiming pool of connections: #solution_my_pgpool

[8] Результат: #result

[9] Документация и использованный материал: #doc

[10] для Galera(Mysql cluster): https://github.com/kubernetes/kubernetes/tree/master/examples/mysql-galera

[11] найденное: http://www.slony.info/

[12] Streaming Replication: https://wiki.postgresql.org/wiki/Streaming_Replication

[13] Осмотревшись вокруг: https://wiki.postgresql.org/wiki/Replication,_Clustering,_and_Connection_Pooling

[14] мол, что рабство это плохо: https://youtu.be/0O5h4enjrHw?t=140

[15] с конфигурацией для включения Streaming Replication: https://github.com/paunin/postgres-docker-cluster/blob/master/configs/postgresql.conf

[16] WAL логи: https://www.postgresql.org/docs/9.5/static/wal-intro.html

[17] Docker Image: https://hub.docker.com/r/paunin/postgresql-cluster-pgsql/

[18] Для Primary: https://github.com/paunin/postgres-docker-cluster/blob/master/bin/primary.entrypoint.sh

[19] Для Standby: https://github.com/paunin/postgres-docker-cluster/blob/master/bin/standby.entrypoint.sh

[20] в коде: https://github.com/paunin/postgres-docker-cluster/tree/master/bin

[21] docker-compose файле: https://github.com/paunin/postgres-docker-cluster/blob/master/docker-compose.yml#L15

[22] Split brain: https://en.wikipedia.org/wiki/Split-brain_(computing)

[23] Repmgr: https://github.com/2ndQuadrant/repmgr

[24] событиях внутри кластера: https://github.com/2ndQuadrant/repmgr#generating-event-notifications-with-repmgrrepmgrd

[25] Docker Image: https://hub.docker.com/r/paunin/postgresql-cluster-pgpool/

[26] c этим тоже, как оказалось, не все просто: http://www.pgpool.net/docs/latest/pgpool-en.html#md5

[27] примере для Kubernetes: https://github.com/paunin/postgres-docker-cluster/tree/master/k8s

[28] Replication Controller: http://kubernetes.io/docs/user-guide/replication-controller/

[29] Deployment: http://kubernetes.io/docs/user-guide/deployments/

[30] README.md в github: https://github.com/paunin/postgres-docker-cluster#streaming-replication-cluster-for-pgsql--pgpool2

[31] виртуальную руку помощи: mailto:d.m.paunin@gmail.com

[32] Исходники и документация на GitHub: https://github.com/paunin/postgres-docker-cluster

[33] Pgpool2: http://www.pgpool.net/docs/latest/pgpool-en.html

[34] Kubernetes: http://kubernetes.io/

[35] Источник: https://habrahabr.ru/post/301370/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best