Tarantool Cartridge: Lua backend шардинг в три реда

Tarantool Cartridge: Lua backend шардинг в три реда

В Mail.ru Group имаме Tarantool - това е сървър за приложения в Lua, който също служи като база данни (или обратното?). Това е бързо и готино, но възможностите на един сървър все още не са неограничени. Вертикалното мащабиране също не е панацея, така че Tarantool има инструменти за хоризонтално мащабиране - модулът vshard [1]. Позволява ви да разделяте данни на няколко сървъра, но трябва да се заемете с него, за да го настроите и прикачите бизнес логиката.

Добри новини: събрахме няколко големи снимки (напр [2], [3]) и създаде друга рамка, която значително ще опрости решението на този проблем.

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

Какъв точно е проблемът?

Имаме тарантула, имаме vshard - какво повече можете да искате?

Първо, това е въпрос на удобство. Конфигурацията на vshard се конфигурира чрез Lua таблици. За да работи правилно една разпределена система от множество процеси на Tarantool, конфигурацията трябва да е една и съща навсякъде. Никой не иска да прави това ръчно. Затова се използват всякакви скриптове, Ansible и системи за внедряване.

Самата касета управлява конфигурацията на vshard, тя прави това въз основа на своята собствена разпределена конфигурация. По същество това е прост YAML файл, копие от който се съхранява във всеки екземпляр на Tarantool. Опростяването е, че самата рамка следи конфигурацията си и гарантира, че тя е еднаква навсякъде.

Второ, пак става въпрос за удобство. Конфигурацията vshard няма нищо общо с развитието на бизнес логиката и само отвлича вниманието на програмиста от работата му. Когато говорим за архитектурата на даден проект, най-често говорим за отделни компоненти и тяхното взаимодействие. Твърде рано е да се мисли за внедряване на клъстер към 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

И така, имаме един работещ възел за бъдещото шардирано приложение. Любознателен мирянин може веднага да отвори уеб интерфейса, да конфигурира клъстер от един възел с мишката и да се наслади на резултата, но е твърде рано да се радваме. Засега приложението не може да направи нищо полезно, така че ще ви разкажа за внедряването по-късно, но сега е време да напишете код.

Разработка на приложения

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

Tarantool Cartridge: Lua backend шардинг в три реда

Започваме да чертаем диаграма и да поставим три компонента върху нея: шлюз, хранилище и планировчик. Работим по архитектурата допълнително. Тъй като използваме vshard като хранилище, добавяме vshard-router и vshard-storage към схемата. Нито шлюзът, нито планировчикът ще имат директен достъп до хранилището; за това е предназначен рутерът, за това е създаден.

Tarantool Cartridge: Lua backend шардинг в три реда

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

Tarantool Cartridge: Lua backend шардинг в три реда

Няма голям смисъл да държите vshard-router и gateway на отделни инстанции. Защо трябва да сърфираме в мрежата отново, ако това вече е отговорност на рутера? Те трябва да се изпълняват в рамките на един и същ процес. Тоест и шлюзът, и vshard.router.cfg се инициализират в един процес и им позволяват да си взаимодействат локално.

На етапа на проектиране беше удобно да се работи с три компонента, но аз, като разработчик, докато пиша кода, не искам да мисля за стартиране на три екземпляра на Tarnatool. Трябва да направя тестове и да проверя дали съм написал правилно gateway. Или може би искам да демонстрирам функция на моите колеги. Защо трябва да се занимавам с разгръщането на три копия? Така се роди концепцията за ролите. Ролята е обикновен luash модул, чийто жизнен цикъл се управлява от Cartridge. В този пример има четири от тях - шлюз, рутер, хранилище, планировчик. Може да има още в друг проект. Всички роли могат да се изпълняват в един процес и това ще бъде достатъчно.

Tarantool Cartridge: Lua backend шардинг в три реда

И когато става дума за внедряване в етап или производство, тогава ще присвоим на всеки процес на Tarantool свой собствен набор от роли в зависимост от хардуерните възможности:

Tarantool Cartridge: Lua backend шардинг в три реда

Управление на топологията

Информацията за това къде кои роли се изпълняват трябва да се съхранява някъде. И това „някъде“ е разпределената конфигурация, която вече споменах по-горе. Най-важното нещо за него е топологията на клъстера. Ето 3 групи за репликация от 5 процеса на Tarantool:

Tarantool Cartridge: Lua backend шардинг в три реда

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

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

Tarantool Cartridge: Lua backend шардинг в три реда

Животът на ролите

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

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

Всяка роля има функция init. Извиква се веднъж или когато ролята е активирана, или когато Tarantool се рестартира. Там е удобно, например, да инициализирате box.space.create или планировчикът може да стартира някакво фоново влакно, което ще изпълнява работа на определени интервали от време.

Една функция init може да не е достатъчно. Касетата позволява на ролите да се възползват от разпределената конфигурация, която използва за съхраняване на топологията. Можем да декларираме нов раздел в същата конфигурация и да съхраним фрагмент от бизнес конфигурацията в него. В моя пример това може да е схема на данни или настройки на график за ролята на планировчика.

Клъстерни повиквания validate_config и apply_config всеки път, когато разпределената конфигурация се промени. Когато конфигурация се приложи чрез двуфазов комит, клъстерът проверява дали всяка роля е готова да приеме тази нова конфигурация и, ако е необходимо, докладва грешка на потребителя. Когато всички се съгласят, че конфигурацията е нормална, тогава apply_config.

Ролите също имат метод stop, който е необходим за почистване на изхода на ролята. Ако кажем, че Scheduler вече не е необходим на този сървър, той може да спре тези влакна, с които е започнал init.

Ролите могат да взаимодействат една с друга. Свикнали сме да пишем извиквания на функции в Lua, но може да се случи даден процес да няма ролята, от която се нуждаем. За да улесним разговорите по мрежата, ние използваме помощния модул rpc (remote procedure call), който е изграден на базата на стандартния нетбокс, вграден в Tarantool. Това може да бъде полезно, ако например вашият шлюз иска директно да поиска от планировчика да свърши работата точно сега, вместо да чака един ден.

Друг важен момент е осигуряването на устойчивост на грешки. Касетата използва протокола SWIM за наблюдение на здравето [4]. Накратко, процесите обменят „слухове“ помежду си през UDP – всеки процес съобщава на своите съседи последните новини и те отговарят. Ако изведнъж отговорът не дойде, Tarantool започва да подозира, че нещо не е наред и след известно време рецитира смъртта и започва да казва на всички около тази новина.

Tarantool Cartridge: Lua backend шардинг в три реда

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

Tarantool Cartridge: Lua backend шардинг в три реда

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

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

За разлика от Docker, ние не можем просто да вземем роля "image", да го пренесем на друга машина и да го стартираме там. Нашите роли не са толкова изолирани като Docker контейнерите. Освен това не можем да изпълняваме две идентични роли на един екземпляр. Една роля или съществува, или не; в известен смисъл тя е единична. И трето, ролите трябва да са еднакви в рамките на цялата репликационна група, защото иначе би било абсурдно - данните са едни и същи, но конфигурацията е различна.

Инструменти за внедряване

Обещах да покажа как 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

Тук има интересен нюанс. Вместо да посочваме само порта на двоичния протокол, ние посочваме целия публичен адрес на процеса, включително името на хоста. Това е необходимо, за да могат възлите на клъстера да знаят как да се свързват един с друг. Лоша идея е да използвате 0.0.0.0 като advertise_uri адрес; трябва да е външен IP адрес, а не свързване на сокет. Без него нищо няма да работи, така че Cartridge просто няма да ви позволи да стартирате възел с грешен advertise_uri.

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

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

В конфигурацията сме посочили HTTP порта, на който Cartridge обслужва уеб интерфейса - 8080. Нека отидем до него и да разгледаме:

Tarantool Cartridge: Lua backend шардинг в три реда

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

Нека си налеем чаша любима напитка и да се отпуснем след дългата работна седмица. Приложението може да се използва.

Tarantool Cartridge: Lua backend шардинг в три реда

Резултати от

Какви са резултатите? Опитайте, използвайте, оставете отзиви, създайте билети в Github.

Позоваването

[1] Tarantool » 2.2 » Справочник » Справочник за скали » Модул vshard

[2] Как внедрихме ядрото на инвестиционния бизнес на Alfa-Bank, базирано на Tarantool

[3] Архитектура за таксуване от ново поколение: трансформация с прехода към Tarantool

[4] SWIM - протокол за изграждане на клъстер

[5] GitHub - tarantool/cartridge-cli

[6] GitHub - tarantool/касета

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

Добавяне на нов коментар