Настроить выполнение заданий CI/CD SourceCraft в кластере Kubernetes

В пользовательском воркере self-hosted-processor поддерживается режим, в котором задания CI/CD выполняются в кластере Kubernetes. Каждое задание выполняется в отдельном поде. Поды создаются воркером заранее и поддерживаются в пуле. Когда появляется новое задание, воркер выбирает доступный под из пула и передает задание агенту, который запущен в поде. После выполнения задания под автоматически удаляется.

Примечание

Поддержка Kubernetes-режима доступна начиная с версии воркера 0.14.1.

Обзор

Воркер может быть запущен:

  • вне кластера Kubernetes, например на компьютере пользователя. В таком случае в параметры воркера передается kubeconfig для работы с кластером Kubernetes. Для передачи задания агенту используется проброс порта port forward.

    Конфигурация kubeconfig должна содержать единственный контекст, и этот контекст должен быть выбран текущим.

  • внутри кластера Kubernetes. Для передачи задания агенту используется IP-адрес пода podIP.

    Пространство имен, в котором запущен воркер, должно отличаться от пространства имен, выделенного под пул подов.

Работа воркера состоит из следующих этапов:

Старт воркера

При старте воркер в пространстве имен Kubernetes, выделенном под пул подов, удаляет и создает заново секрет agent-secrets. Секрет содержит конфигурацию агента и сертификат удостоверяющего центра (certificate authority, CA), необходимые для запуска агента. Сертификат удостоверяющего центра используется для авторизации запросов воркера к агенту по протоколу mTLS.

Обслуживание пула подов

Воркер периодически запускает процедуру обслуживания пула подов:

  • Внутренние состояния подов синхронизируются с состояниями в кластере Kubernetes.

  • Обрабатываются переходы подов между состояниями.

  • Создаются новые поды.

  • Неизвестные поды удаляются, в том числе поды, оставшиеся от предыдущего запуска воркера, или поды, созданные другим воркером.

    Важно

    Не запускайте несколько экземпляров воркера с пулом подов в одном пространстве имен. Воркеры будут удалять поды друг друга.

В пуле поддерживается количество доступных подов, равное минимальному размеру пула, заданному в конфигурации воркера. Общее количество подов в пуле не может превышать максимальный размер пула, также заданный в конфигурации воркера.

Выполнение задания

После получения задания воркер ищет в пуле доступный под и передает задание агенту, который запущен в этом поде.

Процесс отправки задания агенту отличается в зависимости от того, где размещен воркер:

Воркер пробрасывает порт агента на свой локальный порт (port forward), отправляет задание агенту через локальный порт и закрывает соединение, использованное для проброса порта.

Воркер отправляет задание на порт агента по IP-адресу пода.

Важно

Разрешите взаимодействие воркера с подом в пространстве имен пула — такое взаимодействие может быть ограничено сетевыми политиками или другими инструментами.

При передаче задания агенту воркер авторизует свой запрос с помощью клиентского сертификата и ключа. Агент проверяет авторизацию с помощью сертификата удостоверяющего центра. Соединение воркера и агента защищено по протоколу mTLS.

Далее агент выполняет задание самостоятельно. После завершения выполнения задания воркер удаляет под.

Процесс настройки

Чтобы настроить выполнение заданий CI/CD в кластере Kubernetes:

  1. Подготовьте кластер Kubernetes
  2. Создайте конфигурационный файл воркера
  3. Создайте шаблон пода
  4. Запустите воркер

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

Совет

Для выполнения заданий CI/CD SourceCraft вы можете использовать кластер Yandex Managed Service for Kubernetes.

  1. Установите kubectl и подключитесь к кластеру Kubernetes.

  2. Создайте пространство имен для пула подов воркера:

    kubectl create namespace pods-namespace
    
  3. Создайте файл role.yaml с описанием роли пользователя, от имени которого воркер будет взаимодействовать с кластером Kubernetes. Пользователь должен иметь права на управление секретами и подами в пространстве имен пула подов. Если воркер запущен вне кластера Kubernetes, дополнительно требуются права на проброс портов.

    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: processor-role
    rules:
      - apiGroups: [""]
        resources: ["secrets", "pods"]
        verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
      - apiGroups: [""]
        resources: ["pods/portforward"]
        verbs: ["create"]
    
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: processor-role
    rules:
      - apiGroups: [""]
        resources: ["secrets", "pods"]
        verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
    
  4. Примените манифест для пространства имен, созданного ранее:

    kubectl apply -f role.yaml -n pods-namespace
    

Создайте конфигурационный файл воркера

Создайте файл config.yaml со следующей конфигурацией:

kubernetes:
  kubeconfig_path: /path/to/.kube/config
  namespace: <пространство_имен_пула>
  pod_template_path: /path/to/pod-template.yaml
  agent:
    port: 8693
    ca_cert_path: /path/to/ca.crt
    client_cert_path: /path/to/client.crt
    client_key_path: /path/to/client.key
  pool_max_size: 10
  pool_min_size: 3
  pool_maintenance_period: 10s
  secret_updating_timeout: 20s
  pods_listing_timeout: 10s
  pod_creation_timeout: 1m
  pod_startup_timeout: 2m
  pod_deletion_timeout: 30s
  endpoint_resolving_timeout: 10s

endpoint:
  host: ci.sourcecraft.tech
  port: 443
  ssl_no_verify: false

executor_type: kubernetes

logger_type: json
logger_level: info

tags: ["my-tag", "+my-required-tag"]

auth:
  pat: <персональный_токен>

Где:

  • kubernetes — раздел с параметрами, специфичными для режима Kubernetes:
    • kubeconfig_path — путь до файла kubeconfig. Также можно задать через переменную окружения KUBECONFIG. Если параметр не задан, используется внутрикластерная конфигурация.
    • namespace — пространство имен, выделенное под пул подов. Обязательный параметр.
    • pod_templateшаблон пода в формате YAML. Также можно задать через переменную окружения KUBERNETES_POD_TEMPLATE. Должен быть задан либо pod_template, либо pod_template_path. Указывать одновременно оба параметра нельзя.
    • pod_template_path — путь до файла с шаблоном пода. Должен быть задан либо pod_template, либо pod_template_path. Указывать одновременно оба параметра нельзя.
    • agent — подраздел с параметрами для взаимодействия с агентом:
      • port — порт агента. Значение по умолчанию — 8693. Порт должен отличаться от всех других портов пода. Попадает в конфигурацию агента.
      • ca_cert — сертификат удостоверяющего центра. Также можно задать через переменную окружения KUBERNETES_AGENT_CA_CERT. Должен быть задан либо ca_cert, либо ca_cert_path. Указывать оба параметра одновременно нельзя. Попадает в конфигурацию агента.
      • ca_cert_path — путь до файла с сертификатом удостоверяющего центра. Должен быть задан либо ca_cert, либо ca_cert_path. Указывать одновременно оба параметра нельзя. Попадает в конфигурацию агента.
      • client_cert — клиентский сертификат. Также можно задать через переменную окружения KUBERNETES_AGENT_CLIENT_CERT. Должен быть задан либо client_cert, либо client_cert_path. Указывать одновременно оба параметра нельзя.
      • client_cert_path — путь до файла с клиентским сертификатом. Должен быть задан либо client_cert, либо client_cert_path. Указывать одновременно оба параметра нельзя.
      • client_key — клиентский ключ. Также можно задать через переменную окружения KUBERNETES_AGENT_CLIENT_KEY. Должен быть задан либо client_key, либо client_key_path. Указывать одновременно оба параметра нельзя.
      • client_key_path — путь до файла с клиентским ключом. Должен быть задан либо client_key, либо client_key_path. Указывать одновременно оба параметра нельзя.
    • pool_max_size — максимальный размер пула подов. Обязательный параметр. Должен быть не меньше значения pool_min_size.
    • pool_min_size — минимальный размер пула подов. Обязательный параметр. Должен быть больше 0.
    • pool_maintenance_period — периодичность обслуживания пула подов. Значение по умолчанию — 10s.
    • secret_updating_timeout — таймаут обновления секрета. Значение по умолчанию — 20s.
    • pods_listing_timeout — таймаут запроса для получения списка подов. Значение по умолчанию — 10s.
    • pod_creation_timeout — таймаут запроса создания пода. Значение по умолчанию — 1m.
    • pod_startup_timeout — таймаут ожидания запуска пода. Значение по умолчанию — 2m. Включает время ожидания запуска пода со всеми его контейнерами.
    • pod_deletion_timeout — таймаут запроса удаления пода. Значение по умолчанию — 30s.
    • endpoint_resolving_timeout — таймаут разрешения конечной точки. Значение по умолчанию — 10s. Включает время ожидания проброса порта агента.
  • endpoint — раздел с параметрами для взаимодействия с сервисом SourceCraft. Раздел полностью попадает в конфигурацию агента:
    • host — хост сервиса SourceCraft. Обязательный параметр.
    • port — порт сервиса SourceCraft. Обязательный параметр.
    • ssl_no_verify — флаг отключения проверки серверного сертификата сервиса SourceCraft. Значение по умолчанию — false.
  • executor_type — тип воркера. Для режима Kubernetes должно быть указано значение kubernetes.
  • logger_type — формат логирования. Допустимые значения: json или console.
  • logger_level — уровень логирования. Допустимые значения: debug, info, warn, error. Необязательный параметр.
  • tags — список меток воркера. Необязательный параметр.
  • auth — раздел с параметрами аутентификации воркера в сервисе SourceCraft:

Создайте шаблон пода

Шаблон пода задает конфигурацию, по которой воркер создает поды в пространстве имен пула.

Важно

В шаблоне обязательно должен быть контейнер с именем cicd-worker. В этом контейнере запускается агент, который выполняет задание. Ресурсы для контейнера cicd-worker выбирайте с учетом предполагаемой нагрузки при выполнении заданий.

Создайте файл pod-template.yaml с шаблоном пода, например:

---
apiVersion: v1
kind: Pod
metadata:
  name: pod-template
spec:
  containers:
    - name: cicd-worker
      image: cr.yandex/sourcecraft/ci/self-hosted-processor:latest
      resources:
        requests:
          cpu: 250m
          memory: 256Mi
        limits:
          cpu: "1"
          memory: 1Gi

При создании пода воркер автоматически дополняет шаблон следующими параметрами:

  • В контейнер cicd-worker добавляются переменные окружения из секрета agent-secrets, контейнерный порт agent-port для порта агента и проба запуска.
  • Имя пода меняется на cicd-worker-<уникальный_идентификатор>.
  • Пространство имен меняется на пространство имен пула подов.
  • Запрещается перезапуск пода.
Пример дополненной конфигурации
---
apiVersion: v1
kind: Pod
metadata:
  name: cicd-worker-<уникальный_идентификатор>
  namespace: <пространство_имен_пула>
spec:
  containers:
    - name: cicd-worker
      image: cr.yandex/sourcecraft/ci/self-hosted-processor:latest
      resources:
        requests:
          cpu: 250m
          memory: 256Mi
        limits:
          cpu: "1"
          memory: 1Gi
      envFrom:
        - secretRef:
            name: agent-secrets
      ports:
        - name: agent-port
          containerPort: 8693
          protocol: TCP
      startupProbe:
        exec:
          command: ["nc", "-z", "localhost", "8693"]
        initialDelaySeconds: 5
        periodSeconds: 5
        failureThreshold: 10
  restartPolicy: Never

Docker-образ агента

В качестве Docker-образа агента рекомендуется использовать базовый образ контейнера воркера cr.yandex/sourcecraft/ci/self-hosted-processor:latest. Если для выполнения заданий требуется собственный образ, при его создании учитывайте следующие требования:

  • Воркер должен запускаться без указания пути к файлу конфигурации в командной строке — конфигурация передается через переменную окружения PROCESSOR_CONFIG:

    ENTRYPOINT ["/self-hosted-processor", "run"]
    
  • В образе должна быть создана директория /home/sourcecraft-ci-runner. Пользователь, от имени которого работает контейнер, должен иметь полный доступ к этой директории.

  • В образе должны быть установлены и доступны для запуска следующие исполняемые файлы: bash, sh, git, git-lfs, docker, nc.

Использование Docker в задании

Если для выполнения заданий нужен Docker, добавьте в шаблон пода контейнер с Docker-in-Docker.

Пример шаблона пода с Docker-in-Docker
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-template
spec:
  containers:
    - name: cicd-worker
      image: cr.yandex/sourcecraft/ci/self-hosted-processor:latest
      resources:
        requests:
          cpu: 250m
          memory: 256Mi
        limits:
          cpu: "1"
          memory: 1Gi
      env:
        - name: DOCKER_HOST
          value: "tcp://localhost:2376"
        - name: DOCKER_TLS_VERIFY
          value: "1"
        - name: DOCKER_CERT_PATH
          value: "/.docker-certs/client"
      volumeMounts:
        - name: docker-certs
          mountPath: /.docker-certs
    - name: dind
      image: docker:dind
      securityContext:
        privileged: true
      startupProbe:
        exec:
          command: ["docker", "info"]
        initialDelaySeconds: 5
        periodSeconds: 5
        failureThreshold: 30
      env:
        - name: DOCKER_TLS_CERTDIR
          value: "/.docker-certs"
      resources:
        requests:
          cpu: 500m
          memory: 512Mi
        limits:
          cpu: "2"
          memory: 2Gi
      volumeMounts:
        - name: docker-certs
          mountPath: /.docker-certs
  volumes:
    - name: docker-certs
      emptyDir: {}

Ресурсы для контейнера с Docker-in-Docker выбирайте с учетом предполагаемой нагрузки. Проба запуска для контейнера с Docker-in-Docker обязательна — она позволяет избежать ошибок выполнения заданий из-за неготовности Docker-in-Docker к работе при высокой нагрузке.

Также можно использовать более безопасный образ docker:dind-rootless. В этом случае на узлы кластера Kubernetes необходимо установить дополнительный компонент sysbox.

Запустите воркер

  1. Подготовьте окружение.

  2. Запустите воркер с конфигурацией, созданной ранее, одним из способов:

    • Передать путь до файла конфигурации в командной строке:

      self-hosted-processor run --config-path <путь_к_config.yaml>
      
    • Передать содержимое конфигурации через переменную окружения PROCESSOR_CONFIG:

      PROCESSOR_CONFIG="<содержимое_конфигурации>" self-hosted-processor run
      

    Важно

    Без передачи конфигурации воркер не запустится. Одновременно использовать оба способа передачи конфигурации нельзя — в этом случае воркер также не запустится.

В реестре доступен базовый Docker-образ контейнера воркера cr.yandex/sourcecraft/ci/self-hosted-processor:latest, а также теги конкретных версий.

В этом образе воркер запускается без указания пути к файлу конфигурации:

ENTRYPOINT ["/self-hosted-processor", "run"]

Поэтому при запуске контейнера передайте конфигурацию через переменную окружения PROCESSOR_CONFIG:

docker run --rm -e "PROCESSOR_CONFIG=<содержимое_конфигурации>" cr.yandex/sourcecraft/ci/self-hosted-processor:latest

Пример манифеста для запуска воркера внутри кластера Kubernetes приведен в подразделе Запуск воркера внутри кластера Kubernetes.

Примеры конфигураций

Запуск воркера вне кластера Kubernetes

Файл конфигурации config.yaml:

kubernetes:
  kubeconfig_path: /path/to/.kube/config
  namespace: pods-namespace
  pod_template_path: /path/to/pod-template.yaml
  agent:
    port: 8693
    ca_cert_path: /path/to/ca.crt
    client_cert_path: /path/to/client.crt
    client_key_path: /path/to/client.key
  pool_max_size: 10
  pool_min_size: 3

endpoint:
  host: ci.sourcecraft.tech
  port: 443

executor_type: kubernetes

logger_type: json

tags: ["my-tag", "+my-required-tag"]

auth:
  pat: <персональный_токен>

Команда запуска:

self-hosted-processor run --config-path /path/to/config.yaml

Запуск воркера внутри кластера Kubernetes

Манифест с сервисным аккаунтом, ролью, секретом и развертыванием воркера:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: processor-sa
  namespace: processor-namespace

---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: processor-role
  namespace: pods-namespace
rules:
  - apiGroups: [""]
    resources: ["secrets", "pods"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: processor-role-binding
  namespace: pods-namespace
subjects:
  - kind: ServiceAccount
    name: processor-sa
    namespace: processor-namespace
roleRef:
  kind: Role
  name: processor-role
  apiGroup: rbac.authorization.k8s.io

---
apiVersion: v1
kind: Secret
metadata:
  name: processor-secrets
  namespace: processor-namespace
type: Opaque
stringData:
  PROCESSOR_CONFIG: |
    ---
    kubernetes:
      namespace: pods-namespace
      agent:
        port: 8693
      pool_max_size: 10
      pool_min_size: 3

    endpoint:
      host: ci.sourcecraft.tech
      port: 443

    executor_type: kubernetes

    logger_type: json

    tags: ["my-tag", "+my-required-tag"]

  KUBERNETES_POD_TEMPLATE: |
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-template
    spec:
      containers:
        - name: cicd-worker
          image: cr.yandex/sourcecraft/ci/self-hosted-processor:latest
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              cpu: "1"
              memory: 1Gi
          env:
            - name: DOCKER_HOST
              value: "tcp://localhost:2376"
            - name: DOCKER_TLS_VERIFY
              value: "1"
            - name: DOCKER_CERT_PATH
              value: "/.docker-certs/client"
          volumeMounts:
            - name: docker-certs
              mountPath: /.docker-certs
        - name: dind
          image: docker:dind
          securityContext:
            privileged: true
          startupProbe:
            exec:
              command: ["docker", "info"]
            initialDelaySeconds: 5
            periodSeconds: 5
            failureThreshold: 30
          env:
            - name: DOCKER_TLS_CERTDIR
              value: "/.docker-certs"
          resources:
            requests:
              cpu: 500m
              memory: 512Mi
            limits:
              cpu: "2"
              memory: 2Gi
          volumeMounts:
            - name: docker-certs
              mountPath: /.docker-certs
      volumes:
        - name: docker-certs
          emptyDir: {}

  KUBERNETES_AGENT_CA_CERT: |
    -----BEGIN CERTIFICATE-----
    certificate content
    -----END CERTIFICATE-----

  KUBERNETES_AGENT_CLIENT_CERT: |
    -----BEGIN CERTIFICATE-----
    certificate content
    -----END CERTIFICATE-----

  KUBERNETES_AGENT_CLIENT_KEY: |
    -----BEGIN PRIVATE KEY-----
    key content
    -----END PRIVATE KEY-----

  PAT_CREDENTIALS: <персональный_токен>

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kubernetes-processor
  namespace: processor-namespace
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kubernetes-processor
  template:
    metadata:
      labels:
        app: kubernetes-processor
    spec:
      serviceAccountName: processor-sa
      containers:
        - name: kubernetes-processor
          image: cr.yandex/sourcecraft/ci/self-hosted-processor:latest
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              cpu: "1"
              memory: 1Gi
          envFrom:
            - secretRef:
                name: processor-secrets

Полезные ссылки