Управляем кластером на Tarantool из командной строки
Два года назад мы уже рассказывали вам, что такое Cartridge и как с его помощью разрабатывать распределенные приложения. Это полноценный фреймворк, в который входит CLI-интерфейс, который сильно упрощает разработку и эксплуатацию приложений на Tarantool Cartridge.
Я расскажу вам, как можно использовать Cartridge CLI для эффективного использования ваших локальных приложений, и об интересных фичах самого CLI. Статья больше ориентирована на тех, кто уже использует Cartridge или хочет начать им пользоваться. Поехали!
Какие проблемы решает Cartridge CLI?
У нас есть Tarantool Cartridge, который решает проблемы взаимодействия и масштабирования нескольких микросервисов в рамках одного приложения. Поговорим о трудностях, которые возникают в процессе разработки.
С чего начать?
Вы захотели использовать Cartridge. Первая мысль, которая возникает в вашей голове: «Как мне запустить мое приложение?» Как минимум, вам нужно реализовать точку входа. Но при этом вы решите лишь часть проблемы — дальше нужно будет понять, как вообще запускать приложение.
Cartridge CLI содержит готовый шаблон приложения. В этом шаблоне есть все необходимые файлы (и не только) для того, чтобы запустить и сконфигурировать ваш кластер. Вам не нужно думать, какие файлы создать в проекте, что они должны из себя представлять и так далее. Кроме того, в стандартный шаблон легко вносить изменения в соответствии с вашими нуждами.
Сборка, запуск и настройка приложения
Приложение нужно собрать: как минимум — поставить сам Cartridge (он устанавливается как отдельный Lua-пакет), а как максимум — еще десяток зависимостей в виде Lua-пакетов (и не только), которые используются в вашем приложении. Конечно, вы можете написать свой скрипт, который будет собирать приложение за вас, и использовать его в каждом вашем приложении. Но зачем всякий раз придумывать велосипед?
Допустим, приложение вы успешно собрали. Теперь вы хотите локально запустить экземпляры и сконфигурировать приложение: создать наборы реплик, выставить им роли и так далее… В Cartridge CLI есть всего три команды, с помощью которых можно выполнить вышеуказанные действия в одну строчку: собрать, запустить и настроить ваше приложение.
Настройка набора реплик и failover
Да, управление через GUI нельзя называть проблемой. Для кого-то, может быть, это будет плюсом. Но я всё равно решил выделить как отдельное преимущество Cartridge CLI, и сейчас объясню почему:
- Для того, чтобы сконфигурировать кластер через GUI, нужно зайти в браузер и сделать N кликов. Чтобы сделать то же самое с помощью CLI, достаточно ввести одну команду. Как минимум, это экономия времени.
- Если вы решите сбросить конфигурацию кластера в GUI и заново настроить его, то всё придется повторить — сделать еще N кликов вместо вызова одной команды.
- Вы можете однажды собрать минимальную конфигурацию кластера, сохранить её в файл и закоммитить в репозиторий. После этого кто угодно (в том числе и вы при каждом перезапуске кластера) сможет поднимать настроенный кластер одной командой.
- Может быть, вам просто совсем не нравится пользоваться GUI.
Упаковка приложения
Представьте, что вы написали приложение и хотите отправить его клиенту в формате rpm-пакета. Сначала вам нужно будет описать файл .spec, установить (например) утилиту rpmbuild, и только после этого начать сборку пакета. Утомительно, не правда ли?
В Cartridge CLI процесс упаковки приложения унифицирован, содержит основные формы упаковки приложения (deb, rpm, tgz и Docker image) и много упрощающих этот процесс опций. Всё это позволяет не думать об упаковке в принципе, а просто вызвать одну команду с удобным интерфейсом.
Создаем и запускаем первое приложение
Для начала нам потребуется установить Cartridge CLI.
Установка на Debian или Ubuntu:
curl -L https://tarantool.io/IMYxqhi/release/2.7/installer.sh | bash
sudo apt install cartridge-cli
Установка на CentOS, Fedora или ALT Linux:
curl -L https://tarantool.io/IMYxqhi/release/2.7/installer.sh | bash
sudo yum install cartridge-cli
Установка на MacOS:
brew install cartridge-cli
Чтобы убедиться в успешной установке введите команду:
cartridge version
В случае, если всё прошло успешно, вы увидите следующее сообщение:
![2be9fef2b8c4b451fafe59afaf3f3936.jpg](https://habrastorage.org/r/w780q1/getpro/habr/post_images/2be/9fe/f2b/2be9fef2b8c4b451fafe59afaf3f3936.jpg)
Не обращайте внимание на предупреждение, ведь проект мы еще не создали (и, соответственно, не установили сам Cartridge).
Команда cartridge create
создает приложение с использованием стандартного шаблона приложения на Cartridge:
cartridge create --name myapp && cd myapp
![fa1a4a707611a7cbac1d70b23647ac83.jpg](https://habrastorage.org/r/w780q1/getpro/habr/post_images/fa1/a4a/707/fa1a4a707611a7cbac1d70b23647ac83.jpg)
Стандартный шаблон приложения содержит файлы:
### Файлы приложения
├── README.md
├── init.lua
├── stateboard.init.lua
├── myapp-scm-1.rockspec
├── app
│ ├── admin.lua
│ └── roles
### Сборка и упаковка
├── cartridge.pre-build
├── cartridge.post-build
├── Dockerfile.build.cartridge
├── Dockerfile.cartridge
├── package-deps.txt
├── systemd-unit-params.yml
### Локальный запуск приложения
├── instances.yml
├── replicasets.yml
├── failover.yml
├── .cartridge.yml
├── tmp
### Тестирование
├── deps.sh
├── test
│ ├── helper.lua
│ ├── integration
│ └── unit
Если вам не нравится стандартный шаблон приложения, то можете создать свой шаблон и использовать его:
cartridge create --from mytemplate --name mycustomapp && cd mycustomapp
В корне проекта сразу инициализируется локальный Git-репозиторий, который уже содержит первый коммит:
![ff2bcc80be699348561e808b700bd0cc.jpg](https://habrastorage.org/r/w780q1/getpro/habr/post_images/ff2/bcc/80b/ff2bcc80be699348561e808b700bd0cc.jpg)
Чтобы запустить экземпляры, соберем наше приложение:
cartridge build
Сборка выполняется с помощью утилиты tarantoolctl: она устанавливает все необходимые зависимости, в том числе Cartridge. Зависимости описаны в файле myapp-scm-1.rockspec. Если в вашем проекте понадобилась еще какая-либо зависимость, отредактируйте файл .rockspec, поместив зависимость туда, и введите команду cartridge build
.
Проект содержит файл cartridge.pre-build: он запускается перед установкой зависимостей. Например, вы можете установить нестандартные модули rocks с помощью той же tarantoolctl:
#!/bin/sh
tarantoolctl rocks make --chdir ./third_party/my-custom-rock-module
Проверим, что проект был успешно собран и все зависимости установлены:
![b4f21f7d81a18e1f052a37b7cac0264b.jpg](https://habrastorage.org/r/w780q1/getpro/habr/post_images/b4f/21f/7d8/b4f21f7d81a18e1f052a37b7cac0264b.jpg)
На экране появилась информация о версии Cartridge и список rocks проекта — информация о них выводится на экран, если указан флаг --rocks
.
В файле instances.yml можно сконфигурировать ваши экземпляры (имя, порт и так далее). Описание должно быть в формате <имя-приложения>.<имя экземпляра>
. Стандартый шаблон приложения уже содержит этот файл:
myapp.router:
advertise_uri: localhost:3301
http_port: 8081
myapp.s1-master:
advertise_uri: localhost:3302
http_port: 8082
myapp.s1-replica:
advertise_uri: localhost:3303
http_port: 8083
myapp.s2-master:
advertise_uri: localhost:3304
http_port: 8084
myapp.s2-replica:
advertise_uri: localhost:3305
http_port: 8085
Чтобы запустить описанные в этом файле экземпляры, используйте команду cartridge start
:
cartridge start -d
# убедимся, что все инстансы приложения были успешно запущены
cartridge status
![2e3ac0009bd2a258f24f1b37b113ce25.jpg](https://habrastorage.org/r/w780q1/getpro/habr/post_images/2e3/ac0/009/2e3ac0009bd2a258f24f1b37b113ce25.jpg)
Не обязательно запускать сразу все экземпляры. Можно, например, запустить лишь один s1-master
:
cartridge start s1-master -d
cartridge status s1-master
Точкой входа в наше приложение является файл с именем init.lua. Именно он под капотом запускает cartridge start
.
Стандартный шаблон приложения в своём корне содержит папку tmp.
- tmp/run — директория, хранящая PID процессов-экземпляров и socket-файлы;
- tmp/data — директория, хранящая данные экземпляров;
- tmp/log — директория, хранящая логики экземпляров.
Вы можете изменить стандартные пути к вышеописанным директориям, указав новые пути в файле .cartridge.yml или с помощью флагов команды cartridge start
. Чтобы запустить экземпляры в фоновом режиме, используйте флаг -d
. В таком случае логи будут сохраняться в файл и мы сможем их посмотреть с помощью команды cartridge log
:
![17ad7261beae0232e769cd6a5d582a6d.jpg](https://habrastorage.org/r/w780q1/getpro/habr/post_images/17a/d72/61b/17ad7261beae0232e769cd6a5d582a6d.jpg)
С помощью флага --stateboard
или настройки stateboard: true
в файле конфигурации .cartridge.yml вы можете также запустить изолированный экземпляр Tarantool, который можно будет использовать в качестве поставщика состояний для failover. Я предлагаю всегда запускать экземпляр stateboard (в дальнейшем его можно использовать для настройки failover), поэтому стандартный шаблон приложения уже содержит флаг stateboard: true
. При необходимости вы можете безболезненно убрать этот флаг из файла конфигурации.
Настраиваем топологию
Команда cartridge replicasets
позволяет выполнять различные действия по изменению топологии кластера (например, из конфигурационного файла) и сохранять конфигурацию кластера в файл. Созданный командой cartridge create
шаблон приложения содержит файл replicasets.yml, с помощью которого мы можем сконфигурировать базовую топологию нашего кластера:
cartridge replicasets setup --bootstrap-vshard
# убедимся, что топология была успешно настроена
cartridge replicasets list
![026d9efb72535cc5dce4a25b79576ac4.jpg](https://habrastorage.org/r/w780q1/getpro/habr/post_images/026/d9e/fb7/026d9efb72535cc5dce4a25b79576ac4.jpg)
Всё! Одной командой мы настроили топологию, а также включили шардирование. Круто, не правда ли? Рассмотрим файл replicasets.yml подробнее:
router:
instances:
- router
roles:
- failover-coordinator
- vshard-router
- app.roles.custom
all_rw: false
s-1:
instances:
- s1-master
- s1-replica
roles:
- vshard-storage
weight: 1
all_rw: false
vshard_group: default
s-2:
instances:
- s2-master
- s2-replica
roles:
- vshard-storage
weight: 1
all_rw: false
vshard_group: default
В нем содержится три набора реплик с именами router, s-1 и s-2.
instances
— в этом блоке описаны экземпляры, которые содержит каждый из набора реплик. Имена экземпляров должны совпадать с именами, которые описаны в instances.yml.- В блоке
roles
описаны роли для каждого набора реплик; weight
— vshard-вес набора реплик.all_rw
— флаг, указывающий, что все экземпляры в наборе реплик должны быть доступны как для чтения, так и для записи.vshard_group
— имя группы vshard, к которой принадлежит набор реплик.
Если вдруг вам захотелось настроить всё это в ручную (без использования конфига replicasets.yml), то можете воспользоваться другими опциями cartridge replicasets
:
# объединим экземпляры s1-master и s1-replica в набор реплик s-1:
cartridge replicasets join --replicaset s-1 s1-master s1-replica
# добавим реплику router:
cartridge replicasets join --replicaset router router
# посмотрим текущие доступные роли и выберем из них подходящие для каждой из реплик:
cartridge replicasets list-roles
# добавим роль vshard-storage для набора реплик s-1:
cartridge replicasets add-roles --replicaset s-1 vshard-storage
# также добавим роли для реплики router:
cartridge replicasets add-roles \
--replicaset router \
vshard-router app.roles.custom failover-coordinator metrics
# и наконец забутстрапим vshard:
cartridge replicasets bootstrap-vshard
# посмотрим конфигурацию набора реплик:
cartridge replicasets list
![ac87b26eb3880363e62871c3f535d9d2.jpg](https://habrastorage.org/r/w780q1/getpro/habr/post_images/ac8/7b2/6eb/ac87b26eb3880363e62871c3f535d9d2.jpg)
Сбросить заданную конфигурацию кластера можно с помощью следующих команд:
cartridge stop
cartridge clean
Настраиваем failover
После конфигурации топологии кластера, можно настроить failover:
cartridge failover setup
# посмотрим состояние failover
cartridge failover status
![a4574f722b8223422a97989432aa17b7.jpg](https://habrastorage.org/r/w780q1/getpro/habr/post_images/a45/74f/722/a4574f722b8223422a97989432aa17b7.jpg)
Команда cartridge failover setup
использует конфигурацию, описанную в файле failover.yml, который находится в корне созданного приложения.
mode: stateful
state_provider: stateboard
stateboard_params:
uri: localhost:4401
password: passwd
У failover может быть три состояния: eventual, stateful и disabled:
- eventual и disabled не требуют никаких дополнительных настроек;
- stateful требует указания поставщика состояний (поле
state_provider
) и указания параметров для этого поставщика. На данный момент поддерживаются поставщики stateboard и etcd2.
Подробнее об архитектуре failover вы можете прочитать здесь. А здесь вы можете прочитать о всех параметрах, которые вы можете указать при его конфигурации. Вы также можете использовать команду cartridge failover set
для ввода настроек failover прямо в командной строке:
cartridge failover set stateful \
--state-provider etcd2 \
--provider-params '{"lock_delay": 15}'
Для отключения failover используйте следующие команды:
cartridge failover disable
# или
cartridge failover set disabled
Подключаемся к экземплярам
Вам вдруг понадобилось подключиться экземпляру и ввести там интересующие вас команды, например, выполнить cartridge.reload_roles ()? Легко!
С помощью cartridge enter
вы можете подключиться к экземпляру через консольный сокет, размещенный в run-dir. Никаких дополнительных параметров не нужно, достаточно ввести имя экземпляра, указанного в instances.yml
:
cartridge enter instance-name
![4e05d5db31e1b2402002a7d8cfdbbd9c.jpg](https://habrastorage.org/r/w780q1/getpro/habr/post_images/4e0/5d5/db3/4e05d5db31e1b2402002a7d8cfdbbd9c.jpg)
Вы также можете использовать cartridge connect
для подключения к интересующему вас экземпляру. Отличие этого подхода в том, что вы можете указать адрес экземпляра или путь к UNIX-сокету.
cartridge connect localhost:3301 \
--username admin \
--password secret-cluster-cookie
# либо
cartridge connect admin:secret-cluster-cookie@localhost:3301
Упаковываем приложение
Для упаковки приложения есть команда cartridge pack
. На данный момент поддерживается четыре варианта упаковки:
deb
— deb-пакет;rpm
— rpm-пакет;tgz
— tgz-архив;docker
— Docker-образ.
Например, для упаковки вашего приложения в tgz-архив используйте следующую команду:
cartridge pack tgz
![41ecbb946c399d1d77711633ccb441ff.jpg](https://habrastorage.org/r/w780q1/getpro/habr/post_images/41e/cbb/946/41ecbb946c399d1d77711633ccb441ff.jpg)
Хотите собрать rpm- или deb-пакет, но при этом используете MacOS? Вы не можете сделать это просто так: в упакованном приложении будут rocks и исполняемые файлы, которые нельзя использовать в Linux. Специально для такого случая существует флаг --use-docker
, который собирает в Docker:
cartridge pack deb --use-docker
Помимо --use-docker
, команда cartridge pack
имеет множество других полезных опций. Рассмотрим самые интересные из них.
Добавляем зависимости в пакет
Добавим в наш rpm- или deb-пакет какую-нибудь зависимость. Например, unzip:
cartridge pack deb --deps unzip>=6.0
Либо вы можете описать зависимости для вашего пакета в файле package-deps.txt, который уже находится в корне созданного приложения:
unzip==6.0
neofetch>=6,<7
gcc>8
Теперь, упаковав приложение с помощью команды cartridge pack deb
, ваш пакет будет содержать зависимости unzip, neofetch и gcc. Вы можете использовать файл с другим именем для указания ваших зависимостей с помощью флага --deps-file
:
cartridge pack rpm --deps-file=path-to-deps-file
Добавляем сборочные сценарии до (и после)
А что если во время упаковки вам нужно создать файл, папку, поставить какую-либо утилиту — то есть внести какие-либо изменения в сценарий упаковки приложения? Для этого используются файлы preinst.sh и postinst.sh.
Все пути к исполняемым файлам в сценариях до и после установки должны быть абсолютными. Либо используйте /bin/sh -c ''
:
/bin/sh -c 'touch file-path'
/bin/sh -c 'mkdir dir-path'
С помощью флагов --preinst
и --postinst
вы можете использовать файлы с любым именем:
cartridge pack rpm \
--preinst=path-to-preinst-script \
--posints=path-to-posinst-script
Сценарии работают только для сборки rpm- и deb-пакетов.
Кешируем пути
При каждом запуске упаковки приложение собирается с нуля. Например, сборка всех зависимостей (т.е. rocks) начинается заново. Чтобы этого избежать (и уменьшить время переупаковки приложения), существует опция кеширования путей и файл pack-cache-config.yml:
- path: '.rocks':
key-path: 'myapp-scm-1.rockspec'
- path: 'node_modules':
always-cache: true
- path: 'third_party/custom_module':
key: 'simple-hash-key'
Рассмотрим подробнее параметры конфигурационного файла:
path
— путь от корня проекта до кешируемого пути;key-path
— путь до файла, содержимое которого будет ключом кеширования. В примере выше для пути .rocks ключом кеширования является файл myapp-scm-1.rockspec — если изменить его, то cache hit не произойдет и все rocks приложения будут собираться заново;always-cache
— кеширование указанного пути всегда, независимо от каких-либо ключей;key
— простой ключ кеширования в виде строки.
В стандартном шаблоне приложения уже содержится один кешируемый путь:
- path: '.rocks'
key-path: myapp-scm-1.rockspec
Я предлагаю всегда кешировать содержимое папки .rocks опираясь на содержимое файла .rockspec. Для одного пути может быть только один кеш. Например, у вас в кеше находится папка .rocks, вы меняете ключ и запускаете упаковку приложения. В этот момент старый кеш .rocks удаляется и заменяется новым на основе нового ключа.
Чтобы отключить кеширование путей, используйте флаг --no-cache
. С полным списком опций команды cartridge pack
вы можете ознакомиться здесь.
Подробнее о процессе упаковки
Команда cartridge pack
помимо упаковки приложения в пакет ещё и собирает его (аналогично команде cartridge build
). По умолчанию сборка выполняется во временной директории ~/.cartridge/tmp. Вы можете изменить её на свою, установив значение переменной окружения CARTRIDGE_TEMPDIR
:
- Если эта директория не существует, она будет создана и использована для сборки приложения, а затем удалена.
- В противном случае сборка будет выполнена в директории
CARTRIDGE_TEMPDIR/cartridge.tmp
.
Создание временной директории (в которой будет выполняться сборка) с исходными файлами вашего приложения происходит в три этапа:
- Копирование файлов во временную директорию и её очистка. Папка с приложением копируется во временную директорию, выполняется команда
git clean -X -d -f
для удаления неотслеживаемых файлов и удаляются папки .rocks и .git. - Сборка приложения в очищенной директории.
- Запуск скрипта cartridge.post-build (если он существует).
В корне проекта находится файл cartridge.post-build. Это скрипт, основная цель которого — удаление артефактов сборки из результирующего пакета. После сборки приложения во временной директории генерируются специальные файлы, такие как VERSION и VERSION.lua, которые содержат версию приложения. В случае сборки в rpm и deb инициализируются директории systemd и tmpfiles. Далее приложение упаковывается.
Подробнее о структуре и дальнейшей работе с полученными rpm- и deb-пакетами вы можете прочитать здесь. А здесь — про работу с Docker-образами.
Итоги
Cartridge CLI содержит удобный и унифицированный интерфейс для управления приложением и позволяет не придумывать велосипед. В этой статье я рассказал о том, как можно из командной строки максимально эффективно и удобно управлять вашим локальным приложением, написанным на Tarantool Cartridge: запускать, настраивать топологию и failover, упаковывать приложение и подключаться к его экземплярам.
Cartridge CLI имеет еще одну команду, о которой я не рассказал в этой статье. Команда cartridge admin
призвана упростить разработчикам написание и поддержку эксплуатационных кейсов, повысить переиспользование операций, оптимизировать поставку в эксплуатацию. Почитать подробнее об этом вы можете в этой статье.
Если у вас что-то вдруг пошло не так, или вы знаете, как можно улучшить продукт, то всегда можете завести тикет в нашем GitHub-репозитории. Мы всегда поможем с решением вашей проблемы и будем рады интересным предложениям!