У нас в профсоюзе недавно появился сервис, который используется для создания публикаций в социальных сетях. Проект находится в GitLab, но с недавних пор использовать Shared Runners в GitLab не представляется возможным, поскольку для этого требуется подключить банковскую карту, а российские карты не принимаются. Открыть счёт в банке другой страны непросто, поэтому было решено развернуть GitLab Runner в Yandex Cloud, поскольку сервис уже развёрнут в Yandex Serverless Containers.
Развернуть GitLab Runner в Yandex Cloud можно в Managed Service for GitLab или на виртуальных машинах (далее ВМ). Сравнение этих сервисов в схожих конфигурациях:
Чистая ВМ — наиболее подходящий вариант, поскольку Managed Service for GitLab и ВМ с образом GitLab обходятся относительно дороже и имеют излишнюю функциональность для текущей задачи. Кроме того, чистая ВМ более гибкая в выборе вычислительных ресурсов, так что стоимость использования может быть ниже. Например, следующая конфигурация обойдётся в 754,65 / месяц:
Но было бы слишком расточительно оставлять ВМ запущенной круглые сутки, ведь работа GitLab Runner может занимать всего несколько минут. Поэтому после недолгих раздумий был придуман финт ушами функциями.
Суть финта: запускать ВМ только при создании нового пайплайна в проекте, а в остальное время держать в выключенном состоянии. В таком случае стоимость использования ВМ сократится до пары сотен рублей. Диаграмма последовательности для такой функциональности представлена ниже.
Пререквизиты
Необходим аккаунт в Yandex Cloud и репозиторий в GitLab. В этом примере репозиторий уже содержит приложение и Dockerfile к нему. Требуется собрать и протестировать приложение, затем собрать Docker-образ и опубликовать его в реестре проекта.
Заострять внимание на самом проекте нет смысла, так как приложение роли не играет. Если у вас другой стек, то изменения будут в Dockerfile и .gitlab-ci.yml. В данном случае приложение — это Web API на ASP.NET Core, сгенерированное командой:
dotnet new webapi –use-minimal-apis –no-openapi
Создание и настройка ВМ в Compute Cloud
Создание ВМ в Compute Cloud
Переходим в раздел Compute Cloud / Виртуальные машины и нажимаем «Создать ВМ».
Присваиваем имя, выбираем зону доступности и ОС, которая больше нравится. Например, Ubuntu:
Настраиваем диск и вычислительные ресурсы, исходя из потребностей. Для простых приложений, в том числе и для нашего примера, подойдёт 20% гарантированной доли двух vCPU и 4 Гб RAM.
Выбираем сервисный аккаунт с ролью compute.operator. Этой роли достаточно для запуска и остановки ВМ. Если такого сервисного аккаунта нет, то создаём. Этот аккаунт необходим для управления ВМ из функции, которая будет создана далее.
Вводим логин для пользователя ОС, SSH-ключ для подключения по SSH и нажимаем «Создать ВМ».
Дожидаемся создания ВМ и переходим к следующему шагу.
Установка GitLab Runner
Сперва подключимся к ВМ. Для этого копируем публичный IP-адрес ВМ и подключаемся по SSH.
Есть несколько способов установить GitLab Runner. В нашем примере будет использоваться Docker, так как это наиболее универсальный вариант. Инструкция по установке Docker Engine для разных ОС лежит тут.
После завершения установки добавляем Docker в автозагрузки, чтобы не приходилось запускать каждый раз руками:
sudo systemctl enable docker
Осталось загрузить и запустить образ с GitLab Runner. Сделаем это с подключением локальных томов.
docker run -d --name gitlab-runner --restart always \ -v /srv/gitlab-runner/config:/etc/gitlab-runner \ -v /var/run/docker.sock:/var/run/docker.sock \ gitlab/gitlab-runner:latest
Отключаться от ВМ не нужно, так как ещё необходимо зарегистрировать раннер. Как это сделать, будет показано позже, при настройке GitLab.
Создание и настройка функции в Cloud Functions
Создание функции
Переходим в раздел Cloud Functions / Функции и нажимаем «Создать функцию».
Присваиваем имя функции и нажимаем «Создать»:
Выбираем среду выполнения. В этом примере используется Node.js. Затем снимаем галочку с «Добавить файлы с примерами кода» и нажимаем «Продолжить».
Настройка функции
Нажимаем «Создать файл», присваиваем файлу имя, например, index.js и вставляем код функции. Затем определяем точку входа. Формат должен быть такой:
<имя файла без расширения>.<имя функции>
Аналогичным образом создаём package.json. Этот файл необходим, чтобы указать зависимость на Node.js SDK от Yandex.
Результат выглядит примерно так:
В параметрах функции указываем сервисный аккаунт с ролью compute.operator, который был выбран при создании ВМ. Через этот аккаунт функция будет запускать и останавливать ВМ. Остальные значения оставляем по умолчанию:
Осталось нажать «Создать версию», дождаться обновления функции и сделать её публичной:
Прежде чем идти дальше, разберём, что происходит в index.js.
Сравнивает значение заголовка X-Gitlab-Token со значением в константе gitlabToken для того, чтобы функция обрабатывала запросы только от нашего репозитория. Значение токена можно сгенерировать любым удобным способом: GUID-генератор, генератор паролей и т.д.
Парсит тело запроса и проверяет значение object_kind. Обрабатываться должны только события от pipeline. Остальные игнорируются.
Проверяет значение detailed_status. Если в проекте создан новый pipeline, то статус у него будет pending. В таком случае запускаем ВМ. Если статус passed, значит, работа pipeline завершена успешно, можно останавливать ВМ.
Функции startInstance и stopInstance:
Создают новую сессию с токеном сервисного аккаунта, который мы указали при создании функции. Данные этого аккаунта будут доступны в параметре context.
Создают запрос на запуск и остановку ВМ соответственно.
Отправляют запрос в инстанс, который указан в константе virtualMachineInstanceId.
Настройка проекта GitLab
Регистрация и настройка раннера
Возвращаемся к ВМ. Для регистрации необходимо выполнить одну из команд из этой инструкции. Для нашего примера:
docker run --rm -it -v /srv/gitlab-runner/config:/etc/gitlab-runner gitlab/gitlab-runner register
После чего будет предложено определить значения для параметров конфигурации.
URL и токен находим на странице Settings / CI/CD:
Указываем при регистрации теги, которые описаны в файле .gitlab-ci.yml, иначе раннер не будет брать джобы.
Если теги не используются, то после завершения регистрации указываем раннеру брать джобы без тегов:
В результате в проекте появится доступный раннер.
Поскольку в этом примере будет собираться Docker-образ, включаем privileged mode в настройках раннера. Для этого флагу privileged присваивается значение true в файле /srv/gitlab-runner/config/config.toml
Создание webhook
Переходим в раздел Settings / Webhook. Тут вводим URL функции, затем токен, который указали в константе gitlabToken, после чего выбираем триггер pipeline и нажимаем Add webhook:
Проверяем работу CI/CD
Сперва остановим ВМ. После этого создадим новую ветку в репозитории, изменим какой-нибудь файл и сделаем merge request.
Все три стейджа отработали без ошибок:
В реестре контейнеров создан Docker-образ:
В логах функции видно, что с момента запуска ВМ до завершения работы CI/CD прошло примерно 5 минут 20 секунд.
Анализ решения
Ниже приведена стоимость использования одной виртуальной машины с GitLab Runner в Compute Cloud за неделю. Если экстраполировать такое потребление на месяц, то выйдет примерно 200-250 рублей, что дешевле использования полноценной ВМ.
Основной ресурс, на который уходит бюджет — хранилище. Если сумма в 200-250 рублей — всё ещё много, то нужно сменить тип хранилища с SSD на HDD.Также в качестве платформы можно выбрать Intel Cascade Lake, так как она позволяет снизить гарантированную долю vCPU до 5%. Стоимость такой ВМ будет на 30-40% ниже, но и производительность её также будет ниже.
Относительно простая конфигурация, которая легко настраивается.
Невысокая стоимость ресурсов облака, подходит для небольших pet-проектов.
Если на одной ВМ развёрнуты раннеры от нескольких проектов, то нужно усложнять логику включения / отключения ВМ, так как ВМ может внезапно отключиться при завершении работы одного из раннеров.