Cloister → просте керування кластером OTP

Практично кожен успішний бізнес-додаток рано чи пізно вступає у фазу, коли потрібне горизонтальне масштабування. У багатьох випадках можна просто запустити новий екземпляр та зменшити середнє навантаження. Але бувають і менш тривіальні випадки, коли ми повинні забезпечити, щоб різні ноди знали один про одного та акуратно розподіляли робоче навантаження.

Cloister → просте керування кластером OTP

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

Передача повідомлень між процесами на різних вузлах, а також між посиланнями та моніторами є прозорою […]

На практиці дещо складніше. Розподілений ерланг був розроблений, коли «контейнер» означав таку велику залізну скриньку для перевезення, а «докер» був просто синонімом портового вантажника. У 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: :rehashingstatus: :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

Додати коментар або відгук