
Dockerize Python Queue Manager Project for Easy Deployment (en anglais)
Le mois dernier, je vous ai montré comment construire une application de gestion de file d'attente par application de gestion de file d'attente par SMS avec Python et Flask. Cette application était idéale pour montrer un exemple de base de l'utilisation de l'API Nexmo SMS APINexmo, mais elle n'était vraiment bonne que pour le prototypage et le développement local. Dans ce post, je vais parcourir quelques étapes que vous pouvez prendre pour rendre cette application plus prête pour la production, le résultat final étant une application de gestion des files d'attente. Dockerisée de l'application que vous pouvez déployer directement sur Heroku.
En cours de route, nous parlerons un peu de.. :
Meilleures pratiques pour la gestion des secrets
Les conteneurs et leur utilité
Travailler avec un serveur d'application capable de gérer le trafic de production
Conditions préalables
A compte Heroku Account
L'interface CLI Heroku
ngrok (pour les tests locaux)
Vonage API Account
To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.
This tutorial also uses a virtual phone number. To purchase one, go to Numbers > Buy Numbers and search for one that meets your needs.
Choisir un point de départ
Si vous avez suivi mon premier article sur la création d'une application de gestion de file d'attente, tout ce dont vous avez besoin pour démarrer avec celle-ci est de couvrir tous les pré-requis listés ci-dessus. Si vous venez d'arriver, vous pouvez cloner l'application terminée précédemment :
git clone https://github.com/nexmo-community/sms-queue-notify.gitVous pouvez également commencer avec la version version finie, Dockerisée du projet :
git clone https://github.com/nexmo-community/docker-queue-manager.gitLa plupart des étapes d'installation et des commandes de construction présentées dans ce billet devront encore être effectuées, mais vous n'aurez pas à modifier le code.
Quel que soit votre point de départ, assurez-vous que vous vous trouvez dans le répertoire de votre projet :
cd sms-queue-notifyou
cd docker-queue-manager Gérer les secrets
Auparavant, je vous faisais placer votre clé Nexmo, votre secret et votre numéro de téléphone directement en haut de main.py. Bien que cela ait fonctionné pour la démonstration d'une application simple sans beaucoup de pièces mobiles, en général, vous voudriez vous assurer que vos informations d'identification sont séparées de votre code. Ainsi, la première modification que nous allons apporter à main.py est de lire nos secrets directement à partir des variables d'environnement. Changez les lignes suivantes :
NEXMO_KEY = <Your Nexmo Key>
NEXMO_SECRET = <Your Nexmo Secret>
NEXMO_NUMBER = <Your Nexmo Number>à :
NEXMO_KEY = os.environ['NEXMO_KEY']
NEXMO_SECRET = os.environ['NEXMO_SECRET']
NEXMO_NUMBER = os.environ['NEXMO_NUMBER']Ensuite, vous devez vous assurer que vous ne publiez pas accidentellement vos secrets dans un lieu public. Si vous n'en avez pas encore, créez un fichier .gitignore et assurez-vous que vous avez .env dans la liste. Pendant que vous y êtes, créez un fichier .dockerignore avec les éléments suivants :
.env
.gitVous pouvez maintenant créer un nouveau fichier nommé .env qui contiendra vos informations sensibles. Le contenu du fichier doit être le suivant
NEXMO_KEY=<Your Nexmo Key>
NEXMO_SECRET=<Your Nexmo Secret>
NEXMO_NUMBER=<Your Nexmo Number>Notez que le formatage est important ici. Il n'y a pas d'espace autour des signes égaux et, contrairement à ce qui se passait lorsque vous aviez ces valeurs dans main.pyelles ne doivent pas être placées entre guillemets.
Si vous voulez tester votre application maintenant, vous pouvez exécuter ce qui suit dans le terminal pour définir les variables d'environnement basées sur le fichier .env dans le terminal :
set -o allexport
source .env
set +o allexportNous utiliserons Docker pour tester les modifications apportées à notre application, qui peut lire directement le fichier .env directement.
Modifications de la configuration
À la toute fin de main.pyvous verrez ce qui suit :
if __name__ == '__main__':
app.run(debug=True, threaded=True)Cette configuration, avec debug=Trueest idéale pour tester l'application, car elle permet d'apporter des modifications sans avoir à redémarrer le serveur à chaque fois. Ce mode de débogage est uniquement destiné au développement et ne doit pas être utilisé en production.
L'autre élément de configuration, threaded=Trueconcerne les événements envoyés par le serveur, qui nécessitent un threading pour fonctionner correctement. Comme vous l'apprendrez bientôt, nous traiterons les demandes avec un serveur d'application séparé, et nous pouvons donc supprimer cet élément de configuration. La version mise à jour de main.py mise à jour devrait ressembler à ce qui suit :
if __name__ == '__main__':
app.run() Créer un fichier Docker
Pour faciliter le déploiement de notre projet, nous allons tout regrouper dans un conteneur Docker. Les conteneurs sont un moyen léger de s'assurer que votre application dispose de toutes les ressources dont elle a besoin pour fonctionner, y compris le bon système d'exploitation et les dépendances. Regrouper notre application dans un conteneur permet de la déployer sur diverses plateformes sans avoir à se soucier des autres processus, configurations et logiciels déjà existants.
Docker construit des images de conteneurs en utilisant ce que l'on appelle un Dockerfile, qui énumère ligne par ligne les étapes nécessaires pour créer l'environnement dans lequel votre application fonctionnera. Il s'agit d'une recette qui indique à Docker comment reproduire la configuration que vous savez nécessaire au bon fonctionnement de votre application. Chaque ligne du Dockerfile crée une couche, et lorsque vous reconstruisez une image Docker, seules les couches qui ont été modifiées seront reconstruites. Cela signifie que vous voulez commencer par la configuration la plus générale (système d'exploitation et paquets requis) et progresser vers des exigences plus spécifiques.
Pour notre application, commençons par un nouveau fichier nommé Dockerfile qui contient ce qui suit :
FROM python:3.6-slim-buster
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txtCela indique à Docker de commencer avec une image de base qui inclut une version légère de l'application Debian Buster ainsi que la version 3.6 de Python. Il s'agit d'une image de base standard disponible dans le Docker Hubun registre public d'images de conteneurs.
Les deux lignes suivantes du fichier Dockerfile garantissent que les dépendances requises pour notre application (Flask, Flask-SQLAlchemy et le SDK Nexmo) sont installées. À moins que des changements ne soient apportés à requirements.txt ou qu'une image de base différente soit utilisée, ces étapes n'auront besoin d'être effectuées qu'une seule fois. Les constructions ultérieures (pour des choses comme des modifications du code) peuvent utiliser ces couches déjà existantes.
Serveur d'application de production
Flask dispose d'un serveur web intégré à des fins de test, et c'est ce que nous avons utilisé dans l'article précédent. Ce serveur n'est pas destiné à une utilisation en production - il est principalement conçu pour traiter une requête à la fois et il n'évoluera pas pour gérer le trafic attendu d'une application de production.
En production, vous devez vous assurer que vous disposez d'un serveur web dédié et un serveur d'application distinct qui assure la communication avec votre application Python. Les deux choix les plus courants sont Nginx et Gunicorn. Comme nous prévoyons de déployer sur Heroku, qui fournit un serveur web pour vous, nous n'aurons besoin que d'inclure Gunicorn (ainsi que gevent pour gérer le threading). Ajoutez ce qui suit à votre fichier Docker :
RUN pip install gunicorn gevent Configuration de la base de données
Si vous vous souvenez du premier billetnous avons créé notre base de données en utilisant des commandes Python directement en ligne de commande. Cela ne fonctionne pas très bien lorsque vous déployez votre application dans un conteneur, car vous ne voulez pas avoir à faire des étapes manuelles pour configurer votre environnement. Pour automatiser les choses, créez un nouveau fichier create_db.py qui ressemble à ceci :
from main import db
db.create_all()C'était facile ! Nous pouvons maintenant terminer notre Dockerfile avec ce qui suit :
COPY . /app
WORKDIR /app
CMD python create_db.py && gunicorn -k gevent -b 0.0.0.0:$PORT main:app
Ces dernières étapes copient le contenu du répertoire de votre projet dans un dossier nommé /app dans le conteneur, qui devient alors le répertoire de travail. La dernière ligne indique au conteneur les commandes à exécuter au démarrage : d'abord créer la base de données, puis lancer un serveur gunicorn pour exécuter notre application. La variable d'environnement $PORT est définie par Heroku lorsque le conteneur est lancé.
Tester localement avec Docker
Maintenant que le fichier Docker est complet, il est facile de tester que tout fonctionne localement. Tout d'abord, assurez-vous que Docker fonctionne sur votre ordinateur. Ensuite, exécutez ce qui suit dans votre répertoire de projet pour construire une image Docker, en utilisant --tag pour définir un nom facile à référencer :
docker build --tag queue_app .Si la compilation est réussie, vous pouvez maintenant exécuter le conteneur :
docker run -d -p 5000:5000 --env-file .env -e PORT=5000 queue_appRemarquez que nous chargeons nos secrets à partir du fichier .env et que nous définissons nous-mêmes la variable d'environnement PORT nous-mêmes.
Une fois le conteneur lancé, vous devriez pouvoir ouvrir un navigateur, aller sur localhost:5000et voir votre application !
Il reste encore une étape à franchir si vous voulez tester complètement votre application. Dans l'article précédent, vous avez configuré ngrok pour rendre votre application accessible via le web. Vous devrez le faire à nouveau si vous voulez tester l'envoi d'un SMS à l'application. Ouvrez une nouvelle fenêtre de terminal et exécutez ce qui suit :
ngrok http 5000Ensuite, assurez-vous d'aller sur le tableau de bord de Nexmo et de copier l'URL de transfert dans les paramètres de votre numéro dans le champ Inbound Webhook URL dans les paramètres de votre numéro, comme suit : https://<your ngrok ID>.ngrok.io/webhooks/inbound-sms (voir article précédent pour plus de détails).
Vous devriez maintenant être en mesure d'interagir avec votre application via le texte, comme auparavant ! Seulement maintenant, si vous arrêtez votre conteneur et que vous le redémarrez, vous verrez que la base de données sera complètement réinitialisée.
Base de données Postgres
Dans notre version de développement de l'application, nous avons utilisé une base de données SQLite pour stocker les informations sur les personnes qui attendent dans la file d'attente. SQLite créait la base de données sous la forme d'un fichier dans le répertoire du projet, ce qui rendait l'installation simple. Dans une configuration basée sur un conteneur, cela ne fonctionne pas, car le système de fichiers du conteneur ne persiste pas si le conteneur doit être redémarré. Il est également difficile de faire évoluer l'application dans plusieurs conteneurs, car il n'y a pas de source de données partagée.
Heureusement, nous avons utilisé Flask-SQLAlchemy pour abstraire les spécificités de la base de données de notre code, ce qui nous a permis d'échanger notre SQLite contre une base de données de Postgres fournie par Heroku est incroyablement simple. La base de données Postgres se trouve à l'extérieur du conteneur, de sorte qu'elle persiste même lorsque le conteneur est redémarré et qu'elle est accessible à plusieurs conteneurs.
Lorsque Heroku crée une base de données Postgres, l'url de la base de données est stockée dans la variable d'environnement DATABASE_URL la variable d'environnement. Le seul changement que nous devons apporter à notre code pour passer de SQLite à la base de données Postgres d'Heroku est de remplacer cette ligne dans main.py:
db_path = "sqlite:///queue.db"avec ceci :
db_path = os.environ['DATABASE_URL']Nous devons ensuite mettre à jour la ligne de notre Dockerfile qui dit :
RUN pip install gunicorn geventde dire :
RUN pip install gunicorn gevent psycopg2-binaryLe paquet psycopg2 est un adaptateur de base de données Postgres spécialement conçu pour Python.
La dernière étape consiste à créer une base de données Postgres sur Heroku, ce qui nécessite que vous vous connectiez d'abord au CLI Heroku et que vous créiez une application Heroku :
heroku login
heroku create <your application name>Ensuite, créez la base de données, en veillant à inclure le nom de votre application :
heroku addons:create heroku-postgresql:hobby-dev -a <your application name> Déployez votre conteneur sur Heroku
Avec votre application et votre base de données initialisées sur Heroku, il ne reste plus que quelques étapes nécessaires pour déployer votre application Dockerisée. Tout d'abord, vous devez définir vos identifiants Nexmo en tant que variables de configuration Heroku, ce qui se fait sur le tableau de bord Heroku sous "Settings" :
Config Vars interface in Heroku
Ensuite, vous devrez reconstruire votre conteneur Docker pour vous assurer que vous avez bien pris en compte les changements récents :
docker build --tag queue_app .Vous devez ensuite vous connecter au registre des conteneurs Heroku :
heroku container:loginEnfin, vous allez pousser et libérer votre conteneur sur Heroku :
heroku container:push web -a <your application name>
heroku container:release web -a <your application name>Ça y est ! Enfin, presque. Lancez votre application depuis votre tableau de bord Heroku pour vous assurer qu'elle fonctionne, puis allez dans votre tableau de bord Numbers et mettez à jour le champ Inbound Webhook URL pour qu'il ressemble à ceci : https://<your application name>.herokuapp.com/webhooks/inbound-sms.
Vous avez réussi ! Grâce à la puissance de la conteneurisation, vous disposez désormais d'une application prête pour la production, facilement évolutive et reproductible.
Si vous rencontrez des problèmes ou si vous avez des questions, contactez-nous sur notre Communauté Slack. Merci pour votre lecture !
