У меня в проекте есть кластер Docker Swarm, в котором динамически запускаются Jenkins Agents, на которых выполняются все джобы из Jenkins. Почему не в Kubernetes? Потому что держать Кубер-кластер только для джобов - неоптимально, Swarm сильно проще в поддержке. Вот даже есть пост “ Не усложняй”.
Недавно разворачивал Self-Hosted Gitlab, но не нашел официальной инструкции, чтобы запустить gitlab runner в swarm-кластере, чтобы еще autoscaling работал — то есть чтобы runner сам поднимал дополнительные контейнеры на swarm-нодах. Пришлось собирать по крупицам информацию. Вот, делюсь способом.
Инструкция состоит из трех этапов:
- Получить токен для регистрации
- Разложить конфигурации раннеров на машины
- Задеплоить раннеры
Получить Runner Authentication Token #
Раньше для регистрации раннеров использовался runner registration token, но он перешел в статус Deprecated. Gitlab начал использовать Runner Authentication Token для подключения раннеров.
Идем в настройки проекта, группы или всего инстанса, чтобы зарегистрировать новый Runner: Admin Settings -> CI/CD -> Runners -> New instance runner:
Указываем Tags. В последствие мы будем указывать тэги в наших gitlab-ci пайплайнах, чтобы они запускались на определенных раннерах. Например, у меня это тэг swarm
.
На следующей странице копируем токен и сохраняем где-нибудь:
Теперь Gitlab будет ждать, что какой-нибудь раннер зарегистрируется с этим токеном.
Конфигурация раннеров #
Мы можем запустить контейнер с gitlab runner, зайти в него и выполнить указанную команду register
. Gitlab Runner при регистрации сохранит токен и минимальные настройки в файл /etc/gitlab/config.toml
внутри контейнера. Но чтобы не ходить на каждой машине в контейнер и не делать все руками, давай сделаем шаблон конфигурации и разложим его на swarm-ноды.
Готовим конфигурационный файл:
concurrent = 5 # Concurrent containers per node
check_interval = 0
[[runners]]
name = "docker-swarm-runner"
url = "https://<YOUR-GITLAB-INSTANCE>/"
token = "<GITLAB AUTHENTICATION TOKEN>"
executor = "docker"
[runners.custom_build_dir]
[runners.docker]
tls_verify = false
image = "alpine:latest" # Default image for jobs
privileged = true # If we need Docker-in-Docker
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache"]
shm_size = 0
allowed_pull_policies = ["always", "if-not-present", "never"]
[runners.cache]
[runners.cache.s3]
[runners.cache.gcs]
Меняем token на тот, который получили в прошлом этапе. Так же указываем адрес нашего Gitlab-инстанса.
Теперь на каждой swarm-ноде нужно создать директорию для конфигурации и положить туда этот файл. Как это сделать — решайте сами, я использую Ansible.
Копируем конфигурационный файл на ноды #
Вот пример Ansible-кода для копирования конфигурационного файла:
templates/config.toml.j2
:
concurrent = 5
check_interval = 0
[[runners]]
name = "docker-swarm-runner"
url = "{{ gitlab_instance_url }}"
token = "{{ gitlab_runner_swarm_auth_token.value }}"
executor = "docker"
[runners.custom_build_dir]
[runners.docker]
tls_verify = false
image = "alpine:latest" # Default image for jobs, can be overridden in .gitlab-ci.yml
privileged = true # Enable if you need to run Docker-in-Docker or privileged containers
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache"]
shm_size = 0
allowed_pull_policies = ["always", "if-not-present", "never"]
[runners.cache]
[runners.cache.s3]
[runners.cache.gcs]
tasks/main.yml
:
- name: Create directories
ansible.builtin.file:
path: /etc/gitlab-runner/config
state: directory
recurse: true
- name: Copy config file
ansible.builtin.template:
src: config.toml.j2
dest: /etc/gitlab-runner/config/config.toml
Конфигурационный файл должен быть на всех нодах swarm-кластера.
Деплой Gitlab Runner в Swarm #
Swarm работает с привычными docker-compose файлами, поэтому создаем такой. Для копирования я так же пользуюсь Ansible, поэтому вот пример jinja-шаблона:
services:
gitlab-runner:
image: gitlab/gitlab-runner:{{ gitlab_runner_image_version }}
deploy:
mode: global
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /etc/gitlab-runner/config:/etc/gitlab-runner
networks:
- gitlab-net
networks:
gitlab-net:
external: false
Обратите внимание:
- мы указываем deploy mode: global, чтобы контейнер запустился на всех нодах кластера. Это как DaemonSet в Kubernetes.
- мы подключаем директорию с
config.toml
файлом - дополнительно мы монтируем docker socket, чтобы gitlab runner мог управлять нашим docker engine и создавать новые контейнеры (это нужно для работы autoscaling)
- указываем версию gitlab-runner контейнера, посмотреть доступные можно в Docker Hub.
Я копирую этот файл с помощью Ansible:
- name: Copy compose file
ansible.builtin.template:
src: docker-compose.yml.j2
dest: /etc/gitlab-runner/docker-compose.yml
Запускать swarm stack нужно с менеджер-ноды (главной ноды кластера), но сам файл будет лежать на всех нодах. Избыточно, но мне так проще, чтобы не добавлять условия в Ansible.
Идем на менеджер-ноду и выполняем:
docker stack deploy -c docker-compose.yml gitlab-runner
Через минутку проверяем:
root@swarm-node01:/home/ysemyenkov# docker stack ps gitlab-runner
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
pnkxq7yf3wm3 gitlab-runner.1tlong-name gitlab/gitlab-runner:ubuntu-v17.3.0 swarm-node01 Running Running 2 days ago
ih4822qch9ew gitlab-runner.3tlong-name gitlab/gitlab-runner:ubuntu-v17.3.0 swarm-node03 Running Running 2 days ago
xzj5yt1cv5ae gitlab-runner.2tlong-name gitlab/gitlab-runner:ubuntu-v17.3.0 swarm-node02 Running Running 2 days ago
Если все нормально, то в Gitlab мы увидем зарегистрированный Runner:
Проверяем работу #
Можем создать тестовый пайплайн, который запустит десяток джобов одновременно:
stages:
- test
parallel-jobs:
stage: test
tags:
- swarm
script:
- echo "Starting sleep for 600 seconds"
- sleep 600
parallel:
matrix:
- JOB_NAME: "job1"
- JOB_NAME: "job2"
- JOB_NAME: "job3"
- JOB_NAME: "job4"
- JOB_NAME: "job5"
- JOB_NAME: "job6"
- JOB_NAME: "job7"
- JOB_NAME: "job8"
- JOB_NAME: "job9"
- JOB_NAME: "job10"
Запускаем пайплайн и видим, что контейнеры запустились в нужном количестве и на разных нодах. Посмотреть можно как через docker ps
на нодах, так и через средства мониторинга. Например я смотрю метрики cAdvisor в Grafana:
Видим, что три верхних контейнера — это основные процессы gitlab-runner, которые получают задания и создают динамические контейнеры под джобы. А ниже, наши контейнеры с sleep джобами запущенны параллельно на разных нодах.
Вот и всё.
🙋♂️С комментариями можно смело приходить в наш телеграм-чат.
🌀Так же рекомендую подписаться на телеграм-канал, чтобы не пропускать классные посты и анонсы новых статей.
🎬 А видео выходят на Youtube, канал @etogeek.