1 points par GN⁺ 2023-10-13 | 1 commentaires | Partager sur WhatsApp
  • The Twelve-Factor App est une méthodologie conçue pour exploiter et faire évoluer sur le long terme des applications web et des SaaS, en couvrant à la fois l’automatisation de la configuration, la portabilité, le déploiement cloud et le déploiement continu
  • Elle n’est liée ni à un langage de programmation précis ni à une combinaison donnée de services annexes comme une base de données, une file de messages ou un cache mémoire, ce qui la rend applicable à divers types d’applications de service
  • Elle s’appuie sur une expérience acquise en participant directement au développement et au déploiement de centaines d’applications sur la plateforme Heroku, ainsi qu’en observant indirectement le développement, l’exploitation et la montée en charge de plusieurs centaines de milliers d’applications
  • L’idée centrale est de fournir un vocabulaire commun pour réduire les coûts de collaboration et l’érosion logicielle qui apparaissent lorsqu’une application croît de manière organique
  • Elle peut servir de référence pratique non seulement aux développeurs qui créent des applications de service, mais aussi aux ingénieurs d’exploitation chargés de les déployer et de les administrer

12 principes d’exploitation pour les applications SaaS

  • Les logiciels modernes sont souvent proposés sous la forme d’applications web ou de SaaS, et The Twelve-Factor App est une méthodologie destinée à construire ce type d’applications
  • L’objectif est de rendre le développement, le déploiement et l’exploitation des applications plus prévisibles
    • utiliser une automatisation déclarative de la configuration pour réduire le temps et le coût d’intégration des nouveaux développeurs au projet
    • établir un contrat clair avec le système d’exploitation sous-jacent afin d’améliorer la portabilité entre environnements d’exécution
    • concevoir l’application de manière à réduire la charge liée aux serveurs et à l’administration système, et à s’adapter au déploiement sur les plateformes cloud modernes
    • réduire l’écart entre l’environnement de développement et l’environnement de production afin de permettre le déploiement continu
    • permettre la montée en charge sans modifier en profondeur les outils, l’architecture ou les pratiques de développement
  • Le champ d’application n’est pas limité à une pile technologique particulière
    • elle peut s’appliquer à toute application, quel que soit le langage de programmation utilisé
    • les services annexes incluent les bases de données, les files de messages, les caches mémoire, etc.

Un cadre issu de l’expérience Heroku

  • Les contributeurs ont participé directement au développement et au déploiement de centaines d’applications sur la plateforme Heroku, et ont observé indirectement le développement, l’exploitation et la montée en charge de plusieurs centaines de milliers d’applications
  • Ils ont formalisé les pratiques idéales de développement applicatif à partir de l’expérience et des observations recueillies sur de véritables applications SaaS
    • une attention particulière est portée à la manière dont les applications croissent de manière organique au fil du temps
    • le texte traite de la collaboration de plusieurs développeurs sur une même base de code
    • l’évitement du coût de l’érosion logicielle est considéré comme un objectif important
  • La forme s’inspire de Patterns of Enterprise Application Architecture et de Refactoring de Martin Fowler

Les 12 facteurs

  • I. Codebase : une base de code versionnée unique, avec plusieurs déploiements
  • II. Dependencies : déclarer et isoler explicitement les dépendances
  • III. Config : stocker la configuration dans l’environnement
  • IV. Backing services : traiter les services annexes comme des ressources attachées
  • V. Build, release, run : séparer strictement les étapes de build, de release et d’exécution
  • VI. Processes : exécuter l’application sous forme d’un ou plusieurs processus sans état
  • VII. Port binding : exposer les services via le port binding
  • VIII. Concurrency : faire du scale-out via le modèle de processus
  • IX. Disposability : améliorer la robustesse grâce à un démarrage rapide et un arrêt propre
  • X. Dev/prod parity : garder le développement, la préproduction et la production aussi proches que possible
  • XI. Logs : traiter les logs comme un flux d’événements
  • XII. Admin processes : exécuter les tâches d’administration sous forme de processus ponctuels

1 commentaires

 
GN⁺ 2023-10-13
Avis sur Hacker News
  • 12-Factor App est une recommandation élaborée en 2011 en s’appuyant davantage sur Heroku et sur les limites de l’infrastructure conteneurisée de l’époque, et ne ressemble pas à un document profondément fondé sur des principes d’ingénierie.
    Par exemple, l’idée de mettre la configuration dans des variables d’environnement vient du fait que les auteurs travaillaient chez Heroku, et que Heroku remplissait les variables d’environnement via des champs de saisie dans l’interface des apps web.
    Si l’on veut suivre l’historique de la configuration avec le versioning, utiliser GitOps, utiliser des ConfigMap k8s, ou placer des fichiers de configuration sur un volume monté, ce sont globalement de bons choix. Parce qu’ils séparent l’état de la configuration de l’état de déploiement de l’application.
    Ce document confond la forêt et les arbres, et recommande surtout des pratiques alignées sur les fonctionnalités produit de l’entreprise qui l’a rédigé plutôt que sur de vrais principes d’ingénierie ; je le considère donc comme un guide nocif.

    • Il est vrai que cela a été influencé dans une certaine mesure par le fonctionnement de Heroku, mais ConfigMap et GitOps ne satisfont pas de la même manière les exigences de sécurité et d’ergonomie que la configuration/les variables d’environnement de Heroku.
      Si l’on veut stocker de la configuration de manière sûre dans Kubernetes, on finit par utiliser des Secrets, qui sont alors sous forme clé-valeur comme les variables d’environnement. Pour obtenir une sécurité similaire dans Git, il faut une couche de chiffrement, ce qui casse les diffs et nécessite des outils supplémentaires.
      Au final, on revient à la raison pour laquelle des outils de déploiement de haut niveau comme Heroku ont été créés.
    • Aujourd’hui, plus personne n’appelle vraiment cela 12 factor, mais grâce à lui, les principes généraux restent suivis. 12-Factor était un document d’avant la généralisation de Docker et Kubernetes.
      Le principe selon lequel les logs doivent être traités comme des flux reste valable. Il suffit d’écrire les logs sur STDOUT plutôt que dans un fichier, et de laisser l’orchestrateur les lire et les stocker.
      La configuration vient de l’environnement. Selon le mode de déploiement, les apps ont tendance à lire leur configuration depuis différentes sources : un .env en local, un coffre de secrets en production, etc.
      Même chose pour le port binding : l’application ouvre un port, puis on place quelque chose comme nginx devant pour configurer un reverse proxy. Les services et ingress K8S jouent ce rôle.
      La plus grande critique que l’on puisse faire aujourd’hui à 12-Factor, c’est que le document n’est pas très bien rédigé en pratique et qu’il suppose que le lecteur sait déjà exactement de quoi il parle.
    • Heroku n’a pas inventé les variables d’environnement, ni l’idée de les utiliser pour configurer une application. C’était une pratique ancienne, bien antérieure à Heroku.
      Cela dit, il faut reconnaître que Heroku a popularisé ce concept et en a diffusé l’usage.
    • Dire « stocker la configuration dans l’environnement » ne signifie pas forcément mettre la configuration dans des variables d’environnement. Cela signifie que la configuration vient de l’environnement d’hébergement, et non de l’application elle-même.
      Ce n’est pas ajouter un settings.json à la machine avant de lancer l’application ; c’est plutôt que, si le même code source est déployé sur un cluster AKS dans Azure EU north, il utilise les valeurs configurées dans ce cluster, et s’il est déployé sur un cluster Docker Swarm tournant sur des RPi Zero dans un cadre Ikea, il utilise la configuration de ce cluster.
      Ce cluster s’appelle Gibson.
    • Les apps 12-Factor qui mettaient la configuration dans des variables d’environnement étaient trivialement migrables vers des ConfigMap. À l’inverse, les applications métier d’aujourd’hui avec des layouts ConfigMap complexes, voire dans le pire des cas des dépendances directes à l’API k8s, vont vivre un enfer lorsqu’il faudra migrer vers ce qui viendra après k8s.
  • J’ai l’impression que chacun de ces points peut être réfuté de manière assez raisonnable.
    Premièrement, le principe « une application, un dépôt » n’est pas fondamentalement faux. Il n’y a pas de problème à développer dans un même dépôt plusieurs applications fortement couplées fonctionnellement et partageant un cycle de release, mais qui doivent être déployées séparément afin de bénéficier des avantages de processus distincts et d’un scaling indépendant. Je pense par exemple à la séparation entre une API publique et des workers, comme Sidekiq en Ruby, Celery en Python, ou des consommateurs Kafka classiques.
    Deuxièmement, l’idée selon laquelle « une app 12-Factor ne dépend pas de l’existence implicite de packages installés globalement sur le système » est très difficile à atteindre en pratique sans utiliser quelque chose comme Nix. Les dépendances à l’API des appels système du noyau fuient aussi, et la plupart des apps Rust dépendent implicitement de glibc, sauf avec musl. En dehors des distributions slim comme Alpine, elle est présente dans les principales distributions Linux. Je vois aussi Docker comme une béquille nécessaire pour atténuer ce problème.
    Troisièmement, stocker la configuration dans des variables d’environnement me semble plus fragile. Forcer les secrets à être placés dans l’environnement peut dégrader la sécurité, et cela fait renoncer à une configuration structurée dans des fichiers, avec sûreté de typage, autocomplétion IDE et parsing automatique. Avec les variables d’environnement, il faut implémenter soi-même des parseurs pour les valeurs de configuration complexes qui ne sont pas de simples chaînes. En pratique, elles sont aussi souvent stockées sous forme de fichiers .env committés dans le dépôt, ce qui rend également caduc l’argument de la sécurité des commits.

    • C’est une mauvaise interprétation de cette recommandation. Le texte original parle des bibliothèques de support installées via des systèmes de packaging propres aux langages, comme CPAN ou Rubygems, et explique que ces bibliothèques peuvent être installées comme “site packages” globalement sur le système.
      Cela ne veut pas dire qu’il ne faut pas dépendre du système d’exploitation, y compris glibc, mais qu’il ne faut pas rendre l’application dépendante de la présence sur la machine d’un package installé par un gestionnaire de paquets propre à un langage.
      Je suis d’accord avec le reste des remarques. En particulier, l’histoire des variables d’environnement ressemble moins à un conseil réellement fondé qu’à une pratique courante du développement Ruby de l’époque que les auteurs ont supposée être la meilleure.
    • Si c’est déployé séparément, alors cela ne partage pas le même cycle de release. À un moment ou à un autre, une combinaison de versions incompatibles peut finir par tourner. Par exemple, un seul morceau peut échouer à se déployer.
      Il faut donc se préparer à cette situation, et pour tester facilement différentes combinaisons de versions, je pense que des dépôts séparés sont préférables.
      La plupart des langages de haut niveau ne dépendent pas d’une glibc spécifique. Si le runtime du langage fonctionne correctement, l’application fonctionne dessus. Bien sûr, dans certains cas, on finit par utiliser quelque chose comme Docker. Le fait que ce soit difficile ne signifie pas que cela n’a pas de valeur.
    • Si c’est le point fondamental, je ne suis pas d’accord. Je pense qu’une plus grande partie du monde est d’accord avec moi et avec les principes 12-Factor.
    • Je suis d’accord avec ce point de vue sur la première règle, et je mettrais volontiers plusieurs applications liées dans un même dépôt. Comme aucune justification n’est donnée pour expliquer pourquoi un dépôt ne devrait contenir qu’une seule application, on peut ignorer cette règle.
      Les langages dynamiques ou managés permettent souvent d’ignorer la plupart des tracas liés aux packages système.
    • L’utilité du monorepo suit une courbe en U. La plupart des projets se situent quelque part au milieu, en particulier ceux gérés par des équipes d’ingénierie indépendantes qui n’ont pas d’équipe centralisée plateforme/DevOps/quel que soit le nom qu’on lui donne aujourd’hui.
  • Globalement, j’aime bien, mais des non-techniciens ou des personnes à moitié techniques ont tellement souvent dégainé le « 12 factor » comme un carton jaune universel pour retarder des releases que j’ai fini par l’ignorer presque complètement
    En réalité, « agile » a connu un sort similaire. Je comprends l’intention de ce genre de lignes directrices, mais leur valeur réelle semble bien plus utile aux personnes qui ne peuvent offrir qu’un leadership technique de tour d’ivoire

    • Par principe, l’ignorer totalement est aussi mauvais que de le traiter comme un dogme obligatoire
      J’ai déjà vu des ingénieurs juniors trop zélés ou des aspirants architectes utiliser l’article sur le 12-Factor comme une exigence indispensable pour chaque mise en production
      Ce sont de bons objectifs à poursuivre, mais il faut expliquer fermement et systématiquement que, dans la réalité, il faut faire des compromis pour sortir une release et choisir quels aspects ralentir ou repousser
    • On appelle ça un dogme instrumentalisé. Cela dit, je trouve ça quand même préférable à ceux qui s’accrochent à DRY au point de créer des fonctions helper génériques incompréhensibles pour économiser 3 ou 4 lignes de code dupliqué
    • Il faut donner des exemples. Comme la plupart des choses, c’est dépendant du contexte et plein de zones grises, mais je pense que la majorité des développeurs utilisent le 12-Factor comme une étoile polaire
      On ne va pas bloquer une release pour de petits écarts, mais si l’ensemble n’est pas aligné, il faut au minimum le traiter comme de la dette technique. Si une release régresse fortement sur un point, il est légitime de la bloquer, ou au moins d’imposer un examen plus approfondi des raisons pour lesquelles ce compromis a été jugé valable
    • Aucun dogme ne devrait empêcher le déploiement dans le périmètre d’un MVP. Une fois sorti du MVP, tout ce qui n’a pas été traité du point de vue des bonnes pratiques devient de la dette technique
      Si une organisation a adopté 12FA comme bonne pratique, il faut s’y conformer, mais cela ne doit pas bloquer le déploiement
      12FA n’est pas une case unique à cocher. À mesure que le produit mûrit, on peut, et dans la plupart des cas on devrait, l’implémenter en ajoutant chaque élément un par un au produit, voire en les découpant davantage si nécessaire
      Si une bonne ingénierie a été faite au départ, c’est-à-dire avec des abstractions et interfaces appropriées sans tout hardcoder, cela ne devrait pas poser problème
      YAGNI est tout autant détourné que 12FA. Ce qui manque le plus à 12FA, ce sont des exemples concrets auxquels les ingénieurs juniors pourraient se référer
    • J’ai déjà rencontré ce problème, et la solution a été de mettre en place un processus clairement documenté puis d’y renvoyer
      J’ai appris à considérer ce genre de problèmes de bonne foi. Il faut comprendre pourquoi la question est soulevée, si le processus existant est flou, défectueux ou manque de fiabilité
      Si des préoccupations raisonnables apparaissent, il suffit de modifier le processus et de mettre la documentation à jour
  • The Twelve-Factor App recommande d’utiliser l’environnement pour la configuration, tandis que Docker dit de ne pas utiliser l’environnement pour la configuration, parce que ce n’est pas sûr
    J’aime et j’utilise beaucoup de patterns du 12-Factor, mais certains ont été pensés dans un contexte VPS. À l’époque, l’environnement était stable, sûr et plus figé, alors qu’avec les conteneurs, l’environnement peut se retrouver embarqué dans n’importe quelle couche
    À l’ère des conteneurs, ce point précis a été une vraie source de réflexion. Les Docker secrets ne conviennent pas toujours non plus, et il faut parfois pas mal d’acrobaties pour que ça marche

    • Tu peux détailler ? Je me demande en quoi les variables d’environnement ne sont pas sûres. Si des secrets doivent exister pendant toute la durée de vie de l’application, je ne vois pas vraiment quelle serait une meilleure alternative
      Injecter des secrets dans l’application via des variables d’environnement ne veut pas dire que tout le traitement autour est lui aussi non sécurisé. Par exemple, les conteneurs AWS ECS ont une prise en charge intégrée pour récupérer des secrets depuis Secret Manager au démarrage et les transmettre sous forme de variables d’environnement : https://docs.aws.amazon.com/AmazonECS/latest/developerguide/...
      Les secrets sont récupérés au démarrage du conteneur, depuis Secret Manager, avec les identifiants IAM de l’application en cours d’exécution. Il faut donc disposer des droits sur ces secrets
      L’intérêt des variables d’environnement semble donc dépendre entièrement de la manière dont elles sont finalement définies. Avec ce type de mécanisme, je ne vois pas de gros inconvénient
      Le principal inconvénient visible est qu’un malware générique cherchant à dumper les variables d’environnement peut capturer les valeurs, mais si l’on ne va pas jusqu’à éviter de stocker les secrets durablement en mémoire, se protéger contre cette menace relève surtout de l’obfuscation
    • À l’inverse, sans prise en charge par la plateforme, il n’y a pas beaucoup d’alternatives simples. Au minimum, cela encourage la séparation de la configuration et du code, et permet de garder les secrets hors du système de gestion de versions
    • C’est pour ce genre de raison que je n’ai jamais aimé mettre des secrets dans des variables d’environnement. Des consoles de debug mal fichues ont exposé accidentellement des variables d’environnement étonnamment souvent
      Il faut vraiment éviter que des clés privées soient exposées de cette manière
      Monter des secrets dans le système de fichiers avec k8s ne me pose pas vraiment de problème. Bien sûr, tout cela dépend de l’environnement de déploiement
      Parfois, les variables d’environnement sont l’option la moins mauvaise. Les secrets sont toujours difficiles par nature
    • On pourrait soutenir qu’aucune entrée ne devrait être considérée comme « sûre à consommer ». Si l’on écrit le code applicatif de manière défensive, on ne devrait jamais supposer que ENV est sûr ou a la forme attendue, et il faut le nettoyer avant de l’utiliser
      Faire aveuglément confiance à une entrée au motif qu’on est en mono-tenant conduit, si les conditions changent plus tard à cause d’un virage métier arbitraire, à des bugs difficiles à retracer et à de longues nuits
    • Avec le recul, le titre de cette section aurait mieux été séparer la configuration du code, comme dans le premier paragraphe
  • Ces dernières années, j’ai beaucoup discuté des apps 12-Factor et j’ai aussi vu beaucoup de confusion. Le site 12-Factor est excellent, mais il s’adresse surtout aux personnes qui comprennent déjà pourquoi ces points sont importants.
    Pour celles qui ne connaissent pas les raisons derrière les règles, il fallait une explication plus approfondie. J’ai donc réalisé la vidéo « What are 12 Factor Apps and Why Should You Care? »[1], et plusieurs entreprises m’ont dit qu’elle leur avait été très utile pour former leurs nouvelles recrues ingénieurs/DevOps.
    Quel que soit l’endroit où on l’apprend, cela vaut la peine de consacrer une ou deux heures à étudier les apps 12-Factor. La plupart des « règles » sont des choses qui demandent une prise de conscience, et elles ne sont pas immédiatement évidentes tant qu’on n’a pas fait soi-même des erreurs et souffert de leurs conséquences.
    [1] https://youtu.be/REbM4BDeua0

    • C’est comparable aux tests unitaires. L’idée d’écrire du code qui teste du code m’a paru tellement évidente dès mes débuts en programmation que j’ai été surpris de voir combien de temps il a fallu aux autres pour l’adopter.
      Le concept de « tests unitaires » a commencé à gagner en traction vers la fin des années 90, et j’ai été soulagé qu’une personne faisant autorité le défende.
      Si je ne l’ai pas fait moi-même avant que Kent Beck ne publie JUnit, c’est parce que le code sur lequel je travaillais n’était pas structuré de façon à pouvoir être facilement piloté par un autre code. Variables globales, état disséminé partout, dépendances à des systèmes externes et à une disposition précise du système de fichiers, absence de modularité : rien ne pouvait être exécuté en dehors du contexte pour lequel il avait été conçu.
      Tout cela relevait d’une « mauvaise conception », mais les délais étaient tenus, donc tout le monde faisait ainsi. J’espérais que, si les tests unitaires gagnaient en traction, les programmeurs s’éloigneraient des conceptions monolithiques.
      Après 25 ans à voir des tests unitaires pour des getter/setter et un énorme test unitaire qui crée une base de données en mémoire parce que chaque fonction de l’app a besoin d’une base de données live rien que pour s’exécuter, puis finit par échouer et être commenté, j’ai perdu la conviction que les tests unitaires deviendraient autre chose qu’une case à cocher vide de sens. Tout le monde la coche parce que c’est une « bonne pratique », sans jamais s’arrêter pour réfléchir à pourquoi on le fait.
  • Je n’ai jamais vraiment été d’accord avec les conseils sur la configuration. La configuration finit par être définie par plusieurs acteurs, souvent aussi par les développeurs, et il est donc souvent préférable d’embarquer des valeurs par défaut raisonnables avec l’application, puis de les surcharger via des fichiers propres à chaque environnement et des variables d’environnement.
    Pour la plupart des applications côté serveur, c’est l’approche la plus flexible. On sait souvent déjà quelle doit être une partie de la configuration, et il vaut mieux qu’elle soit versionnée, tandis que les secrets doivent être injectés à l’exécution.
    Pour éviter de passer énormément de temps à configurer le développement, les tests et la production, la configuration a besoin de surcharges hiérarchiques.

    • La question est de savoir si les « valeurs par défaut raisonnables » sont des valeurs par défaut de développement ou de production.
      Si elles sont prévues pour le développement, elles finiront par casser la production ; et des valeurs par défaut de production peuvent n’avoir aucun sens en développement.
    • Le contexte dans lequel The Twelve-Factor App a été écrit incluait la possibilité de basculer de mode avec des variables d’environnement comme RAILS_ENV=test.
      Une façon de penser la section configuration est : « cette stratégie de configuration fonctionne-t-elle bien avec les conteneurs ? ». Quand on construit une image, on obtient un état disque statique dont les changements ne persistent pas, sauf à créer une nouvelle image.
      Si la configuration repose uniquement sur des fichiers, il faut construire une toute nouvelle image pour basculer entre le comportement de test et celui de production.
      Pouvoir modifier la configuration indépendamment du disque de base aide à isoler les changements. Il faut pouvoir distinguer si l’app est cassée parce que le déploiement, c’est-à-dire la création de l’image, est cassé, ou parce que la configuration est incorrecte.
      En séparant la création de l’image des changements de configuration, cette question disparaît.
    • Si vous empêchez systématiquement une instance QA de se connecter aux ressources de production, il peut être acceptable de déployer l’essentiel de la configuration avec l’app.
      Par exemple, il ne faut pas permettre l’erreur humaine tout à fait prévisible consistant, depuis la QA, à envoyer 100 000 tentatives de refus d’authentification vers la file de soumission des tâches de production.
      Cela dit, beaucoup de configuration ne concerne pas l’infrastructure, mais plutôt des choses comme le bean ResolverStrategy à câbler dans chaque environnement.
    • On peut définir des valeurs par défaut raisonnables, et on devrait probablement le faire.
      La configuration doit être versionnée, mais gérée séparément du code source. En effet, la configuration ne décrit pas l’image utilisée pour le déploiement, mais le déploiement lui-même.
    • Je ne vois pas pourquoi les valeurs par défaut ne pourraient pas elles aussi être exprimées comme de la configuration. Si c’est ce qui était voulu, je ne pense pas que cela contredise 12-Factor.
  • C’était clairement une norme d’ingénierie influente. Il existe aujourd’hui beaucoup d’abstractions simples pour l’hébergement, comme Render ou Vercel, mais il est un peu étrange de se rappeler que ce document a été écrit en 2012 et qu’à l’époque, les apps web ressemblaient beaucoup plus à un Far West en matière de pratiques communes acceptées.

  • Ce qui manque largement dans ce document, c’est la justification des règles. Presque tout n’est que règles.
    Il est difficile de juger si elles sont bonnes, et ce document n’aide pas à le déterminer.

  • En voyant seulement le titre, j’ai cru que c’était un commentaire sur l’authentification à deux facteurs. Du genre une app qui exige, pour une seule connexion, une photo de passeport, un scan du visage, un permis de conduire, un SMS, Google Authenticator, un lien par e-mail, un mot de passe et une empreinte digitale.

    • Ça ressemble à la plateforme d’échange de cryptomonnaies centralisée moyenne, non ?
    • C’est à peu près ce que la plateforme moyenne de trading d’actions ou de cryptomonnaies demande avant d’autoriser une transaction.
  • Aux débuts de Docker, j’ai fait pas mal de travail pour faire en sorte que WordPress se comporte comme une Twelve-Factor App.
    Traditionnellement, WordPress ne fonctionnait pas comme ça, et c’est assez compréhensible. WordPress a grandi dans un monde où les serveurs longue durée, dotés d’un disque local persistant et accessible en écriture, étaient courants.
    Les choses ont probablement beaucoup changé depuis. C’était vers 2016, mais c’était un défi vraiment intéressant.

    • Je me souviens du moment où j’ai appris à rendre les serveurs sans état, par exemple en stockant les informations de session dans une base de données et en n’écrivant pas sur le disque.
      J’ai été surpris de voir à quel point cela simplifiait les choses, et on pouvait répartir la charge sur plusieurs nœuds sans se soucier de l’affinité de session.
      Bien sûr, cela rendait d’autres aspects plus difficiles, comme le besoin d’une base de données distincte pour les sessions.