Тarantool Cartridge: шаліраванне Lua-бэкенда ў тры радкі

Тarantool Cartridge: шаліраванне Lua-бэкенда ў тры радкі

У нас у Mail.ru Group ёсць Tarantool – гэта такі сервер прыкладанняў на Lua, які па сумяшчальніцтве яшчэ і база дадзеных (ці наадварот?). Ён хуткі і класны, але магчымасці аднаго сервера ўсё роўна не бязмежныя. Вертыкальнае маштабаванне таксама не панацэя, таму ў Tarantool ёсць прылады для гарызантальнага маштабавання – модуль vshard [1]. Ён дазваляе шаляваць дадзеныя па некалькіх серверах, але прыйдзецца павазіцца, каб яго наладзіць і прыкруціць бізнэс-логіку.

Добрыя навіны: мы сабралі шышак (напрыклад [2], [3]) і запілавалі чарговы фрэймворк, які прыкметна спросціць вырашэнне гэтай праблемы.

Тarantool Cartridge - гэта новы фрэймворк для распрацоўкі складаных размеркаваных сістэм. Ён дазваляе сфакусавацца на напісанні бізнес-логікі замест вырашэння інфраструктурных праблем. Пад катом я раскажу, як гэты фрэймворк уладкованы і як з яго дапамогай пісаць размеркаваныя сэрвісы.

А ў чым, уласна, праблема?

У нас ёсць тарантул, ёсць vshard - чаго яшчэ пажадаць?

Па-першае, справа ў зручнасці. Канфігурацыя vshard наладжваецца праз Lua-табліцы. Каб размеркаваная сістэма з некалькіх працэсаў Tarantool працавала правільна, канфігурацыя павінна ўсюды быць аднолькавай. Ніхто не жадае займацца гэтым уручную. Таму ў ход ідуць усялякія скрыпты, Ansible, сістэмы разгортвання.

Cartridge сам кіруе канфігурацыяй vshard, ён робіць гэта на аснове сваёй сваёй размеркаванай канфігурацыі. Па сутнасці, гэта просты YAML-файл, копія якога захоўваецца ў кожным экзэмпляры Tarantool. Спрашчэнне заключаецца ў тым, што фрэймворк сам сочыць за сваёй канфігурацыяй і за тым, каб яна ўсюды была аднолькавая.

Па-другое, справа зноў у зручнасці. Канфігурацыя ўшард не мае ніякага дачынення да распрацоўкі бізнес-логікі і толькі адцягвае праграміста ад працы. Калі мы абмяркоўваем архітэктуру таго ці іншага праекту, то часцей за ўсё гаворка ідзе аб асобных кампанентах і іх узаемадзеянні. Аб выкатцы кластара на 3 датацэнтра думаць рана.

Мы вырашалі гэтыя праблемы штораз, і ў нейкі момант у нас атрымалася выпрацаваць падыход, які дазваляе спрасціць працу з дадаткам на ўсім яго жыццёвым цыкле: стварэнне, распрацоўка, тэставанне, CI/CD, суправаджэнне.

Cartridge уводзіць паняцце ролі для кожнага працэсу Tarantool. Ролі - гэта тая канцэпцыя, якая дазваляе сфакусавацца распрацоўніку на напісанні кода. Усе наяўныя ў праекце ролі можна запусціць на адным асобніку Tarantool, і для тэстаў гэтага будзе дастаткова.

Асноўныя магчымасці Tarantool Cartridge:

  • аўтаматызаванае аркестраванне кластара;
  • пашырэнне функцыянальнасці дадатку з дапамогай новых роляў;
  • шаблон дадатку для распрацоўкі і разгортвання;
  • убудаванае аўтаматычнае шаліраванне;
  • інтэграцыя з тэставым фрэймворкам Luatest;
  • кіраванне кластарам з дапамогай WebUI і API;
  • інструменты упакоўкі і дэплою.

Прывітанне Сусвет!

Мне не церпіцца паказаць сам фрэймворк, таму аповяд пра архітэктуру пакінем на потым, і пачнем з простага. Калі выказаць здагадку, што сам Tarantool ужо ўсталяваны, тое застаецца зрабіць толькі

$ tarantoolctl rocks install cartridge-cli
$ export PATH=$PWD/.rocks/bin/:$PATH

Гэтыя дзве каманды ўсталююць утыліты каманднага радка і дазволяць стварыць сваё першае прыкладанне з шаблону:

$ cartridge create --name myapp

І вось што мы атрымаем:

myapp/
├── .git/
├── .gitignore
├── app/roles/custom.lua
├── deps.sh
├── init.lua
├── myapp-scm-1.rockspec
├── test
│   ├── helper
│   │   ├── integration.lua
│   │   └── unit.lua
│   ├── helper.lua
│   ├── integration/api_test.lua
│   └── unit/sample_test.lua
└── tmp/

Гэта git-рэпазітар з гатовым «Hello, World!» дадаткам. Давайце адразу паспрабуем яго запусціць, папярэдне ўсталяваўшы залежнасці (у тым ліку сам фрэймворк):

$ tarantoolctl rocks make
$ ./init.lua --http-port 8080

Такім чынам, у нас запушчана адна нода будучага шардаванага прыкладання. Дапытлівы абывацель можа адразу адкрыць вэб-інтэрфейс, мышкай сканфігураваць кластар з аднаго вузла і атрымліваць асалоду ад выніку, але радавацца пакуль рана. Пакуль што прыкладанне не ўмее рабіць нічога карыснага, таму пра дэплой я раскажу потым, а зараз надыходзіць час пісаць код.

Распрацоўка прыкладанняў

Вось уявіце, мы дызайнім праект, які павінен прымаць дадзеныя, захоўваць іх і раз у суткі будаваць справаздачу.

Тarantool Cartridge: шаліраванне Lua-бэкенда ў тры радкі

Мы пачынаем маляваць схему, і змяшчаем на яе тры кампаненты: gateway, storage і scheduler. Прапрацоўваем архітэктуру далей. Раз мы выкарыстоўваем у якасці сховішчы vshard, то дадаем у схему vshard-router і vshard-storage. Ні gateway, ні scheduler звяртацца ў сховішча напрамую не будуць, для гэтага ёсць роўтэр, ён для таго і створаны.

Тarantool Cartridge: шаліраванне Lua-бэкенда ў тры радкі

Гэтая схема ўсё яшчэ не зусім сапраўды адлюстроўвае тое, што мы будзем ствараць у праекце, таму што кампаненты выглядаюць абстрактна. Трэба яшчэ паглядзець, як гэта спраецыруецца на рэальны Tarantool – згрупуем нашы кампаненты па працэсах.

Тarantool Cartridge: шаліраванне Lua-бэкенда ў тры радкі

Трымаць vshard-router і gateway на асобных асобніках сэнсу мала. Навошта нам лішні раз хадзіць па сетцы, калі гэта і так уваходзіць у абавязкі роўтара? Яны павінны быць запушчаны ўнутры аднаго працэсу. Гэта значыць, што ў адным працэсе ініцыялізуюцца і gateway, і vshard.router.cfg, і няхай яны ўзаемадзейнічаюць лакальна.

На этапе праектавання працаваць з трыма кампанентамі было зручна, але я, як распрацоўшчык, пакуль пішу код, не хачу задумвацца аб запуску трох экзэмпляраў Tarnatool. Мне трэба запусціць выпрабаванні і праверыць, што я правільна напісаў gateway. Ці, можа, я хачу прадэманстраваць калегам фічу. Навошта мне мучыцца з разгортваннем трох асобнікаў? Менавіта так нарадзілася канцэпцыя роляў. Роля - гэта звычайны луашны модуль, жыццёвым цыклам якога кіруе Cartridge. У дадзеным прыкладзе іх чатыры gateway, router, storage, scheduler. У іншым праекце іх можа быць больш. Усе ролі можна запусціць у адным працэсе, і гэтага будзе дастаткова.

Тarantool Cartridge: шаліраванне Lua-бэкенда ў тры радкі

А калі гаворка пойдзе аб разгортванні ў staging ці ў эксплуатацыю, тады мы прызначым кожнаму працэсу Tarantool свой набор роляў у залежнасці ад апаратных магчымасцяў:

Тarantool Cartridge: шаліраванне Lua-бэкенда ў тры радкі

Кіраванне тапалогіяй

Інфармацыю аб тым, дзе якія ролі запушчаны, трэба недзе захоўваць. І гэта "недзе" - размеркаваная канфігурацыя, пра якую я ўжо згадваў вышэй. Самае галоўнае ў ёй - гэта тапалогія кластара. Тут намалявана 3 рэплікацыйныя групы з 5 працэсаў Tarantool:

Тarantool Cartridge: шаліраванне Lua-бэкенда ў тры радкі

Мы не жадаем страціць дадзеныя, таму беражліва ставімся да інфармацыі аб запушчаных працэсах. Cartridge сочыць за канфігурацыяй з дапамогай двухфазнага комміта. Як толькі мы жадаем абнавіць канфігурацыю, ён спачатку правярае даступнасць усіх асобнікаў і іх гатовасць прыняць новую канфігурацыю. Пасля гэтага другой фазай прымяняецца канфіг. Такім чынам, нават калі адзін экзэмпляр аказаўся часова недаступны, то нічога страшнага не адбудзецца. Канфігурацыя проста не прымяніцца і вы загадзя ўбачыце памылку.

Таксама ў секцыі тапалогіі паказаны такі важны параметр, як лідэр кожнай рэплікацыйнай групы. Звычайна гэта той экзэмпляр, на які ідзе запіс. Астатнія часцей за ўсё з'яўляюцца read-only, хаця тут могуць быць выключэнні. Часам смелыя распрацоўшчыкі не баяцца канфліктаў і могуць пісаць дадзеныя на некалькі рэплік паралельна, але ёсць некаторыя аперацыі, якія нягледзячы ні на што не павінны выконвацца двойчы. Для гэтага ёсць прыкмета лідара.

Тarantool Cartridge: шаліраванне Lua-бэкенда ў тры радкі

Жыццё роляў

Каб абстрактная роля магла існаваць у такой архітэктуры, фрэймворк павінен імі неяк кіраваць. Натуральна, кіраванне адбываецца без перазапуску працэсу Tarantool. Для кіравання ролямі існуе 4 колбекі. Cartridge сам будзе іх выклікаць у залежнасці ад таго, што ў яго напісана ў размеркаванай канфігурацыі, тым самым ужываючы канфігурацыю да пэўных роляў.

function init()
function validate_config()
function apply_config()
function stop()

У кожнай ролі ёсць функцыя init. Яна выклікаецца адзін раз або пры ўключэнні ролі, або пры перазапуску Tarantool'а. Там зручна, напрыклад, ініцыялізаваць box.space.create, ці scheduler можа запусціць які-небудзь фонавы fiber, які будзе выконваць працу праз пэўныя інтэрвалы часу.

Адной функцыі init можа быць нядосыць. Cartridge дазваляе ролям карыстацца той размеркаванай канфігурацыяй, якую ён выкарыстоўвае для захоўвання тапалогіі. Мы можам у гэтай жа канфігурацыі аб'явіць новую секцыю і захоўваць у ёй фрагмент бізнес-канфігурацыі. У маім прыкладзе гэта можа быць схема дадзеных, альбо налады раскладу для ролі scheduler.

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

Таксама ў роляў ёсць метад stop, Які патрэбен для ачысткі вынікаў жыццядзейнасці ролі. Калі мы кажам, што scheduler на гэтым серверы больш не патрэбен, ён можа спыніць тыя файберы, якія запускаў з дапамогай init.

Ролі могуць узаемадзейнічаць паміж сабой. Мы абвыклі пісаць выклікі функцый на Lua, але можа здарыцца так, што ў дадзеным працэсе няма патрэбнай нам ролі. Каб аблегчыць звароты па сетцы, мы выкарыстоўваем дапаможны модуль rpc (remote procedure call), які пабудаваны на аснове стандартнага netbox, убудаванага ў Tarantool. Гэта можа спатрэбіцца, калі, напрыклад, ваш gateway захоча напрамую папрасіць scheduler зрабіць працу прама зараз, а не чакаць суткі.

Яшчэ адзін важны момант - забеспячэнне адмоваўстойлівасці. Для маніторынгу здароўя ў Cartridge выкарыстоўваецца пратакол SWIM [4]. Калі казаць сцісла, то працэсы абменьваюцца сябар з сябрам «чуткамі» па UDP – кожны працэс распавядае сваім суседзям апошнія навіны, і яны адказваюць. Калі раптам адказ не прыйшоў, Tarantool пачынае падазраваць нешта нядобрае, а праз некаторы час дэкламуе смерць і пачынае расказвае ўсім навакольным гэтую навіну.

Тarantool Cartridge: шаліраванне Lua-бэкенда ў тры радкі

На аснове гэтага пратакола Cartridge арганізуе аўтаматычную апрацоўку адмоў. Кожны працэс сочыць за сваім асяроддзем, і калі лідэр раптам перастаў адказваць, то рэпліка можа ўзяць яго роля на сябе, а Cartridge якая адпавядае выявай канфігуруе запушчаныя ролі.

Тarantool Cartridge: шаліраванне Lua-бэкенда ў тры радкі

Тут трэба быць акуратным, таму што частае пераключэнне туды-сюды можа прывесці да канфліктаў звестак пры рэплікацыі. Уключаць аўтаматычны failover наўгад, вядома, не варта. Трэба дакладна разумець, што адбываецца, і быць упэўненымі, што рэплікацыя не зламаецца пасля таго, як лідэр адновіцца і яму вернуць карону.

З усяго сказанага можа скласціся адчуванне, што ролі падобныя на мікрасэрвісы. У нейкім сэнсе яны імі і з'яўляюцца, толькі як модулі ўсярэдзіне працэсаў Tarantool. Але ёсць і шэраг прынцыповых адрозненняў. Па-першае, усе ролі праекту павінны жыць у адной кодавай базе. І ўсе працэсы Tarantool павінны запускацца з адной кодавай базы, каб не было неспадзевак накшталт тых, калі мы спрабуем ініцыялізаваць scheduler, а яго папросту няма. Таксама не варта дапушчаць адрозненняў у версіях кода, таму што паводзіны сістэмы ў такой сітуацыі вельмі складана прадказваць і адладжваць.

У адрозненне ад Docker, мы не можам проста ўзяць выяву ролі, аднесці яго на іншую машыну і тамака запусціць. Нашы ролі не настолькі ізаляваныя, як Docker-кантэйнеры. Таксама мы не можам запусціць на адным экзэмпляры дзве аднолькавыя ролі. Роля ці ёсць, ці яе няма, у нейкім сэнсе гэта singleton. Ну і па-трэцяе, усярэдзіне ўсёй рэплікацыйнай групы ролі павінны быць аднолькавымі, таму што інакш было бы недарэчна - дадзеныя аднолькавыя, а канфігурацыя розная.

Інструменты дэплою

Я абяцаў паказаць, як Cartridge дапамагае дэплоіць прыкладанні. Каб палегчыць жыццё навакольным, фрэймворк пакуе RPM-пакеты:

$ cartridge pack rpm myapp -- упакует для нас ./myapp-0.1.0-1.rpm
$ sudo yum install ./myapp-0.1.0-1.rpm

Усталяваны пакет нясе ў сабе амаль усё неабходнае: і дадатак, і ўсталяваныя луашныя залежнасці. Tarantool на сервер таксама прыедзе як залежнасць RPM-пакета, і наш сэрвіс готаў да запуску. Робіцца гэта праз systemd, але перш за неабходна напісаць трохі канфігурацыі. Як мінімум, пазначыць URI кожнага працэсу. Трох прыкладу хопіць.

$ sudo tee /etc/tarantool/conf.d/demo.yml <<CONFIG
myapp.router: {"advertise_uri": "localhost:3301", "http_port": 8080}
myapp.storage_A: {"advertise_uri": "localhost:3302", "http_enabled": False}
myapp.storage_B: {"advertise_uri": "localhost:3303", "http_enabled": False}
CONFIG

Тут ёсць цікавы нюанс. Замест таго, каб пазначыць толькі порт бінарнага пратаколу, мы паказваем публічны адрас працэсу цалкам уключаючы hostname. Гэта трэба для таго, каб вузлы кластара ведалі, як сябар з сябрам злучыцца. Дрэнная ідэя выкарыстоўваць у якасці advertise_uri адрас 0.0.0.0, гэта павінен быць знешні IP-адрас, а не bind сокета. Без яго нічога працаваць не будзе, таму Cartridge папросту не дасць запусціць вузел з няправільным advertise_uri.

Цяпер, калі канфігурацыя гатова, можна запусціць працэсы. Бо звычайны systemd-юніт не дазваляе стартаваць больш аднаго працэсу, прыкладанні на Cartridge усталёўвае т.зв. instantiated-юніты, якія працуюць так:

$ sudo systemctl start myapp@router
$ sudo systemctl start myapp@storage_A
$ sudo systemctl start myapp@storage_B

У канфігурацыі мы паказалі HTTP-порт, на якім Cartridge абслугоўвае вэб-інтэрфейс – 8080. Зойдзем на яго і паглядзім:

Тarantool Cartridge: шаліраванне Lua-бэкенда ў тры радкі

Мы бачым, што працэсы хоць і запушчаны, але пакуль не сканфігураваны. Картрыдж пакуль не ведае, хто з кім павінен рэпліцыравацца і не можа прыняць рашэнне самастойна, таму чакае нашых дзеянняў. А ў нас выбар невялікі: жыццё новага кластара пачынаецца з канфігурацыі першага вузла. Потым дадамо да кластара астатнія, прызначым ім ролі, і на гэтым дэплой можна лічыць паспяхова завершаным.

Нальём кружачку каханага напоя і паслабімся пасля доўгага працоўнага тыдня. Прыкладанне можна эксплуатаваць.

Тarantool Cartridge: шаліраванне Lua-бэкенда ў тры радкі

Вынікі

А што вынiкi? Спрабуйце, карыстайцеся, пакідайце зваротную сувязь, заводзіце цікеты на гітхабе.

Спасылкі

[1] Tarantool » 2.2 » Reference » Rocks reference » Module vshard

[2] Як мы ўкаранялі ядро ​​інвестыцыйнага бізнэсу Альфа-Банка на базе Tarantool

[3] Архітэктура білінгу новага пакалення: трансфармацыя з пераходам на Tarantool

[4] SWIM - пратакол пабудовы кластара

[5] GitHub – tarantool/cartridge-cli

[6] GitHub – tarantool/cartridge

Крыніца: habr.com

Дадаць каментар