Aller au contenu principal

Exécuteur Docker Pro

Avec l'exécuteur Docker, un runner démarre chaque tâche dans un nouveau conteneur et le supprime à la fin de la tâche. Le runner communique avec un démon Docker via son API : le démon peut être sur le même hôte, ou distant via TLS.

Le déroulement d'une tâche en conteneur, le contenu du conteneur et ce qui n'est pas pris en charge sont décrits dans Exécuteurs. Cette page couvre la configuration Docker.

Avant de commencer​

  • Un serveur Semaphore avec une licence Pro ou Enterprise.
  • Un runner enregistré sur le serveur, voir Runners. Utilisez l'image semaphoreui/runner ou le binaire officiel semaphore.
  • Docker Engine accessible depuis le runner, avec le droit de l'utiliser : appartenance au groupe docker, accès au socket monté, ou certificats client TLS.
  • Le démon peut récupérer semaphoreui/job et semaphoreui/helper (ou vos propres images).

Démarrage rapide​

Ajoutez l'exécuteur à la configuration du runner et redémarrez le runner :

{
"web_host": "https://semaphore.example.com",
"runner": {
"token_file": "/var/lib/semaphore/runner_token.txt",
"executor": {
"type": "docker"
}
}
}

Sans autre option, le runner utilise le socket local (DOCKER_HOST s'il est défini, sinon unix:///var/run/docker.sock), l'image de build semaphoreui/job:latest et le réseau bridge.

Lancez n'importe quelle tâche dirigée vers ce runner. Le journal de la tâche commence par :

docker: prepared task 42 (workspace volume semaphore-workspace-42-a1b2c3, secrets=true)
=== git-clone ===
Cloning into '/workspace/repo'...
=== build ===
semaphore: container executor: task 42, template 7

Pendant l'exécution de la tâche, vous pouvez voir ses conteneurs :

docker ps --filter label=com.semaphoreui.runner=semaphore

Exemples​

Binaire du runner sur un hôte Docker​

Le runner s'exécute comme service systemd sur le même hôte que Docker. Donnez à son utilisateur l'accès au socket :

sudo usermod -aG docker semaphore
sudo systemctl restart semaphore-runner
{
"web_host": "https://semaphore.example.com",
"tmp_path": "/tmp/semaphore",
"runner": {
"token_file": "/var/lib/semaphore/runner_token.txt",
"executor": {
"type": "docker",
"docker": {
"image": "semaphoreui/job:latest",
"cpu_limit": 2,
"memory_limit": "2g"
}
}
}
}

Runner dans un conteneur avec le socket de l'hôte​

Le conteneur du runner pilote le démon de l'hôte via le socket monté ; les conteneurs des tâches sont des voisins du runner, pas des conteneurs imbriqués. L'image du runner s'exécute avec l'UID 1001 : ajoutez-le donc au groupe propriétaire du socket :

docker run -d --name semaphore-runner \
--restart unless-stopped \
-e SEMAPHORE_WEB_ROOT=https://semaphore.example.com \
-e SEMAPHORE_RUNNER_REGISTRATION_TOKEN=<registration token> \
-e SEMAPHORE_RUNNER_EXECUTOR_TYPE=docker \
-v /var/run/docker.sock:/var/run/docker.sock \
-v semaphore-runner-data:/var/lib/semaphore \
--group-add "$(stat -c %g /var/run/docker.sock)" \
semaphoreui/runner:latest

Le volume semaphore-runner-data conserve le token du runner émis lors de l'enregistrement, afin que le runner ne s'enregistre pas de nouveau après un redémarrage.

La même chose avec Docker Compose :

services:
runner:
image: semaphoreui/runner:latest
restart: unless-stopped
environment:
SEMAPHORE_WEB_ROOT: https://semaphore.example.com
SEMAPHORE_RUNNER_REGISTRATION_TOKEN: ${RUNNER_REGISTRATION_TOKEN}
SEMAPHORE_RUNNER_EXECUTOR_TYPE: docker
SEMAPHORE_RUNNER_DOCKER_IMAGE: semaphoreui/job:latest
SEMAPHORE_RUNNER_DOCKER_PULL_POLICY: if-not-present
SEMAPHORE_RUNNER_DOCKER_MEMORY_LIMIT: 2g
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- runner-data:/var/lib/semaphore
group_add:
- "${DOCKER_GID}" # stat -c %g /var/run/docker.sock

volumes:
runner-data:

Les secrets parviennent à la tâche par un volume nommé sur le démon, jamais par le système de fichiers du runner : aucun autre chemin n'a donc besoin d'être partagé entre le runner et l'hôte.

Démon Docker distant via TLS​

Le runner et le démon sont sur des machines différentes. Protégez le port du démon avec du TLS mutuel (voir Protect the Docker daemon socket) et placez les fichiers client sur le runner :

/etc/semaphore/docker-certs/
├── ca.pem # CA that signed the daemon certificate
├── cert.pem # client certificate
└── key.pem # client key
{
"runner": {
"executor": {
"type": "docker",
"docker": {
"host": "tcp://docker-build-01.internal:2376",
"cert_path": "/etc/semaphore/docker-certs"
}
}
}
}

Lorsque cert_path est défini, le runner vérifie le certificat du démon avec ca.pem et s'authentifie avec cert.pem et key.pem. Les noms de fichiers sont fixes.

attention

Sans cert_path, une connexion tcp:// est du HTTP en clair, et quiconque atteint le port contrôle le démon. tls_verify seul n'active pas TLS (⚠️ 2026-11-05 : l'option est lue mais pas appliquée) ; définissez toujours cert_path pour un démon distant.

Un réseau pour les services internes​

Par défaut, les conteneurs des tâches rejoignent bridge. Pour permettre aux tâches d'atteindre des services par nom de conteneur, ou pour les isoler dans leur propre réseau, créez un réseau et définissez-le :

docker network create semaphore-tasks
{
"runner": {
"executor": {
"type": "docker",
"docker": {
"network": "semaphore-tasks"
}
}
}
}

network accepte tout mode réseau compris par Docker : un nom de réseau, bridge, host ou none. Le conteneur de clonage rejoint le même réseau : avec none, le dépôt ne peut donc pas être cloné.

Images d'un registre privé​

L'exécuteur récupère les images sans identifiants de registre. Pour une image privée, connectez-vous et récupérez-la sur l'hôte du démon, et gardez une politique de pull qui ne la récupère pas de nouveau :

docker login registry.example.com
docker pull registry.example.com/semaphore/job-aws:1.0
{
"runner": {
"executor": {
"type": "docker",
"docker": {
"image": "registry.example.com/semaphore/job-aws:1.0",
"pull_policy": "if-not-present"
}
}
}
}

Utilisez un tag de version plutôt que latest avec if-not-present : un tag déjà présent sur le démon n'est jamais rafraîchi. never fait échouer la tâche si l'image manque, au lieu de tenter un pull.

Le bloc entier dans une seule variable​

Pour les déploiements configurés uniquement par l'environnement, passez le bloc de l'exécuteur en JSON :

SEMAPHORE_RUNNER_EXECUTOR='{"type":"docker","docker":{"network":"semaphore-tasks","memory_limit":"4g","cpu_limit":2}}'

Référence de configuration​

Toutes les options se trouvent sous runner.executor.docker dans la configuration du runner.

OptionVariable d'environnementPar défautDescription
hostSEMAPHORE_RUNNER_DOCKER_HOSTDOCKER_HOST, puis le socket de la plateformeAdresse du démon : unix:///path, tcp://host:port ou npipe:////./pipe/name.
cert_pathSEMAPHORE_RUNNER_DOCKER_CERT_PATH—Répertoire contenant ca.pem, cert.pem et key.pem pour le TLS mutuel. Le définir active TLS.
tls_verifySEMAPHORE_RUNNER_DOCKER_TLS_VERIFYfalsePas encore appliquée ; utilisez cert_path.
imageSEMAPHORE_RUNNER_DOCKER_IMAGEsemaphoreui/job:latestImage de build. Le champ Image de l'exécuteur d'un modèle la remplace.
helper_imageSEMAPHORE_RUNNER_DOCKER_HELPER_IMAGEsemaphoreui/helper:latestImage du conteneur de clonage et du remplissage du volume des secrets.
networkSEMAPHORE_RUNNER_DOCKER_NETWORKbridgeRéseau des conteneurs de clonage et de build.
pull_policySEMAPHORE_RUNNER_DOCKER_PULL_POLICYif-not-presentalways, if-not-present ou never. Une valeur inconnue revient à if-not-present.
cpu_limitSEMAPHORE_RUNNER_DOCKER_CPU_LIMITaucune limiteCPU par conteneur, comme docker run --cpus. Fractions autorisées : 0.5.
memory_limitSEMAPHORE_RUNNER_DOCKER_MEMORY_LIMITaucune limiteMémoire par conteneur : en octets, ou avec k, m, g (512m, 2g). Une valeur illisible signifie aucune limite.
privilegedSEMAPHORE_RUNNER_DOCKER_PRIVILEGEDfalseExécute les conteneurs avec --privileged.
cleanup_grace_secondsSEMAPHORE_RUNNER_DOCKER_CLEANUP_GRACE_SECONDS30Secondes pendant lesquelles docker stop attend après Stop avant de tuer le conteneur.
poll_interval_secondsSEMAPHORE_RUNNER_DOCKER_POLL_INTERVAL_SECONDS2Non utilisée : l'exécuteur attend la fin du conteneur au lieu de l'interroger.

Les limites, le réseau et privileged s'appliquent aussi bien au conteneur de clonage qu'au conteneur de build.

Arrêt et nettoyage​

Stop dans l'interface envoie docker stop au conteneur en cours d'exécution, attend jusqu'à cleanup_grace_seconds, puis supprime les conteneurs et les deux volumes.

Si le processus du runner est tué pendant une tâche, ses conteneurs et volumes restent sur le démon ; le runner ne les récupère pas au démarrage suivant. Tout ce que l'exécuteur crée porte le label com.semaphoreui.runner=semaphore, et com.semaphoreui.task-id contient l'ID de la tâche. Supprimez les restes lorsqu'aucune tâche ne s'exécute sur ce démon :

docker ps -a --filter label=com.semaphoreui.runner=semaphore
docker rm -f $(docker ps -aq --filter label=com.semaphoreui.runner=semaphore)
docker volume rm $(docker volume ls -q --filter label=com.semaphoreui.runner=semaphore)

Dépannage​

MessageCause et solution
docker executor is only available in the proprietary build (journal du runner)Le runner est une version Community. Utilisez semaphoreui/runner ou le binaire officiel.
docker executor: daemon not reachablehost incorrect, démon arrêté, ou l'utilisateur du runner ne peut pas ouvrir le socket (permission denied). Vérifiez avec docker -H <host> info en tant qu'utilisateur du runner ; ajoutez l'utilisateur au groupe du socket.
pull image ...: ... unauthorized ou not foundL'image est privée ou mal orthographiée. Récupérez-la d'abord sur l'hôte du démon, voir registre privé.
git clone exited with status 128L'URL du dépôt, la branche ou la clé SSH est incorrecte, ou le réseau bloque l'hôte Git. La sortie du clonage au-dessus de la ligne montre l'erreur Git.
interpreter python3 not found in image; set a runner image that provides itL'image de build ne contient pas le binaire de l'application. Définissez une image qui le contient, sur le runner ou dans le champ Image de l'exécuteur du modèle.
ssh-agent / ssh-add not found in image; skipping SSH key installL'image n'a pas de client OpenSSH ; Ansible ne peut pas utiliser la clé de l'inventaire. Installez openssh-client.
Permission denied sous /workspaceUne image personnalisée s'exécute avec un utilisateur qui ne peut pas écrire dans /workspace. Voir Construire une image personnalisée.
build container exited with status NL'application a échoué avec le code de sortie N ; la raison figure dans le journal au-dessus.
inventory login_password authentication is not supportedVoir Limitations.