Introduction
Les GitLab Runners mal configurés sont une cause majeure de pipelines instables, de mystérieux échecs de tâches et d'heures d'ingénierie perdues. Le problème est rarement évident dans l'interface de GitLab : une tâche qui échoue avec le code 137, un runner qui ne traite jamais les tâches étiquetées ou un exécuteur qui utilise silencieusement la mauvaise image Docker. Chaque symptôme indique une erreur de configuration spécifique, et chaque erreur possède une commande de diagnostic, une correction et un moyen de prévenir la récurrence.
Cet article s'adresse aux développeurs, ingénieurs DevOps et responsables techniques qui gèrent des GitLab Runners auto-hébergés. Il est organisé autour de symptômes observables plutôt que de réglages abstraits. Pour chaque erreur, vous trouverez la commande ou la configuration exacte à inspecter, la sortie attendue lorsque tout est correct, le signal d'échec indiquant un problème et une voie de récupération qui minimise les interruptions.
Les exemples supposent un GitLab Runner 15.x ou ultérieur sous Linux, utilisant le binaire gitlab-runner et /etc/gitlab-runner/config.toml. Avant toute modification, exécutez sudo gitlab-runner --version et sudo gitlab-runner list pour capturer votre version actuelle et vos runners enregistrés. Sauvegardez toujours config.toml avec sudo cp /etc/gitlab-runner/config.toml /etc/gitlab-runner/config.toml.bak-$(date +%Y%m%d) avant toute édition.
Inventaire de la version et de l'environnement
Une session de dépannage fiable commence par un inventaire précis de la version du runner, du système d'exploitation, de l'exécuteur et de l'état d'enregistrement. Sauter cette étape conduit à appliquer des corrections pour la mauvaise version ou à manquer une incohérence entre le runner et votre instance GitLab.
Exécutez ces commandes en lecture seule sur l'hôte du runner :
sudo gitlab-runner --version
sudo gitlab-runner list
sudo systemctl status gitlab-runner --no-pager
La sortie attendue pour gitlab-runner list comprend un tableau avec les colonnes Executor, Token et URL. Si vous voyez ERROR: Failed to load config file ou si la liste des runners est vide alors que des tâches sont en attente, le runner ne lit pas le config.toml attendu.
La commande de version doit renvoyer quelque chose comme Version: 15.11.0 et Git revision: 5e9f5f4e. Si vous utilisez GitLab.com, la version de votre runner doit respecter la politique de versions supportées : la version mineure ne doit pas avoir plus de deux versions de retard sur la version mineure actuelle de GitLab. Par exemple, si votre instance GitLab est en 16.0, une version de runner 15.9 est encore supportée, mais 15.0 est trop ancienne et peut manquer des correctifs de sécurité ou des fonctionnalités.
Notez l'exécuteur de chaque runner. Une erreur courante est de supposer que l'exécuteur est docker alors qu'il est en réalité shell. Exécutez sudo gitlab-runner list et regardez la colonne Executor. Si elle indique shell, les tâches s'exécutent directement sur l'hôte et peuvent avoir des effets secondaires comme des processus ou des fichiers résiduels. Si elle indique docker, les tâches s'exécutent dans des conteneurs définis par le mot-clé image dans .gitlab-ci.yml ou la section [[runners]] du runner.
Documentez l'architecture du runner : uname -m doit renvoyer x86_64, aarch64, etc. Si vous utilisez l'exécuteur Docker avec des images multi-architectures, assurez-vous que l'hôte du runner et les images de base correspondent. Par exemple, un runner sur un hôte ARM64 ne peut pas exécuter d'images Docker amd64 sans émulation.
Enfin, notez l'URL de l'instance GitLab et la portée du jeton d'enregistrement. Un runner peut être shared (partagé), group (groupe) ou project (projet). Exécutez sudo gitlab-runner list et notez les colonnes URL et Token (le jeton est partiellement masqué). Cette classification affecte les projets qui peuvent utiliser le runner et où chercher les conflits de configuration.
Erreurs de configuration courantes et leurs symptômes
Erreur 1 : Mauvais type d'exécuteur
Symptôme : Les tâches du pipeline échouent avec ERROR: Preparation failed: Cannot connect to the Docker daemon même si Docker est installé, ou les tâches se comportent de manière incohérente entre les environnements.
Pourquoi cela arrive : L'executor du runner dans config.toml est défini sur docker, mais le démon Docker n'est pas accessible, ou l'inverse : il est défini sur shell alors que l'équipe attendait une isolation par conteneur.
Diagnostic :
sudo grep -A2 'executor' /etc/gitlab-runner/config.toml
sudo docker info
Si executor = "docker" et que docker info renvoie Cannot connect to the Docker daemon, le runner ne peut pas démarrer de conteneurs Docker. Si executor = "shell" et que vous voyez des processus au niveau de l'hôte provenant de tâches précédentes, vous n'avez aucune isolation par conteneur.
Correction : Modifiez config.toml avec sudo nano /etc/gitlab-runner/config.toml et définissez le bon exécuteur. Pour Docker :
[[runners]]
name = "my-docker-runner"
url = "https://gitlab.example.com/"
token = "YOUR_TOKEN"
executor = "docker"
[runners.docker]
image = "alpine:latest"
privileged = false
Pour shell :
[[runners]]
name = "my-shell-runner"
url = "https://gitlab.example.com/"
token = "YOUR_TOKEN"
executor = "shell"
Après modification, redémarrez le runner : sudo gitlab-runner restart. Vérifiez avec sudo gitlab-runner verify et un pipeline de test.
Erreur 2 : Étiquettes manquantes ou incorrectes
Symptôme : Les tâches attendent indéfiniment avec This job is stuck because the project has no runners online that can run this job ou sont assignées au mauvais runner.
Pourquoi cela arrive : Les tags de la tâche dans .gitlab-ci.yml ne correspondent à aucune tags de runner dans config.toml, ou le runner n'a pas d'étiquettes alors que la tâche exige une étiquette spécifique.
Diagnostic :
sudo grep -A5 'tags' /etc/gitlab-runner/config.toml
grep -A3 'tags:' .gitlab-ci.yml
Exemple de tags de runner dans config.toml :
[[runners]]
name = "docker-builder"
tags = ["docker", "linux", "amd64"]
Exemple de tags de tâche :
build:
stage: build
tags: ["docker", "linux"]
script: echo "Building..."
La tâche s'exécute uniquement si toutes ses étiquettes sont présentes sur le runner. Si le runner a tags = ["docker"] mais que la tâche exige aussi linux, le runner n'est pas une correspondance.
Correction : Alignez les étiquettes. Si une tâche nécessite plusieurs capacités, ajoutez toutes les étiquettes à un seul runner ou divisez-les entre plusieurs runners. Utilisez l'option --tag-list lors de l'enregistrement : sudo gitlab-runner register --tag-list "docker,linux,amd64". Ou modifiez config.toml et redémarrez.
Prévention : Documentez les conventions d'étiquettes et utilisez le linting CI (éditeur .gitlab-ci.yml dans l'interface GitLab) pour détecter les étiquettes manquantes avant la fusion.
Erreur 3 : Espace disque insuffisant pour les images Docker
Symptôme : Les tâches échouent avec no space left on device ou les journaux du runner affichent Error pulling image avec des erreurs liées au disque. Les builds ralentissent en raison de tirages d'images répétés.
Pourquoi cela arrive : L'exécuteur Docker met en cache les images sur l'hôte du runner, et avec le temps, le cache remplit le disque. Le runner ne nettoie pas automatiquement les images inutilisées sauf configuration contraire.
Diagnostic :
df -h /var/lib/docker
sudo docker system df
Si l'utilisation de /var/lib/docker dépasse 80 % ou si docker system df montre de nombreuses images et conteneurs inutilisés, la pression sur le disque cause probablement les échecs.
Correction : Nettoyez immédiatement les ressources Docker inutilisées :
sudo docker system prune -af --volumes
Ensuite, mettez en place un nettoyage périodique. Une approche consiste à utiliser une tâche cron sur l'hôte du runner :
# Exécuter chaque dimanche à 3 h
0 3 * * 0 docker system prune -af --volumes
Alternativement, configurez le pre_build_script du runner ou utilisez la politique de image cleanup de GitLab si vous utilisez l'exécuteur Kubernetes. Pour l'exécuteur Docker, envisagez d'utiliser DOCKER_IMAGE_CLEANUP dans la configuration du runner :
[runners.docker]
image_cleanup = true
Ce réglage supprime les images qui ne sont plus utilisées par aucune tâche après chaque build, mais cela peut ralentir les tâches suivantes si elles dépendent de couches en cache. Pesez soigneusement le compromis.
Erreur 4 : Mauvaise image Docker ou authentification de registre
Symptôme : Les tâches échouent au démarrage avec ERROR: Job failed: image pull failed: access denied ou le mauvais tag d'image est utilisé, conduisant à des builds imprévisibles.
Pourquoi cela arrive : Le nom de l'image dans .gitlab-ci.yml ou la configuration du runner ne correspond pas au registre prévu, ou le runner ne dispose pas des identifiants pour tirer depuis un registre privé.
Diagnostic :
Inspectez le journal de la tâche pour la commande de tirage exacte. Le runner enregistre le nom de l'image et la tentative de tirage. Vérifiez également la configuration Docker du runner pour les identifiants du registre :
sudo grep -A10 'runners.docker' /etc/gitlab-runner/config.toml
sudo cat /root/.docker/config.json # si le runner s'exécute en tant que root
Si votre projet utilise un registre privé, le runner doit s'authentifier. Le config.toml doit inclure une variable d'environnement DOCKER_AUTH_CONFIG ou utiliser les variables CI_REGISTRY de GitLab.
Correction : Pour les registres privés, configurez le runner avec le DOCKER_AUTH_CONFIG approprié. Exemple dans config.toml :
[[runners]]
environment = ["DOCKER_AUTH_CONFIG={\"auths\":{\"registry.example.com\":{\"auth\":\"BASE64_ENCODED_CREDENTIALS\"}}}"]
Pour le registre de conteneurs de GitLab lui-même, les variables intégrées CI_REGISTRY_USER et CI_REGISTRY_PASSWORD suffisent généralement si la tâche utilise docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY avant de tirer.
Vérifiez toujours le tag de l'image. Si vous spécifiez image: ruby:latest et que le tag latest change soudainement, les builds deviennent non reproductibles. Épinglez les versions : image: ruby:3.1.2.
Erreur 5 : Ignorer les limites de concurrence du runner
Symptôme : L'hôte du runner devient surchargé, l'utilisation du CPU et de la mémoire augmente, et les tâches expirent. Ou certaines tâches ne sont jamais traitées car le runner est occupé par des tâches longues.
Pourquoi cela arrive : Le paramètre concurrent (global) et la limit pour un runner spécifique sont trop élevés pour les ressources de l'hôte, ou ils ne sont pas définis du tout, permettant des tâches concurrentes illimitées.
Diagnostic :
Vérifiez la valeur globale de concurrent et les limites spécifiques au runner :
sudo grep -B2 -A3 'concurrent\|limit' /etc/gitlab-runner/config.toml
Surveillez également la charge de l'hôte pendant un pic de pipeline : uptime, htop.
Correction : Définissez des limites raisonnables. Par exemple, sur une machine avec 4 CPU et 8 Go de RAM, limitez à 2 tâches concurrentes pour un runner Docker :
concurrent = 2
[[runners]]
name = "docker-runner"
limit = 2
Redémarrez le runner et vérifiez : sudo gitlab-runner restart && sudo gitlab-runner verify. Ensuite, observez CPU/mémoire pendant l'utilisation maximale.
Prévention : Dimensionnez correctement les runners en fonction de la charge de travail. Utilisez les métriques du runner de GitLab (si activées) pour suivre les temps de mise en file d'attente des tâches et ajustez concurrent et limit en conséquence.
Chemin de configuration sécurisé
Modifier la configuration d'un runner sur un système de production exige une approche méthodique. Une petite erreur peut mettre hors service toute l'IC pour une équipe. Suivez ces étapes pour minimiser les risques.
Étape 1 : Capturer l'état actuel
Avant de modifier, enregistrez le config.toml actuel et l'état du runner :
sudo cp /etc/gitlab-runner/config.toml /etc/gitlab-runner/config.toml.bak-$(date +%Y%m%d%H%M%S)
sudo gitlab-runner list > ~/runner-list-before.txt
sudo systemctl status gitlab-runner --no-pager > ~/runner-status-before.txt
Notez également la version de GitLab et du runner :
sudo gitlab-runner --version
Étape 2 : Faire un seul changement dans un environnement de test
Si possible, appliquez d'abord le changement sur un runner de préproduction ou une instance de test. C'est particulièrement important pour les modifications d'exécuteur ou les paramètres d'authentification. Utilisez un projet non productif pour déclencher un pipeline de test.
Exemple de modification sécurisée à l'aide de sed pour remplacer une ancienne image :
sudo sed -i 's/image = "ruby:2.7"/image = "ruby:3.1"/' /etc/gitlab-runner/config.toml
Ensuite, validez la syntaxe :
sudo gitlab-runner verify
Sortie attendue si la syntaxe est valide : Verifying runner... is alive pour chaque runner. Si vous voyez ERROR: Invalid config file, ne poursuivez pas ; restaurez la sauvegarde :
sudo cp /etc/gitlab-runner/config.toml.bak-$(ls -t /etc/gitlab-runner/config.toml.bak-* | head -1 | cut -d- -f2-) /etc/gitlab-runner/config.toml
Étape 3 : Appliquer en production avec un plan de restauration
Si le changement passe la vérification en préproduction, appliquez-le au runner de production :
- Arrêtez le service du runner :
sudo gitlab-runner stop(ousudo systemctl stop gitlab-runner). - Modifiez le fichier de configuration avec votre changement.
- Démarrez le runner :
sudo gitlab-runner start. - Vérifiez l'état :
sudo systemctl status gitlab-runner --no-pager. Cherchezactive (running). - Déclenchez un pipeline de test dans un projet à faible risque.
Si le pipeline échoue ou que le runner ne redémarre pas, restaurez immédiatement :
sudo gitlab-runner stop
sudo cp /etc/gitlab-runner/config.toml.bak-YYYYMMDDHHMMSS /etc/gitlab-runner/config.toml
sudo gitlab-runner start
sudo gitlab-runner verify
Remplacez YYYYMMDDHHMMSS par l'horodatage du fichier de sauvegarde créé à l'étape 1.
Étape 4 : Vérifier et documenter
Après un changement réussi, notez ce qui a été changé, pourquoi et comment le vérifier. Mettez à jour votre runbook ou wiki. Confirmez que le comportement attendu est observé : les tâches s'exécutent, les étiquettes correspondent, les limites de concurrence sont respectées, etc.
Vérification et diagnostics
Une vérification efficace va au-delà de la simple vérification que le runner est en vie. Vous devez confirmer que le runner se comporte correctement dans des conditions réelles de tâche. Voici les diagnostics clés.
Vérifier l'enregistrement et la santé du runner
sudo gitlab-runner verify
Cette commande vérifie la connectivité à l'instance GitLab et valide le jeton du runner. Sortie attendue :
Runtime platform arch=amd64 os=linux pid=12345 revision=5e9f5f4e version=15.11.0
Running in system-mode.
Verifying runner... is alive runner=AbC123
Si un runner n'est pas en vie, vérifiez la connectivité réseau : curl -v https://gitlab.example.com/api/v4/runners. Assurez-vous que le jeton du runner est toujours valide ; sinon, réenregistrez le runner.
Tester l'exécution des tâches avec un pipeline minimal
Créez un projet de test et ajoutez un .gitlab-ci.yml :
test-runner:
script:
- echo "Runner works"
- uname -a
Si cette tâche réussit, le runner peut exécuter des commandes de base. Pour tester l'exécuteur Docker, ajoutez image: alpine et une commande spécifique à Docker :
test-docker:
image: alpine
script:
- docker info # échouera si Docker n'est pas disponible dans le conteneur sauf si Docker-in-Docker est utilisé
La commande docker info ne fonctionnera que si le runner est configuré avec Docker-in-Docker ou le mode privilégié. Un test Docker plus fiable est :
test-docker:
image: docker:20.10
services:
- docker:20.10-dind
script:
- docker info
Si la tâche échoue avec Cannot connect to the Docker daemon, le service Docker-in-Docker ne fonctionne pas ou le paramètre privileged du runner est faux. Pour les pipelines CI qui doivent construire des images Docker, définissez privileged = true dans config.toml sous [runners.docker] ou utilisez une variable CI pour désactiver TLS.
Inspecter les journaux du runner pour les erreurs
Les journaux contiennent souvent la cause racine des échecs de tâches. Sur les systèmes systemd :
sudo journalctl -u gitlab-runner -n 50 --no-pager
Cherchez les lignes contenant ERROR, panic ou failed. Par exemple, une erreur de permission lors de l'accès au socket Docker :
ERROR: Failed to create container: permission denied while trying to connect to the Docker daemon socket
Cela indique que l'utilisateur gitlab-runner n'a pas accès à /var/run/docker.sock. Corrigez en ajoutant l'utilisateur au groupe docker :
sudo usermod -aG docker gitlab-runner
sudo systemctl restart gitlab-runner
Modes de défaillance et récupération
Défaillance : Runner hors ligne ou ne prenant pas de tâches
Causes possibles : problèmes réseau, jeton invalide, service du runner arrêté, ou run_untagged du runner défini sur false alors que les tâches n'ont pas d'étiquettes.
Étapes de récupération :
- Vérifiez l'état du service :
sudo systemctl status gitlab-runner. - Vérifiez l'enregistrement du runner :
sudo gitlab-runner list. - Assurez-vous que le runner est assigné au bon projet/groupe et que les étiquettes des tâches correspondent.
- Si vous utilisez un proxy, définissez
HTTP_PROXYetHTTPS_PROXYdans l'environnement du runner. - Si le runner est en ligne mais ne prend pas de tâches, vérifiez
run_untaggeddansconfig.toml. Pour les tâches sans étiquettes, le runner doit avoirrun_untagged = true:
[[runners]]
run_untagged = true
Défaillance : La tâche échoue avec le code 137
Cela signifie généralement que la tâche a été tuée en raison d'un manque de mémoire (OOM). Le conteneur du runner a dépassé les limites de mémoire, ou l'hôte a manqué de mémoire.
Diagnostic : Vérifiez l'utilisation de la mémoire de l'hôte : free -h. Inspectez la limite de mémoire du conteneur : sudo docker inspect <container-id> | grep Memory. Si vous n'avez pas défini de limite de mémoire, envisagez d'en définir une dans la configuration Docker du runner :
[runners.docker]
memory = "2g"
memory_swap = "2g"
Examinez également l'utilisation des ressources de la tâche dans l'interface GitLab (si la surveillance est activée).
Récupération : Augmentez la limite de mémoire si la tâche en a légitimement besoin de plus, ou optimisez la tâche pour utiliser moins de mémoire. Redémarrez la tâche échouée après avoir apporté des modifications.
Défaillance : Le cache ou les artefacts ne fonctionnent pas
Symptômes : Les tâches ne trouvent pas les dépendances en cache, ou les artefacts sont absents après le pipeline.
Diagnostic : Vérifiez la configuration du cache du runner. Pour le cache distribué, le config.toml du runner doit avoir des paramètres [runners.cache] pointant vers un bucket S3 ou GCS. Si vous utilisez un cache local, il est stocké dans le répertoire de build du runner, généralement /home/gitlab-runner/cache.
Erreurs courantes :
- La clé de cache n'est pas cohérente entre les tâches (
cache:keymanquant ou changeant). - Le runner n'a pas accès au stockage du cache.
- Le chemin des artefacts dans
.gitlab-ci.ymlest incorrect.
Correction : Définissez une clé de cache claire dans .gitlab-ci.yml :
cache:
key: "$CI_COMMIT_REF_SLUG"
paths:
- .m2/repository
Pour les artefacts, assurez-vous que artifacts:paths sont relatifs au répertoire de build et que la tâche se termine avec succès.
Liste de contrôle opérationnelle
Utilisez cette liste de contrôle avant et après tout changement de configuration de runner. Chaque élément a un responsable et une fréquence de révision.
| Élément | Commande / Vérification | Résultat attendu | Responsable | Fréquence de révision |
|---|---|---|---|---|
| Version du runner supportée | sudo gitlab-runner --version | Version dans les 2 versions mineures de GitLab | Ingénieur DevOps | Mensuelle |
| Enregistrement du runner valide | sudo gitlab-runner verify | is alive pour tous les runners | Ingénieur DevOps | Hebdomadaire |
| Type d'exécuteur correct | sudo grep executor /etc/gitlab-runner/config.toml | Correspond aux exigences du projet | Responsable CI/CD | Lorsque les projets changent |
| Étiquettes correspondent aux exigences de la tâche | sudo grep -A5 tags /etc/gitlab-runner/config.toml | Les étiquettes du runner sont un sur-ensemble des étiquettes de la tâche | Développeur | Par MR |
| Espace disque sain | df -h /var/lib/docker | Utilisation < 80 % | Administrateur système | Quotidienne (via alerte de surveillance) |
| Nettoyage des images Docker configuré | sudo grep image_cleanup /etc/gitlab-runner/config.toml | image_cleanup = true si souhaité | Ingénieur DevOps | Trimestrielle |
| Limites de concurrence appropriées | sudo grep concurrent /etc/gitlab-runner/config.toml | Correspond à la capacité de l'hôte | Ingénieur DevOps | Lors d'un changement de capacité |
| Sauvegarde de config.toml existe | ls -l /etc/gitlab-runner/config.toml.bak-* | Fichier de sauvegarde récent présent | Administrateur système | Après chaque changement |
| Pipeline de test réussi après changement | Déclencher un pipeline de projet de test | La tâche réussit | Développeur | Après chaque changement |
Attribuez des responsables uniques et nommés, pas des groupes. Par exemple, « Ingénieur DevOps » devrait être une personne nommée comme Priya Shah. Les fréquences de révision sont des minimums ; dans les environnements en évolution rapide, révisez plus souvent.
Conclusion
Les erreurs de configuration de GitLab Runner peuvent être coûteuses, mais elles sont presque toujours évitables avec un diagnostic systématique et des pratiques de changement sûres. La clé est d'observer avant de modifier, de faire un changement à la fois, de vérifier avec des commandes concrètes et d'avoir un plan de restauration. Documentez la version et l'environnement actuels, alignez l'exécuteur et les étiquettes sur les exigences des tâches, gérez les limites de disque et de mémoire, et vérifiez régulièrement la santé du runner.
Pour une prochaine étape à faible risque, choisissez une vérification de la liste de contrôle opérationnelle, exécutez-la sur votre runner et comparez la sortie au résultat attendu. Si vous trouvez une incohérence, appliquez la correction correspondante de la section pertinente, puis déclenchez un pipeline de test pour confirmer. Au fil du temps, intégrez ces vérifications dans votre maintenance et votre surveillance de routine pour garder vos pipelines CI/CD fiables.
Un flux de travail technique fiable rend la défaillance visible, protège les valeurs sensibles, limite les changements à la ressource prévue et définit la vérification de récupération avant qu'un incident ne force la décision.