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».
Мы установили плагин, но, прежде чем настраивать его, нужно выполнить несколько подготовительных действий.
- Создать отдельного пользователя в консоли VK Cloud Solutions (не обязательно). Хоть это и не обязательно, мы рекомендуем так сделать, чтобы у этого аккаунта не было полномочий владельца всего проекта. Как создать нового пользователя и назначить ему роль, описано в справке.
- Установить OpenStack Client. Его можно установить либо на свою локальную машину, либо на мастер с Jenkins, это неважно. Утилита поможет нам узнать один из параметров подключения к облаку.
- Скачать файл 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 бонусных рублей. Вам может потребоваться расширение квот, обратитесь в техподдержку.
Что еще почитать: