· 18 мин чтения

GitLab Runner с Kubernetes Executor: масштабируемая CI-ферма

Я Семёнов Евгений Сергеевич, директор АйТи Фреш. Когда клиенту перестаёт хватать пары раннеров на виртуалках и пайплайны начинают стоять в очереди — пора переводить GitLab CI в Kubernetes. У нас на практике это делается за 1–2 рабочих дня на готовом кластере. Сегодня разберу полный расклад: helm-чарт, DinD, кэш, автоскейлинг, безопасность.

Плюсы и минусы k8s-executor

Плюсы:

Минусы:

Подготовка кластера

Требования:

kubectl create namespace gitlab-runner
kubectl label ns gitlab-runner pod-security.kubernetes.io/enforce=privileged

Регистрационный токен

В GitLab → Admin Area → CI/CD → Runners → New instance runner. Копируем токен вида glrt-xxxxx.

Helm values

# values.yaml
image:
  registry: registry.gitlab.com
  image: gitlab-org/gitlab-runner
gitlabUrl: https://gitlab.company.ru/
runnerToken: "glrt-XXXXXXXXXXXXXXXXXXXX"

concurrent: 30
checkInterval: 10

rbac:
  create: true
  clusterWideAccess: false

runners:
  config: |
    [[runners]]
      name = "k8s-runner"
      executor = "kubernetes"
      [runners.kubernetes]
        namespace = "gitlab-runner"
        image = "ubuntu:22.04"
        privileged = true
        poll_timeout = 600
        cpu_request = "200m"
        memory_request = "256Mi"
        cpu_limit = "2"
        memory_limit = "4Gi"
        service_cpu_request = "100m"
        service_memory_request = "128Mi"
        helper_cpu_request = "50m"
        helper_memory_request = "64Mi"
        [runners.kubernetes.node_selector]
          "role" = "ci"
        [[runners.kubernetes.volumes.empty_dir]]
          name = "docker-certs"
          mount_path = "/certs/client"
          medium = "Memory"
      [runners.cache]
        Type = "s3"
        Shared = true
        [runners.cache.s3]
          ServerAddress = "s3.company.ru"
          AccessKey = "$S3_ACCESS"
          SecretKey = "$S3_SECRET"
          BucketName = "gitlab-cache"
          Insecure = false
helm repo add gitlab https://charts.gitlab.io
helm upgrade --install gitlab-runner gitlab/gitlab-runner \
  -n gitlab-runner -f values.yaml

DinD для сборки образов

Пример .gitlab-ci.yml:

build-image:
  stage: build
  image: docker:27
  services:
    - name: docker:27-dind
      alias: docker
  variables:
    DOCKER_HOST: tcp://docker:2376
    DOCKER_TLS_CERTDIR: "/certs"
    DOCKER_DRIVER: overlay2
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

Альтернатива: BuildKit rootless

Если не хотите privileged:

build-image:
  image: moby/buildkit:rootless
  variables:
    BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
  script:
    - |
      buildctl-daemonless.sh build \
        --frontend dockerfile.v0 \
        --local context=. --local dockerfile=. \
        --output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA,push=true

Кэш зависимостей

СтекCache keyPaths
Node.js$CI_COMMIT_REF_SLUG-nodenode_modules, .npm
Python$CI_COMMIT_REF_SLUG-pip.venv, .cache/pip
Go$CI_COMMIT_REF_SLUG-go.cache/go-build, pkg/mod
Maven$CI_COMMIT_REF_SLUG-m2.m2/repository

Автоскейлинг нод

Для CI-пула включаем cluster-autoscaler с высокой скоростью scale-up и агрессивным scale-down:

extraArgs:
  - --scale-down-unneeded-time=5m
  - --scale-down-delay-after-add=3m
  - --scale-down-utilization-threshold=0.4

На CI-нодах нас не волнует availability, важнее стоимость. Scale-down через 5 минут простоя — оптимум.

Реальный кейс: ускорение CI в 4 раза

Однажды в 2024 году мы работали с клиентом — финтех-стартап, 22 разработчика. У них был один shell-раннер на VM, 40 пайплайнов в очереди каждое утро. Среднее время запуска нового pipeline — 14 минут в очереди + 22 минуты сборка. Миграция на Kubernetes executor заняла 2 рабочих дня: поднятие менеджера в кластере, перенос .gitlab-ci.yml, настройка S3-кэша на MinIO на нашем Dell Xeon Platinum 8280 в дата-центре МТС Москва.

Результат: концурентность 25 параллельных job, очередь исчезла, среднее время pipeline — 8 минут (кэш на S3 + параллельные этапы). Стоимость работ — 68 000 руб., клиент посчитал окупаемость в 3 недели по сэкономленному времени разработчиков.

Безопасность

Грабли

Поднимем CI-ферму в Kubernetes

У нас на практике десятки развёртываний GitLab Runner в кластерах. 15+ лет опыта системного администрирования, 8 серверов Dell Xeon Platinum 8280 с 40G Mellanox в дата-центре МТС Москва под ваши собственные K8s-кластеры. Настройка, миграция пайплайнов, оптимизация.

Телефон: +7 903 729-62-41
Telegram: @ITfresh_Boss
Семёнов Евгений Сергеевич, директор АйТи Фреш

FAQ — GitLab Runner в Kubernetes

Зачем запускать раннеры в Kubernetes?
Автоскейлинг: сколько pipeline-ов — столько pod-ов, ноль простоя ресурсов. Изоляция: каждая job в своём pod. Легко обновлять раннер, откатывать, добавлять несколько параллельных менеджеров. Для команд от 10 разработчиков — почти обязательная архитектура.
DinD или BuildKit для сборки образов?
DinD (docker-in-docker) — классика, но требует privileged. BuildKit с rootless — безопаснее и быстрее за счёт кэша слоёв. На свежих кластерах советую BuildKit или Kaniko, на старых совместимостей ради остаётся DinD.
Где хранить кэш пайплайнов?
S3-совместимый сторадж (MinIO, AWS S3, Ceph RGW). Настройка в helm values: runners.cache.s3.*. Кэш расшаривается между pod-ами, работает быстрее PVC и не привязан к ноде.
Сколько параллельных job может тянуть один раннер-менеджер?
По умолчанию 10 (concurrent). Практический потолок — ресурсы кластера и бандвидт на образы. У нас типично 20–50 concurrent jobs на менеджер, после этого поднимаем второй.
Как изолировать секреты между job-ами?
Используйте protected-переменные в GitLab — они доступны только в protected-ветках. Для продакшн-секретов — External Secrets + Vault, раннер получает короткоживущий токен и читает секрет в job.

Подпишитесь на рассылку ITfresh

Раз в неделю — практические гайды для руководителя IT и сисадмина: безопасность, 1С, миграции, резервные копии, лайфхаки из реальных проектов.

Реквизиты оператора персональных данных

ООО «АЙТИ-ФРЕШ», ИНН 7719418495, КПП 771901001. Юридический адрес: 105523, г. Москва, Щёлковское шоссе, д. 92, корп. 7. Контакт: info@itfresh.ru, +7 903 729-62-41. Оператор обрабатывает e-mail подписчика в целях рассылки информационных и рекламных материалов до момента отзыва согласия.