Guide d'ingénierie
Applications Twelve-Factor modernes : guide pratique cloud-native
Utilisez les principes Twelve-Factor comme contraintes de conception opérationnelle, puis ajoutez des pratiques explicites de fiabilité, de sécurité et d'observabilité pour les systèmes distribués.
La méthodologie Twelve-Factor App reste utile pour rendre les services portables, reproductibles et plus simples à exploiter. Ce n'est ni une liste de contrôle Kubernetes ni une certification d'architecture. Kubernetes peut fournir des primitives utiles, mais un déploiement peut toujours comporter une configuration cachée, un état local persistant, un comportement de mise en production dangereux ou des dépendances opaques.
Pour les ingénieurs DevOps et logiciels, l'objectif pratique est plus simple : construire un service configurable par environnement, démarrable plusieurs fois, observable sous charge et récupérable sans dépendre d'une machine ou de la mémoire d'un opérateur. Cette conception améliore la sécurité des mises en production et offre aux équipes des options plus claires lorsque la latence augmente, qu'une région se dégrade ou que le trafic croît en périphérie.
À quoi sert la méthodologie
Les facteurs d'origine couvrent le code source, les dépendances, la configuration, les services externes, la séparation build/release/run, les processus, la liaison de port, la concurrence, la jetabilité, la parité développement-production, les logs et les processus administratifs. Ils répondent à des défaillances récurrentes de livraison :
- Une application se comporte différemment parce que sa configuration est intégrée à son build.
- Un redémarrage perd un état de session ou de travail qu'aucune autre instance ne peut récupérer.
- Un déploiement inclut une dépendance non examinée ou une modification de schéma irréversible.
- Les opérateurs ne peuvent pas relier une requête lente à la dépendance ou à la mise en production qui l'a provoquée.
Considérez les facteurs comme des questions auxquelles répondre lors de la conception et de la revue, et non comme des raisons d'ajouter des outils. Une API sans état peut par exemple rester peu performante si chaque requête traverse de manière synchrone une région distante vers une base de données. La portabilité n'élimine pas les lois de la physique réseau.
Kubernetes est une plateforme d'implémentation, pas un label de conformité
Kubernetes peut planifier des réplicas, injecter de la configuration et redémarrer des conteneurs défaillants. Il ne rend pas automatiquement une application Twelve-Factor. Le code applicatif et les politiques opérationnelles déterminent toujours la gestion de l'état, les délais d'attente des dépendances, les migrations, les logs et l'arrêt progressif.
Appliquer les facteurs à un service moderne
Un code source et des dépendances explicites
Conservez un seul code source versionné pour un service déployable. Produisez des artefacts immuables à partir d'un graphe de dépendances verrouillé, enregistrez la révision source et analysez les dépendances de l'application comme celles de l'image de base. Ne dépendez pas d'un paquet déjà installé sur un nœud, d'un tag d'image mutable ou d'un répertoire source monté localement.
Dans un flux de travail conteneurisé, construisez une fois et promouvez le même digest d'image entre les environnements. Un tag latest mutable empêche de reproduire une mise en production. Générez un SBOM lorsque votre processus de chaîne d'approvisionnement l'exige, et faites des mises à jour de dépendances une activité habituelle plutôt qu'une activité réservée aux urgences.
La configuration appartient hors de l'artefact
Placez les valeurs propres au déploiement, telles que les endpoints, les feature flags, les identifiants et les délais d'attente, dans une configuration spécifique à l'environnement. Conservez les secrets dans un système de gestion des secrets dédié plutôt que dans le contrôle de source ou une couche d'image. Validez la configuration requise au démarrage, sans journaliser les valeurs secrètes en cas d'échec de validation.
La configuration doit également être bornée. Une variable d'environnement arbitraire qui modifie l'exactitude du cache ou l'autorisation sans revue constitue un chemin de code de production. Utilisez une configuration typée, des valeurs par défaut sûres pour l'environnement, une responsabilité définie et un historique des changements. Consultez Configuration et secrets cloud-native pour les détails d'implémentation.
Les services externes sont des ressources attachées
Les bases de données, files d'attente, stockages d'objets, fournisseurs d'identité et API tierces doivent être adressés par la configuration plutôt que par des noms d'hôte codés en dur ou des hypothèses locales. Cela rend la substitution possible, mais ne rend pas chaque dépendance interchangeable. Documentez le modèle de cohérence, les quotas, l'authentification, le comportement en cas de défaillance et le budget de latence de chaque service.
Utilisez des pools de connexions et des clients bornés. Un pool correct pour un pod peut épuiser une base de données lorsqu'un autoscaler horizontal crée cinquante pods. Budgétez les connexions à l'échelle de la flotte, réservez de la capacité pour les migrations et la récupération, et fermez les clients inactifs lors de l'arrêt.
Build, release et run sont des préoccupations distinctes
Le build produit un artefact. Une release associe cet artefact à une configuration examinée et à des décisions de migration. Run démarre la release. Séparez ces étapes afin qu'un incident de production puisse être rattaché à un digest d'image, une version de configuration et un enregistrement de changement.
Évitez de télécharger des dépendances applicatives au démarrage d'un conteneur et d'éditer des conteneurs en cours d'exécution. Utilisez un contrôleur de déploiement, un flux GitOps ou un autre mécanisme auditable pour déclarer la release attendue. Les plans de retour arrière doivent considérer la compatibilité des données : revenir à une version antérieure du code après une migration de schéma destructive peut être dangereux.
Concevoir des processus remplaçables
Exécutez les processus de service comme des workers jetables. Conservez les données de session des requêtes dans un stockage partagé, correctement sécurisé, ou n'encodez côté client qu'un état protégé en intégrité lorsque cela est adapté. N'utilisez pas le système de fichiers d'un pod, une map en mémoire ou le cookie d'affinité d'un load balancer comme seul enregistrement d'une action métier importante.
La jetabilité exige à la fois un démarrage rapide et un arrêt progressif :
- Gérez
SIGTERMet cessez d'accepter de nouveaux travaux. - Maintenez l'endpoint de readiness à false pendant le drainage.
- Terminez ou annulez de manière sûre le travail en cours dans le délai de grâce de terminaison.
- Prolongez la visibilité de la file ou restituez le travail avant la sortie.
- Fermez proprement les connexions de base de données et HTTP.
Les probes Kubernetes de readiness et de liveness ont des objectifs distincts. Une probe de readiness contrôle si un pod reçoit du trafic. Une probe de liveness redémarre un processus considéré comme bloqué. Ne faites pas échouer une probe de liveness uniquement parce qu'une base de données partagée est temporairement indisponible : redémarrer chaque pod peut amplifier une panne externe. Une startup probe est souvent préférable pour une initialisation lente.
Rendre explicites la latence et la montée en charge
Les réplicas horizontaux améliorent le débit uniquement lorsqu'une ressource contrainte ne se trouve pas ailleurs. Avant de monter en charge, identifiez le signal limitant : CPU, mémoire, nombre de connexions, âge de file, saturation de base de données ou latence aval. Un autoscaling basé uniquement sur le CPU peut ajouter des réplicas pendant un incident de dépendance lente, augmentant les tentatives et la pression sur les connexions sans rétablir les requêtes utilisateur.
Définissez une échéance de requête depuis le point d'entrée exposé aux utilisateurs et allouez des budgets plus petits à chaque appel aval. Propagez l'annulation ainsi qu'un contexte de corrélation ou de trace. Pour un parcours interactif, mettez les réponses sûres en cache près des utilisateurs, gardez les appels dynamiques près de leurs données lorsque possible et évitez les chaînes sérielles d'appels distants. Un cache de périphérie peut réduire le travail à l'origine et la latence mondiale pour le contenu pouvant être mis en cache, mais il ne peut pas corriger une dépendance d'origine non bornée.
Les indicateurs de niveau de service utiles incluent la disponibilité, la latence des requêtes réussies, la saturation, le délai de file et le taux d'erreur des dépendances. Instrumentez-les avec des traces, des métriques et des logs structurés avant un incident. Le Google SRE workbook explique comment transformer ces signaux en objectifs de niveau de service et en budgets d'erreur.
Modes de défaillance courants
- Sessions avec état dans un réplica : un redémarrage ou un événement de mise à l'échelle déconnecte les utilisateurs ou perd des transactions. Déplacez l'état durable vers un service partagé et rendez les opérations idempotentes.
- Variables d'environnement traitées comme un coffre-fort : des manifests de déploiement divulgués, une sortie de débogage ou des autorisations de lecture étendues exposent des identifiants. Utilisez une distribution de secrets suivant le moindre privilège et une rotation.
- Un endpoint de readiness pour tout : un incident de dépendance transitoire retire toute la capacité. Vérifiez si cette instance peut servir la route, puis utilisez un circuit breaker et un comportement dégradé pour les dépendances optionnelles.
- Tentatives illimitées : les tentatives augmentent le trafic précisément quand une dépendance est surchargée. Définissez des échéances, ne réessayez que les opérations sûres, utilisez un backoff exponentiel avec jitter et imposez un budget de tentatives.
- Logs uniquement dans le système de fichiers d'un conteneur : les éléments de preuve disparaissent lors du replanification. Émettez des événements structurés vers un pipeline de collecte durable avec des identifiants de trace et de requête.
Une séquence d'adoption pratique
Choisissez un service critique et cartographiez sa configuration de démarrage, son état durable, ses dépendances, son chemin de release, son budget de latence et son comportement d'arrêt. Corrigez d'abord l'hypothèse la plus dangereuse, généralement une configuration cachée, un état local ou un client de dépendance non borné. Testez ensuite le remplacement d'un pod, un délai d'attente de dépendance, un retour arrière et une augmentation du trafic dans un environnement réaliste.
Intégrez le résultat à la définition de terminé du service : un build épinglé, un contrat de configuration documenté, des probes, un arrêt progressif, des budgets de dépendances, de la télémétrie et une stratégie de release réversible. Réévaluez-le lorsque le service ajoute une région, une file d'attente, un fournisseur externe ou une route servie en périphérie.
Références faisant autorité
- The Twelve-Factor App
- Kubernetes: Container lifecycle hooks and termination
- Kubernetes: Probes
- Google SRE Workbook
- OpenTelemetry documentation
Reliez la conception applicative à l'architecture de livraison
Échangez avec Optimi sur des modèles de livraison résilients en périphérie et à l'origine, qui favorisent une latence prévisible et l'isolation des défaillances.
Discuter de l'architecture de performance