\chapter{Infrastructure et composants}

Pour une mise ne production, le standard \emph{de facto} est le suivant :
\begin{itemize}
    \item Nginx comme reverse proxy
    \item HAProxy pour la distribution de charge
    \item Gunicorn ou Uvicorn comme serveur d'application
    \item Supervisor pour le monitoring
    \item PostgreSQL ou MySQL/MariaDB comme bases de données.
    \item Celery et RabbitMQ pour l'exécution de tâches asynchrones
    \item Redis / Memcache pour la mise à en cache (et pour les sessions ? A vérifier).
    \item Sentry, pour le suivi des bugs
\end{itemize}

Si nous schématisons l'infrastructure et le chemin parcouru par une requête, nous pourrions arriver à la synthèse suivante :
\begin{enumerate}
    \item
        L'utilisateur fait une requête via son navigateur (Firefox ou Chrome)
    \item
        Le navigateur envoie une requête http, sa version, un verbe (GET, POST, \ldots\hspace{0pt}), un port et éventuellement du contenu
    \item
        Le firewall du serveur (Debian GNU/Linux, CentOS, \ldots~ vérifie si la requête peut être prise en compte
    \item
        La requête est transmise à l'application qui écoute sur le port (probablement 80 ou 443; et \emph{a priori} Nginx)
    \item
        Elle est ensuite transmise par socket et est prise en compte par un des \emph{workers} (= un processus Python) instancié par Gunicorn.
        Si l'un de ces travailleurs venait à planter, il serait automatiquement réinstancié par Supervisord.
    \item
        Qui la transmet ensuite à l'un de ses \emph{workers} (= un processus Python).
    \item
        Après exécution, une réponse est renvoyée à l'utilisateur.
\end{enumerate}

\includegraphics{images/diagrams/architecture.png}


\section{Reverse proxy}
Le principe du \textbf{proxy inverse} est de pouvoir rediriger du trafic entrant vers une application hébergée sur le système.
Il serait tout à fait possible de rendre notre application directement accessible depuis l'extérieur, mais le proxy a aussi l'intérêt de pouvoir élever la sécurité du serveur (SSL) et décharger le serveur applicatif grâce à un mécanisme de cache ou en compressant certains résultats \footnote{\url{https://fr.wikipedia.org/wiki/Proxy_inverse}}

\section{Load balancer}
% TODO: à écrire

\section{Workers}
% TODO: à écrire

\section{Supervision des processus}
% TODO: à écrire

\section{Bases de données}
% TODO: à écrire

\section{Tâches asynchrones}
% TODO: à écrire

\section{Mise en cache}
% TODO: à écrire

\section{Niveau application}
Au niveau logiciel (la partie mise en subrillance ci-dessus), la requête arrive dans les mains du processus Python, qui doit encore

\begin{enumerate}
    \item effectuer le routage des données,
    \item trouver la bonne fonction à exécuter,
    \item récupérer les données depuis la base de données,
    \item effectuer le rendu ou la conversion des données,
    \item et renvoyer une réponse à l'utilisateur.
\end{enumerate}

Comme nous l'avons vu dans la première partie, Django est un framework complet, intégrant tous les mécanismes nécessaires à la bonne évolution d'une application.
Il est possible de démarrer petit, et de suivre l'évolution des besoins en fonction de la charge estimée ou ressentie, d'ajouter un mécanisme de mise en cache, des logiciels de suivi, \ldots


\section{Supervision globale}
\subsection{Journaux}
La structure des niveaux de journaux est essentielle.

\begin{quote}
    When deciding whether a message should be ERROR or WARN, imagine being woken up at 4 a.m.
    Low printer toner is not an ERROR.

    ---  Dan North former ToughtWorks consultant
\end{quote}

\begin{itemize}
    \item
        \textbf{DEBUG}: Il s'agit des informations qui concernent tout ce qui peut se passer durant l'exécution de l'application.
        Généralement, ce niveau est désactivé pour une application qui passe en production, sauf s'il est nécessaire d'isoler un comportement en particulier, auquel cas il suffit de le réactiver temporairement.
    \item
        \textbf{INFO} : Enregistre les actions pilotées par un utilisateur - Démarrage de la transaction de paiement, \ldots
    \item
        \textbf{WARN} : Regroupe les informations qui pourraient potentiellement devenir des erreurs.
    \item
        \textbf{ERROR} : Indique les informations internes - Erreur lors de l'appel d'une API, erreur interne, ...
    \item
        \textbf{FATAL} (ou \textbf{EXCEPTION}) : \ldots généralement suivie d'une terminaison du programme - Bind raté d'un socket, etc.
\end{itemize}

La configuration des \emph{loggers} est relativement simple, un peu plus complexe si nous nous penchons dessus, et franchement complète si nous creusons encore.
Il est ainsi possible de définir des formattages, gestionnaires (\emph{handlers}) et loggers distincts, en fonction de nos applications.

Sauf que comme nous l'avons vu avec les 12 facteurs, nous devons traiter les informations de notre application comme un flux d'évènements.
Il n'est donc pas réellement nécessaire de chipoter la configuration, puisque la seule classe qui va réellement nous intéresser concerne les \texttt{StreamHandler}.
La configuration que nous allons utiliser est celle-ci :
\begin{enumerate}
    \item
        Formattage : à définir - mais la variante suivante est complète, lisible et pratique: \texttt{\{levelname\}\ \{asctime\}\ \{module\}\ \{process:d\}\ \{thread:d\}\ \{message\}}
    \item
        Handler : juste un, qui définit un \texttt{StreamHandler}
    \item
        Logger : pour celui-ci, nous avons besoin d'un niveau (\texttt{level}) et de savoir s'il faut propager les informations vers les sous-paquets, auquel cas il nous suffira de fixer la valeur de \texttt{propagate} à \texttt{True}.
\end{enumerate}

Pour utiliser nos loggers, il suffit de copier le petit bout de code suivant :
\begin{minted}{python}
    import logging

    logger = logging.getLogger(__name__)
    logger.debug('helloworld')
\end{minted}

\href{https://docs.djangoproject.com/en/stable/topics/logging/\#examples}{Par exemples}.

\begin{enumerate}
    \def\labelenumi{\arabic{enumi}.}
    \item
        Sentry via sentry\_sdk
    \item
        Nagios
    \item
        LibreNMS
    \item
        Zabbix
\end{enumerate}

Il existe également \href{https://munin-monitoring.org}{Munin}, \href{https://www.elastic.co}{Logstash, ElasticSearch et Kibana (ELK-Stack)} ou \href{https://www.fluentd.org}{Fluentd}.


\subsection{Zabbix, Nagios, ...}
% TODO: a écrire

\subsection{Sentry}
% TODO: a écrire

\subsection{Greylog}
% TODO: a écrire

