GitLab Runner в Kubernetes через Helm
Опубликовано:
Используемые термины: Kubernetes, Helm, Docker, GitLab.
В инструкции рассмотрим установку GitLab Runner в кластер Kubernetes через Helm chart — с authentication token вместо устаревающего сценария registration token. Раннер развернем в отдельном namespace, токен сохраним в Secret, а не в values-файле, и проверим его работу на тестовом CI job.
Создание контекста и файла с секретом
Файл с настройками для Runner
Установка GitLab Runner через Helm
Решение типичных проблем
Пример пайплайна для проверки раннера
Обновление или удаление раннера
Namespace и Secret с токеном
Создадим отдельный namespace для раннера — это изолирует его ресурсы от остальных нагрузок в кластере:
kubectl create namespace gitlab-runner
Создадим каталог для хранения файлов конфигурации и перейдем в него:
mkdir -p /opt/gitlab-runner && cd /opt/gitlab-runner
Authentication token нельзя хранить прямо в values.yml — вынесем его в отдельный Kubernetes Secret. Создадим файл манифеста:
vi secret-gitlab-runner-token.yml
Приведем его к следующему виду:
apiVersion: v1
kind: Secret
metadata:
name: gitlab-runner-secret
namespace: gitlab-runner
type: Opaque
stringData:
runner-token: "glrt-xxxxxxxxxxxxxxxxxxxx"
runner-registration-token: ""
* где:
- runner-token - authentication token готового runner из интерфейса GitLab.
- runner-registration-token - оставляем пустым, чтобы не использовать устаревающий сценарий регистрации через registration token.
Применим манифест и создадим Secret в кластере:
kubectl apply -f secret-gitlab-runner-token.yml
Secret с токеном готов.
Настройка values.yml GitLab Runner
Теперь опишем параметры самого раннера в values-файле — сюда войдут адрес GitLab, ссылка на созданный Secret и ресурсы для pod-ов. Создадим файл:
vi values.yml
И заполним его следующим содержимым:
imagePullPolicy: IfNotPresent
gitlabUrl: https://gitlab.example.com
runnerToken: ""
unregisterRunners: true
terminationGracePeriodSeconds: 60
concurrent: 3
checkInterval: 10
rbac:
create: true
serviceAccount:
create: true
name: gitlab-runner
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
memory: 2G
strategy:
type: Recreate
podSecurityContext:
runAsNonRoot: true
fsGroup: 65533
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
runAsNonRoot: true
capabilities:
drop:
- ALL
metrics:
enabled: true
runners:
secret: gitlab-runner-secret
privileged: false
config: |
[[runners]]
name = "kubernetes-runner"
url = "https://gitlab.example.com"
executor = "kubernetes"
[runners.kubernetes]
namespace = "gitlab-runner"
privileged = false
image = "alpine:3.20"
pull_policy = ["if-not-present"]
poll_timeout = 600
helper_image_flavor = "alpine3.21"
cpu_request = "500m"
memory_request = "512Mi"
memory_limit = "512Mi"
service_cpu_request = "1"
service_memory_request = "1G"
service_memory_limit = "1G"
helper_cpu_request = "100m"
helper_memory_request = "256Mi"
helper_memory_limit = "256Mi"
[runners.kubernetes.pod_labels]
"app" = "gitlab-job"
* где:
- gitlabUrl — адрес вашего GitLab, который должен быть доступен из кластера.
- runnerToken: "" — токен не дублируем в values, потому что chart заберет его из Secret. Строка должна остаться пустой, а не отсутствовать — иначе chart попробует использовать устаревший сценарий с registration token.
- resources — ресурсы pod-а менеджера GitLab Runner; для памяти requests и limits равны, чтобы получить Guaranteed QoS.
- strategy: Recreate — во время обновления сначала удаляется старый pod, и только потом поднимается новый. Раннер на короткое время перестает принимать job-ы.
- tags / protected / locked / runUntagged — при новом workflow с authentication token эти параметры задаем в интерфейсе GitLab, а не в values.yml.
- runners.kubernetes.namespace — namespace, в котором раннер будет создавать pod-ы для CI job. Это тот же namespace, где работает сам раннер, — RBAC (serviceAccount) уже имеет права на создание pod-ов именно в нем.
- runners.kubernetes.* — лимиты и requests уже для job pod-ов, которые runner будет создавать для CI job.
- privileged — если мы планируем запускать dind (Docker in Docker), меняем значение на true (то, что выделено желтым).
** Для job-ов без сборки Docker-образов — оставляем privileged: false, как в примере выше. Это более безопасный вариант.
*** Если job-ам нужен Docker in Docker — меняем оба значения privileged на true. В этом случае учитываем риски, описанные в примечании в конце статьи.
Установка и запуск GitLab Runner
Подключим официальный Helm-репозиторий GitLab:
helm repo add gitlab https://charts.gitlab.io
Обновим локальный индекс репозиториев:
helm repo update
Установим GitLab Runner в подготовленный namespace с нашим values-файлом:
helm upgrade --install gitlab-runner gitlab/gitlab-runner -n gitlab-runner -f values.yml
* если namespace уже существует и chart установлен повторно, команда выполнит обновление без удаления релиза.
Проверим, что pod раннера поднялся и находится в состоянии Running:
kubectl get pods -n gitlab-runner
Убедимся, что Secret с токеном действительно создан в нужном namespace:
kubectl get secret -n gitlab-runner
Посмотрим логи раннера — там должно быть подключение к GitLab без ошибок авторизации:
kubectl logs -n gitlab-runner deployment/gitlab-runner
Если pod не стартовал или завис в Pending, посмотрим подробности через describe:
kubectl describe pod -n gitlab-runner -l app=gitlab-runner
* где:
- kubectl logs - в логах должно быть подключение к GitLab без ошибок авторизации.
- kubectl describe pod - удобно проверить события, readiness и причину Pending/CrashLoopBackOff, если pod не стартовал.
Раннер зарегистрирован и подключен к GitLab.
Частая проблема: ошибка авторизации в логах
Причина: authentication token скопирован из интерфейса GitLab с лишним пробелом или обрезан не полностью, либо Secret применен в другом namespace.
Решение: проверяем содержимое Secret и сверяем токен с карточкой раннера в GitLab:
kubectl get secret gitlab-runner-secret -n gitlab-runner -o yaml
Если токен указан верно, пересоздаем Secret и перезапускаем pod раннера:
kubectl rollout restart deployment/gitlab-runner -n gitlab-runner
Проверка запуска CI job
Чтобы убедиться, что раннер действительно принимает задачи, создадим тестовый pipeline. Откроем файл конфигурации CI в репозитории:
vi .gitlab-ci.yml
И добавим простой job, который использует тег нашего раннера:
stages:
- test
smoke-runner:
stage: test
tags:
- kubernetes
script:
- uname -a
- echo "GitLab Runner in Kubernetes works"
* job должен использовать тег, который вы заранее назначили runner-у в интерфейсе GitLab.
После push job должен запуститься автоматически и выполниться в кластере — успешное завершение подтверждает, что раннер работает.
Обновление конфигурации и удаление раннера
Если параметры в values.yml изменились, применим их к уже установленному релизу:
helm upgrade gitlab-runner gitlab/gitlab-runner -n gitlab-runner -f values.yml
Для полного удаления раннера сначала снимаем сам релиз Helm:
helm uninstall gitlab-runner -n gitlab-runner
Затем удаляем Secret с токеном:
kubectl delete -f secret-gitlab-runner-token.yml
И, если namespace больше не нужен, удаляем его целиком:
kubectl delete namespace gitlab-runner
Если CI job-ы должны собирать Docker-образы, не включайте privileged = true без необходимости. Для production-сценариев безопаснее перейти на Kaniko, BuildKit rootless или buildah, чтобы не открывать лишние привилегии внутри кластера.