Легко и непринужденно деплоим приложения на Tarantool Cartridge (часть 1)

в 12:20, , рубрики: Ansible, ansible roles, tarantool, tarantool cartridge, Администрирование баз данных, Блог компании Mail.Ru Group, высокая производительность, распределенные системы

Легко и непринужденно деплоим приложения на Tarantool Cartridge (часть 1) - 1

Мы уже рассказывали про Tarantool Cartridge, который позволяет разрабатывать распределенные приложения и паковать их. Осталось всего ничего: научиться деплоить эти приложения и управлять ими. Не беспокойтесь, мы всё предусмотрели! Мы собрали вместе все best practices по работе с Tarantool Cartridge и написали ansible-роль, которая разложит пакет на серверы, стартанет инстансы, объединит их в кластер, настроит авторизацию, забутстрапит vshard, включит автоматический failover и пропатчит кластерный конфиг.

Интересно? Тогда прошу под кат, всё расскажем и покажем.

Начнем с примера

Мы рассмотрим только часть функциональности нашей роли. Полное описание всех ее возможностей и входных параметров вы всегда можете найти в документации. Но лучше один раз попробовать, чем сто раз увидеть, поэтому давайте задеплоим небольшое приложение.

У Tarantool Cartridge есть туториал по созданию небольшого Cartridge-приложения, которое хранит информацию о клиентах банка и их счетах, а также предоставляет API для управления данными через HTTP. Для этого в приложении описываются две возможные роли: api и storage, которые могут назначаться инстансам.

Сам Cartridge ничего не говорит о том, как запускать процессы, он лишь предоставляет возможность для настройки уже запущенных инстансов. Остальное пользователь должен сделать сам: разложить конфигурационные файлы, запустить сервисы и настроить топологию. Но мы не будем всем этим заниматься, за нас это сделает Ansible.

От слов к делу

Итак, задеплоим наше приложение на две виртуалки и настроим простую топологию:

  • Pепликасет app-1 будет реализовывать роль api, которая включает в себя роль vshard-router. Здесь будет только один инстанс.
  • Репликасет storage-1 реализует роль storage (и одновременно vshard-storage), сюда добавим два инстанса с разных машин.

Легко и непринужденно деплоим приложения на Tarantool Cartridge (часть 1) - 2

Для запуска примера нам понадобятся Vagrant и Ansible (версии 2.8 или старше).

Сама роль находится в Ansible Galaxy. Это такое хранилище, которое позволяет делиться своими наработками и использовать готовые роли.

Склонируем репозиторий с примером:

$ git clone https://github.com/dokshina/deploy-tarantool-cartridge-app.git
$ cd deploy-tarantool-cartridge-app && git checkout 1.0.0

Поднимаем виртуалки:

$ vagrant up

Устанавливаем ansible-роль Tarantool Cartridge:

$ ansible-galaxy install tarantool.cartridge,1.0.1

Запускаем установленную роль:

$ ansible-playbook -i hosts.yml playbook.yml

Дожидаемся окончания выполнения плейбука, переходим на http://localhost:8181/admin/cluster/dashboard и наслаждаемся результатом:

Легко и непринужденно деплоим приложения на Tarantool Cartridge (часть 1) - 3

Можно лить данные. Круто, правда?

А теперь давайте разберемся, как с этим работать, и заодно добавим еще один репликасет в топологию.

Начинаем разбираться

Итак, что же произошло?

Мы подняли две виртуальные машины и запустили ansible-плейбук, который настроил наш кластер. Давайте посмотрим на содержимое файла playbook.yml:

---
- name: Deploy my Tarantool Cartridge app
  hosts: all
  become: true
  become_user: root
  tasks:
  - name: Import Tarantool Cartridge role
    import_role:
      name: tarantool.cartridge

Здесь ничего интересного не происходит, запускаем ansible-роль, которая называется tarantool.cartridge.

Всё самое важное (а именно, конфигурация кластера) находится в inventory-файле hosts.yml:

---
all:
  vars:
    # common cluster variables
    cartridge_app_name: getting-started-app
    cartridge_package_path: ./getting-started-app-1.0.0-0.rpm  # path to package

    cartridge_cluster_cookie: app-default-cookie  # cluster cookie

    # common ssh options
    ansible_ssh_private_key_file: ~/.vagrant.d/insecure_private_key
    ansible_ssh_common_args: '-o IdentitiesOnly=yes -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'

  # INSTANCES
  hosts:
    storage-1:
      config:
        advertise_uri: '172.19.0.2:3301'
        http_port: 8181

    app-1:
      config:
        advertise_uri: '172.19.0.3:3301'
        http_port: 8182

    storage-1-replica:
      config:
        advertise_uri: '172.19.0.3:3302'
        http_port: 8183

  children:
    # GROUP INSTANCES BY MACHINES
    host1:
      vars:
        # first machine connection options
        ansible_host: 172.19.0.2
        ansible_user: vagrant

      hosts:  # instances to be started on the first machine
        storage-1:

    host2:
      vars:
        # second machine connection options
        ansible_host: 172.19.0.3
        ansible_user: vagrant

      hosts:  # instances to be started on the second machine
        app-1:
        storage-1-replica:

    # GROUP INSTANCES BY REPLICA SETS
    replicaset_app_1:
      vars:  # replica set configuration
        replicaset_alias: app-1
        failover_priority:
          - app-1  # leader
        roles:
          - 'api'

      hosts:  # replica set instances
        app-1:

    replicaset_storage_1:
      vars:  # replica set configuration
        replicaset_alias: storage-1
        weight: 3
        failover_priority:
          - storage-1  # leader
          - storage-1-replica
        roles:
          - 'storage'

      hosts:   # replica set instances
        storage-1:
        storage-1-replica:

Всё, что нам нужно — это научиться управлять инстансами и репликасетами, изменяя содержимое этого файла. Дальше мы будем добавлять в него новые секции. Чтобы не запутаться, куда их добавлять, можете подсматривать в финальную версию этого файла, hosts.updated.yml, который находится в репозитории с примером.

Управление инстансами

В терминах Ansible каждый инстанс — это хост (не путать с железным сервером), т.е. узел инфраструктуры, которым Ansible будет управлять. Для каждого хоста мы можем указать параметры соединения (такие, как ansible_host и ansible_user), а также конфигурацию инстанса. Описание инстансов находится в секции hosts.

Рассмотрим конфигурацию инстанса storage-1:

all:
  vars:
    ...

  # INSTANCES
  hosts:
    storage-1:
      config:
        advertise_uri: '172.19.0.2:3301'
        http_port: 8181

  ...

В переменной config мы указали параметры инстанса — advertise URI и HTTP port.
Ниже находятся параметры инстансов app-1 и storage-1-replica.

Нам нужно сообщить Ansible параметры соединения для каждого инстанса. Кажется логичным объединить инстансы в группы по виртуальным машинам. Для этого инстансы объединены в группы host1 и host2, и в каждой группе в секции vars указаны значения ansible_host и ansible_user для одной виртуалки. А в секции hosts — хосты (они же инстансы), которые входят в эту группу:

all:
  vars:
    ...
  hosts:
    ...
  children:
    # GROUP INSTANCES BY MACHINES
    host1:
      vars:
        # first machine connection options
        ansible_host: 172.19.0.2
        ansible_user: vagrant
       hosts:  # instances to be started on the first machine
        storage-1:

     host2:
      vars:
        # second machine connection options
        ansible_host: 172.19.0.3
        ansible_user: vagrant
       hosts:  # instances to be started on the second machine
        app-1:
        storage-1-replica:

Начинаем изменять hosts.yml. Добавим еще два инстанса, storage-2-replica на первой виртуалке и storage-2 на второй:

all:
  vars:
    ...

  # INSTANCES
  hosts:
    ...
    storage-2:  # <==
      config:
        advertise_uri: '172.19.0.3:3303'
        http_port: 8184

    storage-2-replica:  # <==
      config:
        advertise_uri: '172.19.0.2:3302'
        http_port: 8185

  children:
    # GROUP INSTANCES BY MACHINES
    host1:
      vars:
        ...
      hosts:  # instances to be started on the first machine
        storage-1:
        storage-2-replica:  # <==

    host2:
      vars:
        ...
      hosts:  # instances to be started on the second machine
        app-1:
        storage-1-replica:
        storage-2:  # <==
  ...

Запускаем ansible-плейбук:

$ ansible-playbook -i hosts.yml 
                   --limit storage-2,storage-2-replica 
                   playbook.yml

Обратите внимание на опцию --limit. Поскольку каждый инстанс кластера является хостом в терминах Ansible, мы можем явно указывать, какие инстансы должны быть настроены при выполнении плейбука.

Снова заходим в Web UI http://localhost:8181/admin/cluster/dashboard и наблюдаем наши новые инстансы:

Легко и непринужденно деплоим приложения на Tarantool Cartridge (часть 1) - 4

Не будем останавливаться на достигнутом и освоим управление топологией.

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

Объединим наши новые инстансы в репликасет storage-2. Добавим новую группу replicaset_storage_2 и опишем в ее переменных параметры репликасета по аналогии с replicaset_storage_1. В секции hosts укажем, какие инстансы будут входить в эту группу (то есть наш репликасет):

---
all:
  vars:
    ...
  hosts:
    ...
  children:
    ...
    # GROUP INSTANCES BY REPLICA SETS
    ...
    replicaset_storage_2:  # <==
      vars:  # replicaset configuration
        replicaset_alias: storage-2
        weight: 2
        failover_priority:
          - storage-2
          - storage-2-replica
        roles:
          - 'storage'

      hosts:   # replicaset instances
        storage-2:
        storage-2-replica:

Снова запускаем плейбук:

$ ansible-playbook -i hosts.yml 
                   --limit replicaset_storage_2 
                   --tags cartridge-replicasets 
                   playbook.yml

В параметр --limit мы на этот раз передали имя группы, которая соответствует нашему репликасету.

Рассмотрим опцию tags.

Наша роль последовательно выполняет различные задачи, которые помечены следующими тэгами:

  • cartridge-instances: управление инстансами (настройка, подключение к membership);
  • cartridge-replicasets: управление топологией (управление репликасетами и безвозвратное удаление (expel) инстансов из кластера);
  • cartridge-config: управление остальными параметрами кластера (vshard bootstrapping, режим автоматического failover-а, параметры авторизации и конфигурация приложения).

Мы можем явно указать, какую часть работы хотим сделать, тогда роль пропустит выполнение остальных задач. В нашем случае мы хотим работать только с топологией, поэтому указали cartridge-replicasets.

Давайте оценим результат наших стараний. Находим новый репликасет на http://localhost:8181/admin/cluster/dashboard.

Легко и непринужденно деплоим приложения на Tarantool Cartridge (часть 1) - 5

Ура!

Поэкспериментируйте с изменением конфигурации инстансов и репликасетов и посмотрите, как меняется топология кластера. Вы можете опробовать различные эксплуатационные сценарии, например, rolling update или увеличение memtx_memory. Роль попытается сделать это без рестарта инстанса, чтобы сократить возможный даунтайм вашего приложения.

Не забудьте запустить vagrant halt, чтобы остановить виртуалки, когда закончите с ними работать.

А что под капотом?

Здесь я расскажу подробнее о том, что происходило под капотом ansible-роли во время наших экспериментов.

Рассмотрим по шагам деплой Cartridge-приложения.

Установка пакета и старт инстансов

Сначала нужно доставить пакет на сервер и установить его. Сейчас роль умеет работать с RPM- и DEB-пакетами.

Дальше запускаем инстансы. Тут всё очень просто: каждый инстанс — это отдельный systemd-сервис. Рассказываю на примере:

$ systemctl start myapp@storage-1

Эта команда запустит инстанс storage-1 приложения myapp. Запущенный инстанс будет искать свою конфигурацию в /etc/tarantool/conf.d/. Логи инстанса можно будет посмотреть при помощи journald.

Unit-файл /etc/systemd/system/myapp@.sevice для systemd-сервиса будет доставлен вместе с пакетом.

В Ansible имеются встроенные модули для установки пакетов и управления systemd-сервисами, тут мы ничего нового не изобрели.

Настройка топологии кластера

А вот здесь начинается самое интересное. Согласитесь, было бы странно заморачиваться со специальной ansible-ролью для установки пакетов и запуска systemd-сервисов.

Настроить кластер можно вручную:

  • Первый вариант: открываем Web UI и нажимаем на кнопочки. Для разового старта нескольких инстансов вполне подойдет.
  • Второй вариант: можно воспользоваться GraphQl API. Тут уже можно что-нибудь автоматизировать, например, написать скрипт на Python.
  • Третий вариант (для сильных духом): заходим на сервер, коннектимся к одному из инстансов при помощи tarantoolctl connect и производим все необходимые манипуляции с Lua-модулем cartridge.

Основная задача нашего изобретения — сделать за вас именно эту, самую сложную часть работы.

Ansible позволяет написать свой модуль и использовать его в роли. Наша роль использует такие модули для управления различными компонентами кластера.

Как это работает? Вы описываете желаемое состояние кластера в декларативном конфиге, а роль подает на вход каждому модулю его секцию конфигурации. Модуль получает текущее состояние кластера и сравнивает его с тем, что пришло на вход. Далее через сокет одного из инстансов запускается код, который приводит кластер к нужному состоянию.

Итоги

Сегодня мы рассказали и показали, как задеплоить ваше приложение на Tarantool Cartridge и настроить простую топологию. Для этого мы использовали Ansible — мощный инструмент, который отличается простотой в использовании и позволяет одновременно настраивать множество узлов инфраструктуры (в нашем случае это инстансы кластера).

Выше мы разобрались с одним из множества способов описания конфигурации кластера средствами Ansible. Как только вы поймете, что готовы идти дальше, изучите best practices по написанию плейбуков. Возможно, вам будет удобнее управлять топологией при помощи group_vars и host_vars.

Очень скоро мы расскажем, как безвозвратно удалять (expel) инстансы из топологии, бутстрапить vshard, управлять режимом автоматического failover-а, настраивать авторизацию и патчить кластерный конфиг. А пока вы можете самостоятельно изучать документацию и экспериментировать с изменением параметров кластера.

Если что-то не работает, обязательно сообщите нам о проблеме. Мы оперативно всё разрулим!

Автор: dokshina

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js