Cloister → просто управление на клъсера OTP

Практически всяко успешно бизнес приложение рано или късно влиза в фаза, в която е необходимо хоризонтално мащабиране. В много случаи просто може да стартирате нов инстанция и да намалите средната натовареност. Но съществуват и по-малко тривиални случаи, когато трябва да осигурим различните възли да знаят един за друг и прецизно да разпределят работното натоварване.

Cloister → просто управление на клъсера OTP

Така се случи, че erlang, който избрахме за приятния си синтаксис и шума около него, предлага първокласна подкрепа за разпределени системи. В теоретичен план това звучи напълно тривиално:

Предаването на съобщения между процеси на различни възли, както и между линкове и монитори е прозрачно […]

На практика всичко е малко по-сложно. Разпределеното erlang беше разработено, когато „контейнер“ означаваше такава голяма желязна кутия за транспортиране, а „докер“ просто синоним на пристанищен работник. В IP4 имаше много незаети адреси, в мрежовите пропуски - обикновено бяха виновни гризачи, които прегризаха кабели, а средното време на безотказна работа на производствената система се измерваше в десетилетия.

Сега всички ние сме неимоверно самодостатъчни, опаковани и стартираме разпределеното erlang в среда, където динамичните IP адреси се раздават по принципа на голямата случайност, а възлите могат да се появяват и изчезват по желание на лявата пета на планировчика. За да се избегне трупането на шаблонен код във всеки проект, който стартира разпределено erlang, за справяне с враждебната среда, е необходима помощ.

Забележка: знам, че съществува libcluster. Той наистина е готин, има повече от хиляда звезди, авторът е известен в общността и всичко такова. Ако ви е достатъчно предложеното от този пакет, за да създадете и поддържате клъстера - радвам се за вас. За мен, за съжаление, е нужно много повече. Искам да управлявам настройките в детайли и да не бъда страничен зрител в театъра на преформатирането на клъстера.

Изисквания

Каквото ми беше нужно лично, е библиотека, която да се заеме с управлението на клъстера и да притежава следните характеристики:

  • прозрачна работа както с жестко кодиран списък от възли, така и с динамично откритие чрез услуги erlang;
  • пълнофункционален колбек при всяка промяна в топологията (възел там, възел тук, нестабилност в мрежата, разделяния);
  • прозрачен интерфейс за стартиране на клъстери с дълги и къси имена, както и с :nonode@nohost;
  • поддръжка на Docker от кутията, без да е необходимо да се пише инфраструктурен код.

Последното означава, че след като тествам приложението локално в :nonode@nohost, или в изкуствено разпределена среда с помощта на test_cluster_task, искам просто да стартирам docker-compose up --scale my_app=3 и да видя как то изпълнява три инстанции в Docker без каквито и да е промени в кода. Искам също така зависимите приложения, например mnesia — когато топологията се променя, зад кулисите да се рестартира клъстера на живо без допълнителен тласък от страна на приложението.

Cloister не е замислен като библиотека, способна на всичко: от поддръжка на клъстера до приготвяне на кафе. Това не е сребърна куршума, стремяща се да обхване всички възможни случаи, или да бъде академично завършено решение в смисъла, в който теоретиците от CS вклиняват в този термин. Тази библиотека е предназначена да служи за много ясна цел, но да изпълнява своя не твърде голям обем работа перфектно. Целта е да осигури пълна прозрачност между локалната среда за разработка и разпределената еластична среда, пълна с враждебни контейнери.

Избраният подход

Cloister предполага да се стартира като приложение, макар опитните потребители да могат да работят с компилацията и поддръжката на клъстера ръчно, стартирайки директно Cloister.Manager в дървото на надзорниците на целевото приложение.

При стартиране като приложение, библиотеката разчита на config, от където чете следните основни стойности:

config :cloister,
  otp_app: :my_app,
  sentry: :"cloister.local", # или ~w|n1@foo n2@bar|a
  consensus: 3,              # брой възли, които да се вземат предвид
                             #    клъстера е активен
  listener: MyApp.Listener   # слушател, който да бъде извикан когато
                             #    рингът се е променил

Параметрите по-горе означават буквално следното: Cloister използва се за OTP приложение :my_app, използва erlang service discovery за свързване на узли, поне три, и MyApp.Listener модул (имплементиращ @behaviour Cloister.Listener) е конфигуриран да получава известия за промени в топологията. Подробно описание на пълната конфигурация можете да намерите в документацията.

С такава конфигурация приложението Cloister ще бъде се стартира стъпково, отлагая процеса на стартиране на основното приложение до постигане на консенсус (три възела са свързани и свързани, както в примера по-горе.) Това позволява на основното приложение да предположи, че когато е стартирано, клъстерът вече е достъпен. При всяка промяна в топологията (които ще бъдат много, тъй като възлите не стартират напълно синхронно) ще се извика обработчикът MyApp.Listener.on_state_change/2. В повечето случаи изпълняваме действие, когато получим съобщение със статус %Cloister.Monitor{status: :up}, което означава: „ало, клъстерът е събран”.

В повечето случаи настройка consensus: 3 е оптимална, защото дори ако очакваме да се свържат повече възли, обратната връзка ще премине през status: :rehashing → status: :up при всеки нов добавен или премахнат възел.

При стартиране в режим на разработка е достатъчно просто да се зададе consensus: 1 и Cloister весело ще пропусне чакането на събирането на клъстера, виждайки :nonode@nohost, или :node@host, или :node@host.domain — в зависимост от това как е конфигуриран възелът (:none | :shortnames | :longnames).

Управление на разпределени приложения

Разпределените приложения не съществуват изолирано и обикновено включват разпределени зависимости, като mnesia. Лесно е да се обработва тяхната преконфигурация от същия обратен повик on_state_change/2. Ето, например, подробен опис на начина, по който да се пренастрои mnesia в движение в документацията Cloister.

Основното предимство при използването на Cloister е, че той извършва всички необходими операции по преоразмеряване на клъстера след промяна на топологията. под капакаПриложението просто се стартира в предварително подготвена разпределена среда, с всички свързани възли, независимо дали знаем IP адресите и следователно имената на възлите предварително, или те са присвоени/променени динамично. Това не изисква никакви специални конфигурационни настройки на Docker и от гледна точка на разработчика на приложения, няма никаква разлика между стартиране в разпределена среда или локално на :nonode@nohost. Повече информация за това може да се намери в документацията.

Въпреки че сложната обработка на промените в топологията е възможна чрез собствена реализация MyApp.Listener, винаги могат да има гранични случаи, когато тези ограничения на библиотеката и предубеденият подход към конфигурацията се окажат пречка за внедряване. Това е нормално, просто вземете споменатото по-горе libcluster, който е по-универсален, или дори сами да обработите ниско ниво на клъстери. Целта на тази библиотека от кодове не е да обхване всички възможни сценарии, а да използва най-разпространения сценарий без ненужна болка и обемисто копиране и поставяне.

Бележка: на това място в оригинала беше фразата „Happy clustering!“, а Яндекс, на който превеждам (не че сам да ровя в речниците), ми предложи вариант „Счастливого скопления!“. По-добър превод, вероятно, особено предвид текущата геополитическа ситуация — и не може да се представи.

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster