Регистрация раннера на практике
Ставим раннер, регистрируем его в проекте и учимся направлять джобы на нужный раннер тегами.
Регистрация раннера — процедура, при которой агент получает токен проекта/группы и начинает опрашивать сервер на наличие джоб.
Установка
Раннер — отдельный бинарник. На Linux его ставят пакетом, а проще всего — запустить как Docker-контейнер:
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Том с config хранит настройки между перезапусками, а проброс docker.sock позволит раннеру запускать контейнеры джоб на хосте.
Стоит остановиться на этих двух томах — за ними прячется половина типичных проблем. Первый, /etc/gitlab-runner, — это «память» раннера: туда после регистрации ляжет config.toml с токеном и параметрами. Если не пробросить этот каталог наружу контейнера, при первом же docker rm вся регистрация испарится, и раннер придётся заводить заново. Второй том, docker.sock, отдаёт раннеру управление демоном Docker хоста: благодаря ему docker-executor может поднимать контейнеры джоб «по соседству». Это удобно, но имеет цену: раннер с доступом к сокету фактически получает права root на хосте, поэтому такой раннер нельзя выставлять на ненадёжные публичные проекты — чужой .gitlab-ci.yml сможет через него хозяйничать на машине.
Флаг --restart always здесь не косметика: раннер должен переживать перезагрузку сервера, иначе после ночного апдейта ОС команда обнаружит, что пайплайны молча перестали запускаться. А -d запускает контейнер в фоне — раннер живёт как демон, тихо опрашивая сервер.
Регистрация
В современных версиях GitLab сначала создают раннер в интерфейсе (Settings → CI/CD → Runners) и получают токен аутентификации, затем регистрируют агента этим токеном:
gitlab-runner register \
--url https://gitlab.com \
--token glrt-XXXXXXXXXXXXXXXXXXXX \
--executor docker \
--docker-image alpine:3.19 \
--description "my-docker-runner"Здесь мы выбрали executor docker и образ по умолчанию alpine:3.19 — он используется, если джоба не указала свой image. После регистрации раннер появится в списке проекта со статусом онлайн.
Параметр --url указывает, к какому инстансу GitLab подключаться: для облака это https://gitlab.com, для корпоративной установки — её внутренний адрес. Токен glrt-… — это не пароль пользователя и не «токен регистрации» из старых версий, а authentication token, заранее созданный в интерфейсе под конкретный раннер. Такой токен уже несёт в себе область видимости (проект, группа или весь инстанс) и список тегов — поэтому современная регистрация короче и безопаснее: вы не передаёте секрет, дающий право плодить новые раннеры, а лишь подключаете один заранее описанный.
Почему по умолчанию выбирают docker, а не shell
При регистрации executor можно было бы поставить shell — тогда команды джобы выполнялись бы прямо в оболочке хоста, без всяких контейнеров. Соблазнительно простой вариант, но коварный: всё, что джоба наставила в систему (глобальные пакеты, временные файлы, переменные окружения), остаётся и влияет на следующие джобы. Рано или поздно это даёт классическое «у меня собиралось, а на CI падает» — потому что у вас на машине что-то было доустановлено руками, а в чистом окружении этого нет. Docker-executor лечит проблему радикально: каждая джоба стартует в свежем контейнере из заявленного образа, состояние не накапливается, и пайплайн воспроизводим хоть через год. Именно поэтому в подавляющем большинстве промышленных установок выбирают docker, а shell оставляют для редких задач вроде управления самим хостом.
Образ по умолчанию тоже выбран не случайно. alpine — крошечный дистрибутив в несколько мегабайт, он быстро скачивается и поднимается, поэтому удобен как запасной для джоб, не указавших свой image. Но помните: в alpine нет привычных bash и многих утилит, так что для реальной сборки джоба почти всегда задаёт собственный, более богатый образ, а дефолтный служит лишь подстраховкой.
Теги: направляем джобы на раннер
У раннера могут быть теги — метки вроде docker, gpu, production. Джоба с ключом tags попадёт только на раннер с такими же тегами. Это способ маршрутизации: тяжёлую сборку — на мощный раннер, деплой — на раннер с доступом в прод-сеть.
build-heavy:
tags:
- gpu
script:
- echo "Сборка на мощном раннере"Если у раннера снят флажок «запускать джобы без тегов», то джобы без tags на него не попадут вовсе. Это частая причина зависших в pending пайплайнов.
Полезно держать в голове наглядную картину того, как сервер раздаёт джобы по тегам. Сервер сопоставляет требования джобы со «способностями» каждого онлайн-раннера и отдаёт работу только совпавшему:
джоба tags:[gpu] ┌───────────────┐
───────────►│ GitLab │
│ очередь джоб │
└──────┬────────┘
│ подбор по тегам
┌────────────────┼────────────────┐
▼ ▼ ▼
runner[docker] runner[gpu] runner[deploy]
не подходит ВЫПОЛНЯЕТ не подходитТеги стоит проектировать как «возможности» раннера, а не как его имя. Хорошие теги — gpu, linux, arm64, prod-network: они описывают, что раннер умеет, и джоба явно заявляет, что ей нужно. Плохие теги — вроде runner-7 или ivan-laptop: они привязывают пайплайн к конкретной железке, и при её замене придётся править весь .gitlab-ci.yml. Когда тегов несколько, джоба попадёт только на раннер, у которого есть все перечисленные теги — это логическое И, а не ИЛИ, и об этом часто забывают, потом удивляясь зависшему пайплайну.
Как работает под капотом
После регистрации раннер пишет свой токен и настройки в config.toml. Дальше он в цикле шлёт серверу запрос «дай джобу для моих тегов». Сервер сопоставляет ожидающие джобы с тегами и возможностями раннеров и отдаёт подходящую. Раннер скачивает образ, поднимает контейнер и выполняет джобу, периодически отправляя логи обратно — поэтому вывод в интерфейсе появляется в реальном времени.
Если сравнить с GitHub Actions, регистрация там устроена похоже, но с другими акцентами. Self-hosted раннер в GitHub тоже скачивают, распаковывают и привязывают токеном к репозиторию или организации, однако маршрутизация делается через labels и поле runs-on, а сам агент по умолчанию выполняет шаги прямо на хосте, без обязательного контейнера. В GitLab же связка «executor + config.toml + теги» делает поведение раннера более явным и настраиваемым: вы прямо в конфиге задаёте, как изолировать джобу, какой образ брать по умолчанию и сколько джоб крутить параллельно. Эта явность — общая черта GitLab CI: меньше «магии по умолчанию», больше строк, которые вы контролируете сами.
Частые ошибки
- Джоба висит в pending: либо нет онлайн-раннера с нужными тегами, либо раннер не принимает джобы без тегов.
- Забыть проброс
docker.sockили настроить privileged-режим — тогда docker-команды внутри джоб не работают (об этом подробнее в разделе про Docker-in-Docker). - Регистрировать раннер устаревшей командой с
--registration-token: в новых версиях используется токен аутентификации, созданный в UI.
Итоги
- Раннер ставится бинарником или контейнером, настройки живут в
config.toml. - Регистрация связывает агента с проектом через токен и задаёт executor и образ по умолчанию.
- Теги маршрутизируют джобы на конкретные раннеры; несоответствие тегов — частая причина зависших джоб.