CaenHackYouAcademy
DevSecOps

Anatomie d'une pipeline CI/CD

Les étapes, les agents, les artefacts — et ce qui circule entre eux.

Une développeuse pousse son code sur Git. Quelques minutes plus tard, elle reçoit une notification : les tests passent, l'image Docker est construite, le déploiement en pré-production est terminé. Elle n'a rien fait manuellement — c'est la pipeline qui a tout orchestré. Mais qu'est-ce qui distingue cette pipeline d'un simple script shell, et pourquoi cette abstraction est-elle devenue incontournable ?

En bref

Une pipeline CI/CD est un système d'exécution gouverné qui transforme du code source en logiciel déployable, de façon reproductible, traçable et observable. Ce n'est pas un script : c'est une infrastructure.

Une pipeline n'est pas un script

Vous avez peut-être un script Bash qui compile et déploie votre application. Ce n'est pas une pipeline. La différence ne tient pas à ce que fait le code, mais à comment il s'exécute et à ce qu'on peut en observer.

ScriptPipeline
Tourne sur votre machine ou un serveur fixeS'exécute dans un environnement contrôlé et isolé
Lit les variables d'environnement localesDéclare explicitement ses besoins (secrets, artefacts)
Renvoie « ça marche » ou « ça plante »Expose chaque étape, son statut, sa durée
Aucune trace une fois terminéConserve logs, artefacts, historique complet
Lancé manuellement ou par cronDéclenché par des événements Git (push, PR, tag)

Ce qui sépare un script d'une pipeline : l'environnement, la déclaration, la trace.

Un script peut être une étape d'une pipeline. Mais la pipeline apporte l'orchestration, la gouvernance et la visibilité que le script seul ne fournit pas.

La structure d'exécution

Pour comprendre une pipeline, il faut installer quelques concepts. Prenons-les un par un, du plus petit au plus grand.

Les jobs : l'unité atomique

Un job est la plus petite unité d'exécution. Voyez-le comme un conteneur jetable : il démarre, fait son travail, puis disparaît. Chaque job s'exécute dans un environnement isolé et produit un résultat binaire — succès ou échec. Son cycle de vie ne varie pas : préparation de l'environnement (image Docker, dépendances système), récupération du code source, exécution des commandes, dépôt des artefacts si le job a réussi, nettoyage.

Trois propriétés le définissent :

  • Isolation — chaque job démarre dans un environnement vierge. Ce qui s'est passé dans un job précédent ne pollue pas celui-ci.
  • Atomicité — un job réussit (code de retour 0) ou échoue (code de retour différent de 0). Pas d'état intermédiaire.
  • Parallélisation — plusieurs jobs peuvent tourner en même temps s'ils ne dépendent pas les uns des autres.
JobRôleDépend de
lintvérifie le style du coderien
unit-testsexécute les tests unitairesrien
build-dockerconstruit une image Dockerlint, unit-tests
deploy-stagingdéploie sur l'environnement de testbuild-docker

Quatre jobs indépendants — mais pas sans relation entre eux.

Si les tests plantent, cela n'empêche pas le linter de tourner : les deux sont indépendants et peuvent s'exécuter en parallèle. En revanche, si les tests plantent, on ne construira pas l'image Docker : il y a là une relation de dépendance.

Les stages : organiser les jobs par phase

Un stage (ou phase) regroupe des jobs qui poursuivent le même objectif. Les stages s'exécutent dans l'ordre, mais les jobs d'un même stage peuvent tourner en parallèle.

Les stages s'enchaînent de gauche à droite à l'intérieur d'un stage, les jobs sont parallèles.

Découper en stages sert trois objectifs : échouer vite — si lint échoue, inutile de lancer build la lisibilité — le flux se visualise clairement dans l'interface et l'optimisation — parallélisation maximale au sein d'un stage.

Le graphe d'exécution

Une pipeline complexe forme un graphe acyclique dirigé (DAG). Ce sont les dépendances déclarées entre jobs qui définissent l'ordre d'exécution, et pas seulement la succession des stages.

Graphe acyclique dirigé

Un graphe orienté dans lequel aucun chemin ne revient à son point de départ. Appliqué à une pipeline : un job ne peut pas dépendre, directement ou indirectement, de lui-même — sans quoi la pipeline ne démarrerait jamais.

Comprendre ce graphe, c'est pouvoir optimiser les temps d'exécution : deux jobs sans dépendance mutuelle peuvent toujours tourner en parallèle, même s'ils sont écrits l'un après l'autre.

Runners et agents

Qui exécute le code ?

Un runner (GitHub Actions, GitLab CI) ou agent (Jenkins, Azure DevOps) est la machine qui exécute vos jobs. Le flux est toujours le même : la plateforme CI/CD assigne un job à un runner disponible le runner récupère le job — code et configuration le job s'exécute le runner renvoie les logs et le statut à la plateforme.

Types de runners

TypeAvantagesInconvénients
Hébergé (GitHub, GitLab SaaS)prêt à l'emploi, maintenance incluseressources limitées, pas d'accès au réseau interne
Auto-hébergécontrôle total, accès aux ressources internesmaintenance et sécurité à votre charge
Éphémèreisolation maximale, pas de pollution entre jobstemps de démarrage plus long

Trois modèles de runner — le choix engage la sécurité, la performance et le coût.

Ce que le type de runner change concrètement : la sécurité — un runner partagé, a fortiori public, peut exposer vos secrets s'il est mal configuré la performance — accès à des ressources spécifiques, bases de données internes ou GPU le coût — les minutes gratuites des runners hébergés s'épuisent vite dès qu'on construit souvent.

Isolation et sécurité des runners

Un job a accès à tout ce que le runner peut voir : le système de fichiers du runner, les variables d'environnement, les secrets injectés et le réseau joignable depuis la machine.

Un runner partagé est une surface d'attaque

Si plusieurs projets partagent le même runner, un job malveillant peut accéder aux données d'un autre projet. C'est pourquoi les runners éphémères, détruits après chaque job, sont la règle dès que le contexte est sensible.

Cache et artefacts

La confusion entre cache et artefact est fréquente : les deux stockent des données, mais leur usage est fondamentalement différent.

Le cache : accélérer les builds

Le cache conserve des fichiers entre deux exécutions pour éviter de les recalculer — dépendances npm, modules Python, dépôt local Maven. À la première exécution, npm install télécharge 300 paquets en trois minutes, puis sauvegarde le cache à la suivante, il le retrouve et rend la main en dix secondes.

Un cache est identifié par une clé calculée sur un hachage (fichier de verrouillage, nom de branche). Il est optionnel : le job doit fonctionner même sans lui. Il est partagé entre jobs et entre pipelines, et il peut devenir obsolète — c'est normal.

Les artefacts : transmettre des résultats

Un artefact est le produit d'un job, transmis aux jobs suivants ou téléchargé par un humain. C'est le livrable de l'étape. Pourquoi est-ce important ? Parce que les jobs sont isolés : si le job build produit un fichier, le job deploy ne le verra pas, sauf si ce fichier est déclaré comme artefact.

Les jobs étant isolés, seul un artefact déclaré franchit la frontière — le cache, lui, reste une optimisation locale.

Un artefact est attaché à un job précis, doté d'une durée de rétention configurable — 30 à 90 jours par défaut selon les plateformes —, téléchargeable après la fin de la pipeline, et obligatoire pour les jobs qui en dépendent.

AspectCacheArtefact
Butaccélérertransmettre
Obligatoirenonoui, s'il y a une dépendance
Persistanceentre exécutionsdurée de rétention limitée
Exemplenode_modules/dist/, report.xml
S'il est absentle job est plus lentle job échoue

Le cache est une optimisation, l'artefact est un contrat.

Environnements de déploiement

Un environnement représente une cible de déploiement : développement, pré-production, production. Les pipelines modernes permettent de définir des règles propres à chaque environnement.

Un même artefact, trois cibles — des règles et des secrets qui se durcissent à chaque étage.

Règles de protection

La production mérite des garde-fous : une approbation humaine avant le déploiement un délai de quelques minutes entre pré-production et production, pour pouvoir annuler des plages horaires — pas de déploiement le vendredi après-midi des branches autorisées — seule main peut déployer en production.

Secrets par environnement

Chaque environnement possède ses propres identifiants : la base de données de pré-production n'est pas celle de production, les clés d'API de test ne sont pas celles de production, et les jetons de développement portent des permissions réduites.

À retenir

La séparation des secrets réduit l'impact d'une fuite : un secret de pré-production compromis ne donne pas accès à la production.

Logs et observabilité

Ce que les logs doivent contenir

Un job correctement journalisé permet de comprendre ce qui s'est passé sans relancer la pipeline :

  • les commandes exécutées — exactement ce qui a été lancé
  • la sortie standard et la sortie d'erreur, complètes
  • des horodatages — début et fin de chaque étape
  • les variables d'environnement utiles au contexte d'exécution, secrets exclus
  • les versions des outils, dépendances et images employés.

Masquage des secrets

Les plateformes CI/CD masquent automatiquement les secrets déclarés dans les logs. Restent les fuites indirectes, qu'aucun masquage ne rattrape.

{Deux façons de faire fuiter un jeton, et la parade.}
# À ÉVITER : le secret peut fuiter par un simple echo
echo "Token: MY_TOKEN"

# À ÉVITER : une erreur curl peut réafficher l'en-tête complet
curl -H "Authorization: TOKEN" https://api.example.com

# CORRECT : passer par un fichier temporaire, jamais par la ligne de commande
echo "TOKEN" > /tmp/token.txt
curl -H "Authorization: (cat /tmp/token.txt)" https://api.example.com

Le masquage n'est pas une protection

Il n'agit que sur les valeurs que la plateforme connaît, et seulement dans les logs qu'elle collecte. Un secret transmis en argument de commande reste visible dans la table des processus du runner.

Métriques essentielles

Une pipeline en bonne santé expose ses métriques : la durée moyenne, temps écoulé entre le déclenchement et la fin le taux de succès, part de pipelines au vert sur sept jours les tests instables (flaky), ceux qui échouent de façon aléatoire le temps d'attente en file, délai avant qu'un runner se libère.

Trois alertes à mettre en place

Une pipeline qui dépasse un seuil de durée (régression de performance) un taux de succès qui chute sous un plancher défini (problème systémique) un job qui échoue plus de n fois (test instable à corriger).

Les invariants d'une pipeline

Au-delà des concepts techniques, une pipeline repose sur trois garanties. Si l'une manque, vous avez un script qui s'exécute à distance — pas une pipeline.

Reproductibilité : même entrée, même sortie

Règle d'or

Le même commit doit produire le même artefact, peu importe quand et où la pipeline s'exécute.

Si votre pipeline donne des résultats différents à chaque exécution, elle perd toute valeur : on ne peut plus faire confiance à ce qu'elle produit. Quatre causes reviennent sans cesse :

  • dépendances non épingléesnpm install sans package-lock.json peut installer des versions différentes d'une fois sur l'autre
  • tests qui dépendent de l'heure — un test qui vérifie « il est entre 9 h et 18 h » échouera la nuit
  • appels à des API externes sans bouchon — si l'API change ou devient indisponible, les tests plantent aléatoirement
  • cache non déterministe — si le cache contient des fichiers d'une exécution précédente, le résultat peut varier.

La parade tient en trois gestes : épingler toutes les dépendances — un package-lock.json, un Pipfile.lock ou un go.sum versionné avec le code utiliser des images Docker à tag fixe, jamais latest isoler les tests de l'heure locale comme des données externes.

Traçabilité : remonter de l'artefact au commit

Quand un bug apparaît en production, vous devez pouvoir répondre à quatre questions : quel commit a introduit ce changement ? Quelle pipeline a construit cet artefact ? Quels tests ont été exécutés ? Qui a déclenché le déploiement ?

Sans traçabilité, le débogage devient de l'archéologie : on fouille les logs et les messages de commit en espérant tomber sur le bon élément. Trois pratiques la garantissent : chaque artefact porte le SHA du commit qui l'a produit, chaque déploiement conserve un lien vers la pipeline d'origine, et chaque job expose ses logs complets — pas seulement un verdict.

Observabilité : savoir ce qui se passe

Une pipeline doit exposer son état de santé en temps réel : pas seulement « ça marche » ou « ça plante », mais des métriques actionnables. Pourquoi est-ce critique ? Parce qu'une pipeline qui ralentit progressivement finit par bloquer toute l'équipe. Si vous ne détectez pas qu'un job de test passe de 2 à 15 minutes, vous le découvrirez trop tard — quand plus personne ne voudra pousser de code.

Ce que la pipeline révèle

Une pipeline CI/CD n'est jamais neutre. Elle cristallise les décisions de l'équipe, parfois à son insu. Voici les questions auxquelles elle répond implicitement.

  • Quels tests sont obligatoires ? Si un test peut être ignoré, il ne l'est pas. Si la pipeline bloque sans lui, il l'est.
  • Qui peut déployer en production ? Si chacun peut fusionner sur main et que cela déclenche un déploiement automatique, alors tout le monde déploie. Si une approbation est requise, seuls certains rôles le peuvent.
  • Combien de temps peut-on attendre un retour ? Une pipeline de 45 minutes décourage les petits commits fréquents une pipeline de 5 minutes encourage l'itération rapide.
  • Quelle confiance accorde-t-on aux dépendances externes ? Si la pipeline bloque dès qu'une dépendance ne répond pas, vous avez un point de défaillance unique. Si elle continue sur un cache ou une version épinglée, vous avez anticipé la panne.

Loi de Conway

Une équipe cloisonnée — dev d'un côté, ops de l'autre — produira une pipeline fragmentée : déploiement manuel après la construction. Une équipe sans ownership clair produira une pipeline sans maintenance : personne ne nettoie les jobs obsolètes.

À retenir de cette séance

Jobs, stages, runners, cache, artefacts, environnements : ce vocabulaire décrit une structure d'exécution, pas une suite de commandes. Les trois invariants — reproductibilité, traçabilité, observabilité — sont ce qui rend cette structure digne de confiance. Une pipeline qui les perd redevient un script, quelle que soit la plateforme qui l'héberge.

VérificationQuestion 1 / 3

Réponse unique

Quelle est l'unité atomique d'exécution d'une pipeline ?

Indice · Qu'est-ce qui réussit ou échoue d'un seul tenant ?

On this page