Deckhouse Code поставляет два образа раннера: registry.deckhouse.ru/deckhouse/ee/packages/code/images/runner и registry.deckhouse.ru/deckhouse/ee/packages/code/images/runner-helper. Развёртывание и эксплуатацию раннеров (серверы, манифесты, Helm-чарты, обновление, мониторинг) выполняет администратор.

Раннер — процесс GitLab Runner, который получает задания CI/CD от Deckhouse Code и выполняет их в контейнерах, подах Kubernetes или виртуальных машинах в зависимости от своего executor.

Образы раннера

Тег образа — версия GitLab Runner, например v19.3.1. Оба образа собраны только для linux/amd64.

ОбразСодержимое
runnerGitLab Runner, плагин fleeting-plugin-dvp в /usr/local/bin. Процесс работает от пользователя gitlab-runner (UID и GID 1001) и читает конфигурацию из /etc/gitlab-runner/config.toml
runner-helperВспомогательный процесс, который Docker executor и Kubernetes executor запускают рядом с каждым заданием, чтобы клонировать репозиторий и загрузить артефакты и кеш; содержит git, git-lfs и bash

Docker executor и Kubernetes executor берут вспомогательный образ из параметра helper_image. В примерах ниже в нём указан образ runner-helper той же версии, что и раннер.

Создание раннера

Раннер подключается к Deckhouse Code с токеном аутентификации. Чтобы создать раннер инстанса и получить его токен:

  1. Откройте «Admin» → «CI/CD» → «Раннеры» и нажмите «Создать раннер инстанса».
  2. В поле «Теги» перечислите теги заданий, которые выполняет раннер, или выберите «Запуск заданий без тегов».
  3. Нажмите «Создать раннер».
  4. Скопируйте токен аутентификации раннера (начинается с glrt-) на открывшейся странице.

В примерах ниже <TOKEN> — этот токен, <VERSION> — тег образа, https://code.example.com — адрес Deckhouse Code.

Автомасштабирование на Deckhouse Virtualization Platform

Плагин fleeting-plugin-dvp позволяет executor instance выполнять задания на виртуальных машинах из VirtualMachinePool в Deckhouse Virtualization Platform. Раннер задаёт число виртуальных машин в пуле (spec.replicas): по одной на задание плюс резерв простаивающих. Он подключается к виртуальной машине по SSH и выполняет задание в её оболочке. Ненужную виртуальную машину плагин удаляет и уменьшает spec.replicas.

Плагин не создаёт пул и пользователя на виртуальных машинах: пул и доступ по SSH подготавливаются заранее. В примере ниже раннер работает как Deployment в кластере Deckhouse Virtualization Platform, в неймспейсе code-runner-dvp вместе с пулом.

Неймспейс и SSH-ключ

Создайте неймспейс, пару ключей и Secret с закрытым ключом:

d8 k create namespace code-runner-dvp
ssh-keygen -t ed25519 -N "" -f code-runner-ssh
d8 k -n code-runner-dvp create secret generic code-runner-ssh --from-file=id_ed25519=code-runner-ssh

Открытый ключ code-runner-ssh.pub указывается в манифесте пула.

Пул виртуальных машин

Пул создаётся пустым и удаляет виртуальные машины по имени (scaleDownPolicy: Explicit), как это делает плагин при уменьшении пула. В образе диска нужны SSH-сервер, bash и git; cloud-init создаёт пользователя, от которого подключается раннер. Сохраните манифест в файл code-runner-pool.yaml:

apiVersion: virtualization.deckhouse.io/v1alpha2
kind: VirtualMachinePool
metadata:
  name: code-runner
  namespace: code-runner-dvp
spec:
  replicas: 0
  scaleDownPolicy: Explicit
  virtualMachineTemplate:
    spec:
      virtualMachineClassName: generic
      runPolicy: AlwaysOn
      cpu:
        cores: 2
      memory:
        size: 2Gi
      blockDeviceRefs:
        - kind: VirtualDisk
          name: disk
      provisioning:
        type: UserData
        userData: |
          #cloud-config
          package_update: true
          packages:
            - git
          users:
            - name: gitlab-runner
              shell: /bin/bash
              sudo: ALL=(ALL) NOPASSWD:ALL
              lock_passwd: false
              ssh_authorized_keys:
                - <PUBLIC_KEY>
  virtualDiskTemplates:
    - name: disk
      spec:
        dataSource:
          type: ObjectRef
          objectRef:
            kind: VirtualImage
            name: ubuntu-2204
        persistentVolumeClaim:
          size: 20Gi
          storageClassName: <STORAGE_CLASS_NAME>

<PUBLIC_KEY> — содержимое code-runner-ssh.pub; ubuntu-2204 — VirtualImage в том же неймспейсе. Задания записывают на диск рабочие копии и кеш, поэтому размер диска ограничивает размер задания.

Создайте пул:

d8 k apply -f code-runner-pool.yaml

Развёртывание раннера

  1. Сохраните следующий манифест в файл code-runner-dvp.yaml. Он содержит конфигурацию раннера, сервисный аккаунт с правами на пул и Deployment раннера со смонтированным закрытым ключом:

    apiVersion: v1
    kind: Secret
    metadata:
      name: code-runner-config
      namespace: code-runner-dvp
    stringData:
      config.toml: |
        concurrent = 4
    
        [[runners]]
          name = "dvp"
          url = "https://code.example.com"
          token = "<TOKEN>"
          executor = "instance"
          shell = "bash"
    
          [runners.autoscaler]
            plugin = "fleeting-plugin-dvp"
            max_instances = 5
    
            [runners.autoscaler.plugin_config]
              namespace = "code-runner-dvp"
              name = "code-runner"
    
            [runners.autoscaler.connector_config]
              username = "gitlab-runner"
              key_path = "/ssh/id_ed25519"
              use_static_credentials = true
              use_external_addr = false
    
            [[runners.autoscaler.policy]]
              idle_count = 1
              idle_time = "20m"
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: code-runner
      namespace: code-runner-dvp
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: code-runner
      namespace: code-runner-dvp
    rules:
      - apiGroups: ["virtualization.deckhouse.io"]
        resources: ["virtualmachinepools"]
        verbs: ["get", "patch"]
      - apiGroups: ["virtualization.deckhouse.io"]
        resources: ["virtualmachines"]
        verbs: ["get", "list", "delete"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: code-runner
      namespace: code-runner-dvp
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: code-runner
    subjects:
      - kind: ServiceAccount
        name: code-runner
        namespace: code-runner-dvp
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: code-runner
      namespace: code-runner-dvp
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: code-runner
      template:
        metadata:
          labels:
            app: code-runner
        spec:
          serviceAccountName: code-runner
          securityContext:
            runAsUser: 1001
            runAsGroup: 1001
            fsGroup: 1001
          initContainers:
            - name: config
              image: registry.deckhouse.ru/deckhouse/ee/packages/code/images/runner:<VERSION>
              command: ["cp", "/config-secret/config.toml", "/etc/gitlab-runner/config.toml"]
              volumeMounts:
                - name: config-secret
                  mountPath: /config-secret
                - name: config
                  mountPath: /etc/gitlab-runner
          containers:
            - name: runner
              image: registry.deckhouse.ru/deckhouse/ee/packages/code/images/runner:<VERSION>
              volumeMounts:
                - name: config
                  mountPath: /etc/gitlab-runner
                - name: ssh
                  mountPath: /ssh
          volumes:
            - name: config-secret
              secret:
                secretName: code-runner-config
            - name: config
              emptyDir: {}
            - name: ssh
              secret:
                secretName: code-runner-ssh
                defaultMode: 0440

    Параметры, которые связывают раннер с пулом:

    • plugin_config.namespace и plugin_config.name — неймспейс и имя VirtualMachinePool; без kubeconfig_path плагин использует сервисный аккаунт пода;
    • connector_config.username — пользователь, которого создаёт cloud-init;
    • connector_config.key_path — закрытый ключ, открытая часть которого указана в ssh_authorized_keys;
    • use_external_addr = false — раннер подключается к внутреннему адресу виртуальной машины (status.ipAddress);
    • shell = "bash" — в Debian и Ubuntu /bin/sh — это dash, и скрипт подготовки задания в нём завершается с ошибкой;
    • max_instances — верхний предел числа виртуальных машин в пуле; idle_count и idle_time задают, сколько свободных виртуальных машин держать и как долго.

    Role разрешает плагину читать и изменять пул, читать и удалять его виртуальные машины. Init-контейнер копирует config.toml из Secret в доступную для записи директорию, где раннер хранит и свой системный идентификатор. defaultMode: 0440 разрешает чтение ключа группе gitlab-runner, которую fsGroup назначает файлу ключа.

  2. Примените манифест:

    d8 k apply -f code-runner-dvp.yaml
  3. Проверьте подключение к Deckhouse Code:

    d8 k -n code-runner-dvp exec deploy/code-runner -- gitlab-runner verify

Раннер вне кластера обращается к пулу через файл kubeconfig, указанный в kubeconfig_path; его пользователю нужны права из Role выше.

Перезапущенный раннер заменяет виртуальные машины пула новыми. Чтобы он подхватывал работающие виртуальные машины, включите хранилище состояния автомасштабирования в директории на постоянном томе, смонтированном в контейнер раннера:

  [runners.autoscaler.state_storage]
    enabled = true
    dir = "/home/gitlab-runner/state"
    keep_instance_with_acquisitions = true

Docker executor

Docker executor выполняет каждое задание в контейнере Docker-демона на сервере раннера. Сам раннер работает в контейнере и обращается к демону через его сокет.

  1. Создайте том для конфигурации раннера:

    docker volume create code-runner-config
  2. Зарегистрируйте раннер. Команда записывает config.toml в том:

    docker run --rm -v code-runner-config:/etc/gitlab-runner \
      registry.deckhouse.ru/deckhouse/ee/packages/code/images/runner:<VERSION> \
      register --non-interactive \
        --url https://code.example.com \
        --token <TOKEN> \
        --executor docker \
        --docker-image alpine:3 \
        --docker-helper-image registry.deckhouse.ru/deckhouse/ee/packages/code/images/runner-helper:<VERSION>

    --docker-image — образ для заданий, в .gitlab-ci.yml которых не указан image.

  3. Запустите раннер:

    docker run -d --name code-runner --restart always \
      --group-add "$(stat -c %g /var/run/docker.sock)" \
      -v code-runner-config:/etc/gitlab-runner \
      -v /var/run/docker.sock:/var/run/docker.sock \
      registry.deckhouse.ru/deckhouse/ee/packages/code/images/runner:<VERSION>

    Раннер работает от пользователя gitlab-runner; --group-add добавляет его в группу — владельца сокета Docker.

  4. Проверьте подключение к Deckhouse Code:

    docker exec code-runner gitlab-runner verify

Kubernetes executor

Kubernetes executor выполняет каждое задание в отдельном поде. Сам раннер работает как Deployment в том же кластере и создаёт поды заданий в неймспейсе, указанном в config.toml.

  1. Создайте неймспейс:

    d8 k create namespace code-runner
  2. Сохраните следующий манифест в файл code-runner.yaml. Он содержит конфигурацию раннера, сервисный аккаунт с правами на управление подами заданий и Deployment раннера:

    apiVersion: v1
    kind: Secret
    metadata:
      name: code-runner-config
      namespace: code-runner
    stringData:
      config.toml: |
        concurrent = 4
    
        [[runners]]
          name = "kubernetes"
          url = "https://code.example.com"
          token = "<TOKEN>"
          executor = "kubernetes"
          [runners.kubernetes]
            namespace = "code-runner"
            image = "alpine:3"
            helper_image = "registry.deckhouse.ru/deckhouse/ee/packages/code/images/runner-helper:<VERSION>"
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: code-runner
      namespace: code-runner
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: code-runner
      namespace: code-runner
    rules:
      - apiGroups: [""]
        resources: ["pods", "pods/exec", "pods/attach", "pods/log", "secrets", "configmaps", "services", "events"]
        verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: code-runner
      namespace: code-runner
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: code-runner
    subjects:
      - kind: ServiceAccount
        name: code-runner
        namespace: code-runner
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: code-runner
      namespace: code-runner
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: code-runner
      template:
        metadata:
          labels:
            app: code-runner
        spec:
          serviceAccountName: code-runner
          securityContext:
            runAsUser: 1001
            runAsGroup: 1001
            fsGroup: 1001
          initContainers:
            - name: config
              image: registry.deckhouse.ru/deckhouse/ee/packages/code/images/runner:<VERSION>
              command: ["cp", "/config-secret/config.toml", "/etc/gitlab-runner/config.toml"]
              volumeMounts:
                - name: config-secret
                  mountPath: /config-secret
                - name: config
                  mountPath: /etc/gitlab-runner
          containers:
            - name: runner
              image: registry.deckhouse.ru/deckhouse/ee/packages/code/images/runner:<VERSION>
              volumeMounts:
                - name: config
                  mountPath: /etc/gitlab-runner
          volumes:
            - name: config-secret
              secret:
                secretName: code-runner-config
            - name: config
              emptyDir: {}

    Init-контейнер копирует config.toml из Secret в доступную для записи директорию, где раннер хранит и свой системный идентификатор.

  3. Примените манифест:

    d8 k apply -f code-runner.yaml
  4. Проверьте подключение к Deckhouse Code:

    d8 k -n code-runner exec deploy/code-runner -- gitlab-runner verify