Structure 12 factors
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Fred Pauchet 2022-04-12 19:56:25 +02:00
parent 7e6ea730de
commit 02930bd52f
2 changed files with 325 additions and 318 deletions

View File

@ -1,4 +1,4 @@
\hypertarget{_fiabilituxe9_uxe9volutivituxe9_et_maintenabilituxe9}{%
\section{Fiabilité, évolutivité et
maintenabilité}\label{_fiabilituxe9_uxe9volutivituxe9_et_maintenabilituxe9}}
@ -6,324 +6,7 @@ maintenabilité}\label{_fiabilituxe9_uxe9volutivituxe9_et_maintenabilituxe9}}
\hypertarget{_12_facteurs}{%
\subsection{12 facteurs}\label{_12_facteurs}}
Pour la méthode de travail et de développement, nous allons nous baser
sur les \href{https://12factor.net/fr/}{The Twelve-factor App} - ou plus
simplement les \textbf{12 facteurs}.
L'idée derrière cette méthode, et indépendamment des langages de
développement utilisés, consiste à suivre un ensemble de douze concepts,
afin de:
\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\item
\textbf{Faciliter la mise en place de phases d'automatisation}; plus
concrètement, de faciliter les mises à jour applicatives, simplifier
la gestion de l'hôte, diminuer la divergence entre les différents
environnements d'exécution et offrir la possibilité d'intégrer le
projet dans un processus
d'\href{https://en.wikipedia.org/wiki/Continuous_integration}{intégration
continue} ou
\href{https://en.wikipedia.org/wiki/Continuous_deployment}{déploiement
continu}
\item
\textbf{Faciliter la mise à pied de nouveaux développeurs ou de
personnes souhaitant rejoindre le projet}, dans la mesure où la mise à
disposition d'un environnement sera grandement facilitée.
\item
\textbf{Minimiser les divergences entre les différents environnemens
sur lesquels un projet pourrait être déployé}
\item
\textbf{Augmenter l'agilité générale du projet}, en permettant une
meilleure évolutivité architecturale et une meilleure mise à l'échelle
- \emph{Vous avez 5000 utilisateurs en plus? Ajoutez un serveur et on
n'en parle plus ;-)}.
\end{enumerate}
En pratique, les points ci-dessus permettront de monter facilement un
nouvel environnement - qu'il soit sur la machine du petit nouveau dans
l'équipe, sur un serveur Azure/Heroku/Digital Ocean ou votre nouveau
Raspberry Pi Zéro caché à la cave - et vous feront gagner un temps
précieux.
Pour reprendre de manière très brute les différentes idées derrière
cette méthode, nous avons:
\textbf{\#1 - Une base de code unique, suivie par un système de contrôle
de versions}.
Chaque déploiement de l'application se basera sur cette source, afin de
minimiser les différences que l'on pourrait trouver entre deux
environnements d'un même projet. On utilisera un dépôt Git - Github,
Gitlab, Gitea, \ldots\hspace{0pt} Au choix.
\includegraphics{images/diagrams/12-factors-1.png}
Comme l'explique Eran Messeri, ingénieur dans le groupe Google Developer
Infrastructure, un des avantages d'utiliser un dépôt unique de sources,
est qu'il permet un accès facile et rapide à la forme la plus à jour du
code, sans aucun besoin de coordination. \footnote{The DevOps Handbook,
Part V, Chapitre 20, Convert Local Discoveries into Global
Improvements} Ce dépôt ne sert pas seulement au code source, mais
également à d'autres artefacts et formes de connaissance:
\begin{itemize}
\item
Standards de configuration (Chef recipes, Puppet manifests,
\ldots\hspace{0pt})
\item
Outils de déploiement
\item
Standards de tests, y compris tout ce qui touche à la sécurité
\item
Outils de déploiement de pipeline
\item
Outils d'analyse et de monitoring
\item
Tutoriaux
\end{itemize}
\textbf{\#2 - Déclarez explicitement les dépendances nécessaires au
projet, et les isoler du reste du système lors de leur installation}
Chaque installation ou configuration doit toujours être faite de la même
manière, et doit pouvoir être répétée quel que soit l'environnement
cible.
Cela permet d'éviter que l'application n'utilise une dépendance qui soit
déjà installée sur un des sytèmes de développement, et qu'elle soit
difficile, voire impossible, à répercuter sur un autre environnement.
Dans notre cas, cela pourra être fait au travers de
\href{https://pypi.org/project/pip/}{PIP - Package Installer for Python}
ou \href{https://python-poetry.org/}{Poetry}.
Mais dans tous les cas, chaque application doit disposer d'un
environnement sain, qui lui est assigné, et vu le peu de ressources que
cela coûte, il ne faut pas s'en priver.
Chaque dépendance pouvant être déclarée et épinglée dans un fichier, il
suffira de créer un nouvel environment vierge, puis d'utiliser ce
fichier comme paramètre pour installer les prérequis au bon
fonctionnement de notre application et vérifier que cet environnement
est bien reproductible.
Il est important de bien "épingler" les versions liées aux dépendances
de l'application. Cela peut éviter des effets de bord comme une nouvelle
version d'une librairie dans laquelle un bug aurait pu avoir été
introduit. Parce qu'il arrive que ce genre de problème apparaisse, et
lorsque ce sera le cas, ce sera systématiquement au mauvais moment.
\textbf{\#3 - Sauver la configuration directement au niveau de
l'environnement}
Nous voulons éviter d'avoir à recompiler/redéployer l'application parce
que:
\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\item
l'adresse du serveur de messagerie a été modifiée,
\item
un protocole a changé en cours de route
\item
la base de données a été déplacée
\item
\ldots\hspace{0pt}
\end{enumerate}
En pratique, toute information susceptible de modifier un lien vers une
ressource annexe doit se trouver dans un fichier ou dans une variable
d'environnement, et doit être facilement modifiable. En allant un pas
plus loin, ceci de paramétrer facilement un environnement (par exemple,
un container), simplement en modifiant une variable de configuration qui
spécifierait la base de données sur laquelle l'application devra se
connecter.
Toute clé de configuration (nom du serveur de base de données, adresse
d'un service Web externe, clé d'API pour l'interrogation d'une
ressource, \ldots\hspace{0pt}) sera définie directement au niveau de
l'hôte - à aucun moment, nous ne devons trouver un mot de passe en clair
dans le dépôt source ou une valeur susceptible d'évoluer, écrite en dur
dans le code.
Au moment de développer une nouvelle fonctionnalité, réfléchissez si
l'un des composants utilisés risquerait de subir une modification: ce
composant peut concerner une nouvelle chaîne de connexion, un point de
terminaison nécessaire à télécharger des données officielles ou un
chemin vers un répertoire partagé pour y déposer un fichier.
\textbf{\#4 - Traiter les ressources externes comme des ressources
attachées}
Nous parlons de bases de données, de services de mise en cache, d'API
externes, \ldots\hspace{0pt} L'application doit être capable d'effectuer
des changements au niveau de ces ressources sans que son code ne soit
modifié. Nous parlons alors de \textbf{ressources attachées}, dont la
présence est nécessaire au bon fonctionnement de l'application, mais
pour lesquelles le \textbf{type} n'est pas obligatoirement défini.
Nous voulons par exemple "une base de données" et "une mémoire cache",
et pas "une base MariaDB et une instance Memcached". De cette manière,
les ressources peuvent être attachées et détachées d'un déploiement à la
volée.
Si une base de données ne fonctionne pas correctement (problème matériel
?), l'administrateur pourrait simplement restaurer un nouveau serveur à
partir d'une précédente sauvegarde, et l'attacher à l'application sans
que le code source ne soit modifié. une solution consiste à passer
toutes ces informations (nom du serveur et type de base de données, clé
d'authentification, \ldots\hspace{0pt}) directement \emph{via} des
variables d'environnement.
\includegraphics{images/12factors/attached-resources.png}
Nous serons ravis de pouvoir simplement modifier une chaîne
\texttt{sqlite:////tmp/my-tmp-sqlite.db\textquotesingle{}} en
\texttt{psql://user:pass@127.0.0.1:8458/db} lorsque ce sera nécessaire,
sans avoir à recompiler ou redéployer les modifications.
\textbf{\#5 - Séparer proprement les phases de construction, de mise à
disposition et d'exécution}
\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\item
La \textbf{construction} (\emph{build}) convertit un code source en un
ensemble de fichiers exécutables, associé à une version et à une
transaction dans le système de gestion de sources.
\item
La \textbf{mise à disposition} (\emph{release}) associe cet ensemble à
une configuration prête à être exécutée,
\item
tandis que la phase d'\textbf{exécution} (\emph{run}) démarre les
processus nécessaires au bon fonctionnement de l'application.
\end{enumerate}
\includegraphics{images/12factors/release.png}
Parmi les solutions possibles, nous pourrions nous pourrions nous baser
sur les \emph{releases} de Gitea, sur un serveur d'artefacts ou sur
\href{https://fr.wikipedia.org/wiki/Capistrano_(logiciel)}{Capistrano}.
\textbf{\#6 - Les processus d'exécution ne doivent rien connaître ou
conserver de l'état de l'application}
Toute information stockée en mémoire ou sur disque ne doit pas altérer
le comportement futur de l'application, par exemple après un redémarrage
non souhaité.
Pratiquement, si l'application devait rencontrer un problème, l'objectif
est de pouvoir la redémarrer rapidement sur un autre serveur (par
exemple suite à un problème matériel). Toute information qui aurait été
stockée durant l'exécution de l'application sur le premier hôte serait
donc perdue. Si une réinitialisation devait être nécessaire,
l'application ne devra pas compter sur la présence d'une information au
niveau du nouveau système. La solution consiste donc à jouer sur les
variables d'environnement (cf. \#3) et sur les informations que l'on
pourra trouver au niveau des ressources attachées (cf \#4).
Il serait également difficile d'appliquer une mise à l'échelle de
l'application, en ajoutant un nouveau serveur d'exécution, si une donnée
indispensable à son fonctionnement devait se trouver sur la seule
machine où elle est actuellement exécutée.
\textbf{\#7 - Autoriser la liaison d'un port de l'application à un port
du système hôte}
Les applications 12-factors sont auto-contenues et peuvent fonctionner
en autonomie totale. Elles doivent être joignables grâce à un mécanisme
de ponts, où l'hôte qui s'occupe de l'exécution effectue lui-même la
redirection vers l'un des ports ouverts par l'application, typiquement,
en HTTP ou via un autre protocole.
\includegraphics{images/diagrams/12-factors-7.png}
\textbf{\#8 - Faites confiance aux processus systèmes pour l'exécution
de l'application}
Comme décrit plus haut (cf. \#6), l'application doit utiliser des
processus \emph{stateless} (sans état). Nous pouvons créer et utiliser
des processus supplémentaires pour tenir plus facilement une lourde
charge, ou dédier des processus particuliers pour certaines tâches:
requêtes HTTP \emph{via} des processus Web; \emph{long-running} jobs
pour des processus asynchrones, \ldots\hspace{0pt} Si cela existe au
niveau du système, ne vous fatiguez pas: utilisez le.
\includegraphics{images/12factors/process-types.png}
\textbf{\#9 - Améliorer la robustesse de l'application grâce à des
arrêts élégants et à des démarrages rapides}
Par "arrêt élégant", nous voulons surtout éviter le
\texttt{kill\ -9\ \textless{}pid\textgreater{}} ou tout autre arrêt
brutal d'un processus qui nécessiterait une intervention urgente du
superviseur. De cette manière, les requêtes en cours peuvent se terminer
au mieux, tandis que le démarrage rapide de nouveaux processus
améliorera la balance d'un processus en cours d'extinction vers des
processus tout frais.
L'intégration de ces mécanismes dès les premières étapes de
développement limitera les perturbations et facilitera la prise en
compte d'arrêts inopinés (problème matériel, redémarrage du système
hôte, etc.).
\textbf{\#10 - Conserver les différents environnements aussi similaires
que possible, et limiter les divergences entre un environnement de
développement et de production}
L'exemple donné est un développeur qui utilise macOS, NGinx et SQLite,
tandis que l'environnement de production tourne sur une CentOS avec
Apache2 et PostgreSQL. Faire en sorte que tous les environnements soient
les plus similaires possibles limite les divergences entre
environnements, facilite les déploiements et limite la casse et la
découverte de modules non compatibles dès les premières phases de
développement.
Pour vous donner un exemple tout bête, SQLite utilise un
\href{https://www.sqlite.org/datatype3.html}{mécanisme de stockage
dynamique}, associée à la valeur plutôt qu'au schéma, \emph{via} un
système d'affinités. Un autre moteur de base de données définira un
schéma statique et rigide, où la valeur sera déterminée par son
contenant. Un champ \texttt{URLField} proposé par Django a une longeur
maximale par défaut de
\href{https://docs.djangoproject.com/en/3.1/ref/forms/fields/\#django.forms.URLField}{200
caractères}. Si vous faites vos développements sous SQLite et que vous
rencontrez une URL de plus de 200 caractères, votre développement sera
passera parfaitement bien, mais plantera en production (ou en
\emph{staging}, si vous faites les choses un peu mieux) parce que les
données seront tronquées\ldots\hspace{0pt}
Conserver des environements similaires limite ce genre de désagréments.
\textbf{\#11 - Gérer les journeaux d'évènements comme des flux}
Une application ne doit jamais se soucier de l'endroit où ses évènements
seront écrits, mais simplement de les envoyer sur la sortie
\texttt{stdout}. De cette manière, que nous soyons en développement sur
le poste d'un développeur avec une sortie console ou sur une machine de
production avec un envoi vers une instance
\href{https://www.graylog.org/}{Greylog} ou
\href{https://sentry.io/welcome/}{Sentry}, le routage des journaux sera
réalisé en fonction de sa nécessité et de sa criticité, et non pas parce
que le développeur l'a spécifié en dur dans son code. Cette phase est
critique, dans la mesure où les journaux d'exécution sont la seule
manière pour une application de communiquer son état vers l'extérieur:
recevoir une erreur interne de serveur est une chose; pouvoir obtenir un
minimum d'informations, voire un contexte de plantage complet en est une
autre.
\textbf{\#12 - Isoler les tâches administratives du reste de
l'application}
Evitez qu'une migration ne puisse être démarrée depuis une URL de
l'application, ou qu'un envoi massif de notifications ne soit accessible
pour n'importe quel utilisateur: les tâches administratives ne doivent
être accessibles qu'à un administrateur. Les applications 12facteurs
favorisent les langages qui mettent un environnement REPL (pour
\emph{Read}, \emph{Eval}, \emph{Print} et \emph{Loop}) à disposition (au
hasard: \href{https://pythonprogramminglanguage.com/repl/}{Python} ou
\href{https://kotlinlang.org/}{Kotlin}), ce qui facilite les étapes de
maintenance.
\hypertarget{_concevoir_pour_lopuxe9rationnel}{%
\subsection{Concevoir pour

View File

@ -17,6 +17,330 @@ La poésie est "l'art d'évoquer et de suggérer les sensations, les impressions
Sans aller jusqu'à demander de développer vos algorithmes sur douze pieds, la programmation reste un art régit par un ensemble de bonnes pratiques, par des règles à respecter et par la nécessité de travailler avec d'autres personnes qui ont \sout{parfois} souvent une expérience, des compétences ou une approche différente.
\section{12 facteurs}
Pour la méthode de travail et de développement, nous allons nous baser
sur les \href{https://12factor.net/fr/}{The Twelve-factor App} - ou plus
simplement les \textbf{12 facteurs}.
L'idée derrière cette méthode, et indépendamment des langages de
développement utilisés, consiste à suivre un ensemble de douze concepts,
afin de:
\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\item
\textbf{Faciliter la mise en place de phases d'automatisation}; plus
concrètement, de faciliter les mises à jour applicatives, simplifier
la gestion de l'hôte, diminuer la divergence entre les différents
environnements d'exécution et offrir la possibilité d'intégrer le
projet dans un processus
d'\href{https://en.wikipedia.org/wiki/Continuous_integration}{intégration
continue} ou
\href{https://en.wikipedia.org/wiki/Continuous_deployment}{déploiement
continu}
\item
\textbf{Faciliter la mise à pied de nouveaux développeurs ou de
personnes souhaitant rejoindre le projet}, dans la mesure où la mise à
disposition d'un environnement sera grandement facilitée.
\item
\textbf{Minimiser les divergences entre les différents environnemens
sur lesquels un projet pourrait être déployé}
\item
\textbf{Augmenter l'agilité générale du projet}, en permettant une
meilleure évolutivité architecturale et une meilleure mise à l'échelle
- \emph{Vous avez 5000 utilisateurs en plus? Ajoutez un serveur et on
n'en parle plus ;-)}.
\end{enumerate}
En pratique, les points ci-dessus permettront de monter facilement un
nouvel environnement - qu'il soit sur la machine du petit nouveau dans
l'équipe, sur un serveur Azure/Heroku/Digital Ocean ou votre nouveau
Raspberry Pi Zéro caché à la cave - et vous feront gagner un temps
précieux.
Pour reprendre de manière très brute les différentes idées derrière
cette méthode, nous avons:
\subsection{\#1 - Une base de code unique, suivie par un système de contrôle
de versions}.
Chaque déploiement de l'application se basera sur cette source, afin de
minimiser les différences que l'on pourrait trouver entre deux
environnements d'un même projet. On utilisera un dépôt Git - Github,
Gitlab, Gitea, \ldots\hspace{0pt} Au choix.
\includegraphics{images/diagrams/12-factors-1.png}
Comme l'explique Eran Messeri, ingénieur dans le groupe Google Developer
Infrastructure, un des avantages d'utiliser un dépôt unique de sources,
est qu'il permet un accès facile et rapide à la forme la plus à jour du
code, sans aucun besoin de coordination. \footnote{The DevOps Handbook,
Part V, Chapitre 20, Convert Local Discoveries into Global
Improvements} Ce dépôt ne sert pas seulement au code source, mais
également à d'autres artefacts et formes de connaissance:
\begin{itemize}
\item
Standards de configuration (Chef recipes, Puppet manifests,
\ldots\hspace{0pt})
\item
Outils de déploiement
\item
Standards de tests, y compris tout ce qui touche à la sécurité
\item
Outils de déploiement de pipeline
\item
Outils d'analyse et de monitoring
\item
Tutoriaux
\end{itemize}
\subsection{\#2 - Déclarez explicitement les dépendances nécessaires au
projet, et les isoler du reste du système lors de leur installation}
Chaque installation ou configuration doit toujours être faite de la même
manière, et doit pouvoir être répétée quel que soit l'environnement
cible.
Cela permet d'éviter que l'application n'utilise une dépendance qui soit
déjà installée sur un des sytèmes de développement, et qu'elle soit
difficile, voire impossible, à répercuter sur un autre environnement.
Dans notre cas, cela pourra être fait au travers de
\href{https://pypi.org/project/pip/}{PIP - Package Installer for Python}
ou \href{https://python-poetry.org/}{Poetry}.
Mais dans tous les cas, chaque application doit disposer d'un
environnement sain, qui lui est assigné, et vu le peu de ressources que
cela coûte, il ne faut pas s'en priver.
Chaque dépendance pouvant être déclarée et épinglée dans un fichier, il
suffira de créer un nouvel environment vierge, puis d'utiliser ce
fichier comme paramètre pour installer les prérequis au bon
fonctionnement de notre application et vérifier que cet environnement
est bien reproductible.
Il est important de bien "épingler" les versions liées aux dépendances
de l'application. Cela peut éviter des effets de bord comme une nouvelle
version d'une librairie dans laquelle un bug aurait pu avoir été
introduit. Parce qu'il arrive que ce genre de problème apparaisse, et
lorsque ce sera le cas, ce sera systématiquement au mauvais moment.
\subsection{\#3 - Sauver la configuration directement au niveau de
l'environnement}
Nous voulons éviter d'avoir à recompiler/redéployer l'application parce
que:
\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\item
l'adresse du serveur de messagerie a été modifiée,
\item
un protocole a changé en cours de route
\item
la base de données a été déplacée
\item
\ldots\hspace{0pt}
\end{enumerate}
En pratique, toute information susceptible de modifier un lien vers une
ressource annexe doit se trouver dans un fichier ou dans une variable
d'environnement, et doit être facilement modifiable. En allant un pas
plus loin, ceci de paramétrer facilement un environnement (par exemple,
un container), simplement en modifiant une variable de configuration qui
spécifierait la base de données sur laquelle l'application devra se
connecter.
Toute clé de configuration (nom du serveur de base de données, adresse
d'un service Web externe, clé d'API pour l'interrogation d'une
ressource, \ldots\hspace{0pt}) sera définie directement au niveau de
l'hôte - à aucun moment, nous ne devons trouver un mot de passe en clair
dans le dépôt source ou une valeur susceptible d'évoluer, écrite en dur
dans le code.
Au moment de développer une nouvelle fonctionnalité, réfléchissez si
l'un des composants utilisés risquerait de subir une modification: ce
composant peut concerner une nouvelle chaîne de connexion, un point de
terminaison nécessaire à télécharger des données officielles ou un
chemin vers un répertoire partagé pour y déposer un fichier.
\subsection{\#4 - Traiter les ressources externes comme des ressources
attachées}
Nous parlons de bases de données, de services de mise en cache, d'API
externes, \ldots\hspace{0pt} L'application doit être capable d'effectuer
des changements au niveau de ces ressources sans que son code ne soit
modifié. Nous parlons alors de \textbf{ressources attachées}, dont la
présence est nécessaire au bon fonctionnement de l'application, mais
pour lesquelles le \textbf{type} n'est pas obligatoirement défini.
Nous voulons par exemple "une base de données" et "une mémoire cache",
et pas "une base MariaDB et une instance Memcached". De cette manière,
les ressources peuvent être attachées et détachées d'un déploiement à la
volée.
Si une base de données ne fonctionne pas correctement (problème matériel
?), l'administrateur pourrait simplement restaurer un nouveau serveur à
partir d'une précédente sauvegarde, et l'attacher à l'application sans
que le code source ne soit modifié. une solution consiste à passer
toutes ces informations (nom du serveur et type de base de données, clé
d'authentification, \ldots\hspace{0pt}) directement \emph{via} des
variables d'environnement.
\includegraphics{images/12factors/attached-resources.png}
Nous serons ravis de pouvoir simplement modifier une chaîne
\texttt{sqlite:////tmp/my-tmp-sqlite.db\textquotesingle{}} en
\texttt{psql://user:pass@127.0.0.1:8458/db} lorsque ce sera nécessaire,
sans avoir à recompiler ou redéployer les modifications.
\subsection{\#5 - Séparer proprement les phases de construction, de mise à
disposition et d'exécution}
\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\item
La \textbf{construction} (\emph{build}) convertit un code source en un
ensemble de fichiers exécutables, associé à une version et à une
transaction dans le système de gestion de sources.
\item
La \textbf{mise à disposition} (\emph{release}) associe cet ensemble à
une configuration prête à être exécutée,
\item
tandis que la phase d'\textbf{exécution} (\emph{run}) démarre les
processus nécessaires au bon fonctionnement de l'application.
\end{enumerate}
\includegraphics{images/12factors/release.png}
Parmi les solutions possibles, nous pourrions nous pourrions nous baser
sur les \emph{releases} de Gitea, sur un serveur d'artefacts ou sur
\href{https://fr.wikipedia.org/wiki/Capistrano_(logiciel)}{Capistrano}.
\subsection{\#6 - Les processus d'exécution ne doivent rien connaître ou
conserver de l'état de l'application}
Toute information stockée en mémoire ou sur disque ne doit pas altérer
le comportement futur de l'application, par exemple après un redémarrage
non souhaité.
Pratiquement, si l'application devait rencontrer un problème, l'objectif
est de pouvoir la redémarrer rapidement sur un autre serveur (par
exemple suite à un problème matériel). Toute information qui aurait été
stockée durant l'exécution de l'application sur le premier hôte serait
donc perdue. Si une réinitialisation devait être nécessaire,
l'application ne devra pas compter sur la présence d'une information au
niveau du nouveau système. La solution consiste donc à jouer sur les
variables d'environnement (cf. \#3) et sur les informations que l'on
pourra trouver au niveau des ressources attachées (cf \#4).
Il serait également difficile d'appliquer une mise à l'échelle de
l'application, en ajoutant un nouveau serveur d'exécution, si une donnée
indispensable à son fonctionnement devait se trouver sur la seule
machine où elle est actuellement exécutée.
\subsection{\#7 - Autoriser la liaison d'un port de l'application à un port
du système hôte}
Les applications 12-factors sont auto-contenues et peuvent fonctionner
en autonomie totale. Elles doivent être joignables grâce à un mécanisme
de ponts, où l'hôte qui s'occupe de l'exécution effectue lui-même la
redirection vers l'un des ports ouverts par l'application, typiquement,
en HTTP ou via un autre protocole.
\includegraphics{images/diagrams/12-factors-7.png}
\subsection{\#8 - Faites confiance aux processus systèmes pour l'exécution
de l'application}
Comme décrit plus haut (cf. \#6), l'application doit utiliser des
processus \emph{stateless} (sans état). Nous pouvons créer et utiliser
des processus supplémentaires pour tenir plus facilement une lourde
charge, ou dédier des processus particuliers pour certaines tâches:
requêtes HTTP \emph{via} des processus Web; \emph{long-running} jobs
pour des processus asynchrones, \ldots\hspace{0pt} Si cela existe au
niveau du système, ne vous fatiguez pas: utilisez le.
\includegraphics{images/12factors/process-types.png}
\subsection{\#9 - Améliorer la robustesse de l'application grâce à des
arrêts élégants et à des démarrages rapides}
Par "arrêt élégant", nous voulons surtout éviter le
\texttt{kill\ -9\ \textless{}pid\textgreater{}} ou tout autre arrêt
brutal d'un processus qui nécessiterait une intervention urgente du
superviseur. De cette manière, les requêtes en cours peuvent se terminer
au mieux, tandis que le démarrage rapide de nouveaux processus
améliorera la balance d'un processus en cours d'extinction vers des
processus tout frais.
L'intégration de ces mécanismes dès les premières étapes de
développement limitera les perturbations et facilitera la prise en
compte d'arrêts inopinés (problème matériel, redémarrage du système
hôte, etc.).
\subsection{\#10 - Conserver les différents environnements aussi similaires
que possible, et limiter les divergences entre un environnement de
développement et de production}
L'exemple donné est un développeur qui utilise macOS, NGinx et SQLite,
tandis que l'environnement de production tourne sur une CentOS avec
Apache2 et PostgreSQL. Faire en sorte que tous les environnements soient
les plus similaires possibles limite les divergences entre
environnements, facilite les déploiements et limite la casse et la
découverte de modules non compatibles dès les premières phases de
développement.
Pour vous donner un exemple tout bête, SQLite utilise un
\href{https://www.sqlite.org/datatype3.html}{mécanisme de stockage
dynamique}, associée à la valeur plutôt qu'au schéma, \emph{via} un
système d'affinités. Un autre moteur de base de données définira un
schéma statique et rigide, où la valeur sera déterminée par son
contenant. Un champ \texttt{URLField} proposé par Django a une longeur
maximale par défaut de
\href{https://docs.djangoproject.com/en/3.1/ref/forms/fields/\#django.forms.URLField}{200
caractères}. Si vous faites vos développements sous SQLite et que vous
rencontrez une URL de plus de 200 caractères, votre développement sera
passera parfaitement bien, mais plantera en production (ou en
\emph{staging}, si vous faites les choses un peu mieux) parce que les
données seront tronquées\ldots\hspace{0pt}
Conserver des environements similaires limite ce genre de désagréments.
\subsection{\#11 - Gérer les journeaux d'évènements comme des flux}
Une application ne doit jamais se soucier de l'endroit où ses évènements
seront écrits, mais simplement de les envoyer sur la sortie
\texttt{stdout}. De cette manière, que nous soyons en développement sur
le poste d'un développeur avec une sortie console ou sur une machine de
production avec un envoi vers une instance
\href{https://www.graylog.org/}{Greylog} ou
\href{https://sentry.io/welcome/}{Sentry}, le routage des journaux sera
réalisé en fonction de sa nécessité et de sa criticité, et non pas parce
que le développeur l'a spécifié en dur dans son code. Cette phase est
critique, dans la mesure où les journaux d'exécution sont la seule
manière pour une application de communiquer son état vers l'extérieur:
recevoir une erreur interne de serveur est une chose; pouvoir obtenir un
minimum d'informations, voire un contexte de plantage complet en est une
autre.
\subsection{\#12 - Isoler les tâches administratives du reste de
l'application}
Evitez qu'une migration ne puisse être démarrée depuis une URL de
l'application, ou qu'un envoi massif de notifications ne soit accessible
pour n'importe quel utilisateur: les tâches administratives ne doivent
être accessibles qu'à un administrateur. Les applications 12facteurs
favorisent les langages qui mettent un environnement REPL (pour
\emph{Read}, \emph{Eval}, \emph{Print} et \emph{Loop}) à disposition (au
hasard: \href{https://pythonprogramminglanguage.com/repl/}{Python} ou
\href{https://kotlinlang.org/}{Kotlin}), ce qui facilite les étapes de
maintenance.
\section{Tests unitaires et d'intégration}
\begin{quote}