Автоматическое масштабирование билд-серверов в GitLab CI

На своём рабочем проекте Gitlab CI я использую уже где-то с год, и с большего всё было хорошо. Начинали мы с трёх билд-серверов (GitLab раннеров) под проект, а когда в команде появлялись новые люди, пытающиеся валом коммитов удивить начальство, либо билд-задач просто становилось больше, я добавлял ещё один сервак для возросшей нагрузки, и чувствовал себя героем. Но когда количество серверов пошло на второй десяток, то как-то стало понятно, что такой подход больше не работает.

Во-первых, всё равно случались пиковые нагрузки, с которыми билд-кластер справиться не мог. Хотя бы пару раз в неделю были периоды, когда количество билд-задач было раза в два больше, чем сервера могли вытянуть.

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

В общем, стало ясно, что существующий подход надо на что-то менять. Желательно на что-нибудь динамическое. И что здорово, в GitLab CI -таки встроено автоматическое масштабирование билд-серверов под текущую нагрузку. Её-то мы и будем сегодня пробовать.

Сразу предупреждаю, что я пропущу введение в GitLab, не буду объяснять, что такое GitLab раннер, Vagrant или даже Docker. Писал ведь об этом уже.

Как работает масштабирование в GitLab CI

Идея очень простая. В первом гитлабовском посте, который компилировал TypeScript проект прямо на билд-сервере, для выполнения команд мы использовали shell. Но штука в том, что кроме шелла Гитлаб умеет пользоваться Докером и компилировать проект внутри контейнера. А раз уж у нас есть Докер и контейнеры, то остаётся сделать всего один шаг к docker-machine, которая умеет создавать виртуальные машины с Докером внутри, и делать билды уже там. Виртуальные машины можно делать где угодно и сразу пачками, удалять их тоже не жалко, так что вот оно, масштабирование.
И вот именно это GitLab CI и умеет делать.

Что понадобится

Как минимум нам понадобятся установленные GitLab, docker-machine и VirtualBox. Последние два у меня на Маке уже есть, а для всего остального я заведу виртуальную машину. Виртуалку создадим при помощи Vagrant и внутрь поставим и Гитлаб, и Докер (ведь прежде чем лезть в масштабирование, стоит попробовать собрать хоть один билд локально).

Наконец, чтобы было что билдить, нам нужен какой-нибудь тестовый проект. Думаю, Hello-World, сгенерированный при помощи .NET Core SDK, сгодится на эту роль идеально. Он ведь и компилируется, и Docker образ с установленным SDK можно под него найти.

Устанавливаем GitLab и тестовый проект

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

Настраиваем виртуалку

Для этого сгодится относительно простой Vagrantfile с чуть менее относительно простым provision.sh, который поставит на новую машину и GitLab, и .NET Core SDK, и даже Docker. Вот он какой полезный:

vagrant up в этот раз сработает чуть медленнее, чем обычно, но это-то и понятно — столько всего устанавливаем. Но через пару минут на выходе появится блестящая новая Убунта, по внешней айпишке которой — «192.168.33.10» — можно зайти на не менее блестящий GitLab:

начальное окно логина

Я опущу ту часть, где мы вводили новый рутовый пароль, логинились, заводили репозиторий под проект (мой называется «console-app») — это всё уже было. А приступим сразу к созданию консольного .NET Core проекта.

Создаём консольный .NET Core проект

Это вообще элементарно. Чтобы сделать Hello-World проект на дотнэте, его об этом надо просто попросить.

Круто, правда? Наши отцы ради этого перфокарты прокалывали, а тут всего пара команд. Теперь можно сделать локальный репозиторий (git init), первый коммит (git commit), и отправить его в новоиспечённый гитлаб:

Настраиваем билд шаги

В качестве билд шагов можно скомпилировать проект в Debug и Release конфигурациях, и для настройки этого нужен .gitlab-ci.yml файл.

Если не сделать в нём ошибок, и тоже закоммитить в GitLab, то в последнем на странице проекта, в разделе «Pipelines» появится наш новый билд, который, к сожалению, пока ничего не билдит. Ибо негде. Это сейчас и будем исправлять.

pending build

Настраиваем Docker-раннер

Самый простой способ начать масштабировать билд сервера с docker-machine — это начать делать билды в локальном Docker. Настройки там почти одинаковые, и если уж удалось подобрать правильный образ, на котором билд действительно собирается, то можно менять docker на machine и радоваться жизни.

Для компиляции .NET Core проектов лучше всего подходит майкрософтовский образ microsoft/dotnet:2.0-sdk. Из прошлого поста я взял скрипт, который устанавливал и настраивал раннер для компиляции на локальной машине, поменял буквально пару строк, и теперь он компилирует в контейнере .

Проверяем на странице проекта в GitLab:

running build

Реально в контейнере. Я проверял.

watch -n 1 sudo docker ps:

running container

Первый билд занимает некоторое время, всё-таки Microsoft не мог не создать докер-образ размером с дистрибутив Висты. Но после того, как тот скачается, компиляция проекта занимает считанные секунды.

build succeeded

Итак, компиляция проекта в docker работает, значит можно этот раннер выключать (Settings -> CI/CD -> Runner settings) и переходить к динамическим виртуальным машинам.

Настраиваем docker-machine раннер

Прошлый «docker» раннер мы устанавливали прямо в виртуальной машине, и он не возражал. docker-machine раннер возражать будет, потому что создавать виртуальные машины внутри виртуальных машин попахивает Матрицей, и этого надо избежать. Так что придётся его устанавливать прямо на живом железе Мака.

Я допускаю, что у меня кривые руки, но столько проблем с установкой gitlab-runner сервиса, как на маке, у меня не было даже на винде. Последовательность действий и там и там одинакова, но то созданный файл конфигурации ляжет не туда, и его надо копировать вручную, то сам раннер не зарегистрируется как сервис, и его нужно запускать просто как процесс — gitlab-runner run. В общем, мрак. Но в конечном итоге работает и там.

Как я и обещал, настройка «docker-machine» раннера практически такая же, как и у просто «docker»:

Две строки добавить, одну подправить, и всё.

А теперь можно снова зайти в GitLab, нажать кнопку «retry» напротив нашего завершившегося билда, и вот какую магию можно увидеть в VirtualBox Manager:

runner vm

Блин, это же офигенно! Гитлаб реально создал новую виртуальную машину под конкретный билд, поделал в ней свои дела, и потом удалил. Что любопытно, вывод docker-machine билда — точно такой же, как и у обычного «docker». Только имя раннера отличается.

build docker-machine

Для этого раннера мы использовали VirtualBox в качестве провайдера, но ведь можно было бы в настройках (config.toml), прописать и AWS, и Azure, и что угодно. А потом там же изменить значение ключей Concurrent и Limit, с единицы, например, на 100, и получать до сотни параллельных билдов на временных виртуальных машинах.

И это не всё. Можно держать несколько машин запущенными «про запас» (IdleCount), можно переиспользовать уже созданные машины, можно делать отдельные настройки под рабочее время и нерабочее. Там много чего можно делать.

Мораль

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

Но на самом деле между Hello-World и реальным большим проектом будет некоторая разница. Например, в моих серверах вполне регулярно случается обрабатывать штук 80 параллельных билдов. Если все 80 из них пойдут на виртуальные машины, то CPU может и перестанет быть проблемой для такой нагрузки, но вот сеть и даже сам GitLab, скорее всего, станут.

Pull docker образов и копирование кэшей билдов можно ускорить, положив в локальную сеть свой docker реестр и подружив раннер с, например, AWS S3. Но артифакты билда всё равно придётся гонять между GitLab и виртуальными машинами. Мои сервера и GitLab живут в разных сетях, так что параллельное скачивание пяти гигов репозитория в 80 параллельных потоков, должно быть, будет интересным зрелищем.

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

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

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