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

У нас высоконагруженный сервис: 2,5 млн пользователей по всему миру, 50К+ активных пользователей каждый день. Сервера находятся в Amazone в одном регионе Ирландии: в работе постоянно 100+ различных серверов, из них почти 50 — с базами данных.

Весь backend — большое монолитное stateful-приложение на Java, которое держит постоянное websocket соединение с клиентом. При одновременной работе нескольких пользователей на одной доске все они видят изменения в режиме реального времени, потому что каждое изменение мы записываем в базу. У нас примерно 10К запросов в секунду к нашим базам. В пиковой нагрузке в Redis мы пишем по 80-100К запросов в секунду.



Почему мы перешли с Redis на PostgreSQL


Изначально наш сервис работал с Redis, key-value хранилищем, которое хранит все данные в оперативной памяти сервера.

Плюсы Redis:

  1. Высокая скорость ответа, т.к. всё хранится в памяти;
  2. Удобство бэкапа и репликации.

Минусы Redis для нас:

  1. Нет настоящих транзакций. Мы пытались имитировать их на уровне нашего приложения. К сожалению, это не всегда хорошо работало и требовало написания очень сложного кода.
  2. Объём данных ограничен количеством памяти. При увеличении количества данных память будет расти, и, в конце концов, мы упрёмся в характеристики выбранного инстанса, что в AWS требует остановки нашего сервиса для изменения типа инстанса.
  3. Необходимо постоянно поддерживать уровень низкого latency, т.к. у нас очень большое количество запросов. Оптимальный для нас уровень задержки — 17-20 ms. При уровне 30-40 ms мы получаем долгие ответы на запросы нашего приложения и деградацию сервиса. К сожалению, у нас это случилось в сентябре 2018 года, когда один из инстансов с Redis почему-то получил latency в 2 раза больше обычного. Для решения проблемы мы остановили сервис в середине рабочего дня для внепланового maintenance и заменили проблемный инстанс Redis.
  4. Легко получить неконсинстентность данных даже при незначительных ошибках в коде и потом потратить много времени на написание кода для исправления этих данных.

Мы учли минусы и поняли, что нам необходимо переехать на что-то более удобное, с нормальными транзакциями и меньшей зависимостью от latency. Провели исследование, проанализировали множество вариантов и выбрали PostgreSQL.

На новую БД мы переезжаем уже 1,5 года и перевезли только небольшую часть данных, поэтому сейчас работаем одновременно с Redis и PostgreSQL. Подробнее об этапах переезда и переключении данных между БД написано в статье моего коллеги.

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


Внедрение PgBouncer


Пока мы переезжали, продукт тоже развивался: увеличивалось количество пользователей и количество серверов, которые работали с PostgreSQL, и нам стало не хватать соединений. PostgreSQL на каждое соединение создаёт отдельный процесс и потребляет ресурсы. Увеличивать число коннектов можно до определённого момента, иначе есть шанс получить неоптимальную работу БД. Идеальным вариантом в такой ситуации будет выбор менеджера коннектов, который встанет перед базой.

У нас было два варианта для менеджера соединений: Pgpool и PgBouncer. Но первый не поддерживает транзакционный режим работы с базой, поэтому мы выбрали PgBouncer.

Мы настроили следующую схему работы: наше приложение обращается к одному PgBouncer, за которым находятся masters PostgreSQL, а за каждым мастером — одна реплика с асинхронной репликацией.


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

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


Эта схема проработала до момента, пока единственный инстанс PgBouncer не умер. Мы находимся в AWS, где все инстансы запущены на железе, которое периодически умирает. В таких случаях инстанс просто переезжает на новое железо и снова работает. Так произошло и с PgBouncer, однако он стал недоступен. Результатом этого падения стала недоступность нашего сервиса в течение 25 минут. AWS для таких ситуаций рекомендует использовать избыточность на стороне пользователя, что не было реализовано у нас на тот момент.

После этого мы всерьёз задумались об отказоустойчивости PgBouncer и кластеров PostgreSQL, потому что подобная ситуация могла повториться с любым инстансом в нашем AWS аккаунте.

Схему отказоустойчивости PgBouncer мы построили следующим образом: все сервера приложения обращаются к Network Load Balancer, за которым стоят два PgBouncer. Каждый из PgBouncer смотрит на одни и те же master PostgreSQL каждого шарда. В случае повторения ситуации с падением инстанса AWS, весь трафик перенавравляется через другой PgBouncer. Отказоустойчивость Network Load Balancer обеспечивает AWS.

Такая схема позволяет без проблем добавлять новые сервера PgBouncer.


Создание отказоустойчивого кластера PostgreSQL


При решении этой задачи мы рассматривали разные варианты: самописный failover, repmgr, AWS RDS, Patroni.

Самописные скрипты


Могут мониторить работу мастера и, в случае его падения, продвигать реплику до мастера и обновлять конфигурацию PgBouncer.

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

Минусы:

  • Мастер мог не умереть, вместо этого мог произойти сетевой сбой. Failover, не зная об этом, продвинет реплику до мастера, а старый мастер будет продолжать работать. В результате мы получим два сервера в роли master и не будем знать, на каком из них последние актуальные данные. Такую ситуацию называют ещё split-brain;
  • Мы остались без реплики. В нашей конфигурации мастер и одна реплика, после переключения реплика продвигается до мастера и у нас больше нет реплик, поэтому приходится в ручном режиме добавлять новую реплику;
  • Нужен дополнительный мониторинг работы failover, при этом у нас 12 шардов PostgreSQL, а значит мы должны мониторить 12 кластеров. При увеличении количества шардов надо ещё не забыть обновить failover.

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

Repmgr


Replication Manager for PostgreSQL clusters, который умеет управлять работой кластера PostgreSQL. При этом в нём нет автоматического failover “из коробки”, поэтому для работы потребуется писать свою “обёртку” поверх готового решения. Так что всё может получится даже сложнее, чем с самописными скриптами, поэтому Repmgr мы даже не стали пробовать.

AWS RDS


Поддерживает всё необходимое для нас, умеет делать бэкапы и поддерживает пул коннектов. Имеет автоматическое переключение: при смерти мастера реплика становится новым мастером, а AWS меняет dns запись на нового мастера, при этом реплики могут находится в разных AZ.

К минусам можно отнести отсутствие тонких настроек. Как пример тонких настроек: на наших инстансах стоят ограничения для tcp коннектов, чего, к сожалению, нельзя сделать в RDS:

net.ipv4.tcp_keepalive_time=10
net.ipv4.tcp_keepalive_intvl=1
net.ipv4.tcp_keepalive_probes=5
net.ipv4.tcp_retries2=3

Кроме того у AWS RDS цена почти в два раза дороже обычной цены instance, что и послужило главной причиной отказа от этого решения.

Patroni


Это шаблон на python для управления PostgreSQL с хорошей документацией, автоматическим failover и исходным кодом на github.

Плюсы Patroni:

  • Расписан каждый параметр конфигурации, понятно как что работает;
  • Автоматический failover работает из коробки;
  • Написан на python, а так как мы сами много пишем на python, то нам будет проще разбираться с проблемами и, возможно, даже помочь развитию проекта;
  • Полностью управляет PostgreSQL, позволяет менять конфигурацию сразу на всех нодах кластера, а если для применения новой конфигурации требуется перезапуск кластера, то это можно сделать опять же с помощью Patroni.

Минусы:

  • Из документации непонятно, как правильно работать с PgBouncer. Хотя минусом это назвать сложно, потому что задача Patroni — управлять PostgreSQL, а как будут ходить подключения к Patroni — уже наша проблема;
  • Мало примеров внедрения Patroni на больших объёмах, при этом много примеров внедрения с нуля.

В итоге для создания отказоустойчивого кластера мы выбрали именно Patroni.

Процесс внедрения Patroni


До Patroni у нас было 12 шардов PostgreSQL в конфигурации один мастер и одна реплика с асинхронной репликацией. Сервера приложения обращались к базам данных через Network Load Balancer, за которым стояли два instance с PgBouncer, а за ними находились все PostgreSQL сервера.


Для внедрения Patroni нам нужно было выбрать распределенное хранилище конфигурации кластера. Patroni работает с распределёнными системами хранения конфигураций, такими как etcd, Zookeeper, Сonsul. У нас как раз на проде есть полноценный кластер Consul, который работает в связке с Vault и больше мы его никак не используем. Отличный повод начать использовать Consul по назначению.

Как работает Patroni с Consul


У нас есть кластер Сonsul, который состоит из трёх нод и кластер Patroni, который состоит из лидера и реплики (в Patroni мастер называется лидером кластера, а слейвы — репликами). Каждый инстанс кластера Patroni постоянно посылает в Consul информацию о состоянии кластера. Поэтому из Сonsul всегда можно узнать текущую конфигурацию кластера Patroni и того, кто является лидером в данный момент.



Для подключения Patroni к Сonsul достаточно изучить официальную документацию, в которой написано, что необходимо указать хост в формате http или https в зависимости от того, как мы работаем с Сonsul, и схему подключения, опционально:

host: the host:port for the Consul endpoint, in format: http(s)://host:port
scheme: (optional) http or https, defaults to http

Выглядит просто, но тут начинаются подводные камни. С Сonsul мы работаем по защищённому соединению через https и наш конфиг подключения будет выглядеть следующим образом:

consul:
  host: https://server.production.consul:8080 
  verify: true
  cacert: {{ consul_cacert }}
  cert: {{ consul_cert }}
  key: {{ consul_key }}

Но так не работает. При старте Patroni не может подключиться к Сonsul, потому что пытается всё равно идти по http.

Разобраться с проблемой помог исходный код Patroni. Хорошо, что он написан на python. Оказывается параметр host никак не парсится, а протокол необходимо указать в scheme. Вот так выглядит работающий блок конфигурации для работы с Сonsul у нас:

consul:
  host: server.production.consul:8080
  scheme: https
  verify: true
  cacert: {{ consul_cacert }}
  cert: {{ consul_cert }}
  key: {{ consul_key }}

Consul-template


Итак, хранилище для конфигурации мы выбрали. Теперь нужно понять, как PgBouncer будет переключать свою конфигурацию при смене лидера в кластере Patroni. В документации на этот вопрос ответа нет, т.к. там в принципе не описана работа с PgBouncer.

В поисках решения мы нашли статью (название, к сожалению, не помню), где было написано, что Сonsul-template очень помог в связке PgBouncer и Patroni. Это подтолкнуло нас на исследование работы Consul-template.

Оказалось, что Сonsul-template постоянно мониторит конфигурацию кластера PostgreSQL в Сonsul. При смене лидера он обновляет конфигурацию PgBouncer и отправляет команду на её перезагрузку.



Большой плюс template в том, что он хранится в виде кода, поэтому при добавлении нового шарда достаточно сделать новый коммит и обновить template в автоматическом режиме, поддерживая принцип Infrastructure as code.

Новая архитектура с Patroni


В результате мы получили такую схему работы:


Все сервера приложения обращаются к балансировщику > за ним стоят два instance PgBouncer > на каждом instance запущен Сonsul-template, который мониторит состояние каждого кластера Patroni и следит за актуальностью конфига PgBouncer, который направляет запросы на текущего лидера каждого кластера.

Ручное тестирование


Эту схему перед выводом на прод мы запустили на небольшой тестовой среде и проверили работу автоматического переключения. Открывали доску, передвигали стикер и в этот момент “убивали” лидера кластера. В AWS для этого достаточно выключить инстанс через консоль.



Стикер в течение 10-20 секунд возвращался назад, а потом вновь начинал нормально перемещаться. Значит, кластер Patroni сработал верно: сменил лидера, отправил информацию в Сonsul, а Сonsul-template сразу подхватил эту информацию, заменил конфигурацию PgBouncer и отправил команду на reload.

Как выжить под высокой нагрузкой и сохранить минимальный даунтайм?


Всё работает отлично! Но появляются новые вопросы: Как это сработает под высокой нагрузкой? Как быстро и безопасно раскатать всё на production?

Ответить на первый вопрос нам помогает тестовая среда, на которой мы проводим нагрузочное тестирование. Она полностью идентична production по архитектуре и имеет сгенерированные тестовые данные, которые по объёму примерно равны production. Мы решаем просто “убить” один из мастеров PostgreSQL во время теста и посмотреть, что будет. Но перед этим важно проверить автоматическую раскатку, ведь на этой среде у нас есть несколько шардов PostgreSQL, так что мы получим отличное тестирование конфигурационных скриптов перед продом.

Обе задачи выглядят амбициозно, но у нас PostgreSQL 9.6. Может мы сразу на 11.2 обновимся?

Мы решаем сделать это в 2 этапа: сначала обновить версию до 11.2, потом запустить Patroni.

Обновление PostgreSQL


Для быстрого обновления версии PostgreSQL необходимо использовать опцию -k, в которой создаются hard link на диске и нет необходимости в копировании ваших данных. На базах в 300-400 ГБ обновление занимает 1 секунду.

У нас много шардов, поэтому обновление нужно сделать в автоматическом режиме. Для этого мы написали Ansible playbook, который выполняет весь процесс обновления за нас:

/usr/lib/postgresql/11/bin/pg_upgrade <b>--link \</b>
--old-datadir='' --new-datadir=''  --old-bindir=''  --new-bindir=''  --old-options=' -c config_file='  --new-options=' -c config_file='

Здесь важно отметить, что перед запуском апгрейда необходимо выполнить его с параметром --check, чтобы быть уверенным в возможности апгрейда. Так же наш сценарий делает подмену конфигов на время апгрейда. Сценарий у нас выполнился за 30 секунд, это отличный результат.

Запуск Patroni


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

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

rm -rf /var/lib/postgresql/

Это необходимо сделать только на slave!

При подключении чистой реплики Patroni делает basebackup leader и восстанавливает его на реплику, а затем догоняет актуальное состояние по wal-логам.

Ещё одна сложность, с которой мы столкнулись, — все кластеры PostgreSQL по умолчанию называются main. Когда каждый кластер ничего не знает про другой — это нормально. Но когда вы хотите использовать Patroni, то все кластера должны иметь уникальное имя. Решение — поменять имя кластера в конфигурации PostgreSQL.

Нагрузочный тест


Мы запустили тест, который имитирует работу пользователей на досках. Когда нагрузка достигла нашего среднего дневного значения, мы повторили точно такой же тест, мы выключили один instance с leader PostgreSQL. Автоматический failover сработал так, как мы ожидали: Patroni сменил лидера, Сonsul-template обновил конфигурацию PgBouncer и отправил команду на reload. По нашим графиками в Grafana было видно, что есть задержки на 20-30 секунд и небольшой объём ошибок с серверов, связанных с соединением к базе. Это нормальная ситуация, такие значения допустимы для нашего failover и точно лучше, чем даунтайм сервиса.

Вывод Patroni на production


В итоге у нас получился следующий план:

  • Деплой Сonsul-template на сервера PgBouncer и запуск;
  • Обновления PostgreSQL до версии 11.2;
  • Смена имени кластера;
  • Запуск кластера Patroni.

При этом наша схема позволяет сделать первый пункт практически в любое время, мы можем по очереди убрать каждый PgBouncer из работы и выполнить на него деплой и запуск consul-template. Так мы и сделали.

Для быстрой раскатки мы использовали Ansible, так как все playbook мы уже проверили на тестовой среде, а время выполнения полного сценария было от 1,5 до 2 минут для каждого шарда. Мы могли всё выкатить поочередно на каждый шард без остановки нашего сервиса, но нам пришлось бы на несколько минут выключать каждый PostgreSQL. В этом случае пользователи, чьи данные есть на этом шарде, не могли бы полноценно работать в это время, а это для нас неприемлемо.

Выходом из этой ситуации стал плановый maintenance, который проходит у нас каждые 3 месяца. Это окно для плановых работ, когда мы полностью выключаем наш сервис и обновляем инстансы баз данных. До очередного окна оставалась одна неделя, и мы решили просто подождать и дополнительно подготовиться. За время ожидания мы дополнительно подстраховались: для каждого шарда PostgreSQL подняли по запасной реплике на случай неудачи, чтобы сохранить самые последние данные, и добавили по новому инстансу для каждого шарда, который должен стать новой репликой в кластере Patroni, чтоб не выполнять команду для удаления данных. Всё это помогло максимально снизить риск ошибки.


Мы перезапустили наш сервис, все заработало как надо, пользователи продолжили работать, но на графиках мы заметили аномально высокую нагрузку на Сonsul-сервера.


Почему мы не увидели это на тестовой среде? Эта проблема очень хорошо иллюстрирует, что необходимо следовать принципу Infrastructure as code и дорабатывать всю инфраструктуру, начиная с тестовых сред и заканчивая production. Иначе очень легко получить такую проблему, которую получили мы. Что произошло? Сonsul сначала появился на production, а потом на тестовых средах, в итоге на тестовых средах версия Consul была выше, чем на production. Как раз в одном из релизов была решена утечка CPU при работе с consul-template. Поэтому мы просто обновили Consul, решив таким образом проблему.

Restart Patroni cluster


Однако мы получили новую проблему, о которой даже не подозревали. При обновлении Consul мы просто удаляем ноду Consul из кластера с помощью команды consul leave > Patroni подключается к другому Consul серверу > всё работает. Но когда мы дошли до последнего инстанса кластера Consul и отправили ему команду consul leave, все кластеры Patroni просто перезапустились, а в логах мы увидели следующую ошибку:

ERROR: get_cluster
Traceback (most recent call last):
...
RetryFailedError: 'Exceeded retry deadline'
ERROR: Error communicating with DCS
<b>LOG: database system is shut down</b>

Кластер Patroni не смог получить информацию о своём кластере и перезапустился.

Для поиска решения мы обратились к авторам Patroni через issue на github. Они предложили улучшения наших конфигурационных файлов:

consul:
 consul.checks: []
bootstrap:
 dcs:
   retry_timeout: 8

Мы смогли повторить проблему на тестовой среде и протестировали там эти параметры, но, к сожалению, они не сработали.

Проблема до сих пор остаётся нерешённой. Мы планируем попробовать следующие варианты решения:

  • Использовать Сonsul-agent на каждом инстансе кластера Patroni;
  • Исправить проблему в коде.

Нам понятно место возникновения ошибки: вероятно, проблема в использовании default timeout, который не переопределяется через файл конфигурации. При удалении последнего сервера Сonsul из кластера происходит зависание всего Сonsul-кластера, которое длится дольше секунды, из-за этого Patroni не может получить состояние кластера и полностью перезапускает весь кластер.

К счастью, больше никаких ошибок мы не встретили.

Итоги использования Patroni


После успешного запуска Patroni мы добавили по дополнительной реплике в каждом кластере. Теперь в каждом кластере есть подобие кворума: один лидер и две реплики, — для подстраховки на случай split-brain при переключении.


На production Patroni работает более трёх месяцев. За это время он уже успел нас выручить. Недавно в AWS умер лидер одного из кластеров, автоматический failover сработал и пользователи продолжили работать. Patroni выполнил свою главную задачу.

Небольшой итог использования Patroni:

  • Удобство изменения конфигурации. Достаточно изменить конфигурацию на одном инстансе и она подтянется на весь кластер. Если требуется перезагрузка для применения новой конфигурации, то Patroni об этом сообщит. Patroni может перезапустить весь кластер с помощью одной команды, что тоже очень удобно.
  • Автоматический failover работает и уже успел нас выручить.
  • Обновление PostgreSQL без даунтайма приложения. Необходимо сначала обновить реплики на новую версию, затем сменить лидера в кластере Patroni и обновить старого лидера. При этом происходит необходимое тестирование автоматического failover.

Комментарии (21)


  1. mr_bag
    25.06.2019 12:30

    Добрый день.
    Минусы Редис вы привели.
    Можно узнать, по каким причинам были отвергнуты Mongo, Raven, или результаты анализа являются NDA?
    Спасибо.


    1. oller
      25.06.2019 19:32

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


  1. mommys_little_hacker
    25.06.2019 14:56

    Пытался внедрить patroni год назад и выкинул. Вот почему:
    1. Неофициальные образы в docker hub. Они самописные, куда не так просто добавить свои расширения и прогнаны через docker-squash, что усложняет диагностику.
    2. Они используют свои абстракции над конфигами, поэтому не все директивы конфига постгреса можно переопределить.
    3. Сабж завязан на возможности hot standby. При использовании hot standby пропадает возможность использования максимального уровня изоляции транзакций (serializable), что было неприемлемо для нашего проекта.


    1. trider
      25.06.2019 19:34
      +1

      Я конечно продукт господина Александра Кукушкина — разработчика Patroni из Zolando не продвигаю и не защищаю, но:
      1. Никто не мешает собирать свой образ и хранить в своём репозитории, как и образы с апликухой, которые в рамках релиза выезжают в продакшн
      2. Там всего несколько директив постгрешных: max_connections, max_locks_per_transaction, max_worker_processes, max_prepared_transactions, wal_level, wal_log_hints, track_commit_timestamp, max_wal_senders, max_replication_slots, wal_keep_segments
      3. Вот здесь я не очень понял параллель уровня изоляции транзакций и hot standby — возможность чтения с реплики


      1. mommys_little_hacker
        26.06.2019 11:09

        1. Правда. Но это уменьшает его ценность, как «изкоробочного» решения
        2. И как только потребуется переопределить что-то не входящее в этот список, начинаются сложности
        3. Ограничение PostgreSQL, больше инфо здесь, параграф «Caveats»: www.postgresql.org/docs/10/hot-standby.html


    1. MrAloof
      26.06.2019 06:12
      +1

      И что в итоге внедрили?


      1. mommys_little_hacker
        26.06.2019 11:10

        pgpool2 + master-slave


    1. viktor_eremchenko Автор
      26.06.2019 11:05

      1. Как уже подсказали, ничто не мешает использовать свой docker образ или можно обойтись без docker.
      2. Здесь хочется примеров того, что нельзя переопределить. В нашем случае мы еще не сталкивались с такой ситуацией. И если она существует, то будет полезно знать об этом заранее.


      1. mommys_little_hacker
        26.06.2019 11:14

        Всего не упомню, ибо было год назад. Patroni ставил с помощью helm.


        shared_preload_libraries = 'decoderbufs,wal2json'


  1. citius
    25.06.2019 16:04

    Локальные консул агенты обязательно нужны на всех хостах.
    Просто роли у них должны быть просто agent, а не master.
    Вообще если мне память не изменяет, это best practice для консула.


    1. trider
      25.06.2019 19:35
      +1

      Локальны консул агенты, это одна из точек отказа, но никто не мешает использовать etcd, где нету никаких агентов


    1. viktor_eremchenko Автор
      26.06.2019 10:59

      Спасибо за комментарий,
      да, это best practise, но опять же не для всего и в случае с patroni нет правильного решения. Мы попробуем и использование consul agent и попробуем поправить проблему в коде patroni. И хотим делать эти два решения параллельно и независимо друг от друга, а потом посмотрим что из этого получится.


      1. citius
        26.06.2019 11:43

        Многолетний опыт экспулатации патрони с консулом (и далеко не только патрони) показывает, что локальные агенты решают кучу проблем — распределение нагрузки (особенно днс чтения для клиентского сервис дискавери), да возможность нормально использовать сервис дискавери как таковой.

        У нас на консуле построено вообще все, и без локальных агентов, как изначально мы тоже пробовали, было все сильно хуже.

        И впиливание костыля в патрони, ради избегания best-practice другого инструмента…
        Ну хз. Врядли в апстрим возьмут. ;)


  1. onlinehead
    25.06.2019 21:27

    Интересная история, «но есть нюанс».
    Исключительно из любопытства — вы пробовали посчитать, во сколько обошлось и обойдётся подобное кастомное решение в сравнии с RDS?
    Не с точки зрения стоимости инстанса, а за все, включая работу.


    1. Berkof
      26.06.2019 10:50

      Зато у людей нормальное переносимое решение, а если амазон вдруг решит, что у них теперь очень хороший посгрес и надо поднять ценник за него раза в 2 (гугл ведь так не раз делал, МС делал, чем мы хуже?), то с RDS так просто не спрыгнуть.
      Другое дело — вообще не понимаю зачем они в амазоне железо покупают, если и так куча резервов… чем хетцнер с их бросовыми железяками не подходит?


      1. viktor_eremchenko Автор
        26.06.2019 10:57

        Если посмотреть на status page hetzner, то вопросов не должно быть. У них не так все хорошо с доступностью.


        1. AlexGluck
          26.06.2019 22:12

          Я бы взял 50-80% мощности у хетцнера, остальное у другого хостера, так можно нехилые суммы сэкономить. Распределение между хостерами это бестпрактис.


    1. viktor_eremchenko Автор
      26.06.2019 10:56

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


  1. chemtech
    26.06.2019 09:21

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

    В итоге шардируете на прикладном уровне или нет?
    На Etcd не хотите переходить?


    1. viktor_eremchenko Автор
      26.06.2019 10:54

      Шардируем на прикладном уровне.
      На etcd переходить пока не планируем, есть кластер consul и он должен использоваться не только для patroni, что сейчас и происходит.


  1. Vitaly2606
    26.06.2019 17:11

    На etcd переходить пока не планируем

    Для информации:
    Используем аналогичное HA решение PostgreSQL на выделенных физических серверах (на базе Patroni + etcd), в production (более года), проблем с etcd у нас не было.
    Главное, на этапе планирования, учесть требование к дисковому и сетевому latency, оно не должно быть большим. Мы храним данные etcd на отдельных (от данных БД) дисках (на каждой ноде кластера Patroni).

    Кластер etcd очень чувствителен к задержкам на диске.
    Активность на диске из других процессов может привести к длительным задержкам fsync. В результате, etcd может пропустить биение сердца (heartbeats), вызывая тайм-ауты запроса (request timeouts) и временную потерю лидера etcd.
    Избегайте хранение данных etcd на дисках совместно с другими процессами, интенсивно использующими ресурсы дисковой подсистемы! По возможности, хранить данные etcd на ssd дисках.

    Кластер etcd, как правило, строим из 3-х, либо 5-и серверов, не более.

    Схема выглядит следующим образом:
    github.com/vitabaks/postgresql_cluster/blob/master/TypeA.png

    Для систем без необходимости распределения (читающей) нагрузки, используем упрощённую схему, в которой (в добавок к patroni и etcd) используется только «vip-manager» (Manages a virtual IP based on state kept in etcd or Consul. Monitors state in etcd).
    github.com/vitabaks/postgresql_cluster/blob/master/TypeB.png
    При этом, pgbouncer статичен (нет необходимости менять его конфиг), он привязан к конкретному локальному экземпляру PostgreSQL, и в принципе — опционален. Рассматриваем возможное использование пуллера Yandex odyssey вместо pgbouncer в недалёком будущем.