Практично кожен успішний бізнес-додаток рано чи пізно вступає у фазу, коли потрібне горизонтальне масштабування. У багатьох випадках можна просто запустити новий екземпляр та зменшити середнє навантаження. Але бувають і менш тривіальні випадки, коли ми повинні забезпечити, щоб різні ноди знали один про одного та акуратно розподіляли робоче навантаження.
Так вдало вийшло, що ерланг, який ми вибрали за приємний синтаксис та хайп навколо, має першокласну
Передача повідомлень між процесами на різних вузлах, а також між посиланнями та моніторами є прозорою […]
На практиці дещо складніше. Розподілений ерланг був розроблений, коли «контейнер» означав таку велику залізну скриньку для перевезення, а «докер» був просто синонімом портового вантажника. У IP4 було багато незайнятих адрес, у мережевих розривах — були, як правило, винні перегризлі кабель щури, а середній час безвідмовної роботи виробничої системи вимірювався десятиліттями.
Тепер ми всі неймовірно самодостатні, упаковані і запускаємо розподілений ерланг в середовищі, де динамічні IP-адреси лунають за принципом великої випадковості, а вузли можуть з'являтися та зникати за бажанням лівої п'яти планувальника. Щоб уникнути вороху шаблонного коду в кожному проекті, що запускає розподілений ерлангдля боротьби з ворожим середовищем, потрібна допомога.
Примітка: я в курсі що є libcluster
Вимоги
Що було потрібно особисто мені, так це бібліотека, яка візьме на себе управління кластером і матиме наступні властивості:
- прозора робота як із жорстко закодованим списком вузлів, так і з динамічним виявленням через сервіси ерланг;
- повнофункціональний колбек при кожній зміні топології (вузол туди, вузол сюди, нестабільність мережі, спліт);
- прозорий інтерфейс для запуску кластера з довгими та короткими іменами, як і з
:nonode@nohost
; - підтримка докера із коробки, без необхідності писати інфраструктурний код.
Останнє означає, що після того, як я протестував додаток локально в :nonode@nohost
, або у штучно-розподіленому середовищі за допомогою test_cluster_task
docker-compose up --scale my_app=3
і побачити, як воно виконує три екземпляри в докері без будь-яких змін коду. Я також хочу, щоб залежні програми, наприклад mnesia
— коли топологія змінюється, за лаштунками перебудовували кластер наживу без будь-якого додаткового стусану з боку програми.
Монастир не замислювався як бібліотека, здатна на все: від підтримки кластера до приготування кави. Це не срібна куля, що прагне охопити всі можливі випадки, або бути академічно повним рішенням у тому сенсі, що теоретики від CS вкладають у цей термін. Ця бібліотека покликана служити дуже чіткої мети, але виконувати свій невеликий обсяг робіт ідеально. Ця мета полягає в забезпеченні повної прозорості між локальним середовищем розробки та розподіленим еластичним середовищем, повним ворожих контейнерів.
Вибраний підхід
Монастир передбачається запускати як додаток, хоча досвідчені користувачі можуть працювати зі складанням та підтримкою (assembly and maintainance) кластера вручну, безпосередньо запустивши Cloister.Manager
у дереві супервізорів цільової програми.
При запуску як додаток, бібліотека покладаємося на config
, звідки вичитує такі основні значення:
config :cloister,
otp_app: :my_app,
sentry: :"cloister.local", # or ~w|n1@foo n2@bar|a
consensus: 3, # number of nodes to consider
# the cluster is up
listener: MyApp.Listener # listener to be called when
# the ring has changed
Параметри вище означає дослівно таке: Монастир використовується для програми OTP :my_app
, використовує erlang service discovery для підключення вузлів, трьох щонайменше, та MyApp.Listener
модуль (імплементуючий @behaviour Cloister.Listener
За такої конфігурації, додаток Монастир буде MyApp.Listener.on_state_change/2
%Cloister.Monitor{status: :up}
, Що означає: «Альо, кластер зібраний».
Найчастіше установка consensus: 3
є оптимальною, тому що навіть якщо ми очікуємо, що підключиться більше вузлів, зворотній виклик буде проходити через status: :rehashing
→ status: :up
на будь-якому новому доданому чи віддаленому вузлі.
При запуску в режимі розробки досить просто виставити consensus: 1
и Монастир радісно проскочить очікування складання кластера, побачивши :nonode@nohost
, або :node@host
, або :[email protected]
- в залежності від того, як був налаштований вузол (:none | :shortnames | :longnames
).
Управління розподіленими програмами
Розподілені додатки не у вакуумі зазвичай включають розподілені ж залежності, типу mnesia
. Нам легко обробляти їхню переконфігурацію з того ж зворотного виклику on_state_change/2
. Ось, наприклад, докладний опис того, як переналаштувати mnesia
на льоту в
Головною перевагою використання Монастир є те, що він виконує всі необхідні операції з перебудови кластера після зміни топології під капотом. Програма просто запускається у вже підготовленому розподіленому середовищі, з усіма підключеними вузлами, незалежно від того, чи знаємо ми IP-адреси і, отже, імена вузлів заздалегідь, чи вони динамічно призначені/змінені. Це вимагає ніяких спеціальних налаштувань конфігурації докера і з точки зору розробника додатків, немає ніякої різниці між запуском у розподіленому середовищі або в локальній на :nonode@nohost
. Докладніше про це можна почитати в
Незважаючи на те, що складна обробка змін топології можлива через власну реалізацію MyApp.Listener
, завжди можуть бути прикордонні випадки, коли ці обмеження бібліотеки та упереджений підхід до зміни виявляться наріжним каменем на шляху впровадження. Це нормально, просто візьміть вищезгаданий libcluster
, Що є більш універсальним, або навіть обробити кластер низького рівня самостійно. Мета цієї бібліотеки коду полягає не в тому, щоб охопити всі можливі сценарії, а в тому, щоб використовувати найпоширеніший сценарій без непотрібного болю та громіздких копій-пастів.
Примітка: на цьому місці в оригіналі була фраза Happy clustering!, і Яндекс, яким я перекладаю (не самому ж за словниками лазити), запропонував мені варіант Щасливого скупчення!. Найкращого перекладу, мабуть, особливо у світлі поточної геополітичної ситуації — і уявити неможливо.
Джерело: habr.com