Настройка кластера машин с Ansible

Мне очень понравилось, как было просто конфигурировать виртуальную машину с Ansible. Вот теперь думаю, а что, если машин было бы несколько? Ведь оригинальный пример был о нескольких машинах: один Consul сервер и два рабочих агента. Сервер уже готов, так что будет интересно довести пример до конца. Так что приступим.

И да, логически этот пост очень зависит от предыдущего, так что если что-то кажется бессмыслицей, то это либо было раскрыто в прошлом посте, либо я где-то накосячил, что тоже случается.

Что у нас уже есть

За прошлый пост мы успели сделать вот что:

  1. Vagrantfile, чтобы создать виртуальную машину,
  2. инвентарный файл для Ansible, чтобы знать, где она находится,
  3. Ansible плейбук, чтобы её настроить,
  4. заголовок systemd сервиса для Консула и
  5. файл конфигурации init.json.j2 для него же.

Чтобы не прыгать между постами и гитхабом туда и обратно, вот тела всех перечисленных файлов (много, много букв).

Надеюсь, вы это просто проскролили. Как и в прошлый раз, vagrant up создаст и подготовит новую виртуалку, на которой будет жить готовый Consul сервер. Но пока мы этого делать не будем, потому что сначала нужно создать ещё парочку хостов.

Шаг 0. Добавим ещё виртуальных машин

Vagrant всё-таки прекрасен. Без него пришлось бы бездумно прокликивать менюшки в VirtualBox и много ждать, а тут пара строк кода, и машины будут готовы меньше, чем за минуту.

Код в Vagrantfile, который создавал consul-server VM, уже тогда был подозрительно похож на функцию. Я думаю, что похожесть стоит оформить окончательно, и потом уже переиспользовать эту функцию и для остальных машин. Плюс ко всему, пока Ansible плейбук ничего не знает о остальных хостах, его стоит убрать из Vagrantfile и чуть что, запускать руками. Правда, из-за этого придётся снова вернуть строку, которая настраивала SSH пользователя.

В конечном итоге вот, что у меня получилось:

Функция create_consul_host создаст машину полностью готовую к тому, чтобы на неё натравили Ansible, и мы вызываем её три раза, чтобы получить три идентичные машины: consul-serverconsul-host-1 and consul-host-2vagrant up вдохнёт в них жизнь, и я даже не буду проверять, всё ли с ними в порядке. Конечно, всё ОК.

Шаг 1. Учим Ansible доверять

Если быстренько попытаться отправить новым хостам какую-нибудь Ansible команду (вроде ansible all -i hosts -m ping), то Ansible так же быстренько откажется её выполнять. Хосты-то незнакомые, откуда ему знать, что им можно верить. В прошлый раз мне пришлось руками подтверждать, что верить хосту можно, но делать такое регулярно определённо задолбает. Нужно что-то более перманентное. Например, добавить опцию в настройки Ansible и заставить его верить всем подряд.

Оказывается, просто добавив ansible.cfg в текущую папку и положив туда всего одну настройку, можно начисто отключить у Ansible критическое мышление:

В моём случае, правда, пришлось дополнительно сходить в  ~/.ssh/known_hosts и удалить оттуда записи об айпишках 192.168.99.100-102. Я их использовал раньше, и если бы Ansible заметил, что за старым адресом прячется новый хост, ему бы крышу снесло.

Итак, виртуальные машины запущены, Ansible — сама доверчивость, так что можно запустить какой-нибудь ping и любоваться, как все три хоста счастливо отвечают pong:

Хм, отозвался только consul-server. Но с другой стороны, чему удивляться. Инвентарный файл ведь старый остался, он только про constul-server и знает. Бывает.

Шаг 2. Добавляем новые хосты в инвентарный файл

В первоначальном hosts файле была всего одна строчка с айпишкой и паролем, так что, скопипастив её раза два, можно было бы добавить в него и остальные хосты. Но блин, получится как-то слишком уродливо. К тому же, у хостов будут разные роли, кто-то сервер, кто-то клиент, так что стоит это как-то обозначить на инвентарном уровне.

Попробуем-ка организовать хосты по группам. Например,  consul-server будет единственным представителем группы serversconsul-host-1 и -2 будут принадлежать группе nodes, а обе группы будут включены в родительскую группу cluster. Ну и к тому же общие логины и пароли можно задать прямо на уровне группы и тем самым избежать греха копипасты.

Выглядит сурово и солидно. Как в продакшене. Маска consul-host-[1:2] в середине файла особенно удалась. Она и строку текста экономит, и подчёркивает продвинутость автора. Наверное.

Зато теперь, если пингануть все (all) хосты, то всё будет очень, очень хорошо:

Вместо all, кстати, можно было бы подставить как имена наших групп, так и имена отдельных хостов.

Итак, с настройками покончили, идём писать плейбук.

Шаг 3. Адаптируем плейбук под новые роли

В прошлый раз мы настраивали consul-server в шесть шагов:

  1. Установить unzip.
  2. Установить Consul.
  3. Сделать Consul сервисом.
  4. Убедиться, что папка для настроек существует.
  5. Положить настройки консула.
  6. Запустить consul, если нужно.

В мире Консулов их поведение определяется файлом настроек, поэтому только пятая задача будет меняться под конкретные роли. Всё остальное же будет одинаковое как для сервера, так и для обычных работяг.

Так как в один плейбук можно положить сразу несколько сценариев (у нас пока был только один), то настройку всего кластера можно сделать в четыре подхода:

  1. Установить Consul сервисы на все VM (шаги 1-4).
  2. Положить конфигурацию Consul-сервера (шаг 5).
  3. Положить конфигурацию Consul-нодов (шаг 5).
  4. Запустить Consul-агенты на всех VM (шаг 6).

Шаг 3.1. Устанавливаем Consul-сервисы

Будем кромсать существующий код. Первый сценарий, в принципе, это же старый consul.yml целиком минус несколько вещей:

  1. «Положить настройки консула» и «Запустить консул, если нужно» шаги пока не нужны. Удаляем.
  2. Вместо хоста consul-server, мы теперь настраиваем группу cluster (первая строка).
  3. Переменная consul_server_ip пока не нужна. Сжигаем.
  4. Сам по себе Consul недавно обновили, так что в переменную consul_version (четвёртая строка) кладём новую версию — 0.9.3.

Получившийся consul.yml теперь выглядит вот так:

Так как хосты consul-serverconsul-host-1 и -2 всё ещё запущены, то установить Консул на их все можно одной несчастной командой:

И это срабатывает реально быстро. Секрет в том, что Ansible обслуживает хосты одновременно.

Шаг 3.2. Настраиваем consul-server

Тут можно было бы просто скопипастить кусок старого года, но я думаю, что хоть что-то стоит сделать правильно.

Во-первых, посмотрим на задачу, которую нам нужно добавить:

init.json.j2 файл, название которого имело смысл при конфигурации одного хоста, оный смысл начинает терять сейчас, когда их несколько. Это файл для Консул-сервера? Для клиентов? А вот переименуем его в server.init.json.j2, и всякая неопределённость отвалится сама собой.

Идём дальше. Наша задача ссылается на переменную consul_config_dir, которая вообще-то была определена в предыдущем сценарии и поэтому тут невидима. Можно было бы, конечно, объявить её повторно, но это опять будет грех копипасты. Лучше уж мы её сделаем глобальной переменной, перенеся в инвентарный файл.

Есть ещё один момент: шаблон server.init.json.j2 использует переменную consul_server_ip, которую тоже надо где-то объявить. Но эта переменная даже в прошлом посте казалось избыточной. Серверная айпишка и так ведь была объявлена и в Vagrantfile, и в hosts, и теперь ещё и в consul.yml. Сколько же можно.

С другой стороны, если мы смогли положить consul_config_dir в инвентарный файл, то, может, оттуда можно и взять чего-нибудь. Например, ansible_host, в котором айпишка хоста и так есть.

В результате получается вот такой вот второй сценарий в нашем consul.yml:

Если кто-то забыл, то servers одна из групп, которые мы объявили в hosts.

ansible-playbook -i hosts consul.yml в свою очередь не сделает никакой новой магии, кроме как положит JSON конфигурацию на единственную машину с Consul сервером.

Шаг 3.3. Настраиваем Consul агентов

Это будет интересно. Сам по себе файл настройки агента даже проще, чем у сервера. Назовём его client.init.json.j2 и будем готовиться отправлять на хосты. Но есть одно «но».

Хотя мы и можем раздобыть айпишку самого агента, снова обратившись к переменной ansible_host, где теперь искать айпишку сервера, consul_server_ip? Не объявлять же её снова в плейбуке.

Оказывается, есть немного чёрной магии, которая может помочь и тут. Вы когда-нибудь задумывались, что означает мистическая строка «TASK [Gathering Facts]», которая постоянно мелькает в выводе ansible-playbook? Ansible, в своей доброте душевной, по-умолчанию выполняет неявный таск, который собирает информацию о хостах, прежде чем их надругать плейбуком. Он выясняет и переменные окружения, и версию операционки, и сетевые интерфейсы. Много чего выясняет. Что ещё здорово, эта информация будет потом сгруппирована по тем же группам, что и хосты в hosts. И её можно использовать! Нужно всего-то найти хоть один хост в группе servers и взять его адрес.

Переменная, в которую стекаются эти данные называется hostvars, и вот, как её можно использовать:

Всё колдунство происходит в строках 9 и 10. Сначала создаём переменную (факт) consul_server, в которую кладём первый хост из группы servers, а во вторую переменную — consul_server_ip —  кладём его айпишку. Всё просто же. Узнать, что ещё можно было бы вытянуть из hostvars можно, например, добавив в плейбук задачу debug. Например, - debug: var=hostvars.

Шаг 3.4. Запускаем все консул-сервисы

Вообще ерунда:

Теперь, запустив плейбук целиком, мы получим готовый для экспериментов Консул кластер по адресу 192.168.99.100:8500:

Consul кластер

Шаг 3.5. Запускаем плейбук из Vagrantfile

Тут придётся подумать и, возможно, пойти на компромиссы. В прошлом посте, когда vagrant настраивал один хост, он очень удобно генерировал свой собственный инвентарный файл, и тем самым решал проблемы и с поиском айпишки, и с конфигурацией SSH. Сейчас же, когда в инвентарнике у нас есть и группы, и переменные, Вагрант скорее накосячит, чем сделает удобство. К счастью, через inventory_path ему можно передать уже существующий файл, так что одна проблема решается просто.

Вторая проблема тоже растёт из дефолтных настроек вагранта. В отличие от ansible-playbook, который работает со всеми хостами параллельно, вагрантовский провижинер будет настраивать хосты по одному. Это не только неэффективно, но и ломает нашу формулу поиска айпишки сервера — consul_server_ip. Если в данный момент настраивается только, например, consul-host-1, то в hostvars кроме него никого и не будет.

Но опять же, есть выход. В ansible провиженере есть опция под названием limit , и если ей поставить "all", то все хосты будут обрабатываться параллельно. Вот как это выглядит по итогу:

Теперь, как  и в прошлом посте, vagrant up создаст и настроит всё, от начала и до конца.

Мораль

Как оказалось, настраивать кластер при помощи Ansible не намного тяжелее, чем одну машину. По ощущениям, вообще всё то же самое. Да, плейбук стал побольше, да и инвентарный файл потолстел, но весь процесс тем не менее остался внятным.

Я особенно счастлив, что выяснил, как динамически находить айпишку consul_server_ip. Ну вот вообще доволен. Конечно, для полного счастья, нужно чтобы и в инвентарном файле вообще не осталось адресов, и все текущие настройки шли исключительно от Vagrant, но для начала и это неплохо.

Все сырцы для этого поста можно найти на github.

Один комментарий к “Настройка кластера машин с Ansible

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *