Livraison et déploiement continusDans le dialogue d’impression, choisissez « Enregistrer au format PDF » comme destination.
Retour

DevOps · C3 Intégration et livraison continues · Chapitre 2 · 7 h

Livraison et déploiement continus

Livraison contre déploiement, environnements, stratégies bleu-vert, canari et progressive, retour arrière, gestion des versions et portes de validation.

Dans beaucoup d'organisations, la mise en production est un événement. Elle a une date, une réunion de préparation, une liste de vérifications, et elle a lieu un jeudi soir pour laisser le vendredi aux réparations. Quelqu'un veille. Personne n'aime ça.

Dans les organisations dont parlait le chapitre 1, c'est un non-événement. Cela arrive plusieurs fois par jour, personne ne le remarque, et si quelque chose se passe mal, on revient en arrière en quelques secondes.

La différence n'est pas le courage des équipes : c'est le dispositif. Ce chapitre le construit.

Livraison n'est pas déploiement

Deux termes portent les mêmes initiales et désignent deux engagements différents.

La livraison continue garantit que la branche principale est toujours déployable : à tout instant, un artefact validé existe et pourrait partir en production. La décision de partir reste humaine.

Le déploiement continu va au bout : toute modification qui passe le pipeline part en production automatiquement, sans intervention.

commit → construction → tests → artefact → [recette] → ⏸ décision → production                                    livraison continue ─┘                                    déploiement continu : pas de pause

Le second n'est pas « mieux » : c'est un choix qui dépend du contexte. Un service en ligne interne y gagne. Un logiciel embarqué dans un dispositif médical, un système bancaire soumis à une validation réglementaire, ou un produit installé chez des clients ne peuvent pas s'y conformer — et n'ont pas à le faire.

Mais la livraison continue, elle, est toujours souhaitable, y compris pour ces cas-là : savoir qu'on pourrait déployer à tout instant est ce qui rend le choix de ne pas le faire délibéré plutôt que subi.

Les environnements, et la règle qui les gouverne

développement → recette (staging) → production

Chacun sert à une chose différente : le développement à écrire, la recette à valider dans des conditions proches du réel, la production à servir les utilisateurs.

La règle qui compte tient en une phrase : le même artefact traverse les environnements. L'image construite par le pipeline du chapitre 5 est déployée en recette, validée, puis promue en production — pas reconstruite.

Reconstruire par environnement ruine la garantie centrale du bloc : ce qui a été testé n'est alors plus exactement ce qui tourne. Une version de dépendance a bougé entre les deux constructions, une couche de base a été mise à jour, et le bogue n'apparaît qu'en production.

Ce qui change d'un environnement à l'autre, c'est donc uniquement la configuration — variables d'environnement, secrets, adresses de services — injectée au démarrage, comme au chapitre 3.

Deux conséquences pratiques. L'artefact est immuable et porte un identifiant unique, qui permet de remonter du conteneur en production au commit exact qui l'a produit. Et la recette doit ressembler à la production : mêmes versions, mêmes volumes de données autant que possible — une recette avec cent enregistrements ne dit rien du comportement sur dix millions.

Quatre stratégies de déploiement

C'est le cœur pratique du chapitre, et le choix se fait sur trois critères : exposition en cas de bogue, vitesse de retour arrière, coût en ressources.

StratégiePrincipeExpositionRetour arrièreCoût
Recréationarrêter, remplacer, redémarrer100 %redéployer l'anciennenul
Bleu-vertdeux environnements complets, bascule du routage100 % à la basculeinstantanédouble
Progressifremplacer les exemplaires un à uncroissanteprogressiffaible
Canarirouter une petite part du trafic vers la nouvelle version5 % puis paliersinstantanéfaible

La recréation est la seule qui provoque une interruption de service : elle reste acceptable pour un outil interne, jamais pour un service public.

Le bleu-vert maintient deux environnements complets. On déploie sur l'inactif, on le teste en conditions réelles sans trafic, puis on bascule le routage. Le retour arrière est le même geste en sens inverse : quelques secondes. Le prix est de payer deux environnements.

Le déploiement progressif remplace les exemplaires un par un, en maintenant un nombre minimal d'exemplaires disponibles. C'est le comportement par défaut de Kubernetes, au chapitre 9.

Le canari est le plus prudent : la nouvelle version reçoit d'abord une petite fraction du trafic, on mesure, et l'on progresse par paliers.

Animation · 8 étapes

Déploiement canari : la panne existe, elle ne touche pas tout le monde

  1. Avant tout déploiementLa version 1 sert l'intégralité du trafic, avec un taux d'erreur de référence de 0,1 %. C'est ce chiffre qui servira de comparant : sans ligne de base mesurée, aucune stratégie progressive n'a de sens.
  2. v2 est déployée, et ne sert personneLes deux versions tournent en parallèle. C'est le point qui distingue cette stratégie d'une mise à jour classique : à aucun moment on ne remplace, on ajoute d'abord. Le coût est de faire tourner deux versions à la fois.
  3. 5 % — les premières requêtes réellesVingt utilisateurs sur quatre cents voient la nouvelle version. Aucun jeu de tests ne remplace ce moment : c'est le premier contact avec les vraies données, les vrais volumes et les vrais navigateurs.
  4. Observation : 0,2 % contre 0,1 % de référenceL'écart est dans le bruit sur un si petit échantillon. La durée d'observation compte autant que le seuil : basculer après trente secondes ne prouve rien, et c'est l'erreur la plus fréquente des pipelines automatisés.
  5. 25 % — le palier suivantCent utilisateurs. Le taux reste acceptable, la latence aussi. Chaque palier multiplie l'exposition, donc la qualité de la mesure — et le coût d'une erreur non détectée.
  6. Le taux d'erreur s'envole4,1 % contre 0,1 % de référence : quarante fois la normale. La cause importe peu à cet instant — une requête lente sur un volume qui n'apparaissait pas en recette, par exemple. Ce qui compte est que le seuil soit franchi et que la décision soit AUTOMATIQUE.
  7. Retour arrière : rebasculer le routagev1 n'a jamais été arrêtée : le retour arrière est un changement de routage, pas un redéploiement. Quelques secondes, contre les dizaines de minutes qu'aurait demandées la reconstruction d'une version précédente.
  8. Ce qu'un déploiement d'un bloc aurait coûtéMême bogue, bascule totale : 100 % du trafic touché, et pendant tout le temps qu'il faut pour s'en apercevoir puis reconstruire — typiquement quarante minutes. Le canari n'a pas empêché la panne : il a divisé son ampleur par quatre et sa durée par dix.

Ce que l'animation montre, et qui est le point du chapitre : le canari n'empêche pas la panne. Le bogue était là, il est parti en production, et des utilisateurs l'ont subi. Ce que le dispositif a changé, c'est son ampleur — un quart du trafic au lieu de la totalité — et sa durée — quatre minutes au lieu du temps nécessaire pour s'apercevoir du problème puis reconstruire une version.

C'est le renversement conceptuel du bloc : on ne cherche plus à empêcher tout incident, ce qui est hors d'atteinte, mais à réduire le rayon d'impact et le temps de rétablissement. Les deux derniers indicateurs DORA du chapitre 1 mesurent exactement cela.

Une condition, toutefois, sans laquelle le canari ne sert à rien : il faut des mesures et un seuil décidé à l'avance. Faire du canari sans supervision, c'est exposer 5 % des utilisateurs et attendre qu'ils se plaignent — le chapitre 10 fournit ce qui manque.

Le retour arrière

C'est la capacité la plus importante du chapitre, et la plus souvent négligée.

Un retour arrière doit être testé. Un mécanisme jamais exécuté ne fonctionne pas le jour où l'on en a besoin — et ce jour-là, personne n'est en état de le déboguer. Les équipes sérieuses en déclenchent régulièrement, hors incident.

Il doit être plus rapide que la réparation. Face à un incident, la tentation est de corriger en avant : trouver le bogue, écrire le correctif, le faire passer dans le pipeline, déployer. Cela prend au minimum une demi-heure, souvent plus, et sous pression. Revenir à la version précédente prend quelques secondes, et on diagnostique ensuite, à froid.

Le point dur est la base de données. Le code revient en arrière ; les données, non. Une migration qui a supprimé une colonne rend l'ancienne version incapable de fonctionner. La parade est la migration compatible en avant, en plusieurs étapes :

étape 1   ajouter la nouvelle colonne, écrire dans les DEUXétape 2   déployer le code qui lit la nouvelleétape 3   plus tard, quand le retour arrière n'est plus envisagé,          cesser d'écrire dans l'ancienne, puis la supprimer

C'est plus long à écrire, et c'est ce qui rend le retour arrière possible à tout moment. La règle qui en découle : jamais de suppression ni de renommage destructif dans le même déploiement que le code qui en dépend.

Versions, artefacts, et portes manuelles

La version sémantiqueMAJEUR.MINEUR.CORRECTIF — communique la nature d'un changement : un MAJEUR incrémenté annonce une rupture de compatibilité. Elle est indispensable pour une bibliothèque publiée ; pour un service déployé en continu, on lui préfère souvent une simple étiquette horodatée ou l'empreinte du commit, qui donne la traçabilité : de l'incident au conteneur, du conteneur à l'artefact, de l'artefact au commit.

Restent les portes de validation manuelles. Elles sont légitimes dans trois cas : une exigence réglementaire, une fenêtre de déploiement contrainte par le métier, ou une décision commerciale sur la date de mise à disposition d'une fonctionnalité.

Elles sont du théâtre dans un cas, très fréquent : quand la personne qui valide n'a pas les moyens de décider. Cliquer « approuver » sans lire un tableau de bord ni disposer d'un critère explicite ne réduit aucun risque ; cela ajoute un délai et une signature. Si la porte est là pour rassurer, il vaut mieux se demander ce qui manque au dispositif pour qu'on n'ait plus besoin d'être rassuré — c'est presque toujours de la mesure.

Quiz · 1 question

Une équipe reconstruit son image à chaque environnement : une pour la recette, une pour la production. Où est le problème ?

  • Nulle part : reconstruire garantit que chaque environnement a bien les dernières versionsaucun problème
  • Ce qui a été TESTÉ n'est plus exactement ce qui TOURNE : entre les deux constructions, une dépendance ou une couche de base a pu bouger, et le bogue n'apparaît qu'en production. Le même artefact doit être PROMU d'un environnement à l'autrel'artefact testé n'est plus celui qui tourne
  • Le problème est seulement le temps de construction, doublé inutilementperte de temps

Réponse : C'est la garantie centrale de tout le bloc : ce qui a été validé est, au bit près, ce qui sert les utilisateurs. Reconstruire la détruit. Entre deux constructions séparées de quelques heures, une dépendance transitive a pu être publiée dans une nouvelle version, l'image de base a pu recevoir un correctif, un miroir a pu servir un paquet différent — et l'on obtient deux artefacts qui ne sont pas identiques, dont un seul a été testé. La règle est donc de construire UNE FOIS, puis de PROMOUVOIR le même artefact immuable de la recette vers la production. Ce qui change d'un environnement à l'autre n'est que la CONFIGURATION, injectée au démarrage. Le temps de construction doublé est réel mais anecdotique à côté de cela.

Quiz · 1 question

Un déploiement introduit un bogue. L'équipe cherche la cause pour corriger en avant. Que dit ce chapitre ?

  • C'est la bonne démarche : revenir en arrière ne fait que reporter le problèmecorriger en avant
  • Il faut d'abord REVENIR À LA VERSION PRÉCÉDENTE — quelques secondes — puis diagnostiquer à froid. Corriger en avant prend au minimum une demi-heure, sous pression, pendant que les utilisateurs subissentrevenir d'abord
  • Il faut attendre les premières plaintes utilisateurs pour évaluer la gravité avant d'agirattendre

Réponse : Les deux actions ne se comparent pas. Le retour arrière est une opération connue, rapide et sans réflexion : rebasculer un routage ou redéployer un artefact déjà construit prend quelques secondes. La correction en avant demande de trouver la cause, écrire un correctif, le faire passer par tout le pipeline et déployer — au minimum une demi-heure, en général plus, et sous une pression qui produit précisément les erreurs qu'on veut éviter. Revenir d'abord arrête l'hémorragie et permet de diagnostiquer à froid, avec le même artefact fautif reproduit en recette. Deux conditions y sont attachées : le retour arrière doit être TESTÉ régulièrement hors incident — un mécanisme jamais exécuté ne fonctionne pas le jour venu — et les migrations de base doivent être COMPATIBLES EN AVANT, sans quoi le code revient en arrière mais les données, non.

À vous

L'exercice met les quatre stratégies sur le même incident et calcule ce qui compte vraiment.

On injecte un bogue qui produit un taux d'erreur donné, et le programme calcule pour chaque stratégie le nombre d'utilisateurs touchés et la durée d'exposition, en tenant compte du délai de détection. Vous verrez que le classement dépend du délai de détection : sans supervision, le canari perd une grande partie de son intérêt — ce qui prépare le chapitre 10.

La seconde partie porte sur la migration de base de données. Vous exécuterez un scénario de retour arrière après une migration destructive — l'ancienne version ne démarre plus — puis vous réécrirez la migration en trois étapes compatibles en avant, et vous vérifierez que le retour arrière redevient possible à chaque étape.

Exercice de code

Chiffrez l'impact des quatre stratégies selon le délai de détection, puis rendez une migration réversible.

Point de départ

// ── 1. Quatre stratégies face au même bogue ───────────────────────────────
const TRAFIC = 1000;          // requêtes par minute
const TAUX_ERREUR = 0.30;     // 30 % des requêtes servies par v2 échouent

// paliers : [part du trafic, durée du palier en minutes]
const STRATEGIES = {
  "recréation":  { paliers: [[1.0, Infinity]], interruption: 2, retour: 8 },
  "bleu-vert":   { paliers: [[1.0, Infinity]], interruption: 0, retour: 0.2 },
  "progressif":  { paliers: [[0.2, 2], [0.4, 2], [0.6, 2], [0.8, 2], [1.0, Infinity]],
                   interruption: 0, retour: 3 },
  "canari":      { paliers: [[0.05, 10], [0.25, 10], [0.5, 10], [1.0, Infinity]],
                   interruption: 0, retour: 0.2 },
};

// detection : combien de minutes avant que quelqu'un s'en aperçoive.
function impact(strategie, detection) {
  let t = 0, requetesRatees = 0;
  for (const [part, duree] of strategie.paliers) {
    const restant = detection - t;
    if (restant <= 0) break;
    const passe = Math.min(duree, restant);
    // ← à écrire : requêtes ratées pendant ce palier
    t += passe;
  }
  // Puis le retour arrière, pendant lequel la part courante continue.
  return { requetesRatees: Math.round(requetesRatees), duree: detection + strategie.retour };
}

// ── 2. Migration de base et retour arrière ────────────────────────────────
// Un schéma, deux versions de code, et la question : peut-on revenir ?
const SCHEMA = { colonnes: ["id", "nom_complet"] };

const MIGRATION_DESTRUCTIVE = [
  { nom: "v2 : renommer nom_complet en nom", appliquer: (s) => ({
      colonnes: s.colonnes.map((c) => (c === "nom_complet" ? "nom" : c)) }) },
];

const CODE = {
  v1: { lit: ["id", "nom_complet"], ecrit: ["nom_complet"] },
  v2: { lit: ["id", "nom"],         ecrit: ["nom"] },
};

function peutTourner(version, schema) {
  const besoins = [...CODE[version].lit, ...CODE[version].ecrit];
  return besoins.every((c) => schema.colonnes.includes(c));
}

// ── À VOUS ────────────────────────────────────────────────────────────────
// 1. Écrivez impact(). Comparez les quatre stratégies avec une détection à
//    2 min (supervision), puis à 45 min (une plainte utilisateur).
// 2. Appliquez la migration destructive, déployez v2, puis tentez le retour
//    à v1 : que se passe-t-il ?
// 3. Réécrivez la migration en TROIS étapes compatibles en avant, et
//    vérifiez qu'après chacune, les DEUX versions peuvent tourner.

console.log(JSON.stringify(impact(STRATEGIES["canari"], 2)));

Solution

const TRAFIC = 1000;
const TAUX_ERREUR = 0.30;

const STRATEGIES = {
  "recréation":  { paliers: [[1.0, Infinity]], interruption: 2, retour: 8 },
  "bleu-vert":   { paliers: [[1.0, Infinity]], interruption: 0, retour: 0.2 },
  "progressif":  { paliers: [[0.2, 2], [0.4, 2], [0.6, 2], [0.8, 2], [1.0, Infinity]],
                   interruption: 0, retour: 3 },
  "canari":      { paliers: [[0.05, 10], [0.25, 10], [0.5, 10], [1.0, Infinity]],
                   interruption: 0, retour: 0.2 },
};

function impact(strategie, detection) {
  let t = 0, requetesRatees = 0, partCourante = 0;
  for (const [part, duree] of strategie.paliers) {
    const restant = detection - t;
    if (restant <= 0) break;
    const passe = Math.min(duree, restant);
    partCourante = part;
    // Seule la fraction du trafic servie par la nouvelle version échoue.
    requetesRatees += TRAFIC * part * TAUX_ERREUR * passe;
    t += passe;
  }
  // Pendant le retour arrière, la part courante continue d'échouer.
  requetesRatees += TRAFIC * partCourante * TAUX_ERREUR * strategie.retour;
  // L'interruption de service, elle, rate TOUT le trafic.
  requetesRatees += TRAFIC * strategie.interruption;
  return { requetesRatees: Math.round(requetesRatees), duree: detection + strategie.retour,
           exposition: partCourante };
}

console.log("— 1. même bogue, quatre stratégies —");
for (const detection of [2, 45]) {
  console.log("   détection après " + detection + " min " +
    (detection === 2 ? "(supervision et seuil automatique)" : "(une plainte utilisateur)"));
  const lignes = Object.entries(STRATEGIES).map(([nom, s]) => ({ nom, ...impact(s, detection) }));
  lignes.sort((a, b) => a.requetesRatees - b.requetesRatees);
  for (const l of lignes) {
    console.log("      " + l.nom.padEnd(12) +
      String(l.requetesRatees).padStart(7) + " requêtes ratées" +
      " | exposition " + (l.exposition * 100).toFixed(0).padStart(3) + " %" +
      " | incident " + l.duree.toFixed(1) + " min");
  }
  console.log("");
}
console.log("   Le classement CHANGE avec le délai de détection. Avec supervision,");
console.log("   le canari est de loin le meilleur : il n'a exposé que 5 % du trafic.");
console.log("   Sans supervision, il a eu le temps de monter à 100 % avant qu'on ne");
console.log("   s'en aperçoive, et il ne vaut plus mieux que le bleu-vert.");
console.log("   Conclusion : le canari sans mesure est un canari décoratif — c'est");
console.log("   ce que le chapitre 10 vient compléter.");

console.log("");
console.log("— 2. migration destructive et retour arrière —");
const CODE = {
  v1: { lit: ["id", "nom_complet"], ecrit: ["nom_complet"] },
  v2: { lit: ["id", "nom"],         ecrit: ["nom"] },
};
function peutTourner(version, schema) {
  const besoins = [...CODE[version].lit, ...CODE[version].ecrit];
  return besoins.every((c) => schema.colonnes.includes(c));
}
function etat(etiquette, schema) {
  console.log("      " + etiquette.padEnd(38) + "colonnes [" + schema.colonnes.join(", ") + "]" +
    "  v1 " + (peutTourner("v1", schema) ? "ok " : "KO ") +
    "  v2 " + (peutTourner("v2", schema) ? "ok" : "KO"));
}
let schema = { colonnes: ["id", "nom_complet"] };
etat("départ", schema);
schema = { colonnes: schema.colonnes.map((c) => (c === "nom_complet" ? "nom" : c)) };
etat("après le renommage destructif", schema);
console.log("      → v2 tourne, mais le RETOUR À v1 EST IMPOSSIBLE : la colonne");
console.log("        dont elle a besoin n'existe plus. Le code revient en arrière,");
console.log("        les données non.");

console.log("");
console.log("— 3. la même migration, compatible en avant —");
let s2 = { colonnes: ["id", "nom_complet"] };
etat("départ", s2);
// Étape 1 : AJOUTER, sans rien retirer. Les deux colonnes coexistent.
s2 = { colonnes: [...s2.colonnes, "nom"] };
etat("étape 1 : ajouter nom, écrire dans les deux", s2);
// Étape 2 : déployer v2, qui lit la nouvelle colonne. v1 tourne toujours.
etat("étape 2 : déployer v2", s2);
console.log("      → à cet instant, LES DEUX versions fonctionnent : le retour");
console.log("        arrière reste possible à tout moment.");
// Étape 3 : seulement quand le retour arrière n'est plus envisagé.
s2 = { colonnes: s2.colonnes.filter((c) => c !== "nom_complet") };
etat("étape 3 : supprimer nom_complet (plus tard)", s2);
console.log("      → v1 n'est plus déployable, mais on ne l'envisage plus depuis");
console.log("        plusieurs jours. La règle : JAMAIS de suppression ni de");
console.log("        renommage dans le même déploiement que le code qui en dépend.");

En travaux pratiques

Travaux pratiques 6 · 3 h

Bleu-vert, canari, retour arrière

Déployer l'artefact du TP 5 selon deux stratégies, mesurer ce que chacune expose en cas de bogue, et rendre une migration de base réversible.

Avant de commencer

  • L'image publiée par le pipeline du TP 5, étiquetée par empreinte de commit
  • La pile Compose du TP 4
  • Un outil de charge simple (ab, hey, ou une boucle curl)

Énoncé

  1. Monter le routagePlacez un répartiteur devant l'application. Faites tourner DEUX exemplaires de la pile, en version 1.0, et vérifiez que le trafic se répartit sur les deux. Indice : Un nginx en amont, avec un bloc upstream, suffit — inutile de sortir l'artillerie.
  2. Déployer en bleu-vertConstruisez une version 1.1, déployez-la à côté sans lui envoyer de trafic, testez-la directement, puis basculez le routage d'un coup. Chronométrez la bascule et le retour arrière.
  3. Fabriquer un bogueProduisez une version 1.2 qui renvoie une erreur sur 30 % des requêtes. Déployez-la en bleu-vert pendant qu'une charge tourne, et comptez les requêtes en erreur avant que vous ne réagissiez.
  4. Recommencer en canariMême version fautive, mais en n'envoyant d'abord que 5 % du trafic, puis 25 %. Comptez à nouveau les requêtes en erreur, et comparez à l'étape précédente. Indice : Des poids sur les entrées du bloc upstream suffisent à répartir 5 / 95.
  5. Décider automatiquementÉcrivez un script qui interroge le taux d'erreur toutes les dix secondes et déclenche le retour arrière au-delà d'un seuil que vous fixez d'avance. Relancez le scénario sans intervenir.
  6. La migration destructiveVersion 1.3 : renommez une colonne de la base et adaptez le code. Déployez, puis tentez un retour arrière vers 1.2. Constatez ce qui se passe.
  7. La migration en trois étapesRefaites la même évolution en la découpant, de sorte qu'à chaque étape les DEUX versions puissent tourner. Vérifiez le retour arrière après chacune.

C'est réussi quand

  • Le retour arrière prend moins de cinq secondes et ne reconstruit rien
  • Le canari expose au moins quatre fois moins de requêtes que le bleu-vert sur le même bogue
  • Le script déclenche le retour arrière sans vous
  • Après chaque étape de la migration découpée, l'ancienne version démarre encore

Correction

Le routage pondérénginx.conf
upstream tickets {
  server appli_bleu:3000  weight=95;
  server appli_vert:3000  weight=5;   # le canari
}

server {
  listen 80;
  location / {
      proxy_pass http://tickets;
      proxy_next_upstream off;   # ne pas masquer les erreurs du canari
  }
}

La dernière ligne est celle qu'on oublie : par défaut, nginx réessaie sur un autre serveur quand l'un échoue. Le canari renverrait alors des erreurs que personne ne verrait — et la mesure sur laquelle repose toute la stratégie serait fausse.

Les deux mesures attendues
bleu-vert, réaction humaine à 4 min : ~21 600 requêtes en erreur
canari 5 % puis 25 %, même bogue     :  ~5 400 requêtes en erreur
canari + seuil automatique à 20 s    :    ~300 requêtes en erreur

Le canari n'a EMPÊCHÉ aucune panne : le bogue est parti en production dans les trois cas. Il a divisé l'ampleur, et le seuil automatique a divisé la durée. C'est le renversement du chapitre — on ne cherche pas à empêcher l'incident mais à réduire son rayon et son temps.

La décision automatiquesurveiller.sh
#!/bin/sh
SEUIL=2          # % d'erreurs toléré, décidé À L'AVANCE
while true; do
taux=$(curl -s http://localhost/metriques | awk '/taux_erreur/ {print $2}')
echo "taux = $taux %"
if [ "$(echo "$taux > $SEUIL" | bc)" -eq 1 ]; then
  echo "seuil dépassé : retour arrière"
  sed -i 's/weight=5/weight=0/' nginx.conf
  docker compose exec -T proxy nginx -s reload
  exit 1
fi
sleep 10
done

Le seuil est fixé AVANT le déploiement, pas pendant l'incident. Un canari sans mesure ni seuil expose 5 % des utilisateurs et attend qu'ils se plaignent — c'est ce que le chapitre 10 viendra compléter avec de vraies métriques.

Pourquoi le retour arrière échoue après la migration
-- 1.3 : migration destructive
ALTER TABLE tickets RENAME COLUMN titre TO intitule;

-- l'ancienne version cherche « titre », qui n'existe plus
-- ERROR: column "titre" does not exist

Le code revient en arrière ; les données, non. C'est le point dur du chapitre, et la seule sortie est d'écrire la migration autrement.

La même évolution, réversible
-- étape 1 : AJOUTER, sans rien retirer
ALTER TABLE tickets ADD COLUMN intitule text;
UPDATE tickets SET intitule = titre;
-- le code 1.2 écrit dans « titre », un déclencheur recopie dans « intitule »

-- étape 2 : déployer 1.3, qui LIT « intitule » et écrit dans les DEUX
--           → à cet instant, 1.2 et 1.3 fonctionnent toutes deux

-- étape 3 : plusieurs jours plus tard, quand le retour arrière
--           n'est plus envisagé
ALTER TABLE tickets DROP COLUMN titre;

Trois déploiements au lieu d'un, et le retour arrière reste possible à tout instant sauf après le dernier. La règle générale : jamais de suppression ni de renommage dans le même déploiement que le code qui en dépend.

Ce que la suite en fait

Le bloc IV pose la question laissée de côté : sur quoi déploie-t-on ? Les serveurs, réseaux et bases de ce chapitre ont été supposés existants ; le chapitre 7 les décrit dans des fichiers versionnés, ce qui permet de reconstruire un environnement de recette identique à la production — condition de la règle posée plus haut.

Et le chapitre 10 fournira ce qui manque au canari : sans métriques et sans seuil décidé à l'avance, la décision de progresser ou de revenir n'a aucune base.

À retenir

Flashcards · 6 cartes

Distinguez livraison continue et déploiement continu.
La LIVRAISON continue garantit que la branche principale est TOUJOURS DÉPLOYABLE : un artefact validé existe à tout instant, et la décision de partir reste humaine. Le DÉPLOIEMENT continu supprime la pause : ce qui passe le pipeline part en production. Le second n'est pas « mieux » — un dispositif médical ou un système soumis à validation réglementaire ne peut pas s'y conformer. Mais la livraison continue est toujours souhaitable : elle rend le choix de ne pas déployer DÉLIBÉRÉ plutôt que subi.
Quelle règle gouverne les environnements, et pourquoi ?
LE MÊME ARTEFACT LES TRAVERSE : on construit une fois, puis on PROMEUT l'image de la recette vers la production, sans jamais reconstruire. Reconstruire détruit la garantie centrale du bloc — entre deux constructions, une dépendance ou une couche de base a pu bouger, et ce qui a été testé n'est plus ce qui tourne. Seule la CONFIGURATION change d'un environnement à l'autre, injectée au démarrage. L'artefact est immuable et porte un identifiant qui permet de remonter au commit.
Comparez recréation, bleu-vert, progressif et canari.
RECRÉATION : arrêter puis remplacer — 100 % d'exposition et INTERRUPTION DE SERVICE, acceptable pour un outil interne seulement. BLEU-VERT : deux environnements complets, bascule du routage — retour arrière INSTANTANÉ, au prix du double d'infrastructure. PROGRESSIF : remplacer les exemplaires un à un, exposition croissante, coût faible — c'est le défaut de Kubernetes. CANARI : une petite fraction du trafic, puis des paliers, avec MESURE entre chaque — le plus prudent, mais inutile sans supervision.
Que change réellement un déploiement canari ?
Il n'EMPÊCHE PAS la panne : le bogue part en production et des utilisateurs le subissent. Il change son AMPLEUR (un quart du trafic au lieu de la totalité) et sa DURÉE (quelques minutes au lieu du temps de s'apercevoir puis de reconstruire). C'est le renversement du bloc : on ne cherche plus à empêcher tout incident, hors d'atteinte, mais à réduire le RAYON D'IMPACT et le TEMPS DE RÉTABLISSEMENT — les deux derniers indicateurs DORA.
Quelles sont les trois exigences d'un retour arrière, et le point dur ?
1) IL DOIT ÊTRE TESTÉ régulièrement hors incident : un mécanisme jamais exécuté ne fonctionne pas le jour venu, et ce jour-là personne n'est en état de le déboguer. 2) IL DOIT ÊTRE PLUS RAPIDE que la correction en avant : quelques secondes contre une demi-heure sous pression — on revient d'abord, on diagnostique à froid. 3) LE POINT DUR EST LA BASE : le code revient, les données non. Parade : la migration COMPATIBLE EN AVANT en trois étapes, et jamais de suppression ou de renommage dans le même déploiement que le code qui en dépend.
Quand une porte de validation manuelle est-elle légitime ?
Trois cas : exigence RÉGLEMENTAIRE, fenêtre de déploiement contrainte par le MÉTIER, ou décision COMMERCIALE sur la date de mise à disposition. Elle est du théâtre quand la personne qui valide n'a pas les MOYENS DE DÉCIDER : cliquer « approuver » sans tableau de bord ni critère explicite ne réduit aucun risque, cela ajoute un délai et une signature. Si la porte sert à rassurer, la question est de savoir ce qui manque au dispositif — c'est presque toujours de la mesure.