Comment configurer un flacon avec MongoDB et Docker

L'auteur a sélectionné lesInternet Archive pour recevoir un don dans le cadre du programmeWrite for DOnations.

introduction

Le développement d'applications Web peut devenir complexe et prendre beaucoup de temps lors de la création et de la maintenance d'un certain nombre de technologies différentes. Si vous envisagez des options plus légères conçues pour réduire la complexité et le temps de production de votre application, vous obtiendrez une solution plus flexible et évolutive. En tant que micro-framework Web basé surPython,Flask offre aux développeurs un moyen extensible de développer leurs applications grâce à des extensions pouvant être intégrées dans des projets. Pour continuer l'évolutivité de la pile technologique d'un développeur,MongoDB est une base de données NoSQL conçue pour évoluer et fonctionner avec des changements fréquents. Les développeurs peuvent utiliserDocker pour simplifier le processus d'empaquetage et de déploiement de leurs applications.

Docker Compose a encore simplifié l'environnement de développement en vous permettant de définir votre infrastructure, y compris vos services d'application, vos volumes réseau et vos montages de liaison, dans un seul fichier. L'utilisation de Docker Compose facilite l'utilisation de l'exécution de plusieurs commandesdocker container run. Il vous permet de définir tous vos services dans un seul fichier Compose. En une seule commande, vous créez et démarrez tous les services à partir de votre configuration. Cela garantit qu’il existe un contrôle de version dans l’ensemble de votre infrastructure de conteneur. Docker Compose utilise un nom de projet pour isoler les environnements les uns des autres, ce qui vous permet d'exécuter plusieurs environnements sur un seul hôte.

Dans ce didacticiel, vous allez créer, empaqueter et exécuter votre application Web à faire avec Flask, Nginx et MongoDB à l'intérieur de Dockercontainers. Vous définirez toute la configuration de la pile dans un fichierdocker-compose.yml, ainsi que les fichiers de configuration pour Python, MongoDB et Nginx. Flask nécessite un serveur Web pour traiter les requêtes HTTP, vous utiliserez donc égalementGunicorn, qui est un serveur HTTP Python WSGI, pour servir l'application. Nginx agit comme un serveur proxy inverse qui transmet les demandes à Gunicorn pour traitement.

Conditions préalables

Pour suivre ce tutoriel, vous aurez besoin des éléments suivants:

[[step-1 -—- writing-the-stack-configuration-in-docker-compose]] == Étape 1 - Ecriture de la configuration de la pile dans Docker Compose

Construire vos applications sur Docker vous permet de mettre à niveau l’infrastructure en fonction des modifications apportées à la configuration dans Docker Compose. L'infrastructure peut être définie dans un seul fichier et construite avec une seule commande. Dans cette étape, vous allez configurer le fichierdocker-compose.yml pour exécuter votre application Flask.

Le fichierdocker-compose.yml vous permet de définir votre infrastructure d'application en tant que services individuels. Les services peuvent être connectés les uns aux autres et chacun peut être associé à unvolume pour un stockage persistant. Les volumes sont stockés dans une partie du système de fichiers hôte géré par Docker (/var/lib/docker/volumes/ sous Linux).

Les volumes constituent le meilleur moyen de conserver des données dans Docker, car les données des volumes peuvent être exportées ou partagées avec d'autres applications. Pour plus d'informations sur le partage de données dans Docker, vous pouvez vous référer àHow To Share Data Between the Docker Container and the Host.

Pour commencer, créez un répertoire pour l'application dans le répertoire de base de votre serveur:

mkdir flaskapp

Déplacez-vous dans le répertoire nouvellement créé:

cd flaskapp

Ensuite, créez le fichierdocker-compose.yml:

nano docker-compose.yml

Le fichierdocker-compose.yml commence par un numéro de version qui identifie lesDocker Compose file version. La version du fichier Docker Compose3 cible la version1.13.0+ de Docker Engine, ce qui est une condition préalable à cette configuration. Vous ajouterez également la baliseservices que vous définirez à l'étape suivante:

docker-compose.yml

version: '3'
services:

Vous allez maintenant définirflask comme premier service dans votre fichierdocker-compose.yml. Ajoutez le code suivant pour définir le service Flask:

docker-compose.yml

. . .
  flask:
    build:
      context: app
      dockerfile: Dockerfile
    container_name: flask
    image: digitalocean.com/flask-python:3.6
    restart: unless-stopped
    environment:
      APP_ENV: "prod"
      APP_DEBUG: "False"
      APP_PORT: 5000
      MONGODB_DATABASE: flaskdb
      MONGODB_USERNAME: flaskuser
      MONGODB_PASSWORD: your_mongodb_password
      MONGODB_HOSTNAME: mongodb
    volumes:
      - appdata:/var/www
    depends_on:
      - mongodb
    networks:
      - frontend
      - backend

La propriétébuild définit lescontext de la construction. Dans ce cas, le dossierapp qui contiendra lesDockerfile.

Vous utilisez la propriétécontainer_name pour définir un nom pour chaque conteneur. La propriétéimage spécifie le nom de l'image et ce que l'image Docker sera balisée. La propriétérestart définit comment le conteneur doit être redémarré - dans votre cas, il s'agit deunless-stopped. Cela signifie que vos conteneurs ne seront arrêtés que lorsque le moteur Docker est arrêté / redémarré ou lorsque vous arrêtez explicitement les conteneurs. L'avantage de l'utilisation de la propriétéunless-stopped est que les conteneurs démarrent automatiquement une fois que le moteur Docker est redémarré ou qu'une erreur se produit.

La propriétéenvironment contient les variables d'environnement qui sont transmises au conteneur. Vous devez fournir un mot de passe sécurisé pour la variable d'environnementMONGODB_PASSWORD. La propriétévolumes définit les volumes que le service utilise. Dans votre cas, le volumeappdata est monté à l'intérieur du conteneur dans le répertoire/var/www. La propriétédepends_on définit un service dont Flask dépend pour fonctionner correctement. Dans ce cas, le serviceflask dépendra demongodb puisque le servicemongodb sert de base de données pour votre application. depends_on garantit que le serviceflask ne s'exécute que si le servicemongodb est en cours d'exécution.

La propriéténetworks spécifiefrontend etbackend comme réseaux auxquels le serviceflask aura accès.

Une fois le serviceflask défini, vous êtes prêt à ajouter la configuration MongoDB au fichier. Dans cet exemple, vous utiliserez l'image officielle de4.0.8 versionmongo. Ajoutez le code suivant à votre fichierdocker-compose.yml en suivant lesflask service:

docker-compose.yml

. . .
  mongodb:
    image: mongo:4.0.8
    container_name: mongodb
    restart: unless-stopped
    command: mongod --auth
    environment:
      MONGO_INITDB_ROOT_USERNAME: mongodbuser
      MONGO_INITDB_ROOT_PASSWORD: your_mongodb_root_password
      MONGO_INITDB_DATABASE: flaskdb
      MONGODB_DATA_DIR: /data/db
      MONDODB_LOG_DIR: /dev/null
    volumes:
      - mongodbdata:/data/db
    networks:
      - backend

Lecontainer_name pour ce service estmongodb avec une politique de redémarrage deunless-stopped. Vous utilisez la propriétécommand pour définir la commande qui sera exécutée au démarrage du conteneur. La commandemongod --auth désactivera la connexion au shell MongoDB sans informations d'identification, ce qui sécurisera MongoDB en exigeant une authentification.

Les variables d'environnementMONGO_INITDB_ROOT_USERNAME etMONGO_INITDB_ROOT_PASSWORD créent un utilisateur root avec les informations d'identification données, assurez-vous donc de remplacer l'espace réservé par un mot de passe fort.

MongoDB stocke ses données dans/data/db par défaut, donc les données dans le dossier/data/db seront écrites dans le volume nommémongodbdata pour la persistance. Ainsi, vous ne perdrez pas vos bases de données en cas de redémarrage. Le servicemongoDB n'expose aucun port, donc le service ne sera accessible que via le réseaubackend.

Ensuite, vous allez définir le serveur Web pour votre application. Ajoutez le code suivant à votre fichierdocker-compose.yml pour configurer Nginx:

docker-compose.yml

. . .
  webserver:
    build:
      context: nginx
      dockerfile: Dockerfile
    image: digitalocean.com/webserver:latest
    container_name: webserver
    restart: unless-stopped
    environment:
      APP_ENV: "prod"
      APP_NAME: "webserver"
      APP_DEBUG: "false"
      SERVICE_NAME: "webserver"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - nginxdata:/var/log/nginx
    depends_on:
      - flask
    networks:
      - frontend

Ici, vous avez défini lescontext dubuild, qui est le dossiernginx contenant lesDockerfile. Avec la propriétéimage, vous spécifiez l'image utilisée pour baliser et exécuter le conteneur. La propriétéports configurera le service Nginx pour qu'il soit accessible au public via:80 et:443 etvolumes monte le volumenginxdata à l'intérieur du conteneur à/var/log/nginx répertoire.

Vous avez défini le service sur lequel le service de serveur Webdepends_on estflask. Enfin, la propriéténetworks définit le service de serveur Web des réseaux qui aura accès auxfrontend.

Ensuite, vous allez créerbridge networks pour permettre aux conteneurs de communiquer entre eux. Ajoutez les lignes suivantes à la fin de votre fichier:

docker-compose.yml

. . .
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

Vous avez défini deux réseaux -frontend etbackend - pour les services auxquels se connecter. Les services frontaux, tels que Nginx, se connecteront au réseaufrontend car il doit être accessible au public. Les services back-end, tels que MongoDB, se connecteront au réseau debackend pour empêcher tout accès non autorisé au service.

Vous utiliserez ensuite des volumes pour conserver la base de données, l'application et les fichiers de configuration. Puisque votre application utilisera les bases de données et les fichiers, il est impératif de conserver les modifications qui y ont été apportées. Les volumes sont gérés par Docker et stockés sur le système de fichiers. Ajoutez ce code au fichierdocker-compose.yml pour configurer les volumes:

docker-compose.yml

. . .
volumes:
  mongodbdata:
    driver: local
  appdata:
    driver: local
  nginxdata:
    driver: local

La sectionvolumes déclare les volumes que l'application utilisera pour conserver les données. Ici, vous avez défini les volumesmongodbdata,appdata etnginxdata pour la persistance de vos bases de données MongoDB, des données d'application Flask et des journaux du serveur Web Nginx, respectivement. Tous ces volumes utilisent un pilotelocal pour stocker les données localement. Les volumes sont utilisés pour conserver ces données afin que des données telles que vos bases de données MongoDB et les journaux du serveur Web Nginx puissent être perdus au redémarrage des conteneurs.

Votre fichierdocker-compose.yml complet ressemblera à ceci:

docker-compose.yml

version: '3'
services:

  flask:
    build:
      context: app
      dockerfile: Dockerfile
    container_name: flask
    image: digitalocean.com/flask-python:3.6
    restart: unless-stopped
    environment:
      APP_ENV: "prod"
      APP_DEBUG: "False"
      APP_PORT: 5000
      MONGODB_DATABASE: flaskdb
      MONGODB_USERNAME: flaskuser
      MONGODB_PASSWORD: your_mongodb_password
      MONGODB_HOSTNAME: mongodb
    volumes:
      - appdata:/var/www
    depends_on:
      - mongodb
    networks:
      - frontend
      - backend

  mongodb:
    image: mongo:4.0.8
    container_name: mongodb
    restart: unless-stopped
    command: mongod --auth
    environment:
      MONGO_INITDB_ROOT_USERNAME: mongodbuser
      MONGO_INITDB_ROOT_PASSWORD: your_mongodb_root_password
      MONGO_INITDB_DATABASE: flaskdb
      MONGODB_DATA_DIR: /data/db
      MONDODB_LOG_DIR: /dev/null
    volumes:
      - mongodbdata:/data/db
    networks:
      - backend

  webserver:
    build:
      context: nginx
      dockerfile: Dockerfile
    image: digitalocean.com/webserver:latest
    container_name: webserver
    restart: unless-stopped
    environment:
      APP_ENV: "prod"
      APP_NAME: "webserver"
      APP_DEBUG: "true"
      SERVICE_NAME: "webserver"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - nginxdata:/var/log/nginx
    depends_on:
      - flask
    networks:
      - frontend

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

volumes:
  mongodbdata:
    driver: local
  appdata:
    driver: local
  nginxdata:
    driver: local

Enregistrez le fichier et quittez l'éditeur après avoir vérifié votre configuration.

Vous avez défini la configuration Docker pour l'ensemble de votre pile d'applications dans le fichierdocker-compose.yml. Vous allez maintenant écrire les fichiers Dockerfiles pour Flask et le serveur Web.

[[step-2 -—- writing-the-flask-and-web-server-dockerfiles]] == Étape 2 - Ecriture des Dockerfiles Flask et Web Server

Avec Docker, vous pouvez créer des conteneurs pour exécuter vos applications à partir d'un fichier appeléDockerfile. Dockerfile est un outil qui vous permet de créer des images personnalisées que vous pouvez utiliser pour installer le logiciel requis par votre application et configurer vos conteneurs en fonction de vos besoins. Vous pouvez pousser les images personnalisées que vous créez versDocker Hub ou n'importe quel registre privé.

Dans cette étape, vous écrivez les fichiers Docker pour les services Flask et Web Server. Pour commencer, créez le répertoireapp pour votre application Flask:

mkdir app

Ensuite, créez lesDockerfile pour votre application Flask dans le répertoireapp:

nano app/Dockerfile

Ajoutez le code suivant au fichier pour personnaliser votre conteneur Flask:

app/Dockerfile

FROM python:3.6.8-alpine3.9

LABEL MAINTAINER="FirstName LastName "

ENV GROUP_ID=1000 \
    USER_ID=1000

WORKDIR /var/www/

Dans ceDockerfile, vous créez une image au-dessus des3.6.8-alpine3.9 image basée sur Alpine 3.9 avec Python 3.6.8 pré-installé.

La directiveENV est utilisée pour définir les variables d'environnement pour notre groupe et notre ID utilisateur.
Linux Standard Base (LSB) spécifie queUIDs and GIDs 0-99 sont alloués statiquement par le système. LesUIDs 100-999 sont censés être alloués dynamiquement aux utilisateurs et groupes du système. LesUIDs 1000-59999 sont censés être alloués dynamiquement aux comptes utilisateurs. En gardant cela à l'esprit, vous pouvez attribuer en toute sécurité un UID et un GID de1000, en outre, vous pouvez modifier l'UID / GID en mettant à jour lesGROUP_ID etUSER_ID pour répondre à vos besoins.

La directiveWORKDIR définit le répertoire de travail du conteneur. Assurez-vous de remplacer le champLABEL MAINTAINER par votre nom et votre adresse e-mail.

Ajoutez le bloc de code suivant pour copier l'application Flask dans le conteneur et installez les dépendances nécessaires:

app/Dockerfile

. . .
ADD ./requirements.txt /var/www/requirements.txt
RUN pip install -r requirements.txt
ADD . /var/www/
RUN pip install gunicorn

Le code suivant utilisera la directiveADD pour copier les fichiers du répertoire localapp vers le répertoire/var/www du conteneur. Ensuite, le Dockerfile utilisera la directiveRUN pour installer Gunicorn et les packages spécifiés dans le fichierrequirements.txt, que vous créerez plus tard dans le didacticiel.

Le bloc de code suivant ajoute un nouvel utilisateur et un nouveau groupe et initialise l'application:

app/Dockerfile

. . .
RUN addgroup -g $GROUP_ID www
RUN adduser -D -u $USER_ID -G www www -s /bin/sh

USER www

EXPOSE 5000

CMD [ "gunicorn", "-w", "4", "--bind", "0.0.0.0:5000", "wsgi"]

Par défaut, les conteneurs Docker s'exécutent en tant qu'utilisateurroot. L'utilisateur deroot a accès à tout dans le système, les implications d'une faille de sécurité peuvent donc être désastreuses. Pour atténuer ce risque de sécurité, cela créera un nouvel utilisateur et un groupe qui n'auront accès qu'au répertoire/var/www.

Ce code utilisera d'abord la commandeaddgroup pour créer un nouveau groupe nomméwww. L'indicateur-g définira l'ID de groupe sur la variableENV GROUP_ID=1000 définie précédemment dans lesDockerfile.

Les lignesadduser -D -u $USER_ID -G www www -s /bin/sh créent un utilisateurwww avec un ID utilisateur de1000, comme défini par la variableENV. L'indicateur-s crée le répertoire personnel de l'utilisateur s'il n'existe pas et définit le shell de connexion par défaut sur/bin/sh. L'indicateur-G est utilisé pour définir le groupe de connexion initial de l'utilisateur surwww, qui a été créé par la commande précédente.

La commandeUSER définit que les programmes exécutés dans le conteneur utiliseront l'utilisateurwww. Gunicorn écoutera sur:5000, vous ouvrirez donc ce port avec la commandeEXPOSE.

Enfin, la ligneCMD [ "gunicorn", "-w", "4", "--bind", "0.0.0.0:5000", "wsgi"] exécute la commande pour démarrer le serveur Gunicorn avec quatre workers écoutant sur le port5000. Le nombre doit généralement être compris entre 2 et 4 nœuds de calcul par cœur dans le serveur, la documentation de Gunicorn recommande(2 x $num_cores) + 1 comme nombre de nœuds de calcul pour commencer.

VosDockerfile complétés ressembleront à ceci:

app/Dockerfile

FROM python:3.6.8-alpine3.9

LABEL MAINTAINER="FirstName LastName "

ENV GROUP_ID=1000 \
    USER_ID=1000

WORKDIR /var/www/

ADD . /var/www/
RUN pip install -r requirements.txt
RUN pip install gunicorn

RUN addgroup -g $GROUP_ID www
RUN adduser -D -u $USER_ID -G www www -s /bin/sh

USER www

EXPOSE 5000

CMD [ "gunicorn", "-w", "4", "--bind", "0.0.0.0:5000", "wsgi"]

Enregistrez le fichier et quittez l'éditeur de texte.

Ensuite, créez un nouveau répertoire pour contenir votre configuration Nginx:

mkdir nginx

Ensuite, créez lesDockerfile pour votre serveur Web Nginx dans le répertoirenginx:

nano nginx/Dockerfile

Ajoutez le code suivant au fichier pour créer le fichier Dockerfile qui générera l'image pour votre conteneur Nginx:

nginx/Dockerfile

FROM digitalocean.com/alpine:latest

LABEL MAINTAINER="FirstName LastName "

RUN apk --update add nginx && \
    ln -sf /dev/stdout /var/log/nginx/access.log && \
    ln -sf /dev/stderr /var/log/nginx/error.log && \
    mkdir /etc/nginx/sites-enabled/ && \
    mkdir -p /run/nginx && \
    rm -rf /etc/nginx/conf.d/default.conf && \
    rm -rf /var/cache/apk/*

COPY conf.d/app.conf /etc/nginx/conf.d/app.conf

EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]

Ce NginxDockerfile utilise une image de base dealpine, qui est une petite distribution Linux avec une surface d'attaque minimale conçue pour la sécurité.

Dans la directiveRUN, vous installeznginx et créez des liens symboliques pour publier l'erreur et accéder aux journaux d'erreur standard (/dev/stderr) et de sortie (/dev/stdout). La publication des erreurs dans les erreurs et sorties standard est une pratique recommandée, car les conteneurs sont éphémères. Ainsi, les journaux sont expédiés vers les journaux du menu fixe et à partir de là, vous pouvez transférer vos journaux vers un service de journalisation tel que la pile Elastic pour la persistance. Une fois cela fait, des commandes sont exécutées pour supprimer lesdefault.conf et/var/cache/apk/* afin de réduire la taille de l'image résultante. L'exécution de toutes ces commandes en un seulRUN diminue le nombre de calques dans l'image, ce qui réduit également la taille de l'image résultante.

La directiveCOPY copie la configuration du serveur Webapp.conf à l'intérieur du conteneur. La directiveEXPOSE garantit que les conteneurs écoutent sur les ports:80 et:443, car votre application fonctionnera sur:80 avec:443 comme port sécurisé.

Enfin, la directiveCMD définit la commande pour démarrer le serveur Nginx.

Enregistrez le fichier et quittez l'éditeur de texte.

Maintenant que leDockerfile est prêt, vous êtes prêt à configurer le proxy inverse Nginx pour acheminer le trafic vers l'application Flask.

[[step-3 -—- configuration-the-nginx-reverse-proxy]] == Étape 3 - Configuration du proxy inverse Nginx

Dans cette étape, vous allez configurer Nginx en tant que proxy inverse pour transmettre les requêtes à Gunicorn sur:5000. Un serveur proxy inverse est utilisé pour diriger les demandes des clients vers le serveur principal approprié. Il fournit une couche supplémentaire d'abstraction et de contrôle pour assurer la fluidité du trafic réseau entre les clients et les serveurs.

Commencez par créer le répertoirenginx/conf.d:

mkdir nginx/conf.d

Pour configurer Nginx, vous devez créer un fichierapp.conf avec la configuration suivante dans le dossiernginx/conf.d/. Le fichierapp.conf contient la configuration dont le proxy inverse a besoin pour transmettre les requêtes à Gunicorn.

nano nginx/conf.d/app.conf

Placez le contenu suivant dans le fichierapp.conf:

nginx/conf.d/app.conf

upstream app_server {
    server flask:5000;
}

server {
    listen 80;
    server_name _;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    client_max_body_size 64M;

    location / {
        try_files $uri @proxy_to_app;
    }

    location @proxy_to_app {
        gzip_static on;

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_buffering off;
        proxy_redirect off;
        proxy_pass http://app_server;
    }
}

Cela définira d'abord lesupstream server, qui sont couramment utilisés pour spécifier un serveur Web ou d'application pour le routage ou l'équilibrage de charge.

Votre serveur en amont,app_server, définit l'adresse du serveur avec la directiveserver, qui est identifiée par le nom de conteneurflask:5000.

La configuration du serveur Web Nginx est définie dans le blocserver. La directivelisten définit le numéro de port sur lequel votre serveur écoutera les requêtes entrantes. Les directiveserror_log etaccess_log définissent les fichiers pour l'écriture des journaux. La directiveproxy_pass est utilisée pour configurer le serveur en amont pour transmettre les requêtes àhttp://app_server.

Enregistrez et fermez le fichier.

Avec le serveur Web Nginx configuré, vous pouvez passer à la création de l’API Flask to-do.

[[step-4 -—- creation-the-flask-to-do-api]] == Étape 4 - Création de l'API Flask To-do

Maintenant que vous avez créé votre environnement, vous êtes prêt à créer votre application. Dans cette étape, vous allez écrire une application API de tâches à effectuer qui enregistrera et affichera les notes de tâches envoyées à partir d'une demande POST.

Commencez par créer le fichierrequirements.txt dans le répertoireapp:

nano app/requirements.txt

Ce fichier est utilisé pour installer les dépendances de votre application. L'implémentation de ce didacticiel utiliseraFlask,Flask-PyMongo etrequests. Ajoutez ce qui suit au fichierrequirements.txt:

app/requirements.txt

Flask==1.0.2
Flask-PyMongo==2.2.0
requests==2.20.1

Enregistrez le fichier et quittez l'éditeur après avoir entré les conditions requises.

Ensuite, créez le fichierapp.py pour contenir le code d'application Flask dans le répertoireapp:

nano app/app.py

Dans votre nouveau fichierapp.py, entrez le code pour importer les dépendances:

app/app.py

import os
from flask import Flask, request, jsonify
from flask_pymongo import PyMongo

Le packageos est utilisé pour importer les variables d'environnement. À partir de la bibliothèqueflask, vous avez importé les objetsFlask,request etjsonify pour instancier l'application, gérer les demandes et envoyer des réponses JSON, respectivement. Depuisflask_pymongo, vous avez importé l'objetPyMongo pour interagir avec MongoDB.

Ensuite, ajoutez le code nécessaire pour vous connecter à MongoDB:

app/app.py

. . .
application = Flask(__name__)

application.config["MONGO_URI"] = 'mongodb://' + os.environ['MONGODB_USERNAME'] + ':' + os.environ['MONGODB_PASSWORD'] + '@' + os.environ['MONGODB_HOSTNAME'] + ':27017/' + os.environ['MONGODB_DATABASE']

mongo = PyMongo(application)
db = mongo.db

LeFlask(__name__) charge l'objet d'application dans la variableapplication. Ensuite, le code construit la chaîne de connexion MongoDB à partir des variables d'environnement à l'aide deos.environ. Passer l'objetapplication à la méthodePyMongo() vous donnera l'objetmongo, qui à son tour vous donnera l'objetdb demongo.db.

Maintenant, vous allez ajouter le code pour créer un message d'index:

app/app.py

. . .
@application.route('/')
def index():
    return jsonify(
        status=True,
        message='Welcome to the Dockerized Flask MongoDB app!'
    )

Le@application.route('/') définit la route GET/ de votre API. Ici, votre fonctionindex() renvoie une chaîne JSON en utilisant la méthodejsonify.

Ensuite, ajoutez la route/todo pour lister toutes les tâches:

app/app.py

. . .
@application.route('/todo')
def todo():
    _todos = db.todo.find()

    item = {}
    data = []
    for todo in _todos:
        item = {
            'id': str(todo['_id']),
            'todo': todo['todo']
        }
        data.append(item)

    return jsonify(
        status=True,
        data=data
    )

Le@application.route('/todo') définit la route GET/todo de votre API, qui renvoie les tâches dans la base de données. La méthodedb.todo.find() renvoie toutes les tâches de la base de données. Ensuite, vous parcourez les_todos pour construire unitem qui inclut uniquement lesid ettodo des objets en les ajoutant à un tableaudata et retourne finalement eux comme JSON.

Ensuite, ajoutez le code pour la création de la tâche:

app/app.py

. . .
@application.route('/todo', methods=['POST'])
def createTodo():
    data = request.get_json(force=True)
    item = {
        'todo': data['todo']
    }
    db.todo.insert_one(item)

    return jsonify(
        status=True,
        message='To-do saved successfully!'
    ), 201

Le@application.route('/todo') définit la route POST/todo de votre API, qui crée une note à faire dans la base de données. Lerequest.get_json(force=True) obtient le JSON que vous publiez sur la route, etitem est utilisé pour créer le JSON qui sera enregistré dans la tâche. Ledb.todo.insert_one(item) est utilisé pour insérer un élément dans la base de données. Une fois la tâche enregistrée dans la base de données, vous renvoyez une réponse JSON avec un code d'état201 CREATED.

Maintenant, vous ajoutez le code pour exécuter l'application:

app/app.py

. . .
if __name__ == "__main__":
    ENVIRONMENT_DEBUG = os.environ.get("APP_DEBUG", True)
    ENVIRONMENT_PORT = os.environ.get("APP_PORT", 5000)
    application.run(host='0.0.0.0', port=ENVIRONMENT_PORT, debug=ENVIRONMENT_DEBUG)

La condition__name__ == "__main__" permet de vérifier si la variable globale,__name__, dans le module est le point d'entrée de votre programme, est"__main__", puis lancez l'application. Si le__name__ est égal à"__main__" alors le code à l'intérieur du blocif exécutera l'application en utilisant cette commandeapplication.run(host='0.0.0.0', port=ENVIRONMENT_PORT, debug=ENVIRONMENT_DEBUG).

Ensuite, nous obtenons les valeurs desENVIRONMENT_DEBUG etENVIRONMENT_PORT à partir des variables d'environnement en utilisantos.environ.get(), en utilisant la clé comme premier paramètre et la valeur par défaut comme deuxième paramètre. Leapplication.run() définit les valeurshost,port etdebug pour l'application.

Le fichierapp.py terminé ressemblera à ceci:

app/app.py

import os
from flask import Flask, request, jsonify
from flask_pymongo import PyMongo

application = Flask(__name__)

application.config["MONGO_URI"] = 'mongodb://' + os.environ['MONGODB_USERNAME'] + ':' + os.environ['MONGODB_PASSWORD'] + '@' + os.environ['MONGODB_HOSTNAME'] + ':27017/' + os.environ['MONGODB_DATABASE']

mongo = PyMongo(application)
db = mongo.db

@application.route('/')
def index():
    return jsonify(
        status=True,
        message='Welcome to the Dockerized Flask MongoDB app!'
    )

@application.route('/todo')
def todo():
    _todos = db.todo.find()

    item = {}
    data = []
    for todo in _todos:
        item = {
            'id': str(todo['_id']),
            'todo': todo['todo']
        }
        data.append(item)

    return jsonify(
        status=True,
        data=data
    )

@application.route('/todo', methods=['POST'])
def createTodo():
    data = request.get_json(force=True)
    item = {
        'todo': data['todo']
    }
    db.todo.insert_one(item)

    return jsonify(
        status=True,
        message='To-do saved successfully!'
    ), 201

if __name__ == "__main__":
    ENVIRONMENT_DEBUG = os.environ.get("APP_DEBUG", True)
    ENVIRONMENT_PORT = os.environ.get("APP_PORT", 5000)
    application.run(host='0.0.0.0', port=ENVIRONMENT_PORT, debug=ENVIRONMENT_DEBUG)

Enregistrez le fichier et quittez l'éditeur.

Ensuite, créez le fichierwsgi.py dans le répertoireapp.

nano app/wsgi.py

Le fichierwsgi.py crée un objet d'application (ou appelable) afin que le serveur puisse l'utiliser. Chaque fois qu'une demande est reçue, le serveur utilise cet objet d'application pour exécuter les gestionnaires de demandes de l'application lors de l'analyse de l'URL.

Placez le contenu suivant dans le fichierwsgi.py, enregistrez le fichier et quittez l'éditeur de texte:

app/wsgi.py

from app import application

if __name__ == "__main__":
  application.run()

Ce fichierwsgi.py importe l'objet application à partir du fichierapp.py et crée un objet application pour le serveur Gunicorn.

L'application Tâches est maintenant en place, vous êtes donc prêt à commencer à exécuter l'application dans des conteneurs.

[[step-5 -—- building-and-running-the-containers]] == Étape 5 - Construction et exécution des conteneurs

Maintenant que vous avez défini tous les services dans votre fichierdocker-compose.yml et leurs configurations, vous pouvez démarrer les conteneurs.

Les services étant définis dans un seul fichier, vous devez émettre une seule commande pour démarrer les conteneurs, créer les volumes et configurer les réseaux. Cette commande crée également l'image de votre application Flask et du serveur Web Nginx. Exécutez la commande suivante pour construire les conteneurs:

docker-compose up -d

Lors de la première exécution de la commande, toutes les images Docker nécessaires seront téléchargées, ce qui peut prendre un certain temps. Une fois les images téléchargées et stockées sur votre machine locale,docker-compose créera vos conteneurs. L'indicateur-d démonise le processus, ce qui lui permet de s'exécuter en arrière-plan.

Utilisez la commande suivante pour répertorier les conteneurs en cours d'exécution une fois le processus de génération terminé:

docker ps

Vous verrez une sortie semblable à celle-ci:

OutputCONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                                      NAMES
f20e9a7fd2b9        digitalocean.com/webserver:latest   "nginx -g 'daemon of…"   2 weeks ago         Up 2 weeks          0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   webserver
3d53ea054517        digitalocean.com/flask-python:3.6   "gunicorn -w 4 --bin…"   2 weeks ago         Up 2 weeks          5000/tcp                                   flask
96f5a91fc0db        mongo:4.0.8                     "docker-entrypoint.s…"   2 weeks ago         Up 2 weeks          27017/tcp                                  mongodb

LeCONTAINER ID est un identifiant unique utilisé pour accéder aux conteneurs. LeIMAGE définit le nom de l'image pour le conteneur donné. Le champNAMES est le nom du service sous lequel les conteneurs sont créés, similaire àCONTAINER ID, ils peuvent être utilisés pour accéder aux conteneurs. Enfin, leSTATUS fournit des informations sur l'état du conteneur, qu'il soit en cours d'exécution, redémarré ou arrêté.

Vous avez utilisé la commandedocker-compose pour créer vos conteneurs à partir de vos fichiers de configuration. Dans l'étape suivante, vous allez créer un utilisateur MongoDB pour votre application.

[[step-6 -—- creation-a-user-for-your-mongodb-database]] == Étape 6 - Création d'un utilisateur pour votre base de données MongoDB

Par défaut, MongoDB permet aux utilisateurs de se connecter sans informations d'identification et accorde des privilèges illimités. Dans cette étape, vous allez sécuriser votre base de données MongoDB en créant un utilisateur dédié pour y accéder.

Pour ce faire, vous aurez besoin du nom d'utilisateur et du mot de passe root que vous avez définis dans les variables d'environnement du fichierdocker-compose.ymlMONGO_INITDB_ROOT_USERNAME etMONGO_INITDB_ROOT_PASSWORD pour le servicemongodb. En général, il est préférable d’éviter d’utiliser le compte administratif racine lors de l’interaction avec la base de données. Au lieu de cela, vous allez créer un utilisateur de base de données dédié pour votre application Flask, ainsi qu'une nouvelle base de données à laquelle l'application Flask sera autorisée à accéder.

Pour créer un nouvel utilisateur, démarrez d'abord un shell interactif sur le conteneurmongodb:

docker exec -it mongodb bash

Vous utilisez la commandedocker exec afin d'exécuter une commande dans un conteneur en cours d'exécution avec l'indicateur-it pour exécuter un shell interactif à l'intérieur du conteneur.

Une fois à l'intérieur du conteneur, connectez-vous au compte administratif de MongoDBroot:

mongo -u mongodbuser -p

Vous serez invité à entrer le mot de passe que vous avez entré comme valeur de la variableMONGO_INITDB_ROOT_PASSWORD dans le fichierdocker-compose.yml. Le mot de passe peut être modifié en définissant une nouvelle valeur pour leMONGO_INITDB_ROOT_PASSWORD dans le servicemongodb, auquel cas vous devrez réexécuter la commandedocker-compose up -d.

Exécutez la commandeshow dbs; pour répertorier toutes les bases de données:

show dbs;

Vous verrez la sortie suivante:

Outputadmin    0.000GB
config   0.000GB
local    0.000GB
5 rows in set (0.00 sec)

La base de donnéesadmin est une base de données spéciale qui accorde des autorisations administratives aux utilisateurs. Si un utilisateur a un accès en lecture à la base de donnéesadmin, il aura des droits de lecture et d'écriture sur toutes les autres bases de données. Puisque la sortie répertorie la base de donnéesadmin, l'utilisateur a accès à cette base de données et peut donc lire et écrire dans toutes les autres bases de données.

La sauvegarde de la première note à faire sera automatiquementcreate the MongoDB database. MongoDB vous permet de basculer vers une base de données qui n'existe pas à l'aide de la commandeuse database. Il crée une base de données lorsqu'un document est enregistré dans une collection. Par conséquent, la base de données n'est pas créée ici. cela se produira lorsque vous sauvegarderez votre première note dans la base de données à partir de l'API. Exécutez la commandeuse pour basculer vers la base de donnéesflaskdb:

use flaskdb

Ensuite, créez un nouvel utilisateur qui sera autorisé à accéder à cette base de données:

db.createUser({user: 'flaskuser', pwd: 'your password', roles: [{role: 'readWrite', db: 'flaskdb'}]})
exit

Cette commande crée un utilisateur nomméflaskuser avec un accèsreadWrite à la base de donnéesflaskdb. Assurez-vous d'utiliser un mot de passe sécurisé dans le champpwd. Lesuser etpwd ici sont les valeurs que vous avez définies dans le fichierdocker-compose.yml sous la section des variables d'environnement pour le serviceflask.

Connectez-vous à la base de données authentifiée avec la commande suivante:

mongo -u flaskuser -p your password --authenticationDatabase flaskdb

Maintenant que vous avez ajouté l'utilisateur, déconnectez-vous de la base de données.

exit

Et enfin, sortez du conteneur:

exit

Vous avez maintenant configuré une base de données et un compte utilisateur dédiés pour votre application Flask. Les composants de la base de données sont prêts. Vous pouvez maintenant exécuter l'application Flask to-do.

[[step-7 -—- running-the-flask-to-do-app]] == Étape 7 - Exécution de l'application Flask To-do

Maintenant que vos services sont configurés et en cours d'exécution, vous pouvez tester votre application en accédant àhttp://your_server_ip dans un navigateur. De plus, vous pouvez exécutercurl pour voir la réponse JSON de Flask:

curl -i http://your_server_ip

Vous recevrez la réponse suivante:

Output{"message":"Welcome to the Dockerized Flask MongoDB app!","status":true}

La configuration de l'application Flask est transmise à l'application à partir du fichierdocker-compose.yml. La configuration de la connexion à la base de données est définie à l'aide des variablesMONGODB_* définies dans la sectionenvironment du serviceflask.

Pour tout tester, créez une note à l'aide de l'API Flask. Vous pouvez le faire avec une requête curlPOST vers la route/todo:

curl -i -H "Content-Type: application/json" -X POST -d '{"todo": "Dockerize Flask application with MongoDB backend"}' http://your_server_ip/todo

Cette demande aboutit à une réponse avec un code d'état de201 CREATED lorsque l'élément à faire est enregistré dans MongoDB:

Output{"message":"To-do saved successfully!","status":true}

Vous pouvez lister toutes les notes de tâches de MongoDB avec une requête GET à la route/todo:

curl -i http://your_server_ip/todo
Output{"data":[{"id":"5c9fa25591cb7b000a180b60","todo":"Dockerize Flask application with MongoDB backend"}],"status":true}

Avec cela, vous avez dockérisé une API Flask exécutant un backend MongoDB avec Nginx en tant que proxy inverse déployé sur vos serveurs. Pour un environnement de production, vous pouvez utilisersudo systemctl enable docker pour vous assurer que votre service Docker démarre automatiquement au moment de l'exécution.

Conclusion

Dans ce didacticiel, vous avez déployé une application Flask avec Docker, MongoDB, Nginx et Gunicorn. Vous disposez maintenant d'une application API moderne sans état qui fonctionne et peut être mise à l'échelle. Bien que vous puissiez obtenir ce résultat en utilisant une commande commedocker container run, ledocker-compose.yml simplifie votre travail car cette pile peut être placée dans le contrôle de version et mise à jour si nécessaire.

De là, vous pouvez également jeter un œil à nos autresPython Framework tutorials.