The city of colors by soon38

Jenkins — популярная CI/CD-система. Она хорошо масштабируется горизонтально за счет распределения нагрузки между несколькими slave-нодами. Но не всегда легко заранее определить, сколько же нод нужно. Обычно их либо слишком мало, и тогда очередь сборки постоянно растет, и это тормозит разработку, — либо слишком много, и тогда ресурсы простаивают впустую.

Я Павел Селиванов, Architect и Developer Advocate в VK Cloud Solutions. Я покажу, как настроить Jenkins в облаке, чтобы нод всегда было столько, сколько нужно. Если задач будет много, новые ноды создадутся автоматически. Когда задач станет мало, простаивающие ноды удалятся. Для этого мы установим в Jenkins плагин, который умеет подключаться к любому OpenStack-облаку, создавать и настраивать в нем виртуальные машины.

Если у вас уже развернут Jenkins-мастер в облаке, вы можете сразу переходить к шагу 3 «Установка и настройка плагина OpenStack Cloud».

Шаг 1. Подготовка инфраструктуры


Для начала мы создадим мастер-сервер Jenkins. Я буду показывать этот процесс на примере нашей платформы VK Cloud Solutions.

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

В панели управления VK Cloud Solutions перейдем в раздел «Облачные вычисления» — «Виртуальные машины» и создадим новый сервер.



Для нашего мастера будет достаточно 2 CPU, 2 ГБ оперативной памяти и 10 ГБ диска. Я буду показывать установку на примере CentOS, поэтому выбираем этот дистрибутив.

На следующем экране нужно настроить сеть. Тут важно выбрать нужный SSH-ключ, а также в настройках Firewall выбрать правило «ssh», чтобы открыть 22-й порт. Также поставьте галочку «Назначить внешний IP», чтобы у мастера был IP-адрес, доступный из интернета.



Веб-интерфейс управления мастером Jenkins запускается на порте 8080. По умолчанию в настройках сети на нашей платформе этот порт закрыт. Поэтому пока создается виртуальная машина, давайте создадим новое правило файрвола.

В панели управления VK Cloud Solutions переходим в раздел «Виртуальные сети» — «Настройки Firewall» и нажимаем кнопку «Добавить». Указываем имя, нажимаем «Создать группу».



На следующем экране в разделе «Входящий трафик» нажимаем «Добавить правило». Появляется окно добавления нового правила, где нам нужно выбрать тип HTTP, протокол TCP и порт 8080.



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

Теперь нужно применить это правило к мастер-серверу. Для этого в блоке «Виртуальные машины с группой правил» нажмем кнопку «Добавить» и выберем сервер, который мы недавно создали.



Шаг 2. Установка и настройка Jenkins-мастера


Инфраструктура готова, теперь нужно подключиться к серверу и установить Jenkins. Для начала нам нужно узнать публичный адрес виртуальной машины, чтобы подключиться к ней по SSH. Для этого в панели управления VK Cloud Solutions заходим в настройки виртуальной машины, на вкладке «Общая информация» есть два адреса. Сейчас нам понадобится только внешний адрес, но чуть позже пригодится и внутренний.



Итак, подключаемся к виртуальной машине по SSH:

$ ssh centos@<VM_PUBLIC_IP>

Обновляем систему, устанавливаем зависимости и сам Jenkins:

$ sudo yum upgrade -y
$ sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
$ sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key 
$ sudo yum install epel-release java-11-openjdk-devel -y
$ sudo yum install jenkins -y

Запускаем Jenkins и прописываем его в автозагрузку, чтобы он стартовал при каждом запуске ВМ:

$ sudo systemctl daemon-reload
$ sudo systemctl start jenkins
$ sudo systemctl enable jenkins

Теперь выполним начальную настройку Jenkins-мастера. Для этого в браузере перейдем по адресу:

http://<VM_PUBLIC_IP>:8080

При первом использовании нужно разблокировать Jenkins — ввести начальный пароль администратора.



Чтобы узнать пароль, выполним команду на виртуальной машине:

$ sudo cat /var/lib/jenkins/secrets/initialAdminPassword

На следующем экране выбираем опцию «Install suggested plugins», чтобы автоматически установить рекомендуемые плагины. Затем задаем логин и пароль для доступа интерфейсу.

На следующем экране нужно указать URL-адрес инстанса Jenkins. По умолчанию указан публичный адрес виртуальной машины. Но этот адрес будет использоваться для подключения slave-нод, и нам придется открывать наружу еще один порт. Как минимум это не совсем безопасно, да и зачем перегонять лишний раз трафик в интернет, если все машины будут работать в одной сети. Поэтому мы укажем приватный адрес виртуальной машины, который находится там же, где мы брали публичный адрес.



Все, Jenkins-мастер настроен и готов к работе.

Шаг 3. Установка и настройка плагина OpenStack Cloud


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

Для этого переходим в меню «Настроить Jenkins» — «Управление плагинами», вкладка «Доступные». Находим плагин «OpenStack Cloud» и устанавливаем его с помощью кнопки «Install without restart».



Мы установили плагин, но, прежде чем настраивать его, нужно выполнить несколько подготовительных действий.
  1. Создать отдельного пользователя в консоли VK Cloud Solutions (не обязательно). Хоть это и не обязательно, мы рекомендуем так сделать, чтобы у этого аккаунта не было полномочий владельца всего проекта. Как создать нового пользователя и назначить ему роль, описано в справке.
  2. Установить OpenStack Client. Его можно установить либо на свою локальную машину, либо на мастер с Jenkins, это неважно. Утилита поможет нам узнать один из параметров подключения к облаку.
  3. Скачать файл openrc из консоли VK Cloud Solutions, он нужен для OpenStack-клиента. Для этого перейдите в панель управления VK Cloud Solutions, затем в правом верхнем меню нажмите на название вашего аккаунта и перейдите в раздел «Настройки проекта», а затем на вкладку «API ключи». Скачайте файл, а затем примените его на той машине, где у вас установлен клиент OpenStack:

$ source default.ms-openrc.sh

Оставьте эту вкладку открытой, она нам еще пригодится.



Теперь мы готовы настраивать подключение плагина к облаку. Возвращаемся на главную страницу Jenkins, переходим в раздел «Настроить Jenkins» — «Управление средами сборки» — «Configure Clouds» и добавляем новое облако типа OpenStack.

Имя можно указать любое, а Endpoint URL и Region скопируйте из панели управления VK Cloud Solutions — это та самая вкладка, откуда мы скачивали файл openrc.



Затем в разделе Credential создаем новую запись. Выбираем тип «OpenStack auth v3». Чтобы заполнить поле Project Domain, нужно выполнить команду:

$ openstack domain show <Project Domain ID>

Project Domain ID нужно взять из панели управления VK Cloud Solutions. В выводе команды будет поле name, которое нам и нужно.



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

Поля Project Name, User Domain и User Name копируем из панели управления VK CS, а в поле Password вводим пароль от учетной записи. Поля Id и Description заполнять не обязательно.



Сохраняем и на следующем экране нажимаем «Test Connection». Если все настроено правильно, то должно вернуться «Connection succeeded».



Далее нужно указать порт для конфигурации слейвов по протоколу JNLP (Java Network Launching Protocol). С его помощью slave-ноды будут подключаться к мастеру, скачивать с него необходимое ПО и конфигурации.

Для этого переходим в меню «Настроить Jenkins» — «Глобальные настройки безопасности», блок Agents. Указываем порт 8081.



Теперь настроим шаблон для создания slave-нод. Для начала создадим cloud-config, который будет выполняться на виртуальных машинах при их первом запуске. С помощью этого скрипта мы установим нужные зависимости, создадим рабочую директорию для slave, скачаем jar-файл с мастера и запустим его.

Переходим в меню «Настроить Jenkins» — «Managed files» — «Add a new config». Выбираем тип OpenStack User Data и в поле Content вставляем скрипт:

#cloud-config
runcmd:
- yum install epel-release java-11-openjdk-devel -y
- mkdir -p "${SLAVE_JENKINS_HOME}"
- wget "${SLAVE_JAR_URL}" -O "${SLAVE_JENKINS_HOME}/slave.jar"
- cd "${SLAVE_JENKINS_HOME}"
- java ${SLAVE_JVM_OPTIONS} -jar "${SLAVE_JENKINS_HOME}/slave.jar" -jnlpUrl "${SLAVE_JNLP_URL}" -secret "${SLAVE_JNLP_SECRET}"

Обратите внимание, что тут мы используем встроенные в Jenkins переменные для получения IP-адреса мастера, пути для скачивания jar-файлов и параметров запуска slave-сервера. Эти переменные не нужно менять, они подставятся сами во время запуска скрипта.

Этот шаг можно упростить и даже ускорить создание новых slave-нод. Вместо того чтобы устанавливать зависимости и Jenkins через скрипт cloud-config, можно создать собственный образ ОС, включить в него все необходимое и создавать ноды из этого образа. Это можно сделать, например, с помощью Packer. Я не буду усложнять эту инструкцию, но, если вам интересно, можете  почитать документацию.

Теперь можно создавать шаблон для новых виртуальных машин. Для этого переходим в меню «Настроить Jenkins» — «Управление средами сборки» — «Configure Clouds» — «Add Template». Затем нажимаем кнопку «Provisioning Details».

Тут нужно указать, из какого образа создать виртуальную машину, в какой конфигурации, какую сеть подключить и так далее. Я расскажу только об обязательных и важных параметрах, а полный список с описанием вы можете почитать в документации плагина.
  • Name: любое удобное для вас имя.
  • Labels: vkcs. Это важно, потому что потом при запуске задач мы будем ориентироваться на этот лейбл.
  • Boot Source: выбираем Image и CentOS той же версии, что и мастер (в нашем случае 7.9).
  • Hardware: желаемая конфигурация серверов (CPU, RAM, диск).
  • Network: нужно указать ту же сеть, в которой находится мастер. Плагин не подтягивает список облачных сетей, поэтому придется сходить в панель управления VK Cloud Solutions и скопировать название сети, в которой находится мастер.
  • User Data: выбираем скрипт cloud-config, который мы создавали ранее.
  • Min. No. of Instances: минимальное количество slave-нод. По умолчанию минимальное количество нод — 0. Это значит, что все slave-ноды будут удаляться через некоторое время после простоя. Но когда появятся новые задачи, на создание нод потребуется какое-то время. Поэтому можно установить значение 1, чтобы всегда была одна дежурная нода. Мы оставим значение по умолчанию, чтобы в конце продемонстрировать удаление простаивающей ноды.
  • Max. No. of Instances: максимальное количество slave-нод. Если свободных нод нет, то для каждой задачи Jenkins будет создавать новые ноды до тех пор, пока количество нод не дойдет до максимального значения. Тогда все новые задачи будут ждать в очереди и их будут брать первые освободившиеся ноды.
  • Availability Zone: зона доступности. Выберите такую же, как у мастер-сервера.
  • Retention Time: время, после которого простаивающая нода удаляется. Когда slave выполнит задачу, он ждет указанное время и, если ему не поступает новых задач, удаляется. По умолчанию 30 минут, мы оставим так же.
  • Connection type: выбираем JNLP.



Все, плагин настроен и готов к работе.

Шаг 4. Тестирование автоматического создания slave-нод


Теперь можно протестировать нашу настройку. Мы добавим одну простую задачу, под выполнение которой автоматически создастся новая slave-нода.

Переходим на главную страницу Jenkins, выбираем пункт меню «Создать Item». Указываем имя задачи и выбираем «Создать задачу со свободной конфигурацией».

В поле «Label Expression» указываем vkcs (обратите внимание, что чек-бокс «Ограничить лейблы сборщиков» должен быть отмечен). Важно, что тут должно быть то же самое имя, которое мы указывали при создании шаблона.

В блоке «Триггеры и сборки» выбираем «Запускать периодически» и указываем расписание в cron-формате. Например, запуск каждые 10 минут:

*/10 * * * *

В блоке «Сборка» выбираем «Добавить шаг сборки» — «Выполнить команду shell». И в поле команды вводим простой скрипт, например:

echo "Hello from Jenkins slave $HOSTNAME"



Чтобы не ждать первого запуска задачи, можно в левом меню нажать «Собрать сейчас», чтобы сразу запустить задачу.

В левой панели появилась задача в состоянии Pending. Она ожидает запуска slave-ноды:
 


В панели управления VK Cloud Solutions можно увидеть, что появилась новая виртуальная машина:



А если сейчас пойти в меню «Настроить Jenkins» — «Управление средами сборки», то там тоже можно увидеть, что добавился новый slave, но пока он в состоянии с ошибкой. Когда выполнится cloud-init-скрипт, нода перейдет в статус Active и примет нашу задачу.



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



Мы увидим в консоли вывод нашего скрипта:

Hello from Jenkins slave my-template-0.novalocal

Если теперь удалить задачу и подождать 30 минут, то, согласно нашим настройкам шаблона, виртуальная машина в облаке VK Cloud Solutions удалится. Если поймать момент, можно увидеть соответствующий статус:



Готово. Мы настроили автоматическое создание slave-нод в Jenkins, развернутом в облаке. Наверное, что-то подобное можно сделать и на bare-metal, но все придется автоматизировать самим: писать скрипты, разрабатывать код и тестировать.

Если вы хотите попробовать этот сценарий сами, зарегистрируйтесь на платформе VK Cloud Solutions и получите 3000 бонусных рублей. Вам может потребоваться расширение квот, обратитесь в техподдержку.

Что еще почитать:

Комментарии (0)