Pull to refresh
VK
Building the Internet

Управляем кластером на Tarantool из командной строки

Reading time11 min
Views5.1K


Два года назад мы уже рассказывали вам, что такое 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

В случае, если всё прошло успешно, вы увидите следующее сообщение:


Не обращайте внимание на предупреждение, ведь проект мы еще не создали (и, соответственно, не установили сам Cartridge).

Команда cartridge create создает приложение с использованием стандартного шаблона приложения на Cartridge:

cartridge create --name myapp && cd myapp


Стандартный шаблон приложения содержит файлы:

### Файлы приложения
├── 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-репозиторий, который уже содержит первый коммит:


Чтобы запустить экземпляры, соберем наше приложение:

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

Проверим, что проект был успешно собран и все зависимости установлены:


На экране появилась информация о версии 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


Не обязательно запускать сразу все экземпляры. Можно, например, запустить лишь один 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:


С помощью флага --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


Всё! Одной командой мы настроили топологию, а также включили шардирование. Круто, не правда ли? Рассмотрим файл 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


Сбросить заданную конфигурацию кластера можно с помощью следующих команд:

cartridge stop
cartridge clean

Настраиваем failover


После конфигурации топологии кластера, можно настроить failover:

cartridge failover setup
# посмотрим состояние failover
cartridge failover status


Команда 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


Вы также можете использовать cartridge connect для подключения к интересующему вас экземпляру. Отличие этого подхода в том, что вы можете указать адрес экземпляра или путь к UNIX-сокету.

cartridge connect localhost:3301 \
  --username admin \
  --password secret-cluster-cookie
# либо
cartridge connect admin:secret-cluster-cookie@localhost:3301

Упаковываем приложение


Для упаковки приложения есть команда cartridge pack <tуpe>. На данный момент поддерживается четыре варианта упаковки:

  • deb — deb-пакет;
  • rpm — rpm-пакет;
  • tgz — tgz-архив;
  • docker — Docker-образ.

Например, для упаковки вашего приложения в tgz-архив используйте следующую команду:

cartridge pack tgz


Хотите собрать 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.

Создание временной директории (в которой будет выполняться сборка) с исходными файлами вашего приложения происходит в три этапа:

  1. Копирование файлов во временную директорию и её очистка. Папка с приложением копируется во временную директорию, выполняется команда git clean -X -d -f для удаления неотслеживаемых файлов и удаляются папки .rocks и .git.
  2. Сборка приложения в очищенной директории.
  3. Запуск скрипта 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-репозитории. Мы всегда поможем с решением вашей проблемы и будем рады интересным предложениям!
Tags:
Hubs:
Total votes 21: ↑17 and ↓4+24
Comments10

Articles

Information

Website
vk.com
Registered
Founded
Employees
5,001–10,000 employees
Location
Россия
Representative
Миша Берггрен