Перейти к основному содержимому

Исполнитель Kubernetes Enterprise

С исполнителем Kubernetes раннер создаёт по одному Pod'у на задачу в пространстве имён, передаёт его журнал в Semaphore и удаляет Pod после завершения задачи. Раннер может работать в кластере (рекомендуется) или вне его, с kubeconfig.

Как выполняется задача в контейнере, что находится внутри контейнера и что не поддерживается, описано в разделе Исполнители. Эта страница посвящена настройке Kubernetes.

Перед началом​

  • Сервер Semaphore с лицензией Enterprise.
  • Раннер, развёрнутый в кластере, см. Установка раннера в Kubernetes. Шаги ниже используют его пространство имён semaphore и Deployment semaphore-runner. Для раннера вне кластера см. Раннер вне кластера.
  • kubectl с правами на создание сервисных аккаунтов и объектов RBAC в этом пространстве имён.
  • Кластер может загрузить semaphoreui/job и semaphoreui/helper (или ваши собственные образы).

Что создаёт раннер​

Для задачи с ID 42 раннер создаёт в своём пространстве имён:

ОбъектИмяСодержимое
Podsemaphore-job-42-<random>Init-контейнер git-clone (вспомогательный образ) и контейнер build (образ build), restartPolicy: Never.
Secret<pod>-ssh-<key id>По одному на SSH-ключ, монтируется в /secrets/ssh/<key id>/.
Secret<pod>-vaultПароли Ansible vault.
Secret<pod>-extra-varsvalues.json с дополнительными переменными.
Secret<pod>-envСекреты Variable Group типа Environment variable и SEMAPHORE_JWT, подключаются через envFrom.
ConfigMap<pod>-inventoryСтатический инвентарь.

Создаются только объекты, нужные задаче. Все они помечены меткой semaphoreui.com/runner=semaphore; у Pod'а также есть semaphoreui.com/task-id=42. Раннер удаляет их все после завершения задачи, в том числе после ошибки или Stop.

Настройка​

Шаг 1. Создайте сервисные аккаунты​

Раннеру нужен один сервисный аккаунт, чтобы создавать Pod'ы задач. Pod'ы задач получают второй — без прав и без токена API, чтобы плейбук не мог воспользоваться правами раннера.

semaphore-serviceaccounts.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: semaphore-runner
namespace: semaphore
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: semaphore-job
namespace: semaphore
automountServiceAccountToken: false

Шаг 2. Выдайте раннеру права​

Это полный список вызовов API, которые делает исполнитель:

semaphore-rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: semaphore-runner
namespace: semaphore
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["create", "get", "delete"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
- apiGroups: [""]
resources: ["secrets", "configmaps"]
verbs: ["create", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: semaphore-runner
namespace: semaphore
subjects:
- kind: ServiceAccount
name: semaphore-runner
namespace: semaphore
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: semaphore-runner

Именно Role, а не ClusterRole: раннер может работать только со своим пространством имён.

Шаг 3. Переключите раннер на исполнитель Kubernetes​

Pod'ы задач не задают securityContext, поэтому пространство имён, в котором действует уровень Pod Security restricted, их отклоняет. Разрешите baseline:

kubectl label namespace semaphore pod-security.kubernetes.io/enforce=baseline --overwrite

Назначьте Pod'у раннера его сервисный аккаунт и настройки исполнителя. В Deployment раннера добавьте в spec.template.spec:

semaphore-runner.yaml (fragment)
    spec:
serviceAccountName: semaphore-runner
containers:
- name: runner
env:
# ...SEMAPHORE_WEB_ROOT and SEMAPHORE_RUNNER_TOKEN as before
- name: SEMAPHORE_RUNNER_EXECUTOR_TYPE
value: k8s
- name: SEMAPHORE_RUNNER_K8S_NAMESPACE
value: semaphore
- name: SEMAPHORE_RUNNER_K8S_SERVICE_ACCOUNT
value: semaphore-job
- name: SEMAPHORE_RUNNER_K8S_IMAGE
value: semaphoreui/job:latest
kubectl apply -f semaphore-serviceaccounts.yaml -f semaphore-rbac.yaml -f semaphore-runner.yaml
kubectl -n semaphore rollout status deploy/semaphore-runner

Если kubeconfig не задан, раннер использует внутрикластерные учётные данные semaphore-runner. Задачи больше не выполняются в Pod'е раннера, поэтому его resources можно уменьшить (например, до 100m CPU и 256Mi памяти).

Шаг 4. Запустите задачу​

Направьте шаблон на раннер и запустите задачу. Наблюдайте, как её Pod появляется и исчезает:

kubectl -n semaphore get pods -l semaphoreui.com/runner=semaphore -w

Журнал задачи выглядит так:

k8s: created pod semaphore/semaphore-job-42-a1b2c3 (1 ssh keys installed)
=== git-clone (init) ===
Cloning into '/workspace/repo'...
=== build (build) ===
semaphore: container executor: task 42, template 7
...
k8s: deleted pod semaphore/semaphore-job-42-a1b2c3

Примеры​

Раннер вне кластера​

Раннер работает на ВМ или в Docker и обращается к API-серверу через kubeconfig. Дайте kubeconfig роль из шага 2 и ничего больше:

{
"web_host": "https://semaphore.example.com",
"runner": {
"token_file": "/var/lib/semaphore/runner_token.txt",
"executor": {
"type": "k8s",
"k8s": {
"kubeconfig": "/etc/semaphore/kubeconfig",
"namespace": "semaphore",
"service_account": "semaphore-job"
}
}
}
}

Используется текущий контекст kubeconfig.

Образы из приватного реестра​

Создайте pull secret в пространстве имён и укажите его в pull_secrets; несколько имён разделяются запятыми:

kubectl -n semaphore create secret docker-registry registry-example \
--docker-server=registry.example.com \
--docker-username=<user> --docker-password=<password>
            - name: SEMAPHORE_RUNNER_K8S_IMAGE
value: registry.example.com/semaphore/job-aws:1.0
- name: SEMAPHORE_RUNNER_K8S_PULL_SECRETS
value: registry-example

Секреты подключаются к каждому Pod'у задачи — одинаково для образа build, вспомогательного образа и Executor image шаблона.

CPU и память Pod'ов задач​

Исполнитель не задаёт requests и limits для Pod'ов задач. Задайте значения по умолчанию для пространства имён с помощью LimitRange; он применяется к контейнерам git-clone и build:

semaphore-limits.yaml
apiVersion: v1
kind: LimitRange
metadata:
name: semaphore-task-defaults
namespace: semaphore
spec:
limits:
- type: Container
defaultRequest:
cpu: 250m
memory: 256Mi
default:
cpu: "2"
memory: 2Gi

Добавьте ResourceQuota, чтобы ограничить суммарное потребление всех параллельных задач пространства имён.

Ограничение сети Pod'ов задач​

Pod'ы задач помечены меткой semaphoreui.com/runner=semaphore, поэтому NetworkPolicy может их выбрать. Эта политика разрешает только DNS, Git-сервер и SSH к управляемым хостам:

semaphore-netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: semaphore-tasks
namespace: semaphore
spec:
podSelector:
matchLabels:
semaphoreui.com/runner: semaphore
policyTypes: ["Ingress", "Egress"]
egress:
- ports:
- { protocol: UDP, port: 53 }
- { protocol: TCP, port: 53 }
- to:
- ipBlock: { cidr: 10.20.0.0/16 } # Git server and managed hosts
ports:
- { protocol: TCP, port: 22 }
- { protocol: TCP, port: 443 }

Справочник параметров​

Все параметры находятся в runner.executor.k8s конфигурации раннера.

ПараметрПеременная окруженияПо умолчаниюОписание
kubeconfigSEMAPHORE_RUNNER_K8S_KUBECONFIGin-clusterПуть к файлу kubeconfig. Пусто: сервисный аккаунт Pod'а раннера.
namespaceSEMAPHORE_RUNNER_K8S_NAMESPACEsemaphoreПространство имён Pod'ов, Secrets и ConfigMaps задач. Должно существовать.
imageSEMAPHORE_RUNNER_K8S_IMAGEsemaphoreui/job:latestОбраз контейнера build. Поле Executor image шаблона переопределяет его.
helper_imageSEMAPHORE_RUNNER_K8S_HELPER_IMAGEsemaphoreui/helper:latestОбраз init-контейнера git-clone.
service_accountSEMAPHORE_RUNNER_K8S_SERVICE_ACCOUNTdefaultСервисный аккаунт Pod'ов задач. Используйте аккаунт без прав, см. шаг 1.
pull_secretsSEMAPHORE_RUNNER_K8S_PULL_SECRETS—imagePullSecrets для Pod'ов задач через запятую.
poll_interval_secondsSEMAPHORE_RUNNER_K8S_POLL_INTERVAL_SECONDS3Как часто раннер читает статус Pod'а. Меньшие значения раньше запускают передачу журнала, но дают больше вызовов API.
cleanup_grace_secondsSEMAPHORE_RUNNER_K8S_CLEANUP_GRACE_SECONDS30Период ожидания при удалении Pod'а после завершения или остановки задачи.

Политика загрузки образов соответствует умолчанию Kubernetes: Always для :latest, иначе IfNotPresent.

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

  • Минимальные права для раннера. Роль ограничена пространством имён и содержит только то, что вызывает исполнитель. Не привязывайте cluster-admin.
  • Никакого доступа к API для задач. С service_account: default по умолчанию Pod задачи получает токен аккаунта default пространства имён. Используйте отдельный аккаунт с automountServiceAccountToken: false.
  • Пространство имён только для Semaphore. Раннер ничего не может читать, но может создавать и удалять там Pod'ы и Secrets; не делите это пространство имён с другими нагрузками.
  • Секреты — это отдельные Secrets Kubernetes для каждой задачи, смонтированные только для чтения (секреты окружения — через envFrom, поэтому их значений нет в спецификации Pod'а) и удаляемые вместе с Pod'ом. Тот, кто может читать Secrets в пространстве имён, может прочитать их, пока выполняется задача; включите шифрование при хранении для Secrets.
  • Сеть. Ограничьте доступ задач с помощью NetworkPolicy.

Остановка и очистка​

Stop в интерфейсе прекращает передачу журнала и удаляет Pod с cleanup_grace_seconds, затем — его Secrets и ConfigMaps.

Если раннер убит во время задачи, объекты задачи остаются в пространстве имён; при следующем запуске раннер их не собирает. Удалите их, когда не выполняется ни одна задача:

kubectl -n semaphore get pods,secrets,configmaps -l semaphoreui.com/runner=semaphore
kubectl -n semaphore delete pods,secrets,configmaps -l semaphoreui.com/runner=semaphore

Устранение неполадок​

СимптомПричина и решение
Журнал раннера: k8s executor is only available in the proprietary buildРаннер — сборка Community. Используйте semaphoreui/runner или официальный бинарный файл.
Журнал раннера: failed to initialise executor provider с unable to load in-cluster configurationРаннер работает вне кластера, а kubeconfig пуст, или путь к kubeconfig неверен.
Задача: create pod ...: pods is forbidden: User "system:serviceaccount:..." cannot create resourceRBAC отсутствует или привязан в другом пространстве имён. Проверьте kubectl auth can-i create pods -n semaphore --as=system:serviceaccount:semaphore:semaphore-runner.
Задача: create pod ...: namespaces "semaphore" not foundСоздайте пространство имён; раннер этого не делает.
Задача: create pod ...: violates PodSecurity "restricted"Пометьте пространство имён pod-security.kubernetes.io/enforce: baseline.
Задача остаётся в статусе Running без выводаPod застрял в Pending: нет подходящего узла, образ не загружается (ImagePullBackOff) или его блокирует квота. Раннер ждёт завершения Pod'а. Проверьте kubectl -n semaphore describe pod -l semaphoreui.com/task-id=<task id> и остановите задачу.
Задача: pod ... finished with status FailedОшибка клонирования или приложения. В журнале выше — вывод git-clone и build.
interpreter python3 not found in imageВ образе build нет бинарного файла приложения; укажите другой образ или Executor image шаблона.
No user exists for uid ... (OpenShift)/etc/passwd образа недоступен для записи группе. См. Сборка собственного образа.