Introduction à l'optimisation de vos pipelines GitLab CI
Dans les chapitres précédents, nous avons vu comment créer des automatisations simples. Cependant, dans un environnement DevOps professionnel, un pipeline ne doit pas simplement exécuter des commandes de manière linéaire. Il doit devenir intelligent.
L'enjeu n'est plus seulement de "faire fonctionner" le code, mais de l'optimiser pour qu'il soit plus rapide, moins coûteux en ressources et capable de s'adapter aux modifications complexes de votre projet. Nous allons maintenant explorer comment transformer votre fichier YAML en un véritable chef d'orchestre grâce aux Règles, au DAG, à la Matrice et à la gestion avancée des Artefacts et du Cache.
Maîtriser les Artefacts : Conserver et Transmettre vos fichiers
Qu'est-ce qu'un Artefact dans GitLab CI ?
Un artefact est une liste de fichiers ou de répertoires générés par un job à la fin de son exécution. Par défaut, GitLab nettoie l'environnement du Runner après chaque tâche, ce qui signifie que tout fichier créé (comme un binaire compilé ou un rapport de test) est supprimé. Les artefacts permettent de "sauvegarder" ces fichiers sur le serveur GitLab.
Imaginez qu'un job de construction est un artisan qui fabrique un meuble. Une fois le travail terminé, l'atelier est balayé. Si l'artisan ne met pas le meuble dans un carton d'expédition (l'artefact), le meuble disparaît. Les artefacts servent donc à deux choses : permettre aux jobs suivants de récupérer ces fichiers ou permettre à l'utilisateur de les télécharger manuellement.
Configuration dans le fichier .gitlab-ci.yml
Vous définissez les artefacts directement dans le job qui produit les fichiers.
job_compilation:
stage: build
script:
- npm run build # Génère un dossier 'dist/'
artifacts:
name: "version_du_$CI_COMMIT_REF_NAME" # Nom personnalisable de l'archive
paths:
- dist/ # Le contenu à sauvegarder
expire_in: 1 week # Temps avant suppression automatique sur GitLab
when: on_success # N'upload que si le job réussit
Les options clés :
- name : Donne un nom unique à l'archive téléchargée (très utile pour s'y retrouver).
- expire_in : Définit la durée de vie de l'artefact avant sa suppression pour économiser l'espace disque du serveur.
- when : Vous pouvez choisir d'envoyer des fichiers même en cas d'échec avec on_failure.
Où retrouver vos artefacts ?
GitLab offre plusieurs points d'accès pour récupérer vos fichiers générés :
- Dans le pipeline : Allez dans Build > Pipelines. À droite de chaque pipeline, une icône de téléchargement permet de récupérer les artefacts de tous les jobs.
- Dans un job spécifique : Cliquez sur un job terminé. Dans la colonne de droite, vous verrez une section Job artifacts avec les boutons Download et Browse.
- Via l'API : Utile pour des scripts externes qui souhaitent récupérer la dernière version stable d'un programme (Plus d'informations ici)
"L'interface de job permet de parcourir les fichiers directement dans le navigateur"
Optimisation de la performance : Le Cache GitLab CI
Pourquoi utiliser le Cache ?
Dans un pipeline CI/CD, beaucoup de temps est gaspillé à retélécharger les mêmes dépendances à chaque exécution (comme les dossiers node_modules ou .maven). Le Cache est un mécanisme de stockage temporaire qui permet de conserver ces fichiers entre deux exécutions du pipeline.
Contrairement aux Artifacts, qui servent à transmettre des fichiers entre les étapes (stages) d'un même pipeline, le Cache est conçu pour accélérer les pipelines futurs en réutilisant les données déjà téléchargées. Une stratégie de cache bien configurée peut réduire le temps de vos pipelines de plus de 50%.
Configuration du Cache dans le .gitlab-ci.yml
Pour activer le cache, vous devez définir deux éléments : une clé (key) pour identifier le cache et les chemins (paths) des dossiers à sauvegarder.
# Exemple d'optimisation pour un projet Node.js
variables:
npm_config_cache: "$CI_PROJECT_DIR/.npm"
cache:
key:
files:
- package-lock.json # Le cache change uniquement si ce fichier est modifié
paths:
- .npm/
- node_modules/
job_test:
stage: test
image: node:22-alpine
script:
- npm ci --cache .npm --prefer-offline
- npm test
Explication des paramètres :
- files : GitLab génère une empreinte numérique (hash) basée sur le fichier package-lock.json. Si vous ajoutez une dépendance, la clé change et le cache est recréé.
- paths : Liste les répertoires que le Runner doit compresser et envoyer au serveur GitLab à la fin du job.
Gestion des politiques de téléchargement (Policy)
Pour optimiser encore plus les performances, vous pouvez définir une politique d'utilisation du cache :
- pull-push (par défaut) : Le job télécharge le cache au début et le met à jour à la fin.
- pull : Le job télécharge le cache mais ne le renvoie pas à la fin. Idéal pour les jobs de test.
- push : Le job ne télécharge rien mais crée/écrase le cache à la fin.
Exemple d'utilisation:
job_test_rapide:
stage: test
cache:
key: "dependencies-$CI_COMMIT_REF_SLUG"
paths:
- node_modules/
policy: pull # On gagne du temps en ne renvoyant pas le cache
Cache vs Artifacts : Le verdict
Utilisez le Cache pour les dépendances logicielles (fichiers que vous pouvez retélécharger). Utilisez les Artifacts pour les fichiers produits par votre build (ex: exécutables, rapports) que vous ne voulez surtout pas perdre.
Le contrôle du flux : Maîtriser le mot-clé "rules"
Le mot-clé rules est le remplaçant moderne et plus puissant des anciennes directives only/except. Il permet d'évaluer une liste de conditions pour décider si un job doit être ajouté au pipeline.
deploy_to_production:
stage: deploy
script:
- echo "Déploiement en cours..."
rules:
# Condition 1 : Exécution automatique uniquement sur la branche par défaut (main)
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
changes:
- src/**/* # Uniquement si le dossier source est modifié
when: on_success
# Condition 2 : Déclenchement manuel autorisé pour les autres branches
- if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
when: manual
allow_failure: true
Explication technique du code :
- if : Utilise les variables CI/CD pour tester le contexte (ici, la branche).
- changes : GitLab analyse le commit pour voir si les fichiers spécifiés ont bougé.
- when: manual : Transforme le job en bouton dans l'interface GitLab. Le déploiement n'aura lieu que si un humain clique dessus.
Briser les barrières : Le DAG (Directed Acyclic Graph)
Le mot-clé "needs"
Par défaut, GitLab exécute les jobs de manière strictement linéaire. L'utilisation du mot-clé needs permet de briser cette barrière en créant un Directed Acyclic Graph (DAG).
Pourquoi utiliser "needs" :
- Vitesse (Asynchronisme) : Si un build finit plus vite qu'un autre, les tests associés démarrent immédiatement sans attendre le reste du stage.
- Indépendance des flux : Vous créez des "couloirs" d'exécution séparés. Les erreurs du back-end ne ralentissent plus le front-end.
test_frontend:
stage: test
needs: ["build_frontend"] # Dépendance directe
script: npm run test
L'exécution de masse : parallel:matrix
La Matrice est l'outil ultime pour les tests multi-environnements et la scalabilité.
Pourquoi utiliser la matrice ?
- Maintenance simplifiée : Vous ne modifiez qu'un seul bloc de code pour mettre à jour des dizaines de jobs simultanément.
- Couverture maximale : Idéal pour tester la compatibilité de votre application sur plusieurs versions d'un langage (ex: Node.js 18, 20, 22) ou différents systèmes.
- Gain de temps : GitLab distribue ces jobs sur tous les Runners disponibles, permettant une exécution massivement parallèle.
tests_compatibilite:
stage: test
image: node:${VERSION_NODE}
parallel:
matrix:
- VERSION_NODE: ["18", "20", "22"]
ENV: ["dev", "prod"]
script:
- echo "Exécution du test sur Node $VERSION_NODE pour l'environnement $ENV"
- npm test
Sortie - Jobs générés :
Le moteur GitLab va créer 6 instances de jobs indépendantes :
1. tests_compatibilite: [18, dev]
2. tests_compatibilite: [18, prod]
3. tests_compatibilite: [20, dev]
4. tests_compatibilite: [20, prod]
5. tests_compatibilite: [22, dev]
6. tests_compatibilite: [22, prod]
Hooks globaux : before_script et after_script
Pour éviter la redondance, GitLab propose des Hooks (crochets).
- before_script : S'exécute avant le script principal de chaque job. Idéal pour préparer l'environnement.
- after_script : S'exécute systématiquement à la fin, même si le job a échoué. Crucial pour le nettoyage ou les notifications.
Exemple d'utilisation:
default:
before_script:
- apk add --no-cache curl # Installation d'un outil requis par tous les jobs
after_script:
- echo "Nettoyage du répertoire de travail sur le Runner..."
job_web_test:
stage: test
script:
- curl https://mon-application.local/health
- npm run test
Optimisation : Le Cache vs les Hooks
Si votre before_script contient une commande lourde comme npm install, combinez-le avec le Cache pour diviser par deux le temps total du pipeline.
Conclusion
En maîtrisant ces outils, vous n'êtes plus un simple utilisateur de la CI/CD, vous en êtes l'architecte. Vous savez désormais réduire le temps d'attente grâce au Cache, sécuriser vos productions via les Artefacts et piloter des exécutions massives avec la Matrice.
Cependant, une infrastructure performante doit pouvoir se mesurer. Dans le prochain chapitre, nous utiliserons le Cycle Analytics pour transformer vos gains techniques en données tangibles.
Espace commentaire
Écrire un commentaire
Rejoignez la discussion
Vous devez être connecté pour poster un message.
21 commentaires
Content que ça aide. Le secret c'est vraiment de découpler au maximum les jobs qui n'ont pas de dépendances directes entre eux.
Merci pour le tuto sur le DAG, ça a divisé mon temps de build par 3.
Utilise la syntaxe
**/*pour récursif. Si tu mets justesrc/, il ne verra pas forcément les modifications profondes. Exemple :J'ai testé
rules: changesmais ça ne semble pas détecter les fichiers dans les sous-répertoires, une idée ?C'est pour ça que je recommande de coupler le
before_scriptavec le cache. Voici comment optimiser l'installation d'outils :Oui, dès que le job démarre, le
before_scripttourne. Attention aux coûts si tu installes des trucs lourds à chaque fois.Le
before_scriptglobal s'exécute même si le job est enmanual?Non, pas nativement. Pour forcer un reset, le plus simple reste de changer la
keydans ton fichier de config. Ça force le Runner à en créer un nouveau.Est-ce qu'on peut forcer le nettoyage du cache via l'interface sans vider tout le bucket S3 ?
Vérifie aussi que le job cité dans
needsn'est pas ignoré par une règlerules. Si le job parent n'existe pas, le job enfant échouera.Regarde bien ton indentation. Si ton job n'est pas dans le même pipeline ou si le nom est mal orthographié, le DAG ne peut pas créer le lien.
J'ai une erreur
job not foundquand je mets un nom dansneeds. Comment je debug ça ?C'est par job. Si tu ne précises rien, GitLab garde les artefacts indéfiniment, ce qui finit par saturer ton disque. Mets toujours une expiration raisonnable comme
1 week.Le
expire_indes artefacts est global ou par job ?Oui, tu peux utiliser une variable dans ton
image:. Par exemple :image: node:${VERSION_NODE}. Ça marche très bien chez moi.Quelqu'un a réussi à faire marcher
parallel:matrixavec une image Docker dynamique ?Oui, c'est le comportement attendu. Avec
needs, tu brises la structure linéaire des stages. Tu dois explicitement définirartifacts: truedans tonneedssi tu veux récupérer les fichiers du job parent.J'ai un souci avec le DAG. Quand j'utilise
needs, mes artefacts ne sont pas transmis aux jobs aval. C'est normal ?Exact. Vérifie aussi la valeur de ta
key. Si elle change à chaque commit, tu recrées un nouveau cache à chaque fois au lieu de réutiliser l'existant. Utilisefilescomme indiqué dans mon exemple pour stabiliser la clé.Vérifie tes chemins dans
.gitlab-ci.yml. Si tu es enpullpolicy sans avoir fait depushavant, c'est normal que ton cache soit vide.Super article. Par contre, mon cache
node_modulesne se télécharge jamais. J'ai l'impression que le Runner ignore ma config.