Add a lot of things :D
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Fred Pauchet 2022-05-04 22:01:55 +02:00
parent 5de564b21f
commit 10ec49c302
7 changed files with 203 additions and 130 deletions

View File

@ -5,13 +5,14 @@
possible de modifier un fichier une fois qu'elle aura démarré: si vous souhaitez modifier un paramètre, cela reviendra à couper l'actuelle et envoyer de nouveaux paramètres et recommencer le déploiement depuis le
début.
\begin{figure}
\centering
\includegraphics{images/deployment/heroku.png}
\caption{Invest in apps, not ops. Heroku handles the hard stuff ---
patching and upgrading, 24/7 ops and security, build systems, failovers,
and more --- so your developers can stay focused on building great
apps.}
\begin{figure}[H]
\centering
\scalebox{1.0}{\includegraphics[max size={\textwidth}{\textheight}]{images/deployment/heroku.png}}
\caption{Invest in apps, not ops. Heroku handles the hard stuff ---
patching and upgrading, 24/7 ops and security, build systems, failovers,
and more --- so your developers can stay focused on building great
apps.}
\end{figure}
Pour un projet de type "hobby" et pour l'exemple de déploiement ci-dessous, il est tout à fait possible de s'en sortir sans dépenser un kopek, afin de tester nos quelques idées ou mettre rapidement un \emph{Most Valuable Product} en place. La seule contrainte consistera à pouvoir héberger des fichiers envoyés par vos utilisateurs - ceci pourra être fait en configurant un \emph{bucket compatible S3}, par exemple chez Amazon, Scaleway ou OVH.
@ -44,7 +45,7 @@ Au travers de la commande \texttt{heroku\ create}, vous associez donc une nouvel
\begin{verbatim}
Pour définir de quel type d'application il s'agit, Heroku nécessite un minimum de configuration.
Celle-ci se limite aux deux fichiers suivants:
* Déclarer un fichier `Procfile` qui va simplement décrire le fichier à passer au protocole WSGI
* Déclarer un fichier `requirements.txt` (qui va éventuellement chercher ses propres dépendances dans un sous-répertoire, avec l'option `-r`)
\end{verbatim}
@ -113,7 +114,7 @@ Et créer votre application:
\caption{Notre application est à présent configurée!}
\end{figure}
Ajoutons lui une base de données, que nous sauvegarderons à intervalle
régulier:
@ -127,7 +128,7 @@ régulier:
! data from another database with pg:copy
Created postgresql-clear-39693 as DATABASE_URL
Use heroku addons:docs heroku-postgresql to view documentation
$ heroku pg:backups schedule --at '14:00 Europe/Brussels' DATABASE_URL
Scheduling automatic daily backups of postgresql-clear-39693 at 14:00
Europe/Brussels... done
@ -145,21 +146,21 @@ régulier:
heroku config:set DJANGO_DEBUG=False
heroku config:set DJANGO_SETTINGS_MODULE=config.settings.production
heroku config:set DJANGO_SECRET_KEY="$(openssl rand -base64 64)"
# Generating a 32 character-long random string without any of the visually
similar characters "IOl01":
heroku config:set DJANGO_ADMIN_URL="$(openssl rand -base64 4096 | tr -dc 'A-
HJ-NP-Za-km-z2-9' | head -c 32)/"
# Set this to your Heroku app url, e.g. 'bionic-beaver-28392.herokuapp.com'
heroku config:set DJANGO_ALLOWED_HOSTS=
# Assign with AWS_ACCESS_KEY_ID
heroku config:set DJANGO_AWS_ACCESS_KEY_ID=
# Assign with AWS_SECRET_ACCESS_KEY
heroku config:set DJANGO_AWS_SECRET_ACCESS_KEY=
# Assign with AWS_STORAGE_BUCKET_NAME
heroku config:set DJANGO_AWS_STORAGE_BUCKET_NAME=
git push heroku master

View File

@ -1,32 +1,68 @@
\chapter{Infrastructure et composants}
Deux types d'applications peuvent être considérées: les applications consommant intensivement des données (\textit{Data-intensive applications}) et les applications consommant principalement des cycles processeur (\textit{Compute-intensive applications}).
Une application consommant principalement des données est rarement limitée par la puisse du CPU, mais plus souvent par la quantité et la complexité des structures de données, et la vitesse à laquelle celles-ci doivent être échangées. \cite[p. 3]{data_intensive}
Nous pouvons distinguer plusieurs types de composants:
\begin{enumerate}
\item \textbf{Les composants fonctionnels}, ceux sans qui l'application fonctionnerait de manière partielle, inadéquate, ou avec des temps de réponse inadapté
\item \textbf{Les composants périphériques}, c'est-à-dire les composants qui aident à avoir une architecture système résiliente, évolutive et maintenable
\item \textbf{Les composants de supervision}, qui permettent de s'assurer que tout fonctionne correctement, et qu'aucun incident n'est prévu ou n'a été constaté récemment.
\end{enumerate}
\section{Composants fonctionnels}
\begin{figure}[H]
\centering
\scalebox{1.0}{\includegraphics[max size={\textwidth}{\textheight}]{images/diagrams/infrastructure.drawio.png}}
\caption{Une architecture possible pour un système de données, qui combine plusieurs composants \cite[p. 5]{data_intensive}}
\caption{Une architecture possible pour un système de données moderne, qui combine plusieurs composants \cite[p. 5]{data_intensive}}
\end{figure}
Pour une mise ne production, le standard \emph{de facto} est le suivant:
Une application \textit{data-intensive} est généralement composée des blocs fonctionnels suivants \cite[p. 3]{data_intensive} \footnote{De manière non-exhaustive, mais assez complète quand même.}:
\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}
\begin{tabular}{|p{0.2\linewidth}|p{0.55\linewidth}|p{0.15\linewidth}|}
\hline
Type & Utilité & Exemples \\
\hline
Bases de données & Stocker des données, pour pouvoir les retrouver (elles-même ou une autre application) plus tard & PostgreSQL, MySQL, MariaDB, SQLite \\
\hline
Caches & Conserver en mémoire des résultats gourmands, afin de pouvoir les réexploiter plus rapidement, sans avoir à les recalculer & Redis, Memcache \\
\hline
Moteurs d'indexation & Autoriser la recherche de résultats sur base de mots-clés ou en utilisant des filtres particuliers & ElasticSearch, Solr \\
\hline
Traitements asynchrones & Exécution de traitements lourds ou pouvant être démarrés \textit{a posteriori} & Celery \\
\hline
\end{tabular}
\subsection{Bases de données}
\subsection{Tâches asynchrones}
\subsection{Mise en cache}
\section{Composants périphériques}
Les composants périphériques gravitent autour du fonctionnement normal de l'application.
Ce sont les "petites mains" qui aident à ce que l'application continue à tourner, redémarre proprement, répartissent la charge entre plusieurs instances, \ldots Il s'agit des composants comme le reverse proxy, les distributeurs de charge ou les outils de monitoring: ce sont des composants qui ne sont pas gérés directement par l'application ou qui se situent un cran en avant.
Ils sont fortement recommandés, mais non-obligatoires à son bon fonctionnement.
\begin{tabular}{|p{0.2\linewidth}|p{0.55\linewidth}|p{0.15\linewidth}|}
\hline
Type & Utilité & Exemples \\
\hline
Firewall & & firewalld, UFW \\
\hline
Reverse Proxy & & Nginx \\
\hline
Load balancers & Distribue la charge sur plusieurs serveurs, en fonction du nombre d'utilisateurs, du nombre de requêtes à gérer et du temps que prennent chacune d'entre elles & HAProxy \\
\hline
Serveurs d'application & & Gunicorn, Uvicorn \\
\hline
Healthcheck & & Supervisord \\
\hline
\end{tabular}
Si nous schématisons l'infrastructure et le chemin parcouru par une requête, nous pourrions arriver à la synthèse suivante:
@ -35,7 +71,7 @@ Si nous schématisons l'infrastructure et le chemin parcouru par une requête, n
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
POST, \ldots), un port et éventuellement du contenu
\item
Le firewall du serveur (Debian GNU/Linux, CentOS, ...
vérifie si la requête peut être prise en compte
@ -56,22 +92,121 @@ Si nous schématisons l'infrastructure et le chemin parcouru par une requête, n
\includegraphics{images/diagrams/architecture.png}
\subsection{Firewall}
\section{Reverse proxy}
\subsection{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}
\subsection{Répartiteur de charge (\textit{Load balancer})}
\section{Workers}
\subsection{Serveurs d'application (\textit{Workers})}
\section{Supervision des processus}
\section{Composants de supervision}
\section{Bases de données}
\begin{tabular}{|p{0.2\linewidth}|p{0.55\linewidth}|p{0.15\linewidth}|}
\hline
Type & Utilité & Exemples \\
\hline
Supervision des processus & & Supervisord \\
\hline
Flux d'évènements & & syslogd, journalctl \\
\hline
Notifications & & Zabbix, Nagios, Munin \\
\hline
\end{tabular}
\section{Tâches asynchrones}
\subsection{Supervision des processus}
\subsection{Journaux}
La présence de journaux, leur structure et de la définition précise de leurs niveaux est essentielle; ce sont eux qui permettent d'obtenir des informations quant au statut de l'application:
\begin{itemize}
\item Que fait-elle pour le moment ?
\item Qu'a-t-elle fait à un moment en particulier ?
\item Depuis quand n'a-t-elle plus émis de nouveaux évènements ?
\end{itemize}
\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, ancien consultant chez ToughtWorks
\end{quote}
Chaque évènement est associé à un niveau; celui-ci indique sa criticité et sa émet un premier tri quant à sa pertinence.
\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, ...
\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, \ldots
\item
\textbf{FATAL} (ou \textbf{EXCEPTION}): \ldots généralement suivie d'une terminaison du programme - Bind raté d'un socket, etc.
\end{itemize}
\subsubsection{Flux d'évènements}
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, différents 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
\textbf{Formattage}: à définir - mais la variante suivante est complète, lisible et pratique:
\texttt{\{levelname\}\ \{asctime\}\ \{module\}\ \{process:d\}\ \{thread:d\}\ \{message\}}
\item
\textbf{Handler}: juste un, qui définit un \texttt{StreamHandler}
\item
\textbf{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}.
\subsection{Exploitation des journaux}
\begin{enumerate}
\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}.
\subsubsection{Zabbix, Nagios, ...}
\subsubsection{Sentry}
\subsubsection{Greylog}
\section{Mise en cache}
\section{Niveau application}
@ -92,88 +227,3 @@ Au niveau logiciel (la partie mise en subrillance ci-dessus), la requête arrive
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, ...
\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, ...
\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}): ... 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, ...}
\subsection{Sentry}
\subsection{Greylog}

5
chapters/security.tex Normal file
View File

@ -0,0 +1,5 @@
\chapter{Sécurité}
\begin{quote}
\end{quote}

View File

@ -1,2 +1,5 @@
\chapter{Remerciements}
$\Phi\phi$ pour son soutien, son entrain, nos discussions, ses remarques, ses idées et sa disponibilité.
Ced', pour son impulsion

View File

@ -48,7 +48,12 @@
fichiers de logs, de données applications, de fichiers média envoyés par
vos utilisateurs, de vidéos et images ou de données de sauvegardes.
\textbf{\url{https://aws.amazon.com/fr/s3/}.}
\includegraphics{images/amazon-s3-arch.png}
\begin{figure}[H]
\centering
\scalebox{1.0}{\includegraphics[max size={\textwidth}{\textheight}]{images/amazon-s3-arch.png}}
\end{figure}
\end{description}

View File

@ -1,4 +1,5 @@
\documentclass[twoside=no,parskip=half,numbers=enddot,bibliography=totoc,index=totoc,listof=totoc]{scrbook}
\usepackage{amsmath}
\usepackage{makeidx}
\usepackage[utf8]{inputenc}
\usepackage[french]{babel}
@ -74,6 +75,7 @@
\include{chapters/templates.tex}
\include{chapters/authentication.tex}
\include{chapters/context-processors.tex}
\include{chapters/security.tex}
\chapter{Conclusions}
\include{parts/soa.tex}

View File

@ -54,6 +54,13 @@
isbn = {978-1-491-95452-2},
url = {http://shop.oreilly.com/product/0636920049555.do}
}
@book{roads_bridges,
title = {Roads and Bridges},
booktitle = {The Unseen Labor Behind Our Digital Infrastructure},
author = {Nadia Eghbal},
year = {2016},
url = {https://fordfoundcontent.blob.core.windows.net/media/2976/roads-and-bridges-the-unseen-labor-behind-our-digital-infrastructure.pdf}
}
@book{two_scoops_of_django,
author = {Daniel Roy and Andrey Greenfeld},
title = {Two Scoops of Django},