Как стать автором
Обновить

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

Отличная статья. Спасибо.
Попробую параллельно поднять ваш конфиг и сравнить.
Благодарю! Сравнивать будете с более традиционным кластером?
Поделитесь потом впечатлением, если не сложно. :)
Спасибо за статью!
Хотел сам написать, да как то руки не доходили :)
Мы использовали patroni, засунув его с postgresql в Docker контейнер.
А мы как-то пока базы в контейнеры не решаемся.
Кстати, какие проблемы в эксплуатации выявили? Интересует именно аспект контейнеризации.
Так ничего же страшного нету. Папка с БД выносится за контейнер, а процесс БД в контейнере работает. Разницы в работе нет никакой. БД в контейнере можно запустить с тем же юзером, что и на хост системе, что позволяет не волноваться за то, кто является владельцем и что будет происходить с папками БД.
Страшного то нет, меня скорее беспокоит то, что с контейнерами меньше возможностей управлять ресурсами, например гарантировать процессорное или дисковое время.
Мне кажется что для кустов микросервисов контейнеры подходят больше чем для таких тяжелых вещей.
Хотя в целом у меня с контейнерами пока и опыта богатого нет. :)
Тут зависит от задач. Так как если у вас небольшая база и вы не хотите управлять процессорным и дисковым временем, то вопрос где запускается БД особо не стоит. Однако если вам уже важны эти времена (я думаю на больших БД), то я думаю, что и Patroni тут уже не будет использоваться, а что то посерьезнее :)
На данный момент юзаем в контейнере postgresql, как уже сказали проблем нет. Но управление ресурсами это уже не совсем про контейнеры. Хотя положительные подвижки в этом аспекте есть.
Много где написано, что если монтировать docker volume на хост начинаются проблемы. У нас это приводило к порче всех файлов docker и сам демон не мог даже стартовать. Какие то особые настойки для docker?
Никогда не сталкивался с такой проблемой. Обычная команда docker run -v /path:/path repo:tag.
Я только пробовал подключать docker volume как Azure File System. Однако из-за отсутствия поддержки симлинков в Azure File System, постгрес не хотел работать :)
Подобные проблемы возникали на старых версиях docker, просто умирал dm в котором был rootfs контейнера. Fedora 25, последнее доступное ядро + данные, персистентность которых нужно обеспечить, пробрасываются через -v. Ну и сам докер держим последний.
Что будет, если не программно тушить сервисы, а выдергивать сетевой кабель?
keepalived потеряют соседа и перевыберут мастера между собой.
Patroni тоже выберут нового мастера, т.к. перестанет обновляться тикет в DCS.
Текущий мастер будет изолирован, и начнет отставать по базе.
После восстановления коннективити нужно будет просто перезапустить Patroni на старом мастере, и он должен штатно догнаться до слейва.
Интересная статья. Хочу попробовать реализовать и потестить.
Замечание: ansible фейлит на zabbix на задаче скопировать скрипты. Их Вы не выложили в репозиторий. Если можно — выложите. Если нет, обойдемся.
Спасибо за труд!
Да, стормозил я с этими скриптами. Надо было роль от лишнего почистить. :)
Впрочем мне не жалко, и ничего секретного там нет. Выложил в репозиторий.
Как пример пойдет.
Спасибо за оперативность!
Теперь ругается на /etc/ansible/files/mysql/.my.cnf, так как его тоже нет. Подкиньте еще пожалуйста этот файлик, чтоб не ругался больше.
Спасибо!
Ну это точно совсем лишняя штука в данном контексте.
Я убрал это из zabbix плейбука, и добавил темлейт конфига забикса, сейчас должно все пройти.
Если будут еще проблемы с ним, лучше дергайте меня напрямую через личку, или контакты в профиле.
Спасибо за статью. А на Stolon не смотрели?
Он мне попадался, но питон мне лично гораздо проще чем Go, поэтому Patroni больше заинтересовала.
Судя по описанию тоже стоящая штука.
«rm -rf /var/lib/pgsql/9.6/data», и перезапустить Patroni. Она сольет базу с мастера целиком.

Хотите повторить опыт Gitlab? :) Пожалуйста, НИКОГДА так не делайте. Специально для таких случаев мы придумали patronictl reinit <cluster> <node>
Эта команда абсолютна безопасна, текущий мастер просто откажется её выполнять.
Реплика-же сделает всё как нужно: Patroni вначале остановит postgres, удалит data директорию, заберёт новый pg_basebackup с мастера и снова запустит postgres.


Огромное Вам спасибо за статью от Zalando!

Ох, крутяк какой. Каким-то образом я это пропустил, хотя точно помню что patronictl я ковырял.
Добавлю в статью, спасибо! :)

service: name=ntpd state=stopped enabled=no
Зачем Вы так жестоко ломаете ntp?

А я писал в статье про проблемы с синхронизацией времени.
Полное описание есть в KB VmWare тут.
Нам пока на данный момент проще убить вообще ntpd, от vSphere мы планируем отказаться.
Т.е. в вашей конфигурации получается, что виртуальный IP кластера может попасть на ноду слейва postgresql? И тогда при большой загрузке канала будет падать скорость, т.к. трафик удваивается (клиент<->слейв<->мастер).

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


Что если в мастер попали данные, на слейв улететь не успели и мастер потерял сеть? Один из слейвов все равно поднимется в мастер, а бывший мастер при возврате в сеть затрет уникальные данные и станет слейвом?
1) Да, это нужно учитывать. Если объем этого трафика это проблема (лаг там все-таки минимальный добавляется), то стоит либо балансеры вынести наружу, либо сделать репликацию по отдельной сети.

2) Это проблема асинхронной репликации: транзакции которые не успеют считать слейвы будут потеряны.
Именно поэтому у меня репликация синхронная, у нас такие потери недопустимы.
Синхронная репликация обеспечивает консистентность на уровне транзакций.

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

А если ляжет слэйв, мастер продолжит выполнять транзакции?

Асинхронный слейв будет переключен в синхронный режим.
Если совсем не будет слейвов, patroni отключит синхронную репликацию.

Вот цитата из документации:
On each HA loop iteration Patroni re-evaluates synchronous standby choice. If the current synchronous standby is connected and has not requested its synchronous status to be removed it remains picked. Otherwise the cluster member avaiable for sync that is furthest ahead in replication is picked.

Если совсем не будет слейвов, patroni отключит синхронную репликацию.

Вот это интересовало. Спасибо.

Всё верно, но скоро ещё добавим synchronous_mode_strict.
В этом случае мастер не будет выполнять транзакции если нет synchronous standby


Но не забывайте, это поведение по умолчанию, и клиент всегда может решить что ему не нужна синхронная репликация и отключить её: SET local synchronous_commit = 'local';

В этом случае мастер не будет выполнять транзакции если нет synchronous standby

гибко регулировать можно будет? Типа из пяти слейвов в кластере минимум два должны быть с синхронной репликацией, чтобы мастер принимал транзакции?

Начиная с 9.6 такое возможно, но Patroni пока-что так не умеет.
Если будет свободное время — сделаю, но с другое стороны мы всегда рады пулл-реквестам :)

Видел, я же даже в «использованные статьи» вас добавил. :)
С Patroni подобная же схема, на мой взгляд гораздо проще и прозрачнее.
Ни разу не прозрачнее для тех кто не имел дело с DSC. Как я выяснил Patroni сам не заведёт Consul и etcd по которым документации с гулькин нос и надо вшиваться в DSC, чтобы понять как запустить всю эту связку

Отличная статья. Спасибо. надо попробовать.

Статья обалденная, но скажите пожалуйста, что вы делаете если ansible trigger перезагружает мастера с которого «шарится» IP? Существует бородатый баг, когда нетворк перезагруажается — keepalive вылетает со скоростью света.
Делали здесь
Ссылка не вставилась, повторите плз.
У нас таких проблем не возникало.
Вот ссылка…
Я попытался сделать реализацию с 2 лбл. Если на мастере перезапустить нетворк — тогда шаред ИР станет недоступным.
Я только что попробовал перезапустить сеть на главном keepalived, ничего не случилось.
Пинги не пропадали, сеть осталась рабочей.

Это Centos 7.2 с ядром kernel-ml 4.9.0, перезапускал через systemctl restart network.
https://blog.a2o.si/2013/10/08/restarting-network-with-keepalived-on-redhat-centos/
Спасибо за труд!
Хотел бы уточнить одну вещь — в шаблоне для haproxy вижу такие строки:
server {{ patroni_node_name }} {{ patroni_node_name }}.local:5432 maxconn 300 check port 8008
server {{ patroni_node_name }} {{ patroni_node_name }}.local:5432 maxconn 300 check port 8008
server {{ patroni_node_name }} {{ patroni_node_name }}.local:5432 maxconn 300 check port 8008
Разве сюда не будет вставляться одно и то же значение 3 раза?
Да, пробрался косяк.
Поправил в репозитории на более явное определение серверов.
Нужно сделать строки соответствующие всем серверам кластера, чтобы хапрокси мог их простукивать и проксировать трафик на мастер:

backend postgres-patroni
  option httpchk

  http-check expect status 200
  default-server inter 3s fall 3 rise 2

  server cluster-pgsql-01 cluster-pgsql-01.local:5432 maxconn 300 check port 8008
  server cluster-pgsql-02 cluster-pgsql-02.local:5432 maxconn 300 check port 8008
  server cluster-pgsql-03 cluster-pgsql-03.local:5432 maxconn 300 check port 8008

Кстати, не пробовал сам, но видел где-то в интернете: если hostname'ы узлов совпадают с hostname_inventory, то можно записать так:
{{ ansible_play_hosts[0] }}
{{ ansible_play_hosts[1] }}
и. д.
Да в ансибле вообще по всякому можно, мощная штука. :)

Есть прекрасная книга с кучей примеров, советую прочесть.
Спасибо за материал. Как раз изучаю вопрос.
В некоторых статьях вместе с haproxy используется pgbouncer. Есть ли смысл добавлять его в эту схему?
От задачи зависит. Если нужен пулинг и ограничения баунсера не помешают, то конечно можно добавить.

Скажите, в чем причина использования ядра 4 версии?
Чем не устроило дефолтное центосовское ядро?

Ну в чейнжлоге между 3.10 и очень 4.10 — много всего, не перечислить. ;)
Вкратце — стараюсь не использовать некрософт, если это не обусловлено какими-то требованиями к совместимости.

Новые ядра, как правило, и быстрее и безопаснее.
Я так понял в статье не раскрыта конфигурация Consul, которую требуется произвести перед запуском Patroni. Я не имел дел с Consul и etcd и не могу сориентироваться какие телодвижения требуется произвести с Consul
В простейшем приближении никаких, демон консула просто запускается где удобно, и с ним можно сразу работать от имени клиентов.
Если нужна отказоустойчивость на его уровне, то есть кластеризация и т.д.
Советую почитать статьи по консулу, их много.

С etcd примерно также, ничего сложного там нет.
C consul'ом я так понял нужен не только пионовский модуль
C consul'ом я так понял нужен не только питоновский модуль python-consul, но и Consul server www.consul.io/downloads.html и я думал patroni с запуском и конфигурацией consul или etcd сам разберётся. Если использую Consul, ругается что не может подключиться к my_internal_ip:8500, если etcd, то говорит:
EtcdKeyNotFound: Key not found: /service/my-db-cluster/leader
Подскажите что мне делать с Консулом:
Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/patroni/dcs/consul.py", line 154, in refresh_session
    return self.retry(self._do_refresh_session)
  File "/usr/lib/python2.7/site-packages/patroni/dcs/consul.py", line 116, in retry
    return self._retry.copy()(*args, **kwargs)
  File "/usr/lib/python2.7/site-packages/patroni/utils.py", line 269, in __call__
    raise RetryFailedError("Exceeded retry deadline")
RetryFailedError: 'Exceeded retry deadline'
2017-09-07 18:47:05,073 INFO: waiting on consul
2017-09-07 18:47:20,057 ERROR: refresh_session
Запустить Patroni я так и не смог, что я только не делал с Consul'ом, убил несколько дней, но Patroni кричал:
INFO: waiting on consul


Поэтому решение с Patroni достаточно мутное, хотите нормальный PostgreSQL кластер не лепите велосипед, надо брать Postgres Pro Enterprise.

trider


Судя по логам очевидно что Patroni не может подключиться к Consul.
Покажи конфиг Patroni.

# cat /etc/patroni/postgres.yml
name: db01
scope: &scope db

consul:
  host: 127.0.0.1:8500


restapi:
  listen: 0.0.0.0:8080
  connect_address: 172.16.128.70:8080
  auth: 'username:test'

bootstrap:
  dcs:
    ttl: &ttl 30
    loop_wait: &loop_wait 10
    maximum_lag_on_failover: 1048576 # 1 megabyte in bytes
    postgresql:
      use_pg_rewind: true
      use_slots: true
      parameters:
        archive_mode: "on"
        wal_level: hot_standby
        archive_command: mkdir -p ../wal_archive && cp %p ../wal_archive/%f
        max_wal_senders: 10
        wal_keep_segments: 8
        archive_timeout: 1800s
        max_replication_slots: 5
        hot_standby: "on"
        wal_log_hints: "on"

pg_hba:  # Add following lines to pg_hba.conf after running 'initdb'
  - host replication replicator 172.16.0.0/12 md5
  - host all all 0.0.0.0/0 md5

postgresql:
  listen: 0.0.0.0:5432
  connect_address: 172.16.128.70:5432
  data_dir: /var/lib/pgsql/9.6/data
  pg_rewind:
    username: superuser
    password: test
  pg_hba:
  - host all all 0.0.0.0/0 md5
  - hostssl all all 0.0.0.0/0 md5
  replication:
    username: replicator
    password: test
    network:  172.16.0.0/12
  superuser:
    username: superuser
    password: test
  admin:
    username: admin
    password: test
  restore: /usr/bin/patroni_wale_restore


# netstat -nap | grep consul
tcp        0      0 127.0.0.1:8400          0.0.0.0:*               LISTEN      2737/consul
tcp        0      0 127.0.0.1:8500          0.0.0.0:*               LISTEN      2737/consul
tcp        0      0 127.0.0.1:8600          0.0.0.0:*               LISTEN      2737/consul
tcp6       0      0 :::8300                 :::*                    LISTEN      2737/consul
tcp6       0      0 :::8301                 :::*                    LISTEN      2737/consul
tcp6       0      0 :::8302                 :::*                    LISTEN      2737/consul
udp        0      0 127.0.0.1:8600          0.0.0.0:*                           2737/consul
udp6       0      0 :::8301                 :::*                                2737/consul
udp6       0      0 :::8302                 :::*                                2737/consul
unix  3      [ ]         STREAM     CONNECTED     83481    2737/consul


Вот что journalctl говорит по поводу consul:
Sep 11 15:09:23 db01.localdomain consul[2737]: 2017/09/11 15:09:23 [ERR] agent: failed to sync remote state: No cluster leader
Sep 11 15:09:27 db01.localdomain consul[2737]: 2017/09/11 15:09:27 [ERR] agent: coordinate update error: No cluster leader
Sep 11 15:09:44 db01.localdomain consul[2737]: 2017/09/11 15:09:44 [ERR] agent: coordinate update error: No cluster leader
Sep 11 15:09:52 db01.localdomain consul[2737]: 2017/09/11 15:09:52 [ERR] agent: failed to sync remote state: No cluster leader

# consul members
Node                Address             Status  Type    Build  Protocol  DC
db01.localdomain  172.16.128.70:8301  alive   server  0.6.4  2         dc1


Мне всё-таки очень интересно запустить этот «автомат» master-slave.
У меня такое ощущение судя по либам patroni, что он сам должен был с consul'ом разобраться

Проблема не в Patroni, а в Consul, он конечно запущен (процесс живой) и даже порт слушает, но при этом неконсистентен и Patroni не может в него ничего записать ни прочитать из него.


К сожалению с кластеризацией Consul я вряд-ли смогу помочь.

Вот по такому шаблону eax.me/consul можно сконфигурить Consul под Postgre для последующей интеграции patroni?

Думаю что да, но есть несколько тонкостей:


  1. Во первых надо запустить Consul кластер на 3 хостах (иначе не будет HA)
  2. Consul agent должен работать на всех машинах где планируется запускать Patroni + Postgres. При этом этот агент не обязательно должен участвовать в кворуме.
  3. Patroni использует Consul исключительно как KV Store.

Может лучше попробовать etcd? Там кластеризация в 100 раз проще: https://github.com/coreos/etcd/blob/master/Documentation/op-guide/clustering.md#static


Если планируется запускать больше двух нод с Patroni+Postgres, то можно попробовать https://github.com/zalando/patroni/pull/375, он не требует внешнего DCS

Да, я планирую запустить 2е ноды master-slave
Не подскажете какой DCS я могу использоваться для организации failover'а master-slave из 2х нод?
Любой из поддерживаемых patroni.
Но для работы Consul минимум 3 ноды, на 2х нодах etcd не заводится тоже пока
Прямо в репе патрони на гитхабе в ридми есть пример как на локалхосте запустить демон etcd и два инстанса патрони.
Да не будет это работать, если просто по дефолту установить и запустить etcd, его нужно конфигурить, иначе patroni выдаст:
EtcdKeyNotFound: Key not found : /service/postgre_cluster/leader

Если вы хотите настоящий HA-кластер, то вам в любом случае понадобятся 3 ноды, т.к. у etcd кворумная кластеризация (т.е. для выбора нового мастера необходимо N/2+1 живых нод)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории