Ejecutor Kubernetes Enterprise
Con el ejecutor Kubernetes, un runner crea un Pod por tarea en un namespace, transmite su log a Semaphore y elimina el Pod cuando la tarea termina. El runner puede ejecutarse dentro del clúster (recomendado) o fuera de él con un kubeconfig.
Cómo se ejecuta una tarea en contenedor, qué hay dentro del contenedor y qué no se admite se describe en Ejecutores. Esta página cubre la configuración de Kubernetes.
Antes de empezar
- Un servidor de Semaphore con licencia Enterprise.
- Un runner desplegado en el clúster; consulte Instalar un runner en Kubernetes.
Los pasos siguientes usan su namespace
semaphorey el Deploymentsemaphore-runner. Para un runner fuera del clúster, consulte Runner fuera del clúster. kubectlcon permisos para crear cuentas de servicio y objetos RBAC en ese namespace.- El clúster puede descargar
semaphoreui/jobysemaphoreui/helper(o sus propias imágenes).
Qué crea el runner
Para una tarea con ID 42, el runner crea en su namespace:
| Objeto | Nombre | Contenido |
|---|---|---|
| Pod | semaphore-job-42-<random> | Contenedor init git-clone (imagen auxiliar) y contenedor build (imagen build), restartPolicy: Never. |
| Secret | <pod>-ssh-<key id> | Uno por clave SSH, montado en /secrets/ssh/<key id>/. |
| Secret | <pod>-vault | Contraseñas de Ansible Vault. |
| Secret | <pod>-extra-vars | values.json con las variables extra. |
| Secret | <pod>-env | Secretos del Variable Group de tipo Variable de entorno y SEMAPHORE_JWT, inyectados con envFrom. |
| ConfigMap | <pod>-inventory | Un inventario estático. |
Solo se crean los objetos que la tarea necesita. Todos llevan la etiqueta
semaphoreui.com/runner=semaphore; el Pod también tiene semaphoreui.com/task-id=42. El
runner los elimina todos cuando la tarea termina, también tras un fallo o Stop.
Configuración
Paso 1. Crear las cuentas de servicio
El runner necesita una cuenta de servicio para crear los Pods de las tareas. Los Pods de las tareas reciben una segunda, sin permisos ni token de API, para que un playbook no pueda usar los derechos del runner.
apiVersion: v1
kind: ServiceAccount
metadata:
name: semaphore-runner
namespace: semaphore
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: semaphore-job
namespace: semaphore
automountServiceAccountToken: false
Paso 2. Conceder permisos al runner
Esta es la lista completa de llamadas a la API que hace el ejecutor:
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
Un Role, no un ClusterRole: el runner solo puede tocar su propio namespace.
Paso 3. Cambiar el runner al ejecutor Kubernetes
Los Pods de las tareas no definen un securityContext, por lo que un namespace que aplica el nivel
de Pod Security restricted los rechaza. Permita baseline:
kubectl label namespace semaphore pod-security.kubernetes.io/enforce=baseline --overwrite
Asigne al Pod del runner su cuenta de servicio y los ajustes del ejecutor. En el Deployment del
runner, añada a spec.template.spec:
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
Sin kubeconfig definido, el runner usa las credenciales dentro del clúster de
semaphore-runner. Las tareas ya no se ejecutan en el Pod del runner, así que puede reducir sus
resources (por ejemplo, 100m de CPU y 256Mi de memoria).
Paso 4. Ejecutar una tarea
Dirija una plantilla al runner e inicie una tarea. Observe cómo su Pod aparece y desaparece:
kubectl -n semaphore get pods -l semaphoreui.com/runner=semaphore -w
El log de la tarea muestra:
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
Ejemplos
Runner fuera del clúster
El runner se ejecuta en una VM o en Docker y accede al servidor de API con un kubeconfig. Dé al kubeconfig el Role del paso 2 y nada más:
{
"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"
}
}
}
}
Se usa el contexto actual del kubeconfig.
Imágenes de un registro privado
Cree un secret de descarga en el namespace e inclúyalo en pull_secrets; varios nombres
se separan con comas:
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
Los secrets se adjuntan a cada Pod de tarea, por igual para la imagen build, la imagen auxiliar y la Imagen del ejecutor de una plantilla.
CPU y memoria de los Pods de tareas
El ejecutor no define requests ni limits en los Pods de tareas. Defina valores
predeterminados para el namespace con un LimitRange; se aplica a los contenedores
git-clone y build:
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
Añada un ResourceQuota para limitar cuánto pueden usar todas las tareas paralelas del namespace.
Restringir la red de los Pods de tareas
Los Pods de tareas llevan la etiqueta semaphoreui.com/runner=semaphore, así que una
NetworkPolicy puede seleccionarlos. Esta solo permite DNS, el servidor Git y SSH hacia
los hosts gestionados:
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 }
Referencia de configuración
Todas las opciones están en runner.executor.k8s en la configuración del runner.
| Opción | Variable de entorno | Predeterminado | Descripción |
|---|---|---|---|
kubeconfig | SEMAPHORE_RUNNER_K8S_KUBECONFIG | in-cluster | Ruta a un archivo kubeconfig. Vacío: la cuenta de servicio del Pod del runner. |
namespace | SEMAPHORE_RUNNER_K8S_NAMESPACE | semaphore | Namespace de los Pods, Secrets y ConfigMaps de las tareas. Debe existir. |
image | SEMAPHORE_RUNNER_K8S_IMAGE | semaphoreui/job:latest | Imagen del contenedor build. La Imagen del ejecutor de una plantilla la sobrescribe. |
helper_image | SEMAPHORE_RUNNER_K8S_HELPER_IMAGE | semaphoreui/helper:latest | Imagen del contenedor init git-clone. |
service_account | SEMAPHORE_RUNNER_K8S_SERVICE_ACCOUNT | default | Cuenta de servicio de los Pods de tareas. Use una sin permisos; consulte el paso 1. |
pull_secrets | SEMAPHORE_RUNNER_K8S_PULL_SECRETS | — | imagePullSecrets separados por comas para los Pods de tareas. |
poll_interval_seconds | SEMAPHORE_RUNNER_K8S_POLL_INTERVAL_SECONDS | 3 | Cada cuánto lee el runner el estado del Pod. Valores menores inician antes la transmisión del log y cuestan más llamadas a la API. |
cleanup_grace_seconds | SEMAPHORE_RUNNER_K8S_CLEANUP_GRACE_SECONDS | 30 | Periodo de gracia de la eliminación del Pod cuando la tarea termina o se detiene. |
La política de descarga de imágenes sigue el valor predeterminado de Kubernetes: Always
para :latest y, en otro caso, IfNotPresent.
Seguridad
- Mínimo privilegio para el runner. El Role
está limitado al namespace y solo enumera lo que llama el ejecutor. No vincule
cluster-admin. - Sin acceso a la API para las tareas. Con el valor predeterminado
service_account: default, el Pod de la tarea obtiene el token de la cuenta default del namespace. Use una cuenta dedicada conautomountServiceAccountToken: false. - Un namespace solo para Semaphore. El runner no puede leer nada, pero sí crear y eliminar Pods y Secrets allí; no comparta el namespace con otras cargas de trabajo.
- Los secretos son Secrets de Kubernetes separados por tarea, montados en solo lectura
(los secretos de entorno mediante
envFrom, así que sus valores no aparecen en la especificación del Pod) y eliminados con el Pod. Quien pueda leer Secrets en el namespace puede leerlos mientras la tarea se ejecuta; active el cifrado en reposo para los Secrets. - Red. Limite lo que alcanzan las tareas con una NetworkPolicy.
Detener y limpiar
Stop en la interfaz termina la transmisión del log y elimina el Pod con
cleanup_grace_seconds y, después, sus Secrets y ConfigMaps.
Si el runner se mata durante una tarea, los objetos de la tarea quedan en el namespace; el runner no los recoge en el siguiente inicio. Elimínelos cuando no haya ninguna tarea en ejecución:
kubectl -n semaphore get pods,secrets,configmaps -l semaphoreui.com/runner=semaphore
kubectl -n semaphore delete pods,secrets,configmaps -l semaphoreui.com/runner=semaphore
Solución de problemas
| Síntoma | Causa y solución |
|---|---|
Log del runner: k8s executor is only available in the proprietary build | El runner es una compilación Community. Use semaphoreui/runner o el binario oficial. |
Log del runner: failed to initialise executor provider con unable to load in-cluster configuration | El runner está fuera de un clúster y kubeconfig está vacío, o la ruta del kubeconfig es incorrecta. |
Tarea: create pod ...: pods is forbidden: User "system:serviceaccount:..." cannot create resource | Falta RBAC o está vinculado en otro namespace. Compruébelo con kubectl auth can-i create pods -n semaphore --as=system:serviceaccount:semaphore:semaphore-runner. |
Tarea: create pod ...: namespaces "semaphore" not found | Cree el namespace; el runner no lo hace. |
Tarea: create pod ...: violates PodSecurity "restricted" | Etiquete el namespace con pod-security.kubernetes.io/enforce: baseline. |
| La tarea se queda en Running sin salida | El Pod está atascado en Pending: ningún nodo encaja, la imagen no se puede descargar (ImagePullBackOff) o una cuota lo bloquea. El runner espera hasta que el Pod termine. Revise kubectl -n semaphore describe pod -l semaphoreui.com/task-id=<task id> y detenga la tarea. |
Tarea: pod ... finished with status Failed | Falló el clonado o la aplicación. El log anterior muestra la salida de git-clone y build. |
interpreter python3 not found in image | A la imagen build le falta el binario de la aplicación; defina otra imagen o la Imagen del ejecutor de la plantilla. |
No user exists for uid ... (OpenShift) | El /etc/passwd de la imagen no tiene permiso de escritura para el grupo. Consulte Crear una imagen personalizada. |
Páginas relacionadas
- Ejecutores: cómo se ejecutan las tareas en contenedor, imágenes, limitaciones.
- Ejecutor Docker: el mismo modelo en un host Docker.
- Instalación en Kubernetes: el servidor y el Deployment del runner.
- Imagen del ejecutor: otra imagen para una sola plantilla.