« Ça marche sur ma machine » a une version plus coûteuse en entreprise : « ça marche en staging, mais staging a été modifié par quelqu’un d’autre entre-temps ». Un environnement de staging partagé, mis à jour par plusieurs PR en même temps, ne teste jamais vraiment une seule PR isolément. Un environnement éphémère par pull request résout ce problème précis : chaque PR obtient sa propre instance complète, jetable, qui n’existe que le temps de la revue.
Le provisioning automatique, déclenché par le cycle de vie de la PR
Un environnement éphémère se crée à l’ouverture de la PR et se détruit à sa fermeture, sans intervention manuelle. La mécanique repose sur un webhook qui déclenche la CI sur les événements de cycle de vie de la PR, pas seulement sur les push :
# GitHub Actions : provisionner à l'ouverture, détruire à la fermeture
on:
pull_request:
types: [opened, synchronize, reopened, closed]
jobs:
deploy-preview:
if: github.event.action != 'closed'
steps:
- run: |
kubectl create namespace pr-${{ github.event.number }} \
--dry-run=client -o yaml | kubectl apply -f -
helm upgrade --install app-pr-${{ github.event.number }} ./chart \
--namespace pr-${{ github.event.number }} \
--set image.tag=${{ github.sha }}
destroy-preview:
if: github.event.action == 'closed'
steps:
- run: kubectl delete namespace pr-${{ github.event.number }} --ignore-not-found
Le job de destruction est le plus souvent négligé à la conception, alors qu’il porte l’essentiel de la valeur économique du système : un environnement qui se crée automatiquement mais ne se détruit jamais correctement accumule un coût d’infrastructure qui grossit avec chaque PR fermée sans nettoyage effectif.
Le namespace ne suffit pas à l’isolation
Un namespace Kubernetes isole les ressources par nom, pas par capacité réseau ni par consommation de ressources. Sans contrainte supplémentaire, un environnement de preview qui tourne un test de charge un peu trop enthousiaste peut affamer les ressources d’un nœud partagé avec d’autres environnements de preview, ou atteindre par erreur un service d’un autre namespace faute de NetworkPolicy restrictive :
# ResourceQuota : plafonner ce qu'un environnement éphémère peut consommer
apiVersion: v1
kind: ResourceQuota
metadata:
name: preview-quota
namespace: pr-1234
spec:
hard:
requests.cpu: "2"
requests.memory: 4Gi
pods: "10"
Un ResourceQuota par namespace de preview, posé au moment du provisioning automatique, évite qu’un environnement éphémère mal maîtrisé ne dégrade la plateforme partagée sur laquelle tournent tous les autres.
Les dépendances externes : ce qui casse le plus souvent
Un service qui dépend d’une base de données, d’une file de messages ou d’un service tiers pose la question la plus délicate d’un environnement éphémère : partager ces dépendances entre previews (risque de collision de données entre PR) ou en provisionner une instance par PR (coût et temps de démarrage). La réponse pragmatique la plus courante : des dépendances légères ou stateless provisionnées par PR (une base PostgreSQL éphémère avec un jeu de données de test réduit), et des dépendances lourdes ou externes (un service tiers payant, un cluster de données volumineux) mockées ou pointées vers une instance partagée en lecture seule.
Le coût réel, et pourquoi il reste caché
Le coût d’un environnement éphémère ne se limite pas au calcul consommé pendant la revue : il inclut le temps de démarrage à chaque PR (qui ralentit la boucle de feedback si le provisioning prend plusieurs minutes), et le coût résiduel des environnements dont la destruction a échoué silencieusement (un webhook manqué, un job de nettoyage qui timeout). Un audit périodique qui liste les namespaces pr-* sans PR ouverte correspondante rattrape ce que le webhook de fermeture n’a pas nettoyé :
# Namespaces de preview orphelins : plus de PR ouverte correspondante
comm -23 \
<(kubectl get ns -l type=preview -o name | sed 's#namespace/##' | sort) \
<(gh pr list --state open --json number --jq '.[] | "pr-" + (.number|tostring)' | sort)
Sans ce filet de sécurité, le système d’environnements éphémères, censé économiser des ressources par rapport à un staging partagé permanent, finit par en consommer davantage en silence. Concevoir ce provisioning automatique proprement, garde-fous inclus, fait partie du travail d’industrialisation d’une chaîne CI/CD.
À retenir
Un environnement éphémère par pull request résout un vrai problème d’isolation entre revues, mais déplace le coût vers deux endroits qu’il faut concevoir dès le départ : la contrainte de ressources par namespace de preview, et un mécanisme de nettoyage qui ne dépend pas uniquement d’un webhook de fermeture de PR toujours fiable. Sans ces deux garde-fous, le gain d’isolation se paie en dérive de coût silencieuse, exactement le problème que le système devait résoudre côté staging partagé.