Русский
Русский
English
Статистика
Реклама

Cluster

Безотказный Zabbix миграция с асинхронной на групповую репликацию

16.05.2021 22:09:51 | Автор: admin

Введение

Zabbix поддерживает несколько баз данных, но под рассмотрение попали только MySQL и PostgreSQL, как наиболее подходящие под мою установку. PostgreSQL с его repomgr и pgbouncer или каким-нибудь stolon с одной стороны и MySQL Group Replication с другой. Из-за использования MySQL в текущей конфигурации и тяге к стандартной комплектации, выбор пал на второй вариант.

Так что же такое MySQL Group Replication. Как видно из названия, это группа серверов, хранящая одинаковый набор данных. Максимальное количество узлов в группе ограничивается 9-ю. Может работать в режиме single-primary или multi-primary. Но самое интересное всё работает автоматически, будь то выборы нового ведущего сервера, определение поломанного узла, Split-brain или восстановление БД. Поставляется данный функционал в качестве плагинов group_replication и mysql_clone, связь происходит по Group Communication System протоколу в основе которого лежит алгоритм Паксос. Поддерживается данный тип репликации с версий 5.7.17 и 8.0.1.

Моя текущая установка работает на Zabbix 5.0 LTS и MySQL 5.7, миграцию будем проводить с повышением версии MySQL на 8.0, так интереснее ).

Мониторинг репликации

Да да. Это как TDD, только в администрировании, сначала нужно подготовить мониторинг, чтобы новый кластер сразу попал на радары нашей системы мониторинга и не одна проблема не ускользнула от её зоркого взгляда. Так как у вас еще нет групповой репликации (ГР), то вывод команд указанных ниже будет пустым, поэтому я привожу пример выводов с работающего кластера.

Основным источником информации о статусе узлов служит команда:

SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+--------------+-------------+--------------+-------------+----------------+| CHANNEL_NAME              | MEMBER_ID                           | MEMBER_HOST  | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |+---------------------------+--------------------------------------+--------------+-------------+--------------+-------------+----------------+| group_replication_applier | 500049c2-99b7-11e9-8d36-e4434b5f9d0c | example1.com |      3306   | ONLINE       | SECONDARY   | 8.0.13         || group_replication_applier | 50024be2-9889-11eb-83da-e4434ba03de0 | example2.com |      3306   | ONLINE       | PRIMARY     | 8.0.13         || group_replication_applier | 500b2035-986e-11eb-a9f8-564d00018ad1 | example3.com |      3306   | ONLINE       | SECONDARY   | 8.0.13         |+---------------------------+--------------------------------------+--------------+-------------+--------------+-------------+----------------+

Значение колонки MEMBER_STATE может быть разное. Статусы можно посмотреть на странице официальной документации https://dev.mysql.com/doc/refman/8.0/en/group-replication-server-states.html. Если сервер к примеру корректно перезагружен или выключен, он исчезнет из этой таблицы, поэтому желательно знать общее количество узлов в вашей схеме и следить за их количеством.

Дополнительно нужно следить за производительностью каждого узла:

SELECT * FROM performance_schema.replication_group_member_stats\G
*************************** 1. row ***************************                              CHANNEL_NAME: group_replication_applier                                   VIEW_ID: 16178860996821458:41                                 MEMBER_ID: 500049c2-99b7-11e9-8d36-e4434b5f9d0c               COUNT_TRANSACTIONS_IN_QUEUE: 0                COUNT_TRANSACTIONS_CHECKED: 75715997                  COUNT_CONFLICTS_DETECTED: 0        COUNT_TRANSACTIONS_ROWS_VALIDATING: 1957048        TRANSACTIONS_COMMITTED_ALL_MEMBERS: 500049c2-99b7-11e9-8d36-e4434b5f9d0c:1-1821470279,500293cf-594c-11ea-aafd-e4434ba03de0:1-622868371,5000d25c-059e-11e8-822b-564d00018ad1:1-140221041,c9aae4fb-97a6-11eb-89d1-e4434b5f9d0c:1-125382195            LAST_CONFLICT_FREE_TRANSACTION: c9aae4fb-97a6-11eb-89d1-e4434b5f9d0c:125471159COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE: 0         COUNT_TRANSACTIONS_REMOTE_APPLIED: 5664         COUNT_TRANSACTIONS_LOCAL_PROPOSED: 75710337         COUNT_TRANSACTIONS_LOCAL_ROLLBACK: 0*************************** 2. row ***************************                              CHANNEL_NAME: group_replication_applier                                   VIEW_ID: 16178860996821458:41                                 MEMBER_ID: 50024be2-9889-11eb-83da-e4434ba03de0               COUNT_TRANSACTIONS_IN_QUEUE: 0                COUNT_TRANSACTIONS_CHECKED: 75720452                  COUNT_CONFLICTS_DETECTED: 0        COUNT_TRANSACTIONS_ROWS_VALIDATING: 1955202        TRANSACTIONS_COMMITTED_ALL_MEMBERS: 500049c2-99b7-11e9-8d36-e4434b5f9d0c:1-1821470279,500293cf-594c-11ea-aafd-e4434ba03de0:1-622868371,5000d25c-059e-11e8-822b-564d00018ad1:1-140221041,c9aae4fb-97a6-11eb-89d1-e4434b5f9d0c:1-125377993            LAST_CONFLICT_FREE_TRANSACTION: c9aae4fb-97a6-11eb-89d1-e4434b5f9d0c:125470919COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE: 0         COUNT_TRANSACTIONS_REMOTE_APPLIED: 75711354         COUNT_TRANSACTIONS_LOCAL_PROPOSED: 9105         COUNT_TRANSACTIONS_LOCAL_ROLLBACK: 0*************************** 3. row ***************************                              CHANNEL_NAME: group_replication_applier                                   VIEW_ID: 16178860996821458:41                                 MEMBER_ID: 500b2035-986e-11eb-a9f8-564d00018ad1               COUNT_TRANSACTIONS_IN_QUEUE: 38727                COUNT_TRANSACTIONS_CHECKED: 49955241                  COUNT_CONFLICTS_DETECTED: 0        COUNT_TRANSACTIONS_ROWS_VALIDATING: 1250063        TRANSACTIONS_COMMITTED_ALL_MEMBERS: 500049c2-99b7-11e9-8d36-e4434b5f9d0c:1-1821470279,500293cf-594c-11ea-aafd-e4434ba03de0:1-622868371,5000d25c-059e-11e8-822b-564d00018ad1:1-140221041,c9aae4fb-97a6-11eb-89d1-e4434b5f9d0c:1-125382195            LAST_CONFLICT_FREE_TRANSACTION: c9aae4fb-97a6-11eb-89d1-e4434b5f9d0c:125430975COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE: 47096         COUNT_TRANSACTIONS_REMOTE_APPLIED: 49908155         COUNT_TRANSACTIONS_LOCAL_PROPOSED: 0         COUNT_TRANSACTIONS_LOCAL_ROLLBACK: 03 rows in set (0.00 sec)

Тут нас интересуют в первую очередь COUNT_TRANSACTIONS_IN_QUEUE, похож на Seconds_Behind_Master в асинхронной репликации. Как видно на третьем сервере количество транзакций в очереди слишком большое, а это повод начать разбираться что же здесь не так.

Резервное копирование

Я надеюсь, у вас делается регулярное резервное копирование базы данных (БД) и конечно на специально выделенный сервер. Так же желательно убедиться перед миграцией, что на всех узлах с БД есть запас по месту, превышающий в два с половиной раза размер каталога с базой. Это нам понадобится для создания локальной резервной копии, на случай если что-то пойдёт не так, и позволит нам вернуть всё как было. Если у вас файловая система с поддержкой моментальных снимков, не в чём себе не отказывайте, но свободного места должно быть много, так как в самые тяжёлые таблицы будет добавлен дополнительный столбец, а это повлечёт за собой их пересоздание.

План миграции

В идеальном варианте, нужно провести тестовую миграцию на каком-нибудь тестовом кластере. Но если всё делать правильно и не пренебрегать созданием резервных копий, то всегда можно будет откатиться назад. План действий на случай непредвиденных обстоятельств, тоже желательно написать.

Если всё идёт гладко:

  1. Пропиливаем необходимые дырки в файрволле (для общения узлов между собой нужно открыть TCP 33061 порт). Выписываем необходимые сертификаты;

  2. Собираем репозиторий с MySQL 8.0 (FreeBSD, Poudriere - у каждого свои причуды);

  3. В системе мониторинга переводим серверы в режим обслуживания, уведомляем всех пользователей Zabbix (чтобы никто не удивлялся);

  4. Выключаем репликацию на всех узлах, которые были Secondary для этого сервера (мы же не хотим, чтобы какие-нибудь наши изменения улетели на действующие сервера). Выключаем репликацию на текущем узле;

  5. Выключаем MySQL 5.7 сервер на первом подопытном узле;

  6. Делаем резервное копирование БД с сохранением атрибутов файлов (сохраняем рядом, чтобы быстро восстановить базу);

  7. Обновляем пакеты на новую версию с новыми зависимостями;

  8. Запускаем MySQL 8.0 сервер (mysql_upgrade не нужен, с 8 версии это действо происходит автоматически);

  9. Добавляем первичные ключи в таблицы, в которых их нет (требования групповой репликации, иначе операции добавления, удаления и т. д. работать не будут). Операция длительная, зависит от размера таблиц и производительности сервера;

  10. Включаем репликацию на этом сервере, отреплецируем все изменения, выключаем репликацию (подтянем изменённые данные с действующего сервера, накопившееся за время наших манипуляций);

  11. Сбрасываем все упоминания об асинхронной репликации на данном сервере (команда RESET SLAVE ALL;);

  12. Настраиваем групповую репликацию и проверяем всё ли работает;

  13. Переключаем Zabbix сервер и Zabbix фронтенд на БД с ГР;

  14. Настраиваем групповую репликацию на других узлах (делаем шаги с 4 по 8, только с удалением каталога с БД перед 8 шагом, т. к. нам нужна чистая установка);

  15. Перенастраиваем мониторинг;

  16. Переделываем Ansible Playbook'и и конфигурационные файлы;

  17. Меняем скрипты и задачи по переключению мастера;

  18. Настраиваем HADNS;

  19. Обновляем документацию;

На непредвиденный случай:

  1. Останавливаем MySQL сервер;

  2. Возвращаем предыдущие версии пакетов;

  3. Удаляем каталог с БД и восстанавливаем из локальной резервной копии, запускаем MySQL сервер;

  4. Настраиваем асинхронную репликацию;

Откатываемся бесконечное количество раз, пока всё не пройдёт гладко.

Далее подробно рассмотрим 9, 12 и 14 шаги.

Шаг 9: Добавление первичных ключей

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

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

SELECT tables.table_schema , tables.table_name , tables.engine FROM information_schema.tables LEFT JOIN ( SELECT table_schema , table_name FROM information_schema.statistics GROUP BY table_schema, table_name, index_name HAVING SUM( case when non_unique = 0 and nullable != 'YES' then 1 else 0 end ) = count(*) ) puksON tables.table_schema = puks.table_schema and tables.table_name = puks.table_nameWHERE puks.table_name is null AND tables.table_type = 'BASE TABLE' AND Engine="InnoDB";

Какой тип ключей и для каких колонок его создать я подсмотрел на форуме Zabbix, вот тут. Чтобы не поломалось обновление Zabbix, в таблице dbversion добавляем первичный ключ по существующему столбцу. Ниже необходимые запросы.

ALTER TABLE history ADD COLUMN `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT;ALTER TABLE history_uint ADD COLUMN `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT;ALTER TABLE history_text ADD COLUMN `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT;ALTER TABLE history_str ADD COLUMN `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT;ALTER TABLE history_log ADD COLUMN `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT;ALTER TABLE dbversion ADD PRIMARY KEY (mandatory);

Напоминаю действие занимает большое количество времени на больших таблицах. Надеюсь, когда-нибудь это внесут в схему БД для Zabbix.

Шаг 12: Запуск групповой репликации

Все настройки, относящиеся к групповой репликации, я собрал в одном месте в конфигурационном файле.

server-id=[номер сервера в кластере по порядку]gtid_mode=ONenforce_gtid_consistency=ONlog_bin=binloglog_slave_updates=ONbinlog_format=ROWmaster_info_repository=TABLErelay_log_info_repository=TABLEtransaction_write_set_extraction=XXHASH64disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"plugin_load_add='group_replication.so;mysql_clone.so'ssl-ca=/usr/local/etc/ssl/mysql/ca.crtssl-cert=/usr/local/etc/ssl/mysql/server.crtssl-key=/usr/local/etc/ssl/mysql/server.keygroup_replication_ssl_mode=VERIFY_IDENTITYgroup_replication_group_name="[одинаковое на всех узлах, генерируем один раз командой SELECT UUID();]"group_replication_start_on_boot=off # включаем после добавления всех узлов в группуgroup_replication_local_address="[полное имя текущего сервера].com:33061"group_replication_group_seeds="example1.com:33061,example2.com:33061,example3.com:33061"group_replication_ip_allowlist="2.2.2.2/32,3.3.3.3/32,4.4.4.4/32"group_replication_member_weight=50group_replication_recovery_use_ssl=ONgroup_replication_recovery_ssl_verify_server_cert=ONgroup_replication_recovery_ssl_ca=/usr/local/etc/ssl/mysql/ca.crtgroup_replication_recovery_ssl_cert=/usr/local/etc/ssl/mysql/server.crtgroup_replication_recovery_ssl_key=/usr/local/etc/ssl/mysql/server.key

Добавляем это всё в my.cnf, дубликаты переменных, которые встречаются в файле до этого, необходимо удалить. Теперь можно перезапустить сервер, либо проверить значение переменных как написано ниже и поменять их вручную. Обратите внимание на переменную group_replication_start_on_boot, она выключена, поэтому при рестарте репликация не запустится.

Проверяем значение переменных командой SHOW VARIABLES LIKE 'binlog_format'; меняем с помощью команды SET GLOBAL binlog_format = RAW; это относится к переменным в верхней части конфига, остальные настройки подтянутся при активации групповой репликации.

Переменные group_replication_ssl_mode и group_replication_recovery_ssl_verify_server_cert установлены в максимально безопасный режим с проверкой сертификата сервера, так что при выписывании сертификата укажите в Subject Alternative Name (SAN) полные имена всех улов кластера, которые есть в group_replication_group_seeds.

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

Создаём пользователя для работы репликации:

SET SQL_LOG_BIN=0;CREATE USER 'replication'@'%' IDENTIFIED BY '[придумайте пароль]' REQUIRE SSL;GRANT replication slave ON *.* TO 'replication'@'%';GRANT BACKUP_ADMIN ON *.* TO 'replication'@'%';FLUSH PRIVILEGES;SET SQL_LOG_BIN=1;

Устанавливаем плагины и проверяем статус:

INSTALL PLUGIN group_replication SONAME 'group_replication.so';INSTALL PLUGIN clone SONAME 'mysql_clone.so';SHOW PLUGINS;

Настраиваем пользователя, используемого при восстановлении БД с работающего сервера:

CHANGE REPLICATION SOURCE TO SOURCE_USER='replication', SOURCE_PASSWORD='[придуманный пароль]' \\  FOR CHANNEL 'group_replication_recovery';

Первый запуск группы. Переменную group_replication_bootstrap_group включаем только на первом сервере, на остальных, просто запускаем групповую репликацию:

SET GLOBAL group_replication_bootstrap_group=ON; # выполняем только на первом сервереSTART GROUP_REPLICATION;SET GLOBAL group_replication_bootstrap_group=OFF; # выполняем только на первом сервере

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

mysql> SELECT * FROM performance_schema.replication_group_members;+---------------------------+--------------------------------------+-------------+-------------+---------------+| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE  |+---------------------------+--------------------------------------+-------------+-------------+---------------+| group_replication_applier | ce9be252-2b71-11e6-b8f4-00212844f856 |example1.com |       3306  | ONLINE        |+---------------------------+--------------------------------------+-------------+-------------+---------------+

Проверьте дополнительно логи MySQL сервера на содержание в них ошибок.

Шаг 14: Добавление узла в группу

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

Запускаем чистую базу данных и проделываем всё тоже самое как в 12-ом шаге, с добавлением специфичных для этого сервера настроек (server-id, group_replication_local_address). Так как группа уже запущена, использовать переменную group_replication_bootstrap_group не нужно.

На этом этапе и будет использоваться так называемый Distributed Recovery механизм в который входит mysql_clone плагин. При подключении к узлу донору в первую очередь он попытается использовать бинарный лог, если информации в нём будет недостаточно, он полностью скопирует базу, включая созданного для репликации пользователя.

Проверьте статус группы и что сервер добавился в неё, а так же его производительность, успевает ли узел применять все транзакции.

После добавления оставшихся серверов, поменяйте в конфиге my.cnf значения переменной group_replication_start_on_boot с off на on и перезагрузите MySQL сервер на любом ведомом сервере и проверьте что он остался в группе.

Полезные команды

SELECT * FROM performance_schema.replication_group_members; - показывает статус всех узлов в группе.

SELECT * FROM performance_schema.replication_group_member_stats\G - показывает производительность каждого отдельного узла.

SELECT group_replication_set_as_primary('[uuid узла]'); - переключение ведущего узла.

Безотказный Zabbix сервер

А что же с Zabbix сервером спросите вы, если дочитаете до этого момента, а всё просто. Я сделал так чтобы он постоянно следовал за ведущим сервером групповой репликации. В кроне на каждом сервере запускается скрипт, который проверяет что узел сейчас Primary в ГП, если да, то запускает Zabbix сервер, если нет, то останавливает его. Дальше включается в работу HADNS, он проверяет на каком сервере запущен Zabbix и отдает нужный IP адрес для DNS записи.

Заключение

Возможно, сделано не всё так элегантно как хотелось бы. Вы наверно захотите использовать mysql-shell, mysqlrouter и преобразовать Group Replication в InnoDB Cluster, а может добавить HAProxy, особенно это полезно, когда разворачиваешь Zabbix с нуля. Надеюсь, этот рассказ послужит неплохой отправной точкой и будет полезен. Спасибо за внимание!

Дополнительная литература

https://dev.mysql.com/doc/refman/8.0/en/group-replication.html

https://blog.zabbix.com/scaling-zabbix-with-mysql-innodb-cluster/8472/

https://en.wikipedia.org/wiki/Paxos_(computer_science)

Подробнее..

Из песочницы Turing Pi кластерная плата для self-hosted приложений и сервисов

16.10.2020 18:23:47 | Автор: admin
image

Turing Pi это решение для self-hosted приложений построенное по принципу rack стоек в дата центре, только на компактной материнской плате. Решение ориентировано на построение локальной инфраструктуры для локальной разработки и хостинга приложений и сервисов. Вообщем это как AWS EC2 только для edge.

Мы небольшая команда разработчиков решили создать решение для построения bare-metal кластеров в edge и назвали проект Turing Pi. Продукт начался на коленке, однако сейчас для проверок своих концепций его заказали отделы разработок в Red Hat, Rancher (SUSE), Toyota Connected, Sony, Electrolux, Facebook, и набирается аудитория разработчиков под 10К.

Но все началось с открытия.

Открытие продукта


Когда-то для себя сделал открытие что мы ничего не придумываем. Все вещи существующие в этом мире, мы открываем. Мы открываем что какие то части можно комбинировать вместе, получая новые свойства продуктов или открываем законы и потом снова комбинируем. Придумать ничего нельзя, но можно открыть во время исследования. На мой взгляд изобретение это результат постоянных наблюдений, эксперименты и поиск + комбинация знаний.

В течении последнего десятилетия я наблюдал как набирает обороты любительское движение homelab (ресурс subreddit), self-hosted (ресурсы subreddit и awesome-selfhosted), одноплатные компьютеры, как растет интерес к запуску контейнеров на одноплатных компьютерах типа Raspberry Pi, растет движение собирающее кластеры из одноплатных компьютеров. Постепенно туда тянется и идея Kubernetes. Уже появилась его легкая версия k3s ориентированная на Edge/IoT. Большая сеть ресторанов быстрого питания Chick-fil-A одни из первых в мире разместили кластеры Kubernetes на своих кухнях kubectl make me a sandwich.

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

Сегодня наша кластерная плата очень простая и в первую очередь ориентирована на тех кто любит cloud native технологии, изучать новое, экспериментировать.

Строительные блоки (Building Blocks)


Итак, что же из себя представляет решение, в чем суть. Суть в том чтобы дать конструктор, строительные блоки (Building Blocks) из которых можно собрать edge инфраструктуру дешевле чем на классических серверах, мобильную без громоздких металлических ящиков, не требовательную к особым условия эксплуатации типа серверных, энергоэффективную, стандартизированную в плане модулей и с возможностью быстрого масштабирования на десятки и сотни вычислительных узлов (процессоры).
Кластерная плата (Clusterboard)

Mini ITX плата объединяет несколько вычислительных модулей с помощью сети на плате, дает периферийные интерфейсы и контроль над модулями
Вычислительный модуль (Compute Module)

Плата в SO-DIMM форм-факторе содержащая процессор и оперативную память, опционально флеш-память для хранения операционной системы
image
Одна из проектируемых конфигураций версии Turing V2
image
Комбинируя кластерную плату и вычислительные модули легко создать инфраструктуру к примеру на 20+ процессоров (примеры ниже), бесшумную и с низким энергопотреблением. Сама кластерная плата увеличивает эффективность используемого объема. Примеры кластеров для сравнения
SBC based*
image
24 CPUs
Автор Afkham Azeez
Turing Pi based
image
21 CPUs
Автор th3st0rmtr00p3r
* SBC Single Board Computer

Со-основателя Rancher Labs и автора k3s такой подход не оставил равнодушным

image

Далее подробнее про строительные блоки.

Вычислительный Модуль (Compute Module)


Для проверки концепции мы выбрали Raspberry Pi Compute Module это идеальная конфигурация для старта. Коммьюнити RPi активно, нет проблем с софтом, сам модуль в формате SO-DIMM (6 x 3 см), доступен по цене, содержит на борту 4-core CPU, 1 GB RAM и опционально флеш-память 8, 16 или 32 GB для ОС и других системных нужд. Подобный форм-фактор часто используют в индустриальных IoT решениях.

Raspberry Pi 1/3/3+ Compute Module

image

Но у модели CM3 есть и серьезные ограничения max. 1 GB RAM и Ethernet через USB HUB с максимальной скоростью до 100 Mbps. Поэтому вторая версия Turing будет поддерживать Raspberry Pi 4 и до 8 GB оперативной памяти на модуль. Ниже пример Nvidia Jetson модулей для Accelerated Computing задач. Возможно, будет их поддержка во второй версии, если мы решим некоторые технические задачи, тогда можно миксовать модули.

Nvidia Jetson Compute Modules

image

В модулях кроется самая главная возможность, которая на первый взгляд не очевидна. Возможность создания других модулей в подобном форм-факторе с разным набором CPU, RAM и eMMC, под разные типы задач от general-computing до machine learning интенсивных вычислений. Это очень похоже на инстансы AWS EC2, но только для edge. При этом кластерная плата остается неизменной или с минорными изменениями.

Кластерная плата (Clusterboard)


Еще ее можно назвать материнской платой или базовой платой, достаточно новое направление и производителей подобных решений сегодня не много и все еще довольно на начальном уровне, среди них Pine64, MiniNodes, Clover Pi, Bitscope Blade, PicoCluster (SBC кластеры). Кластерная плата объединяет модули сетью, обеспечивает питанием и дает шину управления кластером (Cluster Management Bus), по крайней мере мы эту шину решили добавить и она вроде прижилась.
Front
image
Back
image
Backplane Bandwidth 12 Gbps SD слоты для модулей без eMMC, например, можно держать пару модулей с SD для быстрого доступа к данным ноды
В основе кластерной платы лежит микросхема switch для обеспечения сетевого соединения модулей и выхода во внешнюю сеть. В текущей версии мы используем unmanaged switch т.к. не было времени проводить полноценный R&D, но ко второй версии мы выбрали неплохой managed switch. Возможна конфигурация в режиме мастер нода как роутер для раздачи сети на плату, это если требуется более высокий уровень безопасности и изоляции воркеров от доступа извне, в таком случае на мастер ноде необходимо также держать DHCP сервер.

Применение


Текущая версия является экспериментальной и мы позиционируем ее как решение начального уровня изучить что такое кластеры, поработать с софтом или как проверка гипотез в организациях, поиск новых идей, вообщем такая Lite версия.

Чтобы начать мы рекомендуем серию от замечательного Джеффа Гирлинга, который прочитал о нас на Y Combinator, он автор бестселлера Ansible for DevOps. Он так вдохновился что сделал 6-ти серийный обзор начиная от идеи кластеризации в целом до практических примеров работы с платой, изучения Kubernetes и какой софт можно устанавливать

Серия про установку k3s на кластер


А вообще рекомендую смотреть с первой, там в целом про кластеризацию и Kubernetes доступным языком. И немного картинок от коммьюнити

image

Что дальше?


Во-первых, я очень надеюсь что было интересно. Это абсолютно авторский контент, попытка заглянуть за горизонт. Во-вторых, планирую написать вторую часть про проектирование Turing V2. Как происходил поиск продукта, логические суждения, поиск основных свойств которые важны. Статья будет содержать эскизы продукта от самых ранних до финальных. Вторая статья будет особенно интересна продуктологам, кто отвечает за продукты и их развитие в компаниях.

И скорее всего там будет реально long read.
Подробнее..

Мини ITX кластер Turing Pi 2 c 32 GB RAM

21.10.2020 12:13:27 | Автор: admin
image

Приветствую сообщество Хабра! Недавно я писал про нашу кластерную плату первой версии [V1]. И сегодня хочу рассказать как мы работали над версией Turing V2 с 32 GB оперативной памяти.

Мы увлекаемся мини серверами которые можно использовать как для локальной разработки так и для хостинга локально. В отличии от настольных компьютеров или лэптопов наши сервера созданы для работы 24/7 их быстро можно соединять в федерацию, например было 4 процессора в кластере, а через 5 минут стало 16 процессоров (без дополнительного сетевого оборудования) и все это в компактном форм-факторе, безшумно и энергоэффективно.

В основе архитектуры наших серверов кластерный принцип построения т.е. мы делаем кластерные платы которые с помощью ethernet сети на плате соединяют несколько вычислительных модулей (процессоров). Для упрощения мы пока не делаем свои вычислительные модули, а используем Raspberry Pi Compute Modules и мы очень надеялись на новый модуль CM4. Но, все пошло вразрез планам с их новым форм-фактором и думаю многие разочарованы.

Под катом как мы шли от V1 к V2 и как нам пришлось выкручиваться с новым форм-фактором Raspberry Pi CM4.

Итак, после создания кластера на 7 нод, вопросы что дальше? Как увеличить ценность продукта? 8, 10 или 16 нод? Какие производители модулей? Думая над продуктом в комплексе мы поняли что основное здесь не количество нод или кто производитель, а сама суть кластеров как строительного блока. Надо искать минимальный строительный блок, которые

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

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

Третье, минимальные кластерные блоки должны быть достаточно компактны, мобильны, энергоэффективны, cost-effective и не требовательны к условиям эксплуатации. Это одно из ключевых отличий от server racks и всего что с ними связано.

Мы начали с определения количества нод.

Количество нод


Простыми логическими суждениями мы поняли что 4 ноды лучший вариант для минимального кластерного блока. 1 нода это не кластер, 2 ноды мало (1 мастер 1 воркер, нет возможности масштабирования в рамках блока, особенно для гетерогенных вариантов), 3 ноды выглядит ок, но не кратно степеням 2 и лимитировано масштабирование в рамках блока, 6 нод выходит по цене почти как 7 нод (из нашего опыта это уже большая себестоимость), 8 много, не умещается в mini ITX форм факторе и еще более дорогое решение для PoC.

Четыре ноды на блок считаем золотой серединой:

  • меньше материалов на кластерную плату, следовательно дешевле производство
  • кратно 4, всего 4 блока дают 16 физических процессоров
  • стабильная схема 1 мастер и 3 воркера
  • больше гетерогенных вариаций, general-compute + accelerated-compute модули
  • мини ITX форм-фактор с SSD дисками и платами расширения

Вычислительные модули


Вторая версия базируется на CM4 мы думали что ее выпустят в SODIMM форм-факторе. Но
Мы приняли решение сделать дочернюю плату SODIMM и собирать CM4 сразу в модули чтобы пользователи не думали о CM4.

image
Turing Pi Compute Module с поддержкой Raspberry Pi CM4

Вообще в поисках модулей был открыт целый рынок вычислительных модулей от небольших модулей на 128 MB RAM до 8 GB RAM. Впереди модули на 16 GB RAM и более. Для edge хостинга приложений базирующихся на cloud native технологиях 1 GB RAM уже мало, а недавнее появление модулей на 2, 4 и даже 8 GB RAM дает хорошее пространство для роста. Даже рассматривали и варианты с FPGA модулями для приложений машинного обучения, но их поддержка отложена потому что не развита софтверная экосистема. Во время изучения рынка модулей пришли к идеи создания универсального интерфейса для модулей и в V2 начинаем унификацию интерфейса вычислительных модулей. Это позволит владельцам версии V2 подключать модули других производителей и смешивать их под конкретные задачи.

V2 поддерживает всю линейку Raspberry Pi 4 Compute Module (CM4), включая Lite версии и модули на 8 GB RAM

image

Периферия


После определения вендора модулей и количества нод мы подошли к PCI шине, на которой находится периферия. PCI шина является стандартом для периферийных устройств и она есть почти во всех вычислительных модулях. У нас несколько нод и в идеале чтобы каждая нода могла шерить PCI устройства в режиме конкурентных запросов. Например, если это диск подключенный к шине, то он доступен всем нодам. Мы начали искать PCI свичи с возможностью поддержки мультихост и обнаружили что ни один из них не подходит под наши требования. Все эти решения в основном ограничивались 1 хостом или мульти хостами, но без режима конкурентных запросов к endpoints. Вторая проблема высокая стоимость от $50 и более за чип. В V2 мы решили отложить эксперименты с PCI свичами (мы вернемся к ним позже по мере развития) и пошли по пути назначения роли для каждой ноды: первые две ноды exposed mini PCI express port per node, третья нода exposed 2-ports 6 Gbps SATA контроллер. Для получения доступа к дискам с других нод можно использовать сетевую файловую систему в рамках кластера. Why not?

Sneakpeek


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

imageimageimage

В результате мы пришли к кластерному блоку с 4-мя нодами 260-pin, 2-мя mini PCIe (Gen 2) портами, 2-мя SATA (Gen 3) портами. На плате установлен Layer-2 Managed Switch с поддержкой VLAN. С первой ноды выведен mini PCIe порт, в который можно установить сетевую карту и получить еще один Ethernet порт или 5G модем и сделать из первой ноды роутер для сети на кластере и Ethernet портов.

image

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

Применение


Edge infrastructure for self-hosted applications & services


Мы проектировали V2 с целью использовать ее как минимальный строительный блок для consumer/commercial-grade edge инфраструктуры. С V2 дешево начать проверку концепции и масштабироваться по мере роста, постепенно перенося приложения, которые экономически и практически целесообразнее хостить в edge. Кластерные блоки можно соединять вместе, строя кластеры большего размера. Это можно делать постепенно без особых рисков для устоявшихся
процессов. Уже сегодня имеется огромное количество приложений для бизнеса, которые можно хостить локально.

ARM Workstation


С оперативной памятью до 32 GB RAM на кластер, первую ноду можно использовать для desktop версии OS (например Ubuntu Desktop 20.04 LTS) и оставшиеся 3 ноды для задач компиляции, тестирования и отладки, разработки cloud native решений для ARM кластеров. Как узел для CI/CD на ARM периферийную инфраструктуру в проде.

Turing V2 кластер с CM4 модулями архитектурно почти идентичен (разница в минорных версиях ARMv8) кластеру на базе инстансов AWS Graviton. В процессоре CM4 модулей используется архитектура ARMv8 вы можете собирать образы и приложения для AWS инстансов Graviton 1 и 2, которые как известно намного дешевле инстансов x86.
Подробнее..

Заряжай Patroni. Тестируем Patroni Zookeeper кластер (Часть первая)

24.12.2020 20:04:36 | Автор: admin
Кадр из фильма Рембо IVКадр из фильма Рембо IV

Вступление

Если выработаете сcrucial data, торано или поздно задумаетесь отом, что неплохобы поднять кластер отказоустойчивости. Даже если основной сервер сбазой улетит вглухой нокаут, show must goon, нетакли? При этом мыподразумеваем две вещи:

  • база данных совсей ееструктурой нам по-прежнему доступна;

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

Patroni лишь одно изрешений проблемы. Донего япопробовал несколько других сервисов инаGithub одного изних (небуду показывать курсором) досих пор висит без ответа открытый мной баг репорт, хотя прошло уже несколько месяцев. Удругого была несамая полная документация. Какие-то мне показались недостаточно надежными.

ПоPatroniже действительно много инфы, ионкачественно поддерживается разработчиками. Также ониспользует DCS для синхронизации нод, что должно предотвращать ситуации соsplit brain.

Изминусов совершенно точно нужно назватьто, что это неout ofthe box solution. Как сказано вдоке:

Patroni is a template for you to create your own customized, high-availability solution using Python...

Иключевое здесь слово template. Тоесть все придется собирать самому. Новкаком-то смысле это плюс покрайней мере, мыдетально будем знать, что именно идёт впрод.

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

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

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

Что, ещё один туториал о Patroni?

Зачем читать именно этот туториал?

Есть уже немало туториалов, которые рассказывают, как поднять кластер Patroni. Этот затрагивает вопросы деплоя в среде docker swarm и использования Zookeeper в качестве DCS.

Почему Zookeeper?

Ясчитаю, это один измоментов, которые стоит рассмотреть всерьез, прежде чем выбрать конечный сетап для продакшена. Дело втом, что Patroni использует сторонние сервисы чтобы установить иобслуживать коммуникацию между своими нодами. Ихобщее название DCS (Dynamic Configuration Storage).

Если выуже смотрели какие-то изтуториалов оPatroni, то, должно быть, заметили, что самый частый кейс это когда вкачестве DCS используют Etcd кластер.

Интересный момент вработе Etcd кластера заключается втом, что:

Since etcd writes data to disk, its performance strongly depends on disk performance. For this reason, SSD is highly recommended.

(из документации Etcd)

Словом, если увас нет поSSD диску накаждой машине, где будут работать ноды Etcd, товывзоне опасности. Конечно, пока нагрузка небольшая, тоничего критичного происходить небудет, ноесли это рабочий, нагруженный прод, тоочень возможно (идаже вероятно), что выпросто перегрузите Etcd кластер. Аэто приведет кIO ошибкам при доступе кбазе. Звучит скверно? Насамом деле так иесть. Ловить такие ошибки напроде очень неприятно.

Здесь нам напомощь иможет прийти Zookeeper, который ничего непишет надиск ихранит все данные впамяти. Такой вариант оптимален вситуации, когда ненавсех серверах есть SSD, зато RAM хватает.

Почему Docker Swarm?

Уменя небыло выбора, так как одним изключевых требований было, чтобы кластер был развернут вSwarmе. Так что, если это иваш кейс тоже, товывправильном месте!

Для техже, кто открыл пост сжеланием потестировать технологию, выбор Docker Swarmа тоже может быть вполне органичным. Хотябы потой причине, что вам непридется устанавливать инастраивать никаких сторонних сервисов (нузаисключением самого Dockerа, разумеется) или тянуть налокальную машину ворох непонятных зависимостей. Полагаю, недалеко отистины утверждение, что Docker унас увсех итак уже настроен везде где только можно, мывсе знаем, как онработает, так что почемубы неиспользовать его.

Потребуется лишь одна команда, чтобы сделать тюнинг Dockerа, который позволит развернуть налокальной машине кластер Patroni на3ноды без виртуальных машин, Kubernetes или подобных вещей.

Если вынехотите копать всторону каких-то еще инструментов запределами Dockerа ихотите сделать все чисто иаккуратно, тоданный туториал вам более чем подойдет.

А в конце будет небольшой бонус

Вовторой части туториала япокажу различные варианты проверки статуса кластера (целых3), ивконце дам простой скрипт синструкциями для быстрого теста кластера.

Окей, достаточно разговоров. Давайте перейдем кпрактике.

Docker Swarm

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

Яисхожу изпредположения, что увас уже установлен инастроен Docker Engine. Втаком случае, нужно только выполнить следующую команду:

docker swarm init//now check your single-node clusterdocker node lsID                            HOSTNAME       STATUS       AVAILABILITY          a9ej2flnv11ka1hencoc1mer2 *   floitet        Ready          Active       

Одна изважных фич Swarmа заключается втом, что теперь мыможем использовать нетолько обычные Docker контейнеры, ноитак называемые сервисы. Сервисы это посути дела абстракция над контейнерами. Если отталкиваться отаналогии сООП, тосервис это класс, аконтейнер конкретный объект класса. Параметры иправила сервиса задаются при деплое изyml-файла.

Рекомендую запомнить hostname ноды потом мыиспользуем его для указания constraint вконфигурационном файле.

Вцелом, это все приготовления вчасти Docker Swarmа, которые нужно сделать. Здесь никаких проблем быть недолжно. Так что двинемся дальше.

Zookeeper

Прежде чем мыначнем деплой самого Patroni, нам нужно сначала развернуть кластер сDCS (внашем случае, как мыпомним, это Zookeeper). Явзял версию 3.4, иона работает вполне стабильно. Далее идет docker-compose конфиг инекоторые комментарии помоментам, которые, как мне кажется, имеет смысл отдельно упомянуть.

  • docker-compose-zookeeper.yml

docker-compose-zookeeper.yml
version: '3.7'services:  zoo1:    image: zookeeper:3.4    hostname: zoo1    ports:      - 2191:2181    networks:      - patroni    environment:      ZOO_MY_ID: 1      ZOO_SERVERS: server.1=0.0.0.0:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888    deploy:      replicas: 1      placement:        constraints:          - node.hostname == floitet      restart_policy:        condition: any  zoo2:    image: zookeeper:3.4    hostname: zoo2    networks:      - patroni    ports:      - 2192:2181    environment:      ZOO_MY_ID: 2      ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=0.0.0.0:2888:3888 server.3=zoo3:2888:3888    deploy:      replicas: 1      placement:        constraints:          - node.hostname == floitet      restart_policy:        condition: any  zoo3:    image: zookeeper:3.4    hostname: zoo3    networks:      - patroni    ports:      - 2193:2181    environment:      ZOO_MY_ID: 3      ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=0.0.0.0:2888:3888    deploy:      replicas: 1      placement:        constraints:          - node.hostname == floitet      restart_policy:        condition: anynetworks:  patroni:    driver: overlay    attachable: true
Details

Конечноже, важно дать каждой ноде уникальное имя ивнешний порт. Hostname лучше ставить одинаковое сименем сервиса.

zoo1:    image: zookeeper:3.4    hostname: zoo1    ports:      - 2191:2181

Стоит отметить ито, как мыперечисляем hostы встроке ниже: для первого сервиса server.1 будет привязан к0.0.0.0, а, например, для zoo2 это уже будет server.2 соответственно ит.д.

ZOO_SERVERS: server.1=0.0.0.0:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888

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

     placement:        constraints:          - node.hostname == floitet

Ипоследний момент, который мыздесь обсудим, это network. Янамерен деплоить все сервисы Zookeeperа ивсе сервисы Patroni водну сеть сдрайвером overlay, чтобы они были изолированы отдругих сервисов имогли общаться между собой поименам, анепоIP (как это выглядит, будет видно дальше).

networks:  patroni:    driver: overlay// мы должны отметить сеть как attachable  // чтобы потом можно было присоединять к ней остальные сервисы    attachable: true

Итак, можно задеплоить Zookeeper:

sudo docker stack deploy --compose-file docker-compose-zookeeper.yml patroni

Теперь нужно проверить, что все работает. Первое что можно сделать это просто посмотреть список сервисов:

sudo docker service lsgxfj9rs3po7z        patroni_zoo1        replicated          1/1                 zookeeper:3.4         *:2191->2181/tcpibp0mevmiflw        patroni_zoo2        replicated          1/1                 zookeeper:3.4         *:2192->2181/tcpsrucfm8jrt57        patroni_zoo3        replicated          1/1                 zookeeper:3.4         *:2193->2181/tcp

Иследующим шагом можно сделать пинг сервисов спомощью специальной команды mntr:

echo mntr | nc localhost 2191// with the output being smth like thiszk_version3.4.14-4c25d480e66aadd371de8bd2fd8da255ac140bcf, built on 03/06/2019 16:18 GMTzk_avg_latency6zk_max_latency205zk_min_latency0zk_packets_received1745zk_packets_sent1755zk_num_alive_connections3zk_outstanding_requests0zk_server_statefollowerzk_znode_count16zk_watch_count9zk_ephemerals_count4zk_approximate_data_size1370zk_open_file_descriptor_count34zk_max_file_descriptor_count1048576zk_fsync_threshold_exceed_count0

Также можно проверить логи сервиса, если есть желание:

docker service logs $zookeeper-service-id // service-id comes from 'docker service ls' command. // in my case it could be docker service logs gxfj9rs3po7z

Отлично, вот мыиразобрались сZookeeperом. Теперь можно переходить ксамому Patroni.

Patroni

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

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

  • patroni.yml

Это основной конфигурационный файл. Одна изособенностей Patroni, что мыможем задавать параметры для кластера изразных мест иpatroni.yml одно изних. Этот файл мыбудем копировать вкастомный имейдж, так что любые изменения, внесенные внего, требуют ребилда образа.

Явитоге пришел кидее, что буду хранить здесь только тепараметры, которые наверняка почти никогда небуду трогать, поэтому складываю здесь только самые необходимые настройки. Ниже япривожу базовый конфиг. Если захочется, томожно добавить внего каких-то параметров, допустим, для движка Posgtresа (например, max_connections ит.п.). Нодля тестового стенда этого вполне достаточно.

patroni.yml
scope: patroninamespace: /service/bootstrap:    dcs:        ttl: 30        loop_wait: 10        retry_timeout: 10        maximum_lag_on_failover: 1048576        postgresql:            use_pg_rewind: true    postgresql:      use_pg_rewind: true    initdb:    - encoding: UTF8    - data-checksums    pg_hba:    - host replication all all md5    - host all all all md5zookeeper:  hosts:       - zoo1:2181      - zoo2:2181      - zoo3:2181postgresql:    data_dir: /data/patroni    bin_dir: /usr/lib/postgresql/11/bin    pgpass: /tmp/pgpass    parameters:        unix_socket_directories: '.'tags:    nofailover: false    noloadbalance: false    clonefrom: false    nosync: false
Details

Важно указать Patroni путь кбинарным файлам Postgresа. Вмоем случае, так как яиспользую Postgres11, директория выглядит так: /usr/lib/postgresql/11/bin.

Вдиректории, внутри уже созданного контейнера, Patroni будет искать файлы Postgresа. Без этой настройки скорее всего ничего невзлетит (покрайней мере уменя невзлетело). Итакже еще есть data_dir это место вконтейнере, где будут храниться данные. Позже мысделаем mount этой директории кместу налокальном жестком диске, чтобы непотерять все полимеры, если кластер всеже упадет безнадежно. Это добавит нам работы посозданию этих папок локально, но, по-моему, оно того стоит.

postgresql:    data_dir: /data/patroni    bin_dir: /usr/lib/postgresql/11/bin

Также яперечисляю все сервера Zookeeperа вэтом конфиг файле, чтобы потом передать информацию оних утилите patronictl. Стоит отметить, что если неуказать ихвpatroni.yml, томыостанемся витоге снерабочим patronictl. Как вывидите, перечисляя сервера, янепишу никакиеIP, аиспользую ихимена. Это тасамая фича Docker Swarmа окоторой ярассказывал выше.

zookeeper:  hosts:       - zoo1:2181      - zoo2:2181      - zoo3:2181
  • patroni-entrypoint.sh

Изследующего файла подтягивается большая часть настроек вмоей конфигурации. Это небольшой скрипт, который будет выполнен, когда контейнер сервиса окажется создан.

patroni-entrypoint.sh
#!/bin/shreadonly CONTAINER_IP=$(hostname --ip-address)readonly CONTAINER_API_ADDR="${CONTAINER_IP}:${PATRONI_API_CONNECT_PORT}"readonly CONTAINER_POSTGRE_ADDR="${CONTAINER_IP}:5432"export PATRONI_NAME="${PATRONI_NAME:-$(hostname)}"export PATRONI_RESTAPI_CONNECT_ADDRESS="$CONTAINER_API_ADDR"export PATRONI_RESTAPI_LISTEN="$CONTAINER_API_ADDR"export PATRONI_POSTGRESQL_CONNECT_ADDRESS="$CONTAINER_POSTGRE_ADDR"export PATRONI_POSTGRESQL_LISTEN="$CONTAINER_POSTGRE_ADDR"export PATRONI_REPLICATION_USERNAME="$REPLICATION_NAME"export PATRONI_REPLICATION_PASSWORD="$REPLICATION_PASS"export PATRONI_SUPERUSER_USERNAME="$SU_NAME"export PATRONI_SUPERUSER_PASSWORD="$SU_PASS"export PATRONI_approle_PASSWORD="$POSTGRES_APP_ROLE_PASS"export PATRONI_approle_OPTIONS="${PATRONI_admin_OPTIONS:-createdb, createrole}"exec /usr/local/bin/patroni /etc/patroni.yml
Details. Важно!

Насамом деле, основной смысл вообще делать такой скрипт заключается втом, что мыпросто несможем стартануть сервис сPatroni, незнаяIP адрес hostа. Ивтом случае, когда hostом оказывается Docker-контейнер, нам как-то нужно сначала узнать какойже IPэтот контейнер получил, итолько потом мыможем запустить Patroni. Эта потребность закрывается вот здесь:

readonly CONTAINER_IP=$(hostname --ip-address)readonly CONTAINER_API_ADDR="${CONTAINER_IP}:${PATRONI_API_CONNECT_PORT}"readonly CONTAINER_POSTGRE_ADDR="${CONTAINER_IP}:5432"...export PATRONI_RESTAPI_CONNECT_ADDRESS="$CONTAINER_API_ADDR"export PATRONI_RESTAPI_LISTEN="$CONTAINER_API_ADDR"export PATRONI_POSTGRESQL_CONNECT_ADDRESS="$CONTAINER_POSTGRE_ADDR"

Как яуже говорил раньше, параметры конфига Patroni можно передавать разными способами. Вэтом скрипте мыпользуемся тем, что один изтаких способов это Environment configuration. PATRONIRESTAPICONNECTADDRESS, PATRONIRESTAPILISTEN, PATRONIPOSTGRESQLCONNECTADDRESS специальные переменные среды, окоторых Patroni знает заранее икоторые будут считаны. Икстати, они переписывают локальные настройки изpatroni.yml, так что beaware!

Иеще момент. Документация Patroni нерекомендует использовать superuserа для подключения кбазе приложений. Т.е. нужно создать отдельного юзера, который мыбудем использовать непосредственно для коннекта, аsuperuserа иreplicatorа трогать небудем совсем. Создать такого юзера можно также через переменную среды. Если хотите, чтобы юзер назывался как-то иначе чем approle, просто замените вэтой строке approle начто-то другое.

export PATRONI_approle_PASSWORD="$POSTGRES_APP_ROLE_PASS"export PATRONI_approle_OPTIONS="${PATRONI_admin_OPTIONS:-createdb, createrole}"

Ивпоследней строчке, когда всё уже готово кстарту, мыделаем запуск Patroni сервиса суказанием откуда брать основной конфиг файл:

exec /usr/local/bin/patroni /etc/patroni.yml
  • Dockerfile

Dockerfile ярешил сделать настолько простым, насколько это только возможно. Ноэтого вполне достаточно, чтобы сделать билд рабочего Docker-образа. Давайте глянем, что внем все-таки происходит.

Dockerfile
FROM postgres:11 RUN apt-get update -y\     && apt-get install python3 python3-pip -y\    && pip3 install --upgrade setuptools\    && pip3 install psycopg2-binary \    && pip3 install patroni[zookeeper] \    && mkdir /data/patroni -p \    && chown postgres:postgres /data/patroni \    && chmod 700 /data/patroni COPY patroni.yml /etc/patroni.ymlCOPY patroni-entrypoint.sh ./entrypoint.shUSER postgresENTRYPOINT ["bin/sh", "/entrypoint.sh"]
Details

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

// владелец должен быть 'postgres', а mode 700    mkdir /data/patroni -p \    chown postgres:postgres /data/patroni \    chmod 700 /data/patroni     ...// устанавливаем в кач-ве активного юзера внутри контейнера // юзера postgres        USER postgres

Файлы, которые мысоздали, ранее копируются вимейдж вэтих строчках:

COPY patroni.yml /etc/patroni.ymlCOPY patroni-entrypoint.sh ./entrypoint.sh

И, как яуже упомянул ранее, мыхотим запустить этот скрипт сразу после создания контейнера:

ENTRYPOINT ["bin/sh", "/entrypoint.sh"]

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

docker build -t patroni-test .

Самое время обсудить последний посписку, нонеповажности файл для Patroni compose yml.

  • docker-compose-patroni.yml

Правильно написанный compose файл важная часть общей картины. Разберем, что нам нужно иметь ввиду, настраивая конфигурацию.

docker-compose-patroni.yml
version: "3.4"networks:    patroni_patroni:         external: trueservices:    patroni1:        image: patroni-test        networks: [ patroni_patroni ]        ports:            - 5441:5432            - 8091:8091        hostname: patroni1        volumes:          - /patroni1:/data/patroni        environment:            PATRONI_API_CONNECT_PORT: 8091            REPLICATION_NAME: replicator             REPLICATION_PASS: replpass            SU_NAME: postgres            SU_PASS: supass            POSTGRES_APP_ROLE_PASS: appass        deploy:          replicas: 1          placement:            constraints: [node.hostname == floitet]    patroni2:        image: patroni-test        networks: [ patroni_patroni ]        ports:            - 5442:5432            - 8092:8091        hostname: patroni2        volumes:          - /patroni2:/data/patroni        environment:            PATRONI_API_CONNECT_PORT: 8091            REPLICATION_NAME: replicator             REPLICATION_PASS: replpass            SU_NAME: postgres            SU_PASS: supass            POSTGRES_APP_ROLE_PASS: appass        deploy:          replicas: 1          placement:            constraints: [node.hostname == floitet]    patroni3:        image: patroni-test        networks: [ patroni_patroni ]        ports:            - 5443:5432            - 8093:8091        hostname: patroni3        volumes:          - /patroni3:/data/patroni        environment:            PATRONI_API_CONNECT_PORT: 8091            REPLICATION_NAME: replicator             REPLICATION_PASS: replpass            SU_NAME: postgres            SU_PASS: supass            POSTGRES_APP_ROLE_PASS: appass        deploy:          replicas: 1          placement:            constraints: [node.hostname == floitet]
Details

Первое, очем хочется сказать, это момент сexternal network, окотором говорилось ранее. Мыхотим разместить Patroni сервисы тамже, где мыдержим исервисы Zookeeper. Таким образом мысможем обращаться ксервисам поименам, ивсе имена: zoo1, zoo2, zoo3, которые мыперечислили вpatroni.yml, задавая сервера Zookeeperа, будут работать, как надо.

networks:    patroni_patroni:         external: true

Нужно отметить, что унас будут два end pointа: сама база данных иAPI. Идля того идля другого требуется открыть порты:

ports:    - 5441:5432    - 8091:8091...environment:    PATRONI_API_CONNECT_PORT: 8091// также нужно убедиться, что в PATRONI_API_CONNECT_PORT мы передаем// тот же самый, который мы открываем для сервиса   

Также, нам, конечно, нужно передать все переменные среды, которые мызаявили вentrypoint скрипте. Ноиэто еще невсё. Есть вопрос сдиректорией для mountа, который мытоже здесь решаем:

volumes:   - /patroni3:/data/patroni

Как видно изэтой строки, тудиректорию /data/patroni, которая была создана вDockerfile, мымонтируем клокальной директории. Так вот эту локальную директорию нам нужно создать. Инетолько создать, ноивыставить правильного юзера ирежим доступа, например так:

sudo mkdir /patroni3sudo chown 999:999 /patroni3sudo chmod 700 /patroni3// 999 это дефолтный uid для юзера postgres  // эти шаги нужно повторить для каждой ноды Patroni

Мы наконец готовы деплоить Patroni кластер:

sudo docker stack deploy --compose-file docker-compose-patroni.yml patroni

После деплоя влогах сервиса мыдолжны увидеть что-то втаком ключе:

INFO: Lock owner: patroni3; I am patroni1INFO: does not have lockINFO: no action.  i am a secondary and i am following a leader

Былобы печально, еслибы мымогли проверить статус кластера иноды только читая логи. Так что предлагаю коснуться немного способов проверки состояния кластера иначать ссамого простого patronictl. Для этого нужно сначала получить idлюбого контейнера Patroni:

sudo docker psCONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                          NAMESa0090ce33a05        patroni-test:latest   "bin/sh /entrypoint."   3 hours ago         Up 3 hours          5432/tcp                       patroni_patroni1.1.tgjzpjyuip6ge8szz5lsf8kcq...

Ипотом зайти вконтейнер спомощью exec команды:

sudo docker exec -ti a0090ce33a05 /bin/bash// при вызове команды мы должны передать имя кластера// это параметр 'scope' в patroni.yml ('patroni' в нашем случае)patronictl list patroni// и тут ошибка...Error: 'Can not find suitable configuration of distributed configuration store\nAvailable implementations: exhibitor, kubernetes, zookeeper'

Команда patronictl полагается наpatroni.yml, чтобы получить информацию осерверах Zookeeperа. Оннезнает, где мыэтот файл положили. Так что явно укажем ему путь:

patronictl -c /etc/patroni.yml list patroni// and here is the nice output with the current states+ Cluster: patroni (6893104757524385823) --+----+-----------+| Member   | Host      | Role    | State   | TL | Lag in MB |+----------+-----------+---------+---------+----+-----------+| patroni1 | 10.0.1.93 | Replica | running |  8 |         0 || patroni2 | 10.0.1.91 | Replica | running |  8 |         0 || patroni3 | 10.0.1.92 | Leader  | running |  8 |           |+----------+-----------+---------+---------+----+-----------+

PostgreSQL Connection

Готово! Теперь мыможем подключаться кPostgres ичто-то туда писать. Ноделать это мысможем только изнашей сети patroni_patroni. Так что сначала создаем контейнер сподходящим образом, ипотом изнего уже выполняем команды:

docker run --rm -ti --network=patroni_patroni postgres:11 /bin/bash// доступ к конкретной нодеpsql --host patroni3 --port 5432 -U approle -d postgres// доступ к лидеру через haproxy// так что нужно указать какую-либо через флаг '-d' 

Вот мыинастроили сам кластер Patroni. Нонаш сетап былбы неполным, еслибы мынаэтом иостановились. Есть ещё несколько важных моментов, которые мыдолжны закрыть, нообэтом вовторой части.

Все config файлы для первой части можно забрать отсюда.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru