gwift-book/source/part-2-deployment/_main.adoc

12 KiB
Raw Blame History

Déploiement

To be effective, a software system must be deployable. The higher the cost of deployements, the less useful the system is. A goal of a software architecture, then, should be to make a system that can be easily deployed with a single action. Unfortunately, deployment strategy is seldom considered during initial development. This leads to architectures that may be make the system easy to develop, but leave it very difficult to deploy.
— Robert C. Martin
Clean Architecture

Il y a une raison très simple à aborder le déploiement dès maintenant: à trop attendre et à peaufiner son développement en local, on en oublie que sa finalité sera de se retrouver exposé et accessible depuis un serveur. Il est du coup probable doublier une partie des désidérata, de zapper une fonctionnalité essentielle ou simplement de passer énormément de temps à adapter les sources pour quelles puissent être mises à disposition sur un environnement en particulier, une fois que leur développement aura été finalisé, testé et validé. Un bon déploiement ne doit pas dépendre de dizaines de petits scripts éparpillés sur le disque. Lobjectif est quil soit rapide et fiable. Ceci peut être atteint au travers dun partitionnement correct, incluant le fait que le composant principal sassure que chaque sous-composant est correctement démarré intégré et supervisé.

Aborder le déploiement dès le début permet également de rédiger dès le début les procédures dinstallation, de mises à jour et de sauvegardes. A la fin de chaque intervalle de développement, les fonctionnalités auront dû avoir été intégrées, testées, fonctionnelles et un code propre, démontrable dans un environnement similaire à un environnement de production, et créées à partir dun tronc commun au développement cite:{devops_handbook}.

Déploier une nouvelle version sera aussi simple que de récupérer la dernière archive depuis le dépôt, la placer dans le bon répertoire, appliquer des actions spécifiques (et souvent identiques entre deux versions), puis redémarrer les services adéquats, et la procédure complète se résumera à quelques lignes dun script bash.

Because value is created only when our services are running into production, we must ensure that we are not only delivering fast flow, but that our deployments can also be performed without causing chaos and disruptions such as service outages, service impairments, or security or compliance failures.
— DevOps Handbook
Introduction

Le serveur que django met à notre disposition via la commande runserver est extrêmement pratique, mais il est uniquement prévu pour la phase développement: en production, il est inutile de passer par du code Python pour charger des fichiers statiques (feuilles de style, fichiers JavaScript, images, …​). De même, Django propose par défaut une base de données SQLite, qui fonctionne parfaitement dès lors que lon connait ses limites et que lon se limite à un utilisateur à la fois. En production, il est légitime que la base de donnée soit capable de supporter plusieurs utilisateurs et connexions simultanés. En restant avec les paramètres par défaut, il est plus que probable que vous rencontriez rapidement des erreurs de verrou parce quun autre processus a déjà pris la main pour écrire ses données. En bref, vous avez quelque chose qui fonctionne, qui répond à un besoin, mais qui va attirer la grogne de ses utilisateurs pour des problèmes de latences, pour des erreurs de verrou ou simplement parce que le serveur répondra trop lentement.

Lobjectif de cette partie est de parcourir les différentes possibilités qui soffrent à nous en termes de déploiement, tout en faisant en sorte que le code soit le moins couplé possible à sa destination de production. Lobjectif est donc de faire en sorte quune même application puisse être hébergées par plusieurs hôtes sans avoir à subir de modifications. Nous vous renvoyons vers les 12-facteurs dont nous avons déjà parlé et qui vous énormément nous aider, puisque ce sont des variables denvironnement qui vont réellement piloter le câblage entre lapplication, ses composants et son hébergeur.

RedHat proposait récemment un article intitulé *What Is IaaS*, qui présentait les principales différences entre types dhébergement.

iaas focus paas saas diagram
Figure 1. Linfrastructure en tant que service, cc. RedHat Cloud Computing

Ainsi, on trouve:

  1. Le déploiment on-premises ou on-site

  2. Les Infrastructures as a service ou IaaS

  3. Les Platforms as a service ou PaaS

  4. Les Softwares as a service ou SaaS, ce dernier point nous concernant moins, puisque cest nous qui développons le logiciel.

Dans cette partie, nous aborderons les points suivants:

  1. Définir linfrastructure et les composants nécessaires à notre application

  2. Configurer lhôte qui hébergera lapplication et y déployer notre application: dans une machine physique, virtuelle ou dans un container. Nous aborderons aussi les déploiements via Ansible et Salt. A ce stade, nous aurons déjà une application disponible.

  3. Configurer les outils nécessaires à la bonne exécution de ce code et de ses fonctionnalités: les différentes méthodes de supervision de lapplication, comment analyser les fichiers de logs, comment intercepter correctement une erreur si elle se présente et comment remonter correctement linformation.

Infrastructure & composants

Pour une mise ne production, le standard de facto est le suivant:

  • Nginx comme reverse proxy

  • HAProxy pour la distribution de charge

  • Gunicorn ou Uvicorn comme serveur dapplication

  • Supervisor pour le monitoring

  • PostgreSQL ou MySQL/MariaDB comme bases de données.

  • Celery et RabbitMQ pour lexécution de tâches asynchrones

  • Redis / Memcache pour la mise à en cache (et pour les sessions ? A vérifier).

  • Sentry, pour le suivi des bugs

Si nous schématisons linfrastructure et le chemin parcouru par une requête, nous pourrions arriver à la synthèse suivante:

  1. Lutilisateur fait une requête via son navigateur (Firefox ou Chrome)

  2. Le navigateur envoie une requête http, sa version, un verbe (GET, POST, …​), un port et éventuellement du contenu

  3. Le firewall du serveur (Debian GNU/Linux, CentOS, …​) vérifie si la requête peut être prise en compte

  4. La requête est transmise à lapplication qui écoute sur le port (probablement 80 ou 443; et a priori Nginx)

  5. Elle est ensuite transmise par socket et est prise en compte par un des workers (= un processus Python) instancié par Gunicorn. Si lun de ces travailleurs venait à planter, il serait automatiquement réinstancié par Supervisord.

  6. Qui la transmet ensuite à lun de ses workers (= un processus Python).

  7. Après exécution, une réponse est renvoyée à lutilisateur.

architecture

Reverse proxy

Le principe du 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 lextérieur, mais le proxy a aussi linté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 [1]

Load balancer

Workers

Supervision des processus

Base de données

Tâches asynchrones

Mise en cache

Code source

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

  1. effectuer le routage des données,

  2. trouver la bonne fonction à exécuter,

  3. récupérer les données depuis la base de données,

  4. effectuer le rendu ou la conversion des données,

  5. et renvoyer une réponse à lutilisateur.

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

Outils de supervision et de mise à disposition

Logs

Unresolved directive in <stdin> - include::logging.adoc[]

Sentry ! :-D

SENTRY_DSN = env("SENTRY_DSN", default=None)

if SENTRY_DSN is not None:
    import sentry_sdk
    from sentry_sdk.integrations.django import DjangoIntegration

    sentry_sdk.init(
        dsn=SENTRY_DSN,
        integrations=[DjangoIntegration()],

        # Set traces_sample_rate to 1.0 to capture 100%
        # of transactions for performance monitoring.
        # We recommend adjusting this value in production.
        traces_sample_rate=1.0,

        # If you wish to associate users to errors (assuming you are using
        # django.contrib.auth) you may enable sending PII data.
        send_default_pii=True,
        ca_certs=<path_to_pem_file>,
    )

Logging

  1. Sentry via sentry_sdk

  2. Nagios

  3. LibreNMS

  4. Zabbix

Méthode de déploiement

Nous allons détailler ci-dessous trois méthodes de déploiement:

  • Sur une machine hôte, en embarquant tous les composants sur un même serveur. Ce ne sera pas idéal, puisquil ne sera pas possible de configurer un load balancer, de routeur plusieurs basées de données, mais ce sera le premier cas de figure.

  • Dans des containers, avec Docker-Compose.

  • Sur une Plateforme en tant que Service (ou plus simplement, PaaS), pour faire abstraction de toute la couche de configuration du serveur.

Sur une machine hôte

La première étape pour la configuration de notre hôte consiste à définir les utilisateurs et groupes de droits. Il est faut absolument éviter de faire tourner une application en tant quutilisateur root, car la moindre faille pourrait avoir des conséquences catastrophiques.

Une fois que ces utilisateurs seront configurés, nous pourrons passer à létape de configuration, qui consistera à:

  1. Déployer les sources

  2. Démarrer un serveur implémentant une interface WSGI (Web Server Gateway Interface), qui sera chargé de créer autant de petits lutins travailleurs que nous le désirerons.

  3. Démarrer un superviseur, qui se chargera de veiller à la bonne santé de nos petits travailleurs, et en créer de nouveaux sil le juge nécessaire

  4. Configurer un proxy inverse, qui soccupera denvoyer les requêtes dun utilisateur externe à la machine hôte vers notre serveur applicatif, qui la communiquera à lun des travailleurs.

La machine hôte peut être louée chez Digital Ocean, Scaleway, OVH, Vultr, …​ Il existe des dizaines dhébergements typés VPS (Virtual Private Server). A vous de choisir celui qui vous convient [2].

Unresolved directive in <stdin> - include::debian.adoc[]

Unresolved directive in <stdin> - include::heroku.adoc[]

Unresolved directive in <stdin> - include::docker.adoc[]

Warning
le serveur de déploiement ne doit avoir quun accès en lecture au dépôt source.

On peut aussi passer par fabric, ansible, chef ou puppet.

Autres outils

Voir aussi devpi, circus, uswgi, statsd.


2. Personnellement, jai un petit faible pour Hetzner Cloud