На своём рабочем проекте 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. Вот он какой полезный:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure("2") do |config| config.vm.box = "ubuntu/xenial64" config.vm.network "private_network", ip: "192.168.33.10" config.vm.provider "virtualbox" do |vb| vb.memory = "3064" end config.vm.provision "shell", path: "provision.sh" end |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
apt-get update # "IDE" apt-get install -y vim git # Install docker apt-get install -y \ linux-image-extra-$(uname -r) \ linux-image-extra-virtual apt-get install -y \ apt-transport-https \ ca-certificates \ curl \ software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" apt-get update apt-get install -y docker-ce # Install GitLab apt-get install -y openssh-server curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | bash export EXTERNAL_URL="http://192.168.33.10" apt-get install -y gitlab-ce # .NET Core 2.0 SDK curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-xenial-prod xenial main" > /etc/apt/sources.list.d/dotnetdev.list' apt-get update apt-get install -y dotnet-sdk-2.0.0 |
vagrant up
в этот раз сработает чуть медленнее, чем обычно, но это-то и понятно — столько всего устанавливаем. Но через пару минут на выходе появится блестящая новая Убунта, по внешней айпишке которой — «192.168.33.10» — можно зайти на не менее блестящий GitLab:
Я опущу ту часть, где мы вводили новый рутовый пароль, логинились, заводили репозиторий под проект (мой называется «console-app») — это всё уже было. А приступим сразу к созданию консольного .NET Core проекта.
Создаём консольный .NET Core проект
Это вообще элементарно. Чтобы сделать Hello-World проект на дотнэте, его об этом надо просто попросить.
1 2 3 4 5 6 7 |
vagrant ssh # заходим в виртуальную машину #ubuntu@ubuntu-xenial mkdir console-app cd console-app/ dotnet new console dotnet run #"Hello World!" |
Круто, правда? Наши отцы ради этого перфокарты прокалывали, а тут всего пара команд. Теперь можно сделать локальный репозиторий (git init), первый коммит (git commit), и отправить его в новоиспечённый гитлаб:
1 2 |
git remote add origin http://192.168.33.10/root/console-app.git git push --set-upstream origin master |
Настраиваем билд шаги
В качестве билд шагов можно скомпилировать проект в Debug и Release конфигурациях, и для настройки этого нужен .gitlab-ci.yml
файл.
1 2 3 4 5 6 7 8 9 10 11 |
Build: tags: - dotnetcore-2-sdk script: - dotnet build -c Debug Build in Release: tags: - dotnetcore-2-sdk script: - dotnet build -c Release |
Если не сделать в нём ошибок, и тоже закоммитить в GitLab, то в последнем на странице проекта, в разделе «Pipelines» появится наш новый билд, который, к сожалению, пока ничего не билдит. Ибо негде. Это сейчас и будем исправлять.
Настраиваем Docker-раннер
Самый простой способ начать масштабировать билд сервера с docker-machine — это начать делать билды в локальном Docker. Настройки там почти одинаковые, и если уж удалось подобрать правильный образ, на котором билд действительно собирается, то можно менять docker на machine и радоваться жизни.
Для компиляции .NET Core проектов лучше всего подходит майкрософтовский образ microsoft/dotnet:2.0-sdk. Из прошлого поста я взял скрипт, который устанавливал и настраивал раннер для компиляции на локальной машине, поменял буквально пару строк, и теперь он компилирует в контейнере .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# Download gitlab-runner executable sudo wget -O /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64 sudo chmod +x /usr/local/bin/gitlab-runner # Create new user for runner sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash # Connect the runner to GitLab server sudo gitlab-runner register \ -u http://192.168.33.10/ci \ # GitLab CI URL -r psu73HL3bCXbGj4dXcay \ # GitLab CI Token -n \ --executor docker \ # This is the magic! --docker-image "microsoft/dotnet:2-sdk" \ --tag-list "dotnetcore-2-sdk"\ --name "Docker Runner" # Register and run sudo gitlab-runner install --user gitlab-runner --working-directory /home/gitlab-runner sudo gitlab-runner start |
Проверяем на странице проекта в GitLab:
Реально в контейнере. Я проверял.
watch -n 1 sudo docker ps:
Первый билд занимает некоторое время, всё-таки Microsoft не мог не создать докер-образ размером с дистрибутив Висты. Но после того, как тот скачается, компиляция проекта занимает считанные секунды.
Итак, компиляция проекта в docker работает, значит можно этот раннер выключать (Settings -> CI/CD -> Runner settings) и переходить к динамическим виртуальным машинам.
Настраиваем docker-machine раннер
Прошлый «docker» раннер мы устанавливали прямо в виртуальной машине, и он не возражал. docker-machine раннер возражать будет, потому что создавать виртуальные машины внутри виртуальных машин попахивает Матрицей, и этого надо избежать. Так что придётся его устанавливать прямо на живом железе Мака.
Я допускаю, что у меня кривые руки, но столько проблем с установкой gitlab-runner
сервиса, как на маке, у меня не было даже на винде. Последовательность действий и там и там одинакова, но то созданный файл конфигурации ляжет не туда, и его надо копировать вручную, то сам раннер не зарегистрируется как сервис, и его нужно запускать просто как процесс — gitlab-runner run
. В общем, мрак. Но в конечном итоге работает и там.
Как я и обещал, настройка «docker-machine» раннера практически такая же, как и у просто «docker»:
1 2 3 4 5 6 7 8 9 10 |
sudo gitlab-runner register \ -u http://192.168.33.10/ci \ -r psu73HL3bCXbGj4dXcay \ -n \ --executor docker+machine \ --docker-image "microsoft/dotnet:2-sdk" \ --machine-machine-driver "virtualbox" \ --machine-machine-name "%s" \ --tag-list "dotnetcore-2-sdk"\ --name "docker-machine runner" |
Две строки добавить, одну подправить, и всё.
А теперь можно снова зайти в GitLab, нажать кнопку «retry» напротив нашего завершившегося билда, и вот какую магию можно увидеть в VirtualBox Manager:
Блин, это же офигенно! Гитлаб реально создал новую виртуальную машину под конкретный билд, поделал в ней свои дела, и потом удалил. Что любопытно, вывод docker-machine билда — точно такой же, как и у обычного «docker». Только имя раннера отличается.
Для этого раннера мы использовали 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 параллельных потоков, должно быть, будет интересным зрелищем.
Но без автоматического масштабирования билд-серверов уже не обойтись, так что буду экспериментировать.