Goulots d'étranglement courants de Jenkins et comment les résoudre

Vous avez du mal avec une instance Jenkins lente ? Ce guide complet explore les goulots d'étranglement courants de Jenkins, notamment les fuites de mémoire, les problèmes d'espace disque et la journalisation excessive. Apprenez à identifier les symptômes, comprendre les causes profondes et mettre en œuvre des solutions concrètes comme le réglage JVM, la gestion intelligente de l'historique des builds, l'optimisation des logs et le codage efficace des pipelines. Découvrez les outils de surveillance essentiels et les meilleures pratiques pour maintenir vos pipelines CI/CD fluides, garantissant des builds plus rapides, une interface réactive et un processus de livraison logicielle globalement plus efficace.

Goulots d'étranglement courants de Jenkins et comment les résoudre

Une instance Jenkins lente n'a généralement pas une seule cause. L'interface semble lourde, les builds attendent dans la file d'attente, les agents se déconnectent, les logs mettent une éternité à s'ouvrir, et quelqu'un dit : "Jenkins est encore en panne." Derrière cette plainte, le problème est souvent l'un des quelques goulots d'étranglement ordinaires : pression sur le tas du contrôleur, disque lent, agents surchargés, problèmes de plugins, mauvais comportement des pipelines, ou délais réseau vers les systèmes de contrôle de source et d'artefacts.

Le moyen le plus rapide de résoudre les performances de Jenkins est de séparer les problèmes du contrôleur des problèmes de build. Si l'interface Jenkins, la file d'attente et les pages de tâches sont lentes même lorsqu'aucun build n'est en cours, commencez par le contrôleur. Si l'interface est correcte mais que les builds prennent trop de temps, commencez par les agents, les espaces de travail, les caches et les systèmes externes.

Mémoire du contrôleur et garbage collection

Le contrôleur Jenkins est un processus Java. Il a besoin de suffisamment de tas pour la configuration des tâches, les plugins, les métadonnées de build, l'état de la file d'attente et les requêtes web. Lorsque le tas est trop petit, le contrôleur passe trop de temps dans le garbage collection. Lorsqu'un plugin fuit de la mémoire ou stocke trop de données en mémoire, augmenter le tas peut seulement retarder le prochain incident.

Les symptômes incluent une interface lente, de longues pauses, OutOfMemoryError, des déconnexions fréquentes d'agents, ou des retards dans la file d'attente des builds qui ne correspondent pas aux exécuteurs disponibles.

Vérifiez d'abord le processus et les logs :

ps -o pid,rss,vsz,etime,cmd -C java
journalctl -u jenkins --since "2 hours ago" | grep -Ei 'OutOfMemory|GC overhead|heap|killed'

Pour un contrôleur modéré, un tas de 2 à 4 Go peut suffire. Les installations très sollicitées peuvent avoir besoin de plus. Ne définissez pas aveuglément le tas sur la majeure partie de la RAM de la machine. Le système d'exploitation a toujours besoin de mémoire pour le cache du système de fichiers, la surcharge des processus et les agents de surveillance.

Les options de service typiques ressemblent à ceci :

JENKINS_JAVA_OPTS="-Xms1g -Xmx4g -XX:+UseG1GC"

Après avoir modifié les options JVM, redémarrez Jenkins pendant une fenêtre de maintenance et observez le comportement sous charge normale. Si la mémoire augmente régulièrement pendant des jours et ne se stabilise jamais, prenez un heap dump et examinez les plugins récemment mis à jour ou installés. Gardez les plugins à jour, mais évitez de mettre à jour un grand ensemble de plugins sans plan de retour arrière.

Espace disque et E/S disque

Jenkins utilise constamment le disque. JENKINS_HOME stocke la configuration des tâches, les enregistrements de build, les empreintes, les données des plugins, les secrets, les logs, et parfois trop d'artefacts. Les agents utilisent le disque pour les espaces de travail, les caches de dépendances, les couches Docker, les rapports de test et les fichiers temporaires.

Un disque plein est évident. Un disque lent est plus ennuyeux car rien ne semble cassé ; tout attend simplement.

Vérifiez à la fois la capacité et la latence :

df -h
du -sh /var/lib/jenkins/* 2>/dev/null | sort -h | tail
iostat -xz 1

Si %util et les temps await sont élevés pendant les builds, le disque est un goulot d'étranglement. Les correctifs courants consistent à déplacer les espaces de travail vers un stockage plus rapide, à élaguer les anciennes couches Docker, à réduire la rétention des artefacts et à empêcher les tâches d'archiver des répertoires entiers lorsque seuls des rapports ou des packages sont nécessaires.

Définissez des politiques de suppression de builds sur les tâches :

options {
  buildDiscarder(logRotator(numToKeepStr: '30', artifactNumToKeepStr: '10'))
}

Soyez prudent avec le nettoyage manuel dans JENKINS_HOME. Ne supprimez pas des fichiers XML aléatoires ou des répertoires de plugins pendant que Jenkins est en cours d'exécution. Utilisez les paramètres de rétention de Jenkins, les outils de nettoyage spécifiques aux plugins et les sauvegardes.

Trop de travail sur le contrôleur

L'une des configurations les plus dommageables est d'exécuter des builds sur le contrôleur. Le contrôleur ne doit pas compiler, exécuter des tests, construire des images Docker ou effectuer de gros checkouts.

Définissez les exécuteurs du contrôleur à 0 dans la plupart des installations. Placez les builds sur des agents. Si vous exécutez déjà des builds sur le contrôleur, déplacez-les progressivement et surveillez les hypothèses cachées telles que les chemins d'outils locaux, les liaisons d'identifiants ou les fichiers qui n'existent que sur le contrôleur.

Vérifiez également les pipelines pour le code Groovy lourd sur le contrôleur. Les étapes de pipeline telles que sh s'exécutent sur les agents, mais la logique Groovy dans le Jenkinsfile peut s'exécuter sur le contrôleur. Évitez de lire des fichiers volumineux dans des variables Groovy, de construire des maps massives ou d'effectuer un traitement JSON important dans le script de pipeline. Utilisez shell, Python, jq ou votre outil de build sur l'agent pour les travaux de données lourds.

Les agents sont surchargés ou inadaptés

Si le temps d'attente est élevé pour un label, l'ajout d'exécuteurs génériques n'aidera pas. Une tâche nécessitant linux && docker && large-memory a besoin de cette capacité exacte.

Examinez les raisons de la file d'attente et l'utilisation des labels. Vérifiez ensuite le système d'exploitation de l'agent :

uptime
free -h
mpstat 1
iostat -xz 1
docker system df

Si un agent swappe, réduisez les exécuteurs ou augmentez la mémoire. Si le CPU est saturé et que la durée des builds augmente pendant les périodes chargées, réduisez la concurrence ou ajoutez des agents. Si l'attente d'E/S est élevée, déplacez les caches et les espaces de travail vers un stockage plus rapide ou réduisez le nombre de tâches simultanées sur ce nœud.

Pour les agents Kubernetes, les demandes de ressources comptent autant que le nombre d'exécuteurs Jenkins. Un pod demandant trop peu de CPU ou de mémoire peut être planifié sur un nœud déjà occupé, puis Jenkins voit un agent prêt tandis que le build rampe. Pour les pods jetables, un exécuteur par pod est généralement plus facile à raisonner que plusieurs exécuteurs partageant le même conteneur.

Problèmes de plugins

Les plugins sont l'une des forces de Jenkins, et aussi une source courante de problèmes de performances. Un plugin peut ajouter un coût de rendu de page, ralentir le chargement des tâches, conserver trop d'historique de build, ou effectuer des appels externes lors d'actions normales de l'interface.

Lorsque les performances changent soudainement, demandez ce qui a changé récemment :

  • Mise à niveau du cœur de Jenkins.
  • Mise à jour d'un plugin.
  • Installation d'un nouveau plugin.
  • Nouvelle configuration globale.
  • Nouvelle version de bibliothèque de pipeline.

Utilisez les informations de santé "Gérer Jenkins", les logs et les journaux de modifications des plugins. Si l'interface est devenue lente après une mise à jour de plugin, testez un retour arrière dans un contrôleur de staging si vous en avez un. Conservez une sauvegarde de JENKINS_HOME et des versions des plugins avant les mises à niveau importantes.

Ne conservez pas les plugins "au cas où". Chaque plugin installé ajoute une surface de maintenance. Supprimez les plugins inutilisés après avoir vérifié les dépendances des tâches.

Délais du SCM et du référentiel d'artefacts

De nombreux rapports "Jenkins est lent" sont en réalité des problèmes de Git, de registre de packages, de registre de conteneurs ou de référentiel d'artefacts.

Vérifiez les logs de build pour des étapes lentes répétées :

git fetch
mvn dependency:resolve
npm ci
docker pull
docker push
archiveArtifacts

Si chaque tâche attend les téléchargements de dépendances, ajoutez un proxy ou un cache à proximité. Si git fetch est lent, vérifiez la taille du référentiel, la découverte de branches, les paramètres de clone superficiel et le chemin réseau des agents vers le serveur Git. Si les pulls Docker sont lents sur les agents éphémères, utilisez un miroir de registre ou un cache de registre BuildKit.

Gardez le diagnostic honnête : Jenkins planifie le travail, mais il ne peut pas rendre un registre de packages distant rapide.

Gonflement des logs et de l'historique des builds

Les grands logs console ralentissent le rendu des pages et prennent de l'espace de stockage. Les tâches qui impriment chaque fixture de test, chaque réponse HTTP, ou des logs de débogage complets lors des builds normales finissent par rendre Jenkins pénible à utiliser.

Corrigez d'abord la tâche. Réduisez la verbosité normale des logs et archivez les logs détaillés sous forme d'artefacts compressés uniquement en cas de besoin. Gardez la sortie console concentrée sur la progression et le contexte des échecs.

Définissez ensuite la rétention :

options {
  buildDiscarder(logRotator(daysToKeepStr: '30', numToKeepStr: '50'))
}

Pour les environnements à forte conformité, déplacez les artefacts et les logs à long terme vers un système de stockage externe conçu pour la rétention, la recherche et les politiques de cycle de vie.

Un chemin d'incident pratique

Lorsque Jenkins est lent en ce moment, utilisez cet ordre :

  1. Vérifiez si l'interface du contrôleur est lente.
  2. Vérifiez le CPU, la mémoire, les symptômes GC et le disque du contrôleur.
  3. Vérifiez les raisons de la file d'attente et les labels en attente.
  4. Vérifiez les agents les plus occupés pour le CPU, la mémoire, le disque et la croissance de l'espace de travail.
  5. Comparez les modifications récentes des plugins, des tâches et des bibliothèques partagées.
  6. Lisez un log de build lent et identifiez l'étape coûteuse répétée.

Ce chemin évite les réglages aléatoires. Augmenter le tas ne résoudra pas un agent Docker saturé. Ajouter des exécuteurs ne résoudra pas un disque plein. Élaguer les espaces de travail ne résoudra pas un plugin provoquant des pauses du contrôleur.

Gardez Jenkins maintenable

Les installations Jenkins saines ont des habitudes ennuyeuses : exécuteurs du contrôleur à zéro, agents dimensionnés pour leur charge de travail, rétention des builds configurée, caches de dépendances intentionnels, mises à jour des plugins suivies, et métriques de base exportées vers Prometheus, Grafana, CloudWatch, ou tout autre système de surveillance que l'équipe utilise déjà.

Le meilleur correctif est souvent petit et spécifique. Déplacez les builds Docker vers des agents dédiés. Réduisez la sortie de log d'une tâche bruyante. Ajoutez un proxy Maven. Réduisez les exécuteurs sur un nœud qui swappe. Supprimez un plugin inutilisé. Mettez en place une rétention sur une tâche qui a conservé chaque build pendant des années.

Les performances de Jenkins s'améliorent lorsque vous cessez de le traiter comme une boîte noire et commencez à suivre le travail : de la file d'attente, au contrôleur, à l'agent, au système de fichiers, à la dépendance réseau, et de retour au log de build.

Exemple : le build est lent mais Jenkins va bien

Supposons que les développeurs signalent que les vérifications de pull request prennent 25 minutes. L'interface Jenkins est réactive. La file d'attente est courte. Les agents sont en ligne. Le log lent montre :

git fetch : 20 secondes
npm ci : 9 minutes
tests unitaires : 4 minutes
docker build : 10 minutes
archive des artefacts : 1 minute

Ce n'est pas principalement un problème du contrôleur Jenkins. Les correctifs probables sont la mise en cache des packages, l'ordre des couches Dockerfile, le cache BuildKit, et peut-être la division des tests. Augmenter le tas du contrôleur ne changerait rien.

Exemple : tout attend mais les agents sont inactifs

Si les tâches sont en file d'attente alors que les agents semblent inactifs, lisez la raison de la file d'attente. Vous constaterez peut-être que les tâches nécessitent linux && docker, alors que les agents inactifs n'ont que linux. Ou les tâches peuvent être bloquées par disableConcurrentBuilds, une ressource verrouillable, ou un plugin cloud qui ne parvient pas à provisionner des agents correspondants.

Ce type de goulot d'étranglement est une configuration, pas une capacité brute. Ajouter deux agents non correspondants supplémentaires n'aidera pas.

Exemple : le contrôleur ralentit chaque après-midi

Si l'interface se dégrade à la même heure chaque jour, recherchez les tâches planifiées : indexation de branches, sauvegardes, nettoyage important d'artefacts, scans de vulnérabilité, ou pipelines nocturnes commençant trop tôt. Vérifiez le CPU, le tas et les E/S disque du contrôleur pendant cette fenêtre. Vérifiez également si de nombreuses tâches commencent exactement à la même minute en raison d'expressions cron comme 0 2 * * *.

Dans les planifications Jenkins, préférez le timing haché lorsque c'est possible :

H 2 * * *

Cela répartit les tâches au lieu de tout démarrer au début de l'heure.

Ce qu'une bonne surveillance devrait répondre

Au minimum, la surveillance devrait répondre à ces questions sans se connecter au serveur :

  • Le processus du contrôleur est-il vivant et réactif ?
  • Quelle quantité de tas est utilisée, et à quelle fréquence le garbage collection s'exécute-t-il ?
  • Combien de temps les tâches attendent-elles dans la file d'attente par label ?
  • Quels agents sont hors ligne ou se reconnectent-ils de manière répétée ?
  • Les disques du contrôleur et des agents sont-ils presque pleins ?
  • Les builds deviennent-ils plus lents au fil du temps pour les mêmes tâches ?

Vous n'avez pas besoin d'un tableau de bord parfait pour commencer. Même quelques métriques et alertes pour le disque, le tas, la longueur de la file d'attente et la disponibilité des agents détecteront de nombreux échecs avant que les développeurs ne les signalent.