В первой части мы остановились на том, что у нас есть отказоустойчивый кластер и теперь вроде бы все работает. Мы уже можем зайти в базу данных через лидера и что-нибудь туда записать. Последняя проблема, которую нужно решить — программно узнать, где находится лидер. Неужели нужно будет каждый раз делать эту проверку после падения, чтобы понять через какую ноду подключаться к базе?
Это было бы настолько скверно, что испортило бы все решение. Но не беспокойтесь, с помощью HA Proxy вопрос решается довольно легко и незатейливо.
Также как с Patroni, сначала сделаем отдельную директорию под билд/деплой файлы и начнем их там создавать:
haproxy.cfg
Это файл конфига, который мы положим в наш кастомный образ.
haproxy.cfgglobal
maxconn 100
stats socket /run/haproxy/haproxy.sock
stats timeout 2m # Wait up to 2 minutes for input
defaults
log global
mode tcp
retries 2
timeout client 30m
timeout connect 4s
timeout server 30m
timeout check 5s
listen stats
mode http
bind *:7000
stats enable
stats uri /
listen postgres
bind *:5000
option httpchk
http-check expect status 200
default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
server patroni1 patroni1:5432 maxconn 100 check port 8091
server patroni2 patroni2:5432 maxconn 100 check port 8091
server patroni3 patroni3:5432 maxconn 100 check port 8091
В этих строчках мы назначаем порты, по которым будет получать доступ:
// этот для вывода статистики
listen stats
mode http
bind *:7000
//этот для подключения к postgres
listen postgres
bind *:5000
А здесь мы просто перечисляем все сервисы Patroni, которые создали ранее:
server patroni1 patroni1:5432 maxconn 100 check port 8091
server patroni2 patroni2:5432 maxconn 100 check port 8091
server patroni3 patroni3:5432 maxconn 100 check port 8091
И последнее. Эта строка нужна, если мы хотим проверять состояние кластера с помощью специальной утилиты из контейнера с HA Proxy:
stats socket /run/haproxy/haproxy.sock
Dockerfile
Dockerfile выглядит довольно просто и, думаю, не требует комментариев:
DockerfileFROM haproxy:1.7
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
RUN mkdir /run/haproxy &&
apt-get update -y &&
apt-get install -y hatop &&
apt-get clean
Compose файл выглядит тоже достаточно просто:
docker-compose-haproxy.ymlversion: "3.7"
networks:
patroni_patroni:
external: true
services:
haproxy:
image: haproxy-patroni
networks:
- patroni_patroni
ports:
- 5000:5000
- 7000:7000
hostname: haproxy
deploy:
mode: replicated
replicas: 1
placement:
constraints: [node.hostname == floitet]
Когда все файлы готовы, можно и задеплоить весь этот сет:
// build
docker build -t haproxy-patroni
// deploy
docker stack deploy --compose-file docker-compose-haproxy.yml
Когда HА Proxy запустится, можно будет в контейнере посмотреть статистику кластера специальной командой:
sudo docker ps | grep haproxy
sudo docker exec -ti $container_id /bin/bash
hatop -s /var/run/haproxy/haproxy.sock
Выполнив эти три шага, мы увидим прямо в консоли красивый вывод статистики.
Сам я предпочитаю смотреть статистику через patronictl либо Patroni API, но HA Proxy тоже один из вариантов и почему бы не настроить и его заодно.
А теперь немного о Patroni API.
Я обещал показать три способа смотреть статистику кластера, и вот добрался-таки до последнего. Вывод статистики через API хорош тем, что он дает самую полную информацию о кластере (и вообще через него можно делать всё).
Более подробно почитать про API можно в доках, а здесь я покажу самые основные вещи: стандартные запросы и как их выполнить в нашем сетапе.
Также как с подключением к БД, мы не сможем обращаться к API, не находясь в сети ’patroni_patroni’. Так что нам придется слать все наши запросы из контейнера. Чтобы читать вывод json в приятном человеческому глазу формате, сделаем кастомный имейдж с curl’ом и jq.
DockerfileFROM alpine:3.10
RUN apk add --no-cache curl jq bash
CMD ["/bin/sh"]
И потом запустим контейнер с этим образом, подключив его к нужной сети:
docker run --rm -ti --network=patroni_patroni curl-jq
Теперь мы можем обращаться к API Patroni нод по их именам и получать cтаты в таком вот виде:
Работа с API// Статистика ноды
curl -s patroni1:8091/patroni | jq
{
"patroni": {
"scope": "patroni",
"version": "2.0.1"
},
"database_system_identifier": "6893104757524385823",
"postmaster_start_time": "2020-11-15 19:47:33.917 UTC",
"timeline": 10,
"xlog": {
"received_location": 100904544,
"replayed_timestamp": null,
"replayed_location": 100904544,
"paused": false
},
"role": "replica",
"cluster_unlocked": false,
"state": "running",
"server_version": 110009
}
// Статистика кластера
curl -s patroni1:8091/cluster | jq
{
"members": [
{
"port": 5432,
"host": "10.0.1.5",
"timeline": 10,
"lag": 0,
"role": "replica",
"name": "patroni1",
"state": "running",
"api_url": "http://10.0.1.5:8091/patroni"
},
{
"port": 5432,
"host": "10.0.1.4",
"timeline": 10,
"role": "leader",
"name": "patroni2",
"state": "running",
"api_url": "http://10.0.1.4:8091/patroni"
},
{
"port": 5432,
"host": "10.0.1.3",
"lag": "unknown",
"role": "replica",
"name": "patroni3",
"state": "running",
"api_url": "http://10.0.1.3:8091/patroni"
}
]
}
Основная идея, что теперь мы можем ставить свои эксперименты с Patroni кластером так, как если бы это был кластер с тремя реальными серверами. Достаточно просто выключать и включать сервисы Patroni, чтобы сымитировать падения серверов. Если лидер у нас patroni3, то мы делаем:
docker service scale patroni_patroni3=0
И отключаем его, убивая его единственный контейнер. Теперь можно глянуть состояние кластера и убедиться, что лидер перешел на здоровую ноду:
postgres@patroni1:/$ patronictl -c /etc/patroni.yml list patroni
+ Cluster: patroni (6893104757524385823) --+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+----------+-----------+---------+---------+----+-----------+
| patroni1 | 10.0.1.93 | Leader | running | 9 | |
| patroni2 | 10.0.1.91 | Replica | running | 9 | 0 |
+----------+-----------+---------+---------+----+-----------+
Если сделать scale для patroni3 на ’1′, то он вернется в кластер и займет роль реплики.
Теперь вы можете запускать все тесты и проверять, как Patroni кластер будет реагировать на критические ситуации.
Как и обещал, ниже небольшой скрипт для тестов, а также инструкции как с ним работать. Так что, если вам нужно что-то уже готовое для быстрого теста, добро пожаловать. Если же у вас есть свои идеи и сценарии и вам не требуется готовое решение, просто пропустите спойлер.
Тестируем Patroni clusterДля тех читателей, кто хочет потестировать кластер Patroni на каком-то готовом примере прямо сейчас, я сделал вот этот скрипт. Он супер простой, и я уверен у вас не возникнет проблем с тем, чтобы понять, как он работает. По сути, он просто пишет в базу текущее время каждую секунду. Ниже я шаг за шагом покажу, как его запустить и какие результаты теста были у меня.
Допустим, вы уже скачали скрипт и положили его где-то у себя на машине. Если нет, то проделайте эту подготовку. Теперь нам нужно запустить Docker контейнер с официальным образом Miscrosoft SDK такой командой:
docker run --rm -ti --network=patroni_patroni -v /home/floitet/Documents/patroni-test-script:/home mcr.microsoft.com/dotnet/sdk /bin/bash
Два момента. Первое, как и раньше мы хотим подключиться к сети ’patroni_patroni’ и второе, мы делаем mount к той директории, где уже лежит готовый скрипт. Таким образом мы сможем запускать его из контейнера.
Теперь мы хотим, чтобы у нас появился единственный dll, который нужен чтобы скрипт взлетел. Заходим в контейнер и находясь в директории ’/home’ создаем папку ’patroni-test’ для консольного приложения. Заходим в нее и выполняем следующую команду:
dotnet new console
// видим такие строчки
Processing post-creation actions...
Running 'dotnet restore' on /home/patroni-test/patroni-test.csproj...
Determining projects to restore...
Restored /home/patroni-test/patroni-test.csproj (in 61 ms).
Restore succeeded.
И теперь мы можем добавить в проект нужный нам для работы пакет:
dotnet add package npgsql
А потом просто упаковываем проект:
dotnet pack
Если все прошло удачно, то мы получим ’Npgsql.dll’ по адресу: ’patroni-test/bin/Debug/net5.0/Npgsql.dll’.
Этот путь мы и добавляем как референс в скрипте, так что если у вас он отличается от моего, то в скрипте это нужно подправить.
А дальше просто запускаем скрипт:
dotnet fsi /home/patroni-test.fsx
// и в output мы увидим как скрипт пошел писать время:
11/18/2020 22:29:32 +00:00
11/18/2020 22:29:33 +00:00
11/18/2020 22:29:34 +00:00
Важно не закрывать терминал со скриптом, пока идет тест.
Давайте посмотрим, где сейчас находится лидер, чтобы знать кого ронять. Можно использовать любой из трёх способов, я смотрел через patronictl:
+ Cluster: patroni (6893104757524385823) --+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+----------+-----------+---------+---------+----+-----------+
| patroni1 | 10.0.1.18 | Replica | running | 21 | 0 |
| patroni2 | 10.0.1.22 | Leader | running | 21 | |
| patroni3 | 10.0.1.24 | Replica | running | 21 | 0 |
+----------+-----------+---------+---------+----+-----------+
Теперь нам нужно открыть новый терминал и «убить» лидера:
docker service ls | grep patroni
docker service scale $patroni2-id=0
Через какое-то время в окне со скриптом мы увидим сообщения об ошибке:
// давайте запомним время последней удачной записи
11/18/2020 22:33:06 +00:00
Error
Error
Error
Если мы проверим статус кластера, то можем заметить некоторую задержку — он всё ещё показывает patroni2 в качестве лидера. Но спустя n секунд он все-таки перестроится и, пройдя короткую стадию по выбору лидера, придет в такое состояние:
+ Cluster: patroni (6893104757524385823) --+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+----------+-----------+---------+---------+----+-----------+
| patroni1 | 10.0.1.18 | Replica | running | 21 | 0 |
| patroni3 | 10.0.1.24 | Leader | running | 21 | |
+----------+-----------+---------+---------+----+-----------+
Если же вернемся к терминалу со скриптом, то увидим, что соединение наконец-то восстановлено и запись возобновилась:
Error
Error
Error
11/18/2020 22:33:48 +00:00
11/18/2020 22:33:49 +00:00
11/18/2020 22:33:50 +00:00
11/18/2020 22:33:51 +00:00
Теперь проверим, как там поживает сама база данных и всё ли с ней в порядке после падения лидера:
docker run --rm -ti --network=patroni_patroni postgres:11 /bin/bash
psql --host haproxy --port 5000 -U approle -d postgres
postgres=> c patronitestdb
You are now connected to database "patronitestdb" as user "approle".
// Я установил время чуть раньше, чем произошла авария
patronitestdb=> select * from records where time > '22:33:04' limit 15;
time
-----------------
22:33:04.171641
22:33:05.205022
22:33:06.231735
// как мы видим в моем случае Patroni понадобилось 42 секунды
// чтобы восстановить соединение
22:33:48.345111
22:33:49.36756
22:33:50.374771
22:33:51.383118
22:33:52.391474
22:33:53.399774
22:33:54.408107
22:33:55.416225
22:33:56.424595
22:33:57.432954
22:33:58.441262
22:33:59.449541
Из этого небольшого эксперимента мы можем заключить, что Patroni справился со своей задачей. После того, как лидера упал, произошли перевыборы, и мы смогли подсоединиться к базе. Все предыдущие данные также оказались в целости. Возможно, мы могли бы пожелать, чтобы восстановление произошло быстрее, чем за 42 секунды, но, в конце концов, это не так критично.
Полагаю, на этом можно считать туториал завершенным. Надеюсь, он помог вам разобраться с основными моментами в настройке и работе с Patroni и в целом был полезен. Спасибо за внимание и да хранит Patroni ваши данные, отстреливаясь от багов и героически поднимаясь после падений!
За помощь в настройке этого решения отдельное спасибо коллеге Андрею Юрченко. Без этого отзывчивого парня мои Patroni застряли бы в магазине/стволе и не убили бы ни одного врага.
Все файлы использованные во второй части здесь.
Центр управления связью общего пользования (ЦМУ ССОП) Роскомнадзора рекомендовал компаниям из реестра провайдеров ограничить доступ поисковых ботов к информации на российских сайтах.…
Apple возобновила переговоры с OpenAI о возможности внедрения ИИ-технологий в iOS 18, на основе данной операционной системы будут работать новые…
Конкурсный управляющий российской «дочки» Google подготовил 23 иска к участникам рекламного рынка. Общая сумма исков составляет 16 млрд рублей –…
Google завершил обновление основного алгоритма March 2024 Core Update. Раскатка обновлений была завершена 19 апреля, но сообщил об этом поисковик…
У частных продавцов на Авито появилась возможность составлять текст объявлений с помощью нейросети. Новый функционал доступен в категории «Обувь, одежда,…
24 апреля 2024 года в Москве состоялась церемония вручения наград международного конкурса Workspace Digital Awards. В этом году участниками стали…