Ce chapitre est gratuit
Pas besoin de compte pour le lire. Decouvre le contenu et decide si le programme est fait pour toi.
Voir les autres chapitresEn 2011, les ingénieurs d’Heroku ont formalisé 12 principes pour construire des applications SaaS modernes. Plus d’une décennie plus tard, ces principes n’ont pas pris une ride — ils sont même devenus le socle implicite de tout déploiement cloud-native. Kubernetes, Docker, les services managés AWS : tous partent du principe que ton application respecte ces 12 facteurs. Si elle ne les respecte pas, tu te bats contre l’infrastructure au lieu de t’appuyer dessus. Ce cours t’emmène facteur par facteur, avec les implémentations concrètes qui comptent en production.
Pourquoi la 12-Factor App reste incontournable
“Cloud Native” ne signifie pas “qui tourne dans le cloud”. C’est une approche de conception qui exploite la scalabilité horizontale, la résilience par design, le déploiement continu et l’observabilité. La 12-Factor App est la recette pour y arriver.
Le test est simple : si tu fais un lift-and-shift de ta VM on-premise vers EC2 sans rien changer, tu as du “cloud hosted” — pas du cloud native. Tu paies l’infrastructure cloud sans en tirer les bénéfices.
Les 12 facteurs se regroupent en trois catégories : le code et ses dépendances (I, II, V), la configuration et les services externes (III, IV), et le comportement runtime (VI à XII). Voyons-les en détail.
🧠 À retenir : La définition officielle de la CNCF est claire : les technologies cloud-native permettent de construire et d’exécuter des applications scalables dans des environnements modernes et dynamiques (cloud public, privé, hybride). Conteneurs, service meshes, microservices, infrastructure immutable et APIs déclaratives sont les briques de base.
Les facteurs fondamentaux : code, dépendances et build
Facteur I — Codebase : un repo Git = un service = N déploiements (dev, staging, prod). Si deux services partagent du code, extrait-le dans une librairie. Si un repo contient plusieurs services, scinde-le.
Facteur II — Dependencies : chaque dépendance doit être déclarée explicitement dans un manifest (requirements.txt, package.json, go.mod) et isolée. Jamais de “ça marche parce que libxml2 est installé sur le serveur”.
# Dockerfile qui respecte le Factor II
FROM python:3.12-slim
# Dépendances système EXPLICITES
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
Facteur V — Build, Release, Run : trois étapes strictement séparées. Le build produit un artefact (image Docker), le release combine l’artefact + la config (Helm chart + values), le run exécute. Chaque release est immutable et taggée (v1.2.3-prod-20260320). On ne modifie jamais un artefact après le build.
Facteur III — Config : toute configuration qui varie entre les environnements (URLs de DB, clés API, feature flags) va dans des variables d’environnement, jamais dans le code. Le test : pourrais-tu open-sourcer ton repo demain sans exposer un seul credential ?
# Kubernetes: ConfigMap pour la config, Secret pour les credentials
apiVersion: v1
kind: ConfigMap
metadata:
name: order-service-config
data:
LOG_LEVEL: "info"
DB_POOL_SIZE: "25"
CACHE_TTL: "300"
---
apiVersion: v1
kind: Secret
metadata:
name: order-service-secrets
type: Opaque
stringData:
DB_PASSWORD: "super-secret-password"
API_KEY: "sk-xxx123"
---
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: order-service
envFrom:
- configMapRef:
name: order-service-config
- secretRef:
name: order-service-secrets
💡 Tip DevOps : En production, ne stocke pas les secrets dans des Kubernetes Secrets natifs (ils sont juste encodés en base64, pas chiffrés). Utilise un External Secrets Operator connecté à AWS Secrets Manager, HashiCorp Vault ou Azure Key Vault.
Les facteurs runtime : processus, scaling et résilience
Facteur IV — Backing Services : ta base de données, ton cache Redis, ton broker Kafka, ton S3 — ce sont des ressources attachées, interchangeables par une simple URL. Changer de provider = changer une variable d’environnement, pas du code.
Facteur VI — Processes : l’application tourne comme un ou plusieurs processus stateless. Pas de sessions en mémoire, pas de cache local fichier, pas de sticky sessions. Tout état va dans un backing service (Redis pour les sessions, S3 pour les fichiers, PostgreSQL pour les données). Si un pod meurt, un autre prend le relais sans perte.
Facteur VII — Port Binding : l’application est self-contained. Elle expose un port HTTP directement, sans dépendre d’un serveur externe installé séparément (Apache, Tomcat). En conteneur, c’est naturel.
Facteur VIII — Concurrency : on scale horizontalement (plus de processus/pods), pas verticalement (une plus grosse machine). C’est la base du HPA Kubernetes :
# Horizontal Pod Autoscaler — scale de 3 à 20 pods
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "100"
Facteur IX — Disposability : démarrage rapide, arrêt gracieux. Ton application doit gérer SIGTERM proprement — arrêter d’accepter de nouvelles requêtes, finir celles en cours, fermer les connexions, flush les métriques :
import signal, sys
def graceful_shutdown(signum, frame):
print("Shutting down gracefully...")
server.stop_accepting() # Plus de nouvelles requêtes
server.drain(timeout=30) # Finir les requêtes en cours
db.close() # Fermer les connexions DB
metrics.flush() # Envoyer les dernières métriques
sys.exit(0)
signal.signal(signal.SIGTERM, graceful_shutdown)
signal.signal(signal.SIGINT, graceful_shutdown)
⚠️ Attention : En Kubernetes, il y a un piège classique avec le graceful shutdown. Quand un pod est terminé, le Service met un petit moment à le retirer des endpoints. Si ton app s’arrête instantanément sur SIGTERM, des requêtes en vol arrivent sur un pod mort. Solution : ajoute un preStop: sleep 5 pour laisser le temps au load balancer de se mettre à jour.
Les facteurs opérationnels : parité, logs et admin
Facteur X — Dev/Prod Parity : minimise les écarts entre dev et prod. Utilise PostgreSQL en dev (via Docker), pas SQLite. Utilise MinIO en dev, pas le filesystem local. Docker Compose rend ça trivial — les bugs “ça marche sur ma machine” disparaissent quand dev et prod utilisent les mêmes backing services.
Facteur XI — Logs : l’application écrit sur stdout/stderr. Point. C’est l’environnement (le container runtime, Fluentd, OpenTelemetry Collector) qui collecte, route et stocke les logs. Jamais de app.log écrit sur disque.
import logging, json, sys
# Structured logging → stdout (JSON pour parsing automatique)
handler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter(json.dumps({
"timestamp": "%(asctime)s",
"level": "%(levelname)s",
"message": "%(message)s",
"service": "order-service"
}))
handler.setFormatter(formatter)
logger = logging.getLogger()
logger.addHandler(handler)
Facteur XII — Admin Processes : les tâches d’admin (migrations DB, scripts one-off, console REPL) s’exécutent dans le même environnement que l’app, avec le même code et la même config. Pas de SSH en prod. Un Job Kubernetes avec l’image du service :
# Migration DB en pre-sync ArgoCD
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate-v1.2.3
annotations:
argocd.argoproj.io/hook: PreSync
spec:
template:
spec:
containers:
- name: migrate
image: order-service:v1.2.3
command: ["python", "manage.py", "migrate"]
envFrom:
- secretRef:
name: order-service-secrets
restartPolicy: Never
backoffLimit: 3
🔥 Cas réel : Une entreprise e-commerce stockait les sessions utilisateur en mémoire dans ses pods Node.js (violation du Factor VI). Chaque déploiement déconnectait tous les utilisateurs — ce qui générait un pic de “mot de passe oublié” à chaque release du mardi. Migration vers Redis : 2 jours de travail, zéro déconnexion depuis, et le HPA peut enfin scale sans perdre de sessions.
Bonnes pratiques et pièges à éviter
Les quick wins à implémenter en premier :
- Factor III (Config) — C’est le plus impactant et le plus simple. Remplace chaque constante hardcodée par un
os.environ. Crée un.env.exampledocumentant toutes les variables. - Factor XI (Logs) — Supprime tous les
FileHandler. Écris sur stdout en JSON structuré. Laisse l’infra gérer le reste. - Factor VI (Stateless) — Identifie tout état local (sessions, cache fichier, uploads temporaires) et migre-le vers un backing service.
Les pièges classiques :
- Confondre monorepo et violation du Factor I — un monorepo bien structuré (Nx, Turborepo) avec un service par package est parfaitement valide
- Mettre la config dans des fichiers
.yamlembarqués dans l’image Docker — c’est du code déguisé, pas de la config - Ignorer le Factor IX en pensant que “Kubernetes gère” — sans graceful shutdown dans ton code, Kubernetes tue le pod et les requêtes en vol sont perdues
💡 Tip DevOps : Audite tes applications existantes avec un tableau simple — un facteur par ligne, conforme/non-conforme/action requise. C’est un exercice rapide (30 min) qui donne une roadmap claire de modernisation. Commence par les violations les plus impactantes, pas par l’ordre des facteurs.
Résumé
La méthodologie 12-Factor App est le contrat implicite entre ton application et l’infrastructure cloud. Les facteurs I, II et V garantissent un build reproductible. Les facteurs III et IV assurent une configuration portable. Les facteurs VI à IX permettent le scaling et la résilience. Les facteurs X à XII couvrent les opérations quotidiennes. En pratique, commence par la config (III), les logs (XI) et le stateless (VI) — ce sont les trois facteurs qui débloquent le plus de bénéfices cloud-native avec le moins d’effort. Une application 12-factor n’est pas juste “mieux conçue” : elle est deployable sur n’importe quel orchestrateur, scalable à la demande, et opérable sans SSH.
🖥️ Pratique sur ton propre serveur
Pour suivre Platform Engineering en conditions réelles, tu as besoin d'un VPS. DigitalOcean offre 200$ de crédit gratuit pour démarrer.
Série : Platform Engineering
1 / 6Sur cette page
Articles liés
CNCF et architecture microservices
Le paysage CNCF, microservices vs monolithes, et l'architecture microservices complète avec tous les patterns (API Gateway, service mesh, event-driven).
Internal Developer Platform : Backstage et Port
Le Platform Engineering expliqué, l'Internal Developer Platform (IDP), et les outils Backstage (Spotify) et Port pour construire ton portail développeur.
Golden Paths et Developer Experience
Humanitec, Golden Paths pour guider sans contraindre, mesurer la Developer Experience, et guide pratique pour construire ton IDP.