Logo Craft Homelab Docs Контакты Telegram
GitLab Runner: выбор executor для CI-инфраструктуры Трендовые github проекты в нашем телеграм канале. Подпишись →
19 июля 2026 г.

Как подобрать среду выполнения для GitLab CI

GitLab хранит код, разбирает .gitlab-ci.yml, строит очередь и показывает статус пайплайна. Команды из job выполняет отдельный процесс — GitLab Runner. Это бинарный сервис, который периодически запрашивает работу у GitLab, получает одноразовый CI_JOB_TOKEN, забирает репозиторий и артефакты, запускает скрипты через выбранный executor, затем отправляет логи и результат.

У такой схемы есть важное свойство для закрытой инфраструктуры: соединение инициирует Runner. Серверу с runner обычно не требуется принимать входящие подключения из GitLab. Но безопасность и стоимость CI определяются тем, где именно исполняется job и кому доступна эта машина.

Сначала определить область видимости

Runner можно привязать к проекту, группе или всему GitLab-инстансу. Это решение влияет на изоляцию, использование ресурсов и права доступа к секретам.

Instance runner доступен проектам всего инстанса. Он удобен для повторяющихся задач: тестов, сборки контейнеров, публикации типовых артефактов. Такой пул позволяет не держать отдельную машину для каждого проекта. При этом ему нужны строгие ограничения: к нему потенциально попадёт код из большого числа репозиториев.

Group runner обслуживает группу и её подгруппы. Это практичный вариант для команды или направления, которым требуется общий набор образов, кэшей и правил доступа, но нет смысла ждать настройки от администратора инстанса.

Project runner предназначен для одного проекта. Его стоит выбирать для деплоя в production, задач с отдельными учётными данными, GPU и долгих сборок, которым требуется гарантированная ёмкость. Форки не наследуют runners родительского проекта, что уменьшает риск запуска недоверенного кода с доступом к его секретам.

Теги дополняют эту модель. Job с tags: [docker, linux] получит только runner, у которого есть оба тега. Для нетегированных job у runner должно быть явно разрешено выполнение таких задач. Полезно заранее разделить теги для сборки, тестов и деплоя: это делает маршрутизацию пайплайна наблюдаемой и не даёт опасной job случайно попасть на deploy-хост.

Shell: узкая специализация и высокий уровень доверия

Shell executor выполняет команды на хосте от имени пользователя gitlab-runner. Он прост в установке и хорошо подходит для контролируемого деплоя на конкретный сервер:

deploy:
  tags: [deploy-runner]
  script:
    - systemctl --user restart my-app
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Цена простоты — отсутствие изоляции. Job видит файловую систему хоста, может оставить зависимости и рабочие каталоги, а также пересечься с другими проектами. Добавление пользователя runner в группу docker фактически открывает job доступ уровня root через Docker socket. Поэтому shell разумно выделять под один доверенный проект и понятную операцию, например деплой. Для общего пула сборок он создаёт слишком большую поверхность атаки.

Docker как базовый вариант

Docker executor запускает каждую job в отдельном контейнере из образа, указанного в image:. Вспомогательный контейнер получает исходники, работает с кэшем и артефактами; сервисы из services: позволяют поднять рядом PostgreSQL, Redis или другой компонент для тестов. После job контейнеры удаляются, поэтому следующая сборка получает чистую среду.

test:
  tags: [docker-runner]
  image: python:3.13-slim
  services:
    - postgres:17
  variables:
    POSTGRES_PASSWORD: test
  script:
    - pip install -r requirements.txt
    - pytest

Ключевая эксплуатационная задача здесь — поддерживать образы с закреплёнными версиями инструментов и регулярно обновлять их. В config.toml полезно задать дефолтный образ, ограничение параллелизма и кэш. Контейнерная изоляция делает Docker хорошим выбором для тестов и обычных сборок, однако она не отменяет риски привилегированного режима.

Сборка образов внутри job требует отдельного решения. Docker-in-Docker обычно включает privileged = true; это удобно для изолированных параллельных сборок, но ослабляет защиту. Проброс /var/run/docker.sock даёт job полный контроль над Docker на хосте и также не подходит для кода с низким уровнем доверия. Для чувствительных pipelines лучше рассмотреть rootless BuildKit либо одноразовые машины.

Kubernetes: job в отдельном pod

Если Kubernetes уже используется в инфраструктуре, Kubernetes executor естественно продолжает эту модель. Для каждой job создаётся pod с основным контейнером, helper и сервисами. Контейнеры разделяют сетевое пространство pod, а ресурсы можно задавать через requests и limits.

build-fat-thing:
  tags: [k8s]
  variables:
    KUBERNETES_CPU_REQUEST: "4"
    KUBERNETES_MEMORY_REQUEST: "8Gi"
  script:
    - make build

Runner обычно разворачивают Helm chart в отдельном namespace и передают токен через Kubernetes Secret. Важно ограничить RBAC, namespace и допустимые образы, иначе CI получит больше прав в кластере, чем требуется. Кластерный autoscaler позволяет масштабировать ресурсы вместе с очередью job. Разворачивать Kubernetes только ради одного runner редко оправданно: поддержка control plane, сети и хранения будет дороже выигрыша.

Переменная нагрузка и одноразовые ВМ

Docker autoscaler создаёт виртуальные машины под нагрузку и удаляет простаивающие экземпляры. Для провайдеров поддерживаются механизмы наподобие AWS Auto Scaling Group, GCP Instance Group и Azure VMSS через плагины fleeting. На ВМ запускаются Docker job, а max_use_count = 1 позволяет уничтожать машину после одной задачи. Это особенно полезно для привилегированных сборок: состояние не переходит к следующему запуску.

При проектировании такого пула надо согласовать лимиты. Верхняя граница параллелизма равна max_instances × capacity_per_instance. Если эти параметры расходятся с concurrent, часть мощности будет простаивать или job начнут ждать в очереди. Нужен также тёплый резерв: несколько idle-инстансов уменьшают холодный старт, но увеличивают расходы.

Executor instance решает близкую задачу без контейнеров: скрипты выполняются прямо на эфемерной ВМ. Он подходит для сборок образов ОС, тестов, зависящих от хоста, и задач на специализированном железе. Для большинства прикладных тестов Docker executor проще в сопровождении.

Практичная схема выбора

Начните с Docker runner для изолированных тестов и сборок. Выделите отдельный project runner для production-деплоя и не смешивайте его с общим пулом. Подключайте Kubernetes executor, когда кластер уже есть и команда умеет управлять его политиками и ресурсами. При выраженных пиках нагрузки переносите тяжёлые или привилегированные job на autoscaler с одноразовыми ВМ.

Перед запуском проверьте четыре вещи: область видимости runner, теги job, доступные секреты и способ сборки контейнеров. Эти решения важнее конкретной команды установки: они определяют, кто сможет выполнить код на вашей инфраструктуре, сколько времени job проведёт в очереди и насколько легко будет расследовать инцидент.