Les performances de GitLab CI/CD influencent directement la vélocité des équipes d'ingénierie. Des files d'attente trop longues retardent le retour d'information, des runners surchargés provoquent des échecs aléatoires, et des caches mal configurés gaspillent la bande passante et le stockage. Ce guide propose une approche progressive et mesurable pour identifier et corriger les goulets d'étranglement sans mettre en péril votre pipeline de livraison. Vous allez inventorier votre environnement, établir des références, appliquer des optimisations ciblées, valider chaque modification et instaurer une routine d'exploitation reproductible.
Inventorier votre environnement avant tout
Avant toute modification, capturez l'état actuel. Cela clarifie les leviers d'optimisation disponibles et la documentation applicable. Enregistrez les éléments suivants :
- Édition et version de GitLab (SaaS vs autogéré)
- Versions des runners et exécuteurs (shell, Docker, Kubernetes)
- Backends de cache et d'artéfacts (disque local, S3, GCS, NFS)
- Localité réseau (où se situent les runners par rapport à GitLab, aux registres et aux miroirs de paquets)
- Caractéristiques des dépôts (taille, sous-modules, utilisation de LFS)
- Droits d'accès réels (configuration des runners, paramètres de projet, jeton API)
Commandes rapides pour collecter ces données :
# Depuis un journal de job
echo "$CI_SERVER_VERSION $CI_RUNNER_EXECUTOR $CI_PROJECT_PATH"
# Depuis l'hôte du runner
gitlab-runner --version
gitlab-runner list
# Depuis l'API GitLab (remplacez les valeurs entre crochets)
curl -s --header "PRIVATE-TOKEN: <token>" https://<gitlab_url>/api/v4/version
Utilisez ce tableau pour standardiser votre inventaire :
| Champ | Exemple de valeur | Notes |
|---|---|---|
| Version GitLab | 16.11 SaaS | Influence les fonctionnalités et valeurs par défaut disponibles |
| Version(s) runner | 16.11 | Maintenir aligné avec le serveur dans la mesure du possible |
| Exécuteurs runner | shell, docker | Détermine la stratégie de cache et l'isolation |
| Backend cache | S3, bucket gitlab-runner-cache | Permet le partage de cache entre runners |
| Politique artéfacts | expire_in: 2 days | Garder les artéfacts petits et de courte durée |
| Traits du dépôt | 2,1 Go, 5 sous-modules, LFS activé | Clone lourd si non optimisé |
| Localité réseau | Runners dans la même région que GitLab | Réduit la latence de transfert |
| Accès | Config runner, maintainer projet, token read_api | Requis pour appliquer les modifications |
Chemin de configuration sécurisé
Appliquez les changements par petites étapes avec un retour arrière clair. Suivez cette séquence : (1) métriques de référence, (2) gains rapides, (3) dimensionnement du débit, (4) optimisation de la latence et du cache, (5) parallélisation prudente, (6) re-mesure et extension seulement après validation.
1. Établir une référence mesurable
Choisissez un ou deux pipelines représentatifs (par exemple, build et tests de la branche principale) et capturez trois à cinq exécutions récentes. Enregistrez la durée du pipeline, les durées des jobs critiques, le temps de file d'attente et les temps de téléversement/téléchargement des artéfacts. Ajoutez un chronométrage simple autour des étapes lourdes dans vos scripts :
set -euo pipefail
stamp() { date +"%Y-%m-%d %H:%M:%S"; }
step() { echo "[STEP] $(stamp) $*"; }
step "Installation dépendances"
# vos commandes d'installation
step "Exécution tests"
# vos commandes de test
Utilisez l'API pour collecter les données de référence :
# 10 derniers pipelines réussis et leurs durées
curl -s --header "PRIVATE-TOKEN: <token>" \
"https://<gitlab_url>/api/v4/projects/<id>/pipelines?status=success&per_page=10" | jq '[.[] | {id, status, duration}]'
# Jobs d'un pipeline spécifique
curl -s --header "PRIVATE-TOKEN: <token>" \
"https://<gitlab_url>/api/v4/projects/<id>/pipelines/<pipeline_id>/jobs" | jq '[.[] | {name, status, duration, queued_duration}]'
2. Gains rapides dans .gitlab-ci.yml
Réduisez la surcharge du clone Git avec un fetch superficiel :
variables:
GIT_STRATEGY: fetch
GIT_DEPTH: "1"
GIT_SUBMODULE_STRATEGY: none
Dimensionnez correctement les artéfacts. N'archiver pas de gros résultats reproductibles. Gardez des expirations courtes :
artifacts:
when: on_success
expire_in: 2 days
paths:
- build/reports/
Ajoutez des caches pour les répertoires de dépendances que vous savez réutilisables sans risque. Cléyez les caches par les fichiers de verrouillage pour qu'ils s'invalident seulement quand les dépendances changent.
Exemple Node.js :
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
policy: pull-push
Exemple Python :
cache:
key:
files:
- requirements.txt
paths:
- .venv/
- $PIP_CACHE_DIR
policy: pull-push
Exemple Maven :
cache:
key: maven-$CI_COMMIT_REF_SLUG
paths:
- .m2/repository
policy: pull-push
3. Dimensionnement du débit sur les runners
Définissez la concurrence globale pour que l'hôte reste occupé sans être surchargé. Pour des jobs liés au CPU, commencez avec concurrent proche du nombre de vCPU, puis ajustez selon la pression I/O et mémoire.
Dans /etc/gitlab-runner/config.toml, examinez ces paramètres :
concurrent = 6
check_interval = 0
runners
name = "build-runner-01"
url = "https://gitlab.example.com/"
token = "REDACTED"
executor = "docker"
request_concurrency = 2
[runners.cache]
Type = "s3"
Path = "runner"
Shared = true
[runners.cache.s3]
ServerAddress = "s3.amazonaws.com"
BucketName = "gitlab-runner-cache"
BucketLocation = "us-east-1"
Insecure = false
Redémarrez le runner pour appliquer les changements :
systemctl restart gitlab-runner
Lignes directrices de dimensionnement (points de départ ; ajustez à votre charge) :
- Builds CPU-bound :
concurrent≈ nombre de vCPU,request_concurrency= 2 à 4 - Builds I/O-intensifs :
concurrent< vCPU (par exemple, 0,5 × vCPU) - Tests gros consommateurs de mémoire : plafonnez
concurrentpour que le plus gros job ait au moins 1,5× sa mémoire de pointe disponible
4. Optimisation de la latence et du cache
- Colocalisez les runners près de GitLab et de vos registres de conteneurs/paquets pour réduire les temps de transfert.
- Préférez un cache distant (S3/GCS) pour les flottes afin que les caches survivent aux redémarrages et soient partagés entre machines.
- Gardez les clés de cache stables au sein d'une branche mais différentes entre branches pour éviter l'empoisonnement.
- Évitez d'archiver d'énormes caches ; mettez en cache seulement ce qui ne se re-télécharge pas vite (répertoires de dépendances, pas les sorties de build).
5. Parallélisation prudente
Divisez les longs jobs de test avec parallel de GitLab. Commencez petit et assurez l'isolation.
Exemple à nombre fixe :
unit_tests:
stage: test
script: run_tests.sh "$CI_NODE_INDEX" "$CI_NODE_TOTAL"
parallel: 4
Exemple matrice :
build:
stage: build
parallel:
matrix:
- PLATFORM: [linux, darwin]
ARCH: [amd64, arm64]
script:
- ./build.sh "$PLATFORM" "$ARCH"
Notes d'implémentation :
- Assurez-vous que les tests ne partagent pas d'état global mutable (bases de données, ports, répertoires temporaires). Paramétrez par nœud avec
$CI_NODE_INDEX. - Protégez la parallélisation derrière un drapeau de fonctionnalité pour pouvoir revenir en arrière vite :
workflow:
rules:
- if: '$ENABLE_PARALLEL == "1"'
6. Re-mesurer et décider des prochaines étapes
Comparez durées, temps de file d'attente et temps de transfert d'artéfacts à votre référence. Ne conservez que les changements qui produisent des gains constants sur plusieurs exécutions.
Vérification et diagnostics
La vérification prouve que vous avez amélioré le bon indicateur. Utilisez ces contrôles.
Chronométrage pipeline et jobs
- Comparez trois à cinq exécutions avant et trois à cinq après un changement pour lisser la variance aléatoire.
- Inspectez les détails des jobs pour Temps en file vs Temps d'exécution. Le temps en file signale un problème de capacité ; le temps d'exécution signale une inefficacité au niveau du job.
Santé des runners
- Sur les hôtes runners, surveillez CPU, mémoire, I/O disque et réseau aux heures de pointe. Si le CPU est inactif alors que des jobs attendent, il faut probablement plus de runners enregistrés ou augmenter
concurrent. - Validez la connectivité du runner :
gitlab-runner verify
Guide symptôme-métrique
| Symptôme | Métrique à vérifier | Cause probable | Première action |
|---|---|---|---|
| Long temps en file | queued_duration | Capacité runner insuffisante | Augmenter concurrent ou ajouter runners |
| Temps de clone longs | Journaux, taille repo | Historique profond, sous-modules | Utiliser GIT_DEPTH, désactiver sous-modules |
| Étapes dépendances lentes | Chronos par étape | Caches absents ou instables | Ajouter cache clé par fichiers de verrou |
| Artéfacts lents | Temps upload/download | Artéfacts surdimensionnés | Réduire paths, définir expire_in |
| Tests flaky après split | Taux d'échec par nœud | Ressources partagées | Isoler avec CI_NODE_INDEX |
Modes de défaillance et récupération
Problèmes courants et comment récupérer sans risque.
1. Empoisonnement de cache ou ratés de cache
Symptômes : Le build utilise de mauvaises versions de dépendances, ou les caches ne touchent jamais. Cause probable : Clés de cache trop larges ou instables. Récupération :
- Restreignez les clés aux fichiers de verrouillage ou manifestes (pour Node :
key.files: package-lock.json). - Purgez les caches. Pour caches locaux, supprimez
/var/lib/gitlab-runner/cache/*sur le runner. Pour S3, effacez le préfixe concerné.
2. Surcharge runner et timeouts
Symptômes : queued_duration en hausse, échecs sporadiques sous charge. Causes : concurrent trop élevé pour le matériel, saturation réseau. Récupération :
- Réduisez
concurrentdansconfig.tomlet redémarrezgitlab-runner. - Ajoutez des runners ou déplacez les jobs lourds vers des machines plus grosses.
3. Tests flaky après parallélisation
Symptômes : Échecs intermittents, collisions de ports, répertoires temporaires partagés. Récupération :
- Paramétrez les ressources par nœud avec
$CI_NODE_INDEX. Isolez répertoires temporaires et bases de données par nœud. - Revenir en arrière : mettez
ENABLE_PARALLEL=0dans les variables du projet, ou annulez le changement dans.gitlab-ci.yml.
4. Gonflement d'artéfacts ralentissant les pipelines
Symptômes : Longs temps d'upload/download. Causes : Archivage de gros binaires reproductibles ou d'espaces de travail entiers. Récupération :
- Limitez les artéfacts aux rapports nécessaires et petits livrables. Définissez
expire_inagressivement. - Utilisez les caches pour le contenu réutilisé fréquemment et les artéfacts pour les livrables minimaux.
5. Clones superficiels cassant la gestion de versions
Symptômes : Les scripts de build ne trouvent pas les tags ou l'historique. Cause : GIT_DEPTH=1 alors que les scripts nécessitent les tags. Récupération :
- Augmentez
GIT_DEPTHpour inclure l'historique nécessaire, ou récupérez les tags explicitement :
script:
- git fetch --tags --depth=50
6. Erreurs de permissions sur cache distant
Symptômes : 403 ou 404 lors du push/pull de cache. Récupération :
- Vérifiez les identifiants et politiques de bucket pour le profil d'instance runner ou les clés d'accès.
- Désactivez temporairement le cache (
policy: pull) pour débloquer les builds, puis corrigez les permissions.
Liste de contrôle opérationnelle
Utilisez cette routine pour maintenir les performances sous contrôle.
Quotidien
- Surveillez
queued_durationsur les jobs les plus sollicités. - Traitez le job le plus lent des dernières 24 heures.
Hebdomadaire
- Revoyez les tendances de durée des pipelines et les graphiques de ressources runners.
- Purgez les caches plus anciens que la durée de vie moyenne de vos branches.
Par changement
- Référencez trois exécutions, appliquez un changement, re-mesurez trois exécutions.
- Documentez les deltas et conservez ou annulez le changement.
Modèle de journal des changements :
| Changement | Métrique référence | Métrique après | Delta | Conserver/Annuler | Responsable |
|---|---|---|---|---|---|
| GIT_DEPTH=1 sur repo X | Clone 180s | Clone 35s | -81% | Conserver | A. Dev |
| Cache node_modules | Install 240s | Install 55s | -77% | Conserver | B. Ops |
| Tests parallèles ×4 | Test 28m | Test 9m | -68% | Conserver | C. QA |
| Réduire paths artéfacts | Upload 120s | Upload 20s | -83% | Conserver | D. Dev |
Exemples pratiques
1. Réduire le temps de file en ajoutant de la capacité
- Référence :
queued_duration~ 300s aux heures de pointe. - Changement : Augmenter
concurrentde 4 à 8 sur deux runners (capacité logique totale de 8 à 16), en gardant l'utilisation CPU sous 75%. - Résultat attendu :
queued_durationtombe sous 60s. - Vérification : Comparer la moyenne
queued_durationsur 10 pipelines avant/après. - Retour arrière : Si CPU dépasse 90% et échecs montent, revenez sur
concurrentet planifiez un troisième runner.
2. Réduire les temps de clone avec fetch superficiel
- Référence : Clone 150s sur repo 2 Go avec sous-modules.
- Changement :
GIT_STRATEGY: fetch,GIT_DEPTH: 1,GIT_SUBMODULE_STRATEGY: none. - Résultat attendu : Clone sous 40s.
- Vérification : Chronométrage début-job à première commande dans les logs.
- Retour arrière : Si le build nécessite tags ou sous-modules, augmentez
GIT_DEPTHou réactivez les sous-modules sélectivement.
3. Accélérer les dépendances avec cache clé par fichier de verrou
- Référence : Install Node 200s à froid, 200s à chaque build.
- Changement : Cache
node_modulesclé parpackage-lock.json. - Résultat attendu : Install en cache ~40-60s, invalide seulement au changement de dépendances.
- Vérification : Observer message de restauration cache et durée d'install.
- Retour arrière : Si conflits de cache entre branches, incluez
$CI_COMMIT_REF_SLUGdans la clé.
4. Diviser les longs tests en toute sécurité
- Référence : Job de test unique 30min.
- Changement :
parallel: 4et passer$CI_NODE_INDEXpour sharder les tests. - Résultat attendu : 8-12min temps total selon surcoût.
- Vérification : S'assurer que chaque shard dure dans 10-20% des autres ; ajuster la logique de sharding sinon.
- Retour arrière : Mettre
ENABLE_PARALLEL=0pour désactiver pendant l'investigation des flakiness.
À quoi ressemble un bon état
- Temps de file : Sous 60s pour les pipelines courants aux heures ouvrées.
- Temps de clone : Proportionnel à
GIT_DEPTHet taille du repo ; sous 60s pour la plupart des repos avec fetch superficiel. - Étapes dépendances : Sous 1 minute avec caches chauds pour les écosystèmes de langages typiques.
- Artéfacts : Upload/download sous 30s quand limités aux rapports et petits livrables.
- Stabilité : Taux d'échec n'augmente pas après optimisation ; sinon, annulez le dernier changement.
Conclusion
Vous disposez désormais d'une méthode sûre et progressive pour optimiser les performances de GitLab CI/CD : inventorier l'existant, mesurer ce qui compte, appliquer de petits changements réversibles, vérifier les résultats et opérationnaliser la routine. Commencez par le plus petit pilote facile à mesurer et inspecter, conservez ce qui améliore constamment le débit ou la latence, et déployez délibérément. Revisitez votre inventaire et votre dimensionnement chaque trimestre ou après des changements majeurs de charge (nouveaux langages, dépôts plus gros, nouvelles suites de tests). Avec une mesure disciplinée et des retours arrière prudents, la vitesse des pipelines devient une composante gérée et prévisible de votre système d'ingénierie.