So richten Sie Flask mit MongoDB und Docker ein

Der Autor hat dieInternet Archiveausgewählt, um eine Spende im Rahmen desWrite for DOnations-Programms zu erhalten.

Einführung

Die Entwicklung von Webanwendungen kann komplex und zeitaufwendig werden, wenn verschiedene Technologien erstellt und gewartet werden. Die Berücksichtigung von Optionen mit geringerem Gewicht zur Reduzierung der Komplexität und der Produktionszeit für Ihre Anwendung kann zu einer flexibleren und skalierbareren Lösung führen. Als aufPython basierendes Micro-Web-Framework bietetFlask Entwicklern eine erweiterbare Möglichkeit, ihre Anwendungen durch Erweiterungen zu erweitern, die in Projekte integriert werden können. MongoDB ist eine NoSQL-Datenbank, die für die Skalierung und Arbeit mit häufigen Änderungen entwickelt wurde, um die Skalierbarkeit des Tech-Stacks eines Entwicklers fortzusetzen. Entwickler könnenDocker verwenden, um das Packen und Bereitstellen ihrer Anwendungen zu vereinfachen.

Docker Compose hat die Entwicklungsumgebung weiter vereinfacht, indem Sie Ihre Infrastruktur einschließlich Ihrer Anwendungsdienste, Netzwerkvolumes und Bindungsbereitstellungen in einer einzigen Datei definieren können. Die Verwendung von Docker Compose bietet eine einfache Verwendung beim Ausführen mehrererdocker container run-Befehle. Sie können alle Ihre Dienste in einer einzigen Compose-Datei definieren und mit einem einzigen Befehl alle Dienste aus Ihrer Konfiguration erstellen und starten. Dadurch wird sichergestellt, dass Ihre gesamte Containerinfrastruktur über eine Versionskontrolle verfügt. Docker Compose verwendet einen Projektnamen, um Umgebungen voneinander zu isolieren. Auf diese Weise können Sie mehrere Umgebungen auf einem einzigen Host ausführen.

In diesem Tutorial erstellen, verpacken und führen Sie Ihre Aufgaben-Webanwendung mit Flask, Nginx und MongoDB in Dockercontainers aus. Sie definieren die gesamte Stapelkonfiguration in einerdocker-compose.yml-Datei sowie Konfigurationsdateien für Python, MongoDB und Nginx. Für Flask ist ein Webserver erforderlich, um HTTP-Anforderungen zu bearbeiten. Daher verwenden Sie auchGunicorn, einen Python-WSGI-HTTP-Server, um die Anwendung zu bedienen. Nginx fungiert als Reverse-Proxy-Server, der Anfragen zur Verarbeitung an Gunicorn weiterleitet.

Voraussetzungen

Um diesem Tutorial zu folgen, benötigen Sie Folgendes:

[[Schritt-1 - Schreiben der Stapelkonfiguration in Docker-Compose]] == Schritt 1 - Schreiben der Stapelkonfiguration in Docker Compose

Wenn Sie Ihre Anwendungen in Docker erstellen, können Sie die Infrastruktur je nach den in Docker Compose vorgenommenen Konfigurationsänderungen problemlos versionieren. Die Infrastruktur kann in einer einzigen Datei definiert und mit einem einzigen Befehl erstellt werden. In diesem Schritt richten Sie die Dateidocker-compose.yml ein, um Ihre Flask-Anwendung auszuführen.

Mit der Dateidocker-compose.ymlkönnen Sie Ihre Anwendungsinfrastruktur als einzelne Dienste definieren. Die Dienste können miteinander verbunden werden und jedem kann einvolume für eine dauerhafte Speicherung zugeordnet sein. Volumes werden in einem Teil des von Docker verwalteten Host-Dateisystems gespeichert (/var/lib/docker/volumes/ unter Linux).

Volumes sind der beste Weg, um Daten in Docker zu speichern, da die Daten in den Volumes exportiert oder mit anderen Anwendungen geteilt werden können. Weitere Informationen zum Freigeben von Daten in Docker finden Sie unterHow To Share Data Between the Docker Container and the Host.

Erstellen Sie zunächst ein Verzeichnis für die Anwendung im Ausgangsverzeichnis auf Ihrem Server:

mkdir flaskapp

In das neu erstellte Verzeichnis verschieben:

cd flaskapp

Erstellen Sie als Nächstes die Dateidocker-compose.yml:

nano docker-compose.yml

Die Dateidocker-compose.yml beginnt mit einer Versionsnummer, die dieDocker Compose file version identifiziert. Docker Compose-Dateiversion3 zielt auf Docker Engine-Version1.13.0+ ab, was eine Voraussetzung für dieses Setup ist. Sie fügen auch dasservices-Tag hinzu, das Sie im nächsten Schritt definieren werden:

docker-compose.yml

version: '3'
services:

Sie definieren nunflask als ersten Dienst in Ihrerdocker-compose.yml-Datei. Fügen Sie den folgenden Code hinzu, um den Flask-Service zu definieren:

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

Die Eigenschaftbuild definiert diecontext des Builds. In diesem Fall der Ordnerapp, derDockerfile enthält.

Mit der Eigenschaftcontainer_namedefinieren Sie einen Namen für jeden Container. Die Eigenschaftimage gibt den Bildnamen und das Tag an, unter dem das Docker-Bild gekennzeichnet wird. Die Eigenschaftrestart definiert, wie der Container neu gestartet werden soll - in Ihrem Fall ist esunless-stopped. Dies bedeutet, dass Ihre Container nur angehalten werden, wenn die Docker Engine angehalten / neu gestartet wird oder wenn Sie die Container explizit anhalten. Der Vorteil der Verwendung der Eigenschaftunless-stoppedbesteht darin, dass die Container automatisch gestartet werden, sobald die Docker Engine neu gestartet wird oder ein Fehler auftritt.

Die Eigenschaftenvironment enthält die Umgebungsvariablen, die an den Container übergeben werden. Sie müssen ein sicheres Kennwort für die UmgebungsvariableMONGODB_PASSWORD angeben. Die Eigenschaftvolumesdefiniert die Volumes, die der Dienst verwendet. In Ihrem Fall wird das Volumeappdata im Container im Verzeichnis/var/www bereitgestellt. Die Eigenschaftdepends_ondefiniert einen Dienst, von dem Flask abhängt, um ordnungsgemäß zu funktionieren. In diesem Fall hängt der Dienst vonflaskvonmongodbab, da der Dienst vonmongodbals Datenbank für Ihre Anwendung fungiert. depends_on stellt sicher, dass der Dienstflasknur ausgeführt wird, wenn der Dienstmongodbausgeführt wird.

Die Eigenschaftnetworks gibtfrontend undbackend als die Netzwerke an, auf die der DienstflaskZugriff hat.

Wenn der Dienstflaskdefiniert ist, können Sie die MongoDB-Konfiguration zur Datei hinzufügen. In diesem Beispiel verwenden Sie das offizielle4.0.8-Versionsbildmongo. Fügen Sie Ihrerdocker-compose.yml-Datei den folgenden Code hinzu, der denflask service folgt:

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

Diecontainer_name für diesen Dienst sindmongodb mit einer Neustartrichtlinie vonunless-stopped. Mit der Eigenschaftcommanddefinieren Sie den Befehl, der beim Starten des Containers ausgeführt wird. Der Befehlmongod --auth deaktiviert die Anmeldung bei der MongoDB-Shell ohne Anmeldeinformationen, wodurch MongoDB durch Authentifizierung gesichert wird.

Die UmgebungsvariablenMONGO_INITDB_ROOT_USERNAME undMONGO_INITDB_ROOT_PASSWORD erstellen einen Root-Benutzer mit den angegebenen Anmeldeinformationen. Ersetzen Sie daher den Platzhalter durch ein sicheres Kennwort.

MongoDB speichert seine Daten standardmäßig in/data/db, daher werden die Daten im Ordner/data/db zur Persistenz auf das angegebene Volumemongodbdata geschrieben. Dadurch verlieren Sie Ihre Datenbanken bei einem Neustart nicht. Der DienstmongoDBmacht keine Ports verfügbar, sodass auf den Dienst nur über das Netzwerkbackendzugegriffen werden kann.

Als Nächstes definieren Sie den Webserver für Ihre Anwendung. Fügen Sie derdocker-compose.yml-Datei den folgenden Code hinzu, um Nginx zu konfigurieren:

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

Hier haben Siecontext vonbuild definiert. Dies ist der Ordnernginx, derDockerfile enthält. Mit der Eigenschaftimage geben Sie das Bild an, mit dem der Container markiert und ausgeführt wird. Mit der Eigenschaftportswird der Nginx-Dienst so konfiguriert, dass er über:80 und:443 öffentlich zugänglich ist, undvolumes stellt dasnginxdata-Volumen im Container bei/var/log/nginxbereit. s Verzeichnis.

Sie haben den Dienst, auf dem der Webserverdienstdepends_on ausgeführt wird, alsflask definiert. Schließlich definiert die Eigenschaftnetworks, dass der Webserverdienst des Netzwerks Zugriff auffrontend hat.

Als Nächstes erstellen Siebridge networks, damit die Container miteinander kommunizieren können. Fügen Sie die folgenden Zeilen an das Ende Ihrer Datei an:

docker-compose.yml

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

Sie haben zwei Netzwerke definiert -frontend undbackend - für die Dienste, mit denen eine Verbindung hergestellt werden soll. Die Front-End-Dienste wie Nginx stellen eine Verbindung zumfrontend-Netzwerk her, da es öffentlich zugänglich sein muss. Back-End-Dienste wie MongoDB stellen eine Verbindung zumbackend-Netzwerk her, um den unbefugten Zugriff auf den Dienst zu verhindern.

Als Nächstes verwenden Sie Volumes, um die Datenbank-, Anwendungs- und Konfigurationsdateien zu sichern. Da Ihre Anwendung die Datenbanken und Dateien verwendet, müssen die an ihnen vorgenommenen Änderungen unbedingt beibehalten werden. Die Volumes werden von Docker verwaltet und im Dateisystem gespeichert. Fügen Sie diesen Code zur Dateidocker-compose.ymlhinzu, um die Volumes zu konfigurieren:

docker-compose.yml

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

Der Abschnittvolumesde deklariert die Volumes, die die Anwendung zum Speichern von Daten verwendet. Hier haben Sie die Volumesmongodbdata,appdata undnginxdata definiert, um Ihre MongoDB-Datenbanken, Flask-Anwendungsdaten und die Nginx-Webserver-Protokolle beizubehalten. Alle diese Volumes verwenden einenlocal-Treiber, um die Daten lokal zu speichern. Die Volumes werden verwendet, um diese Daten zu speichern, sodass Daten wie MongoDB-Datenbanken und Nginx-Webserver-Protokolle verloren gehen können, wenn Sie die Container neu starten.

Ihre vollständigedocker-compose.yml-Datei sieht folgendermaßen aus:

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

Speichern Sie die Datei und beenden Sie den Editor, nachdem Sie Ihre Konfiguration überprüft haben.

Sie haben die Docker-Konfiguration für Ihren gesamten Anwendungsstapel in der Dateidocker-compose.ymldefiniert. Sie werden nun mit dem Schreiben der Dockerdateien für Flask und des Webservers fortfahren.

[[Schritt-2 - Schreiben der Docker-Dateien für die Flasche und den Webserver]] == Schritt 2 - Schreiben der Docker-Dateien für die Flasche und den Webserver

Mit Docker können Sie Container erstellen, um Ihre Anwendungen aus einer Datei namensDockerfile auszuführen. Die Docker-Datei ist ein Tool, mit dem Sie benutzerdefinierte Images erstellen können, mit denen Sie die für Ihre Anwendung erforderliche Software installieren und Ihre Container gemäß Ihren Anforderungen konfigurieren können. Sie können die von Ihnen erstellten benutzerdefinierten Bilder anDocker Hub oder eine beliebige private Registrierung senden.

In diesem Schritt schreiben Sie die Docking-Dateien für die Flask- und Webserver-Dienste. Erstellen Sie zunächst das Verzeichnisappfür Ihre Flask-Anwendung:

mkdir app

Erstellen Sie als Nächstes dieDockerfile für Ihre Flask-App im Verzeichnisapp:

nano app/Dockerfile

Fügen Sie der Datei den folgenden Code hinzu, um Ihren Flaschenbehälter anzupassen:

app/Dockerfile

FROM python:3.6.8-alpine3.9

LABEL MAINTAINER="FirstName LastName "

ENV GROUP_ID=1000 \
    USER_ID=1000

WORKDIR /var/www/

In diesemDockerfile erstellen Sie ein Image über dem3.6.8-alpine3.9 image, das auf Alpine 3.9 mit vorinstalliertem Python 3.6.8 basiert.

Die DirektiveENV wird verwendet, um die Umgebungsvariablen für unsere Gruppen- und Benutzer-ID zu definieren.
Linux Standard Base (LSB) gibt an, dassUIDs and GIDs 0-99 vom System statisch zugewiesen werden. UIDs 100-999 sollen dynamisch für Systembenutzer und Gruppen zugewiesen werden. UIDs 1000-59999 sollen dynamisch für Benutzerkonten zugewiesen werden. Vor diesem Hintergrund können Sie eine UID und eine GID von1000icher zuweisen. Außerdem können Sie die UID / GID ändern, indem Sie dieGROUP_ID undUSER_ID entsprechend Ihren Anforderungen aktualisieren.

Die DirektiveWORKDIRdefiniert das Arbeitsverzeichnis für den Container. Stellen Sie sicher, dass Sie das FeldLABEL MAINTAINERdurch Ihren Namen und Ihre E-Mail-Adresse ersetzen.

Fügen Sie den folgenden Codeblock hinzu, um die Flask-Anwendung in den Container zu kopieren und die erforderlichen Abhängigkeiten zu installieren:

app/Dockerfile

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

Der folgende Code verwendet die AnweisungADD, um Dateien aus dem lokalen Verzeichnisappin das Verzeichnis/var/wwwim Container zu kopieren. Als Nächstes verwendet die Docker-Datei die AnweisungRUN, um Gunicorn und die in der Dateirequirements.txtangegebenen Pakete zu installieren, die Sie später im Lernprogramm erstellen werden.

Der folgende Codeblock fügt einen neuen Benutzer und eine neue Gruppe hinzu und initialisiert die Anwendung:

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"]

Standardmäßig werden Docker-Container als Benutzer vonrootausgeführt. Der Benutzer vonroothat Zugriff auf alles im System, sodass die Auswirkungen einer Sicherheitsverletzung katastrophal sein können. Um dieses Sicherheitsrisiko zu verringern, wird ein neuer Benutzer und eine neue Gruppe erstellt, die nur Zugriff auf das Verzeichnis/var/wwwhaben.

Dieser Code verwendet zuerst den Befehladdgroup, um eine neue Gruppe mit dem Namenwww zu erstellen. Das Flag-g setzt die Gruppen-ID auf die VariableENV GROUP_ID=1000, die zuvor inDockerfile definiert wurde.

Die Zeilenadduser -D -u $USER_ID -G www www -s /bin/sh erstellen einen Benutzerwww mit einer Benutzer-ID von1000, wie durch die VariableENV definiert. Das Flag-s erstellt das Basisverzeichnis des Benutzers, falls es nicht vorhanden ist, und setzt die Standard-Anmeldeshell auf/bin/sh. Das Flag-G wird verwendet, um die anfängliche Anmeldegruppe des Benutzers aufwww zu setzen, die mit dem vorherigen Befehl erstellt wurde.

Der BefehlUSER definiert, dass die im Container ausgeführten Programme den Benutzerwwwverwenden. Gunicorn hört:5000 ab, also öffnen Sie diesen Port mit dem BefehlEXPOSE.

Schließlich führt die ZeileCMD [ "gunicorn", "-w", "4", "--bind", "0.0.0.0:5000", "wsgi"] den Befehl zum Starten des Gunicorn-Servers aus, wobei vier Mitarbeiter Port5000 abhören. Die Anzahl sollte im Allgemeinen zwischen 2 und 4 Workern pro Kern auf dem Server liegen. In der Gunicorn-Dokumentation wird(2 x $num_cores) + 1 als Anzahl der Worker empfohlen.

Ihre abgeschlossenenDockerfile sehen wie folgt aus:

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"]

Speichern Sie die Datei und beenden Sie den Texteditor.

Erstellen Sie als Nächstes ein neues Verzeichnis für Ihre Nginx-Konfiguration:

mkdir nginx

Erstellen Sie dann dieDockerfile für Ihren Nginx-Webserver im Verzeichnisnginx:

nano nginx/Dockerfile

Fügen Sie der Datei den folgenden Code hinzu, um die Docker-Datei zu erstellen, mit der das Image für Ihren Nginx-Container erstellt wird:

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;"]

Dieses NginxDockerfileverwendet einalpine-Basis-Image, eine winzige Linux-Distribution mit einer minimalen Angriffsfläche, die aus Sicherheitsgründen erstellt wurde.

In der DirektiveRUN installieren Sienginx und erstellen symbolische Links, um die Fehler- und Zugriffsprotokolle auf die Standardfehler (/dev/stderr) und die Ausgabe (/dev/stdout) zu veröffentlichen. Das Veröffentlichen von Fehlern in Standardfehlern und -ausgaben ist eine bewährte Methode, da Container kurzlebig sind. Dabei werden die Protokolle an Docker-Protokolle gesendet, und von dort aus können Sie die Protokolle an einen Protokollierungsdienst wie den Elastic-Stack weiterleiten, um die Dauerhaftigkeit zu gewährleisten. Danach werden Befehle ausgeführt, um diedefault.conf und/var/cache/apk/* zu entfernen, um die Größe des resultierenden Bildes zu verringern. Durch Ausführen aller dieser Befehle in einem einzigenRUN wird die Anzahl der Ebenen im Bild verringert, wodurch auch die Größe des resultierenden Bilds verringert wird.

Die AnweisungCOPY kopiert die Webserverkonfiguration vonapp.confin den Container. Die DirektiveEXPOSE stellt sicher, dass die Container die Ports:80 und:443 überwachen, da Ihre Anwendung auf:80 mit:443 als sicherem Port ausgeführt wird.

Schließlich definiert die AnweisungCMD den Befehl zum Starten des Nginx-Servers.

Speichern Sie die Datei und beenden Sie den Texteditor.

NachdemDockerfile bereit ist, können Sie den Nginx-Reverse-Proxy so konfigurieren, dass der Datenverkehr an die Flask-Anwendung weitergeleitet wird.

[[Schritt-3 - Konfigurieren des Nginx-Reverse-Proxys]] == Schritt 3 - Konfigurieren des Nginx-Reverse-Proxys

In diesem Schritt konfigurieren Sie Nginx als Reverse-Proxy, um Anforderungen an:5000 an Gunicorn weiterzuleiten. Ein Reverseproxyserver wird zum Weiterleiten von Clientanforderungen an den entsprechenden Back-End-Server verwendet. Es bietet eine zusätzliche Abstraktions- und Steuerungsebene, um einen reibungslosen Netzwerkverkehr zwischen Clients und Servern sicherzustellen.

Erstellen Sie zunächst das Verzeichnisnginx/conf.d:

mkdir nginx/conf.d

Um Nginx zu konfigurieren, müssen Sie eineapp.conf-Datei mit der folgenden Konfiguration im Ordnernginx/conf.d/ erstellen. Dieapp.conf-Datei enthält die Konfiguration, die der Reverse-Proxy benötigt, um die Anforderungen an Gunicorn weiterzuleiten.

nano nginx/conf.d/app.conf

Fügen Sie den folgenden Inhalt in die Dateiapp.confein:

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;
    }
}

Dies definiert zuerst dieupstream server, die üblicherweise verwendet werden, um einen Web- oder App-Server für das Routing oder den Lastausgleich anzugeben.

Ihr Upstream-Serverapp_server definiert die Serveradresse mit der Direktiveserver, die durch den Containernamenflask:5000 identifiziert wird.

Die Konfiguration für den Nginx-Webserver wird im Blockserverdefiniert. Die Anweisunglistendefiniert die Portnummer, an der Ihr Server auf eingehende Anforderungen wartet. Die Anweisungenerror_log undaccess_log definieren die Dateien zum Schreiben von Protokollen. Die Anweisungproxy_pass wird verwendet, um den Upstream-Server für die Weiterleitung der Anforderungen anhttp://app_server festzulegen.

Speichern und schließen Sie die Datei.

Wenn der Nginx-Webserver konfiguriert ist, können Sie mit dem Erstellen der Flask-Aufgaben-API fortfahren.

[[Schritt-4 - Erstellen der Flasche-zu-tun-API]] == Schritt 4 - Erstellen der Flasche-zu-tun-API

Nachdem Sie Ihre Umgebung erstellt haben, können Sie Ihre Anwendung erstellen. In diesem Schritt erstellen Sie eine Aufgaben-API-Anwendung, in der die Aufgaben-Notizen gespeichert und angezeigt werden, die von einer POST-Anforderung gesendet wurden.

Erstellen Sie zunächst die Dateirequirements.txt im Verzeichnisapp:

nano app/requirements.txt

Diese Datei wird verwendet, um die Abhängigkeiten für Ihre Anwendung zu installieren. Bei der Implementierung dieses Tutorials werdenFlask,Flask-PyMongo undrequests verwendet. Fügen Sie der Dateirequirements.txtFolgendes hinzu:

app/requirements.txt

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

Speichern Sie die Datei und beenden Sie den Editor, nachdem Sie die Anforderungen eingegeben haben.

Erstellen Sie als Nächstes die Dateiapp.py, die den Flask-Anwendungscode im Verzeichnisapp enthält:

nano app/app.py

Geben Sie in Ihrer neuenapp.py-Datei den Code ein, um die Abhängigkeiten zu importieren:

app/app.py

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

Das Paketoswird zum Importieren der Umgebungsvariablen verwendet. Aus der Bibliothekflask haben Sie die ObjekteFlask,request undjsonifyimportiert, um die Anwendung zu instanziieren, Anforderungen zu verarbeiten und JSON-Antworten zu senden. Ausflask_pymongo haben Sie dasPyMongo-Objekt importiert, um mit der MongoDB zu interagieren.

Fügen Sie als Nächstes den Code hinzu, der für die Verbindung mit MongoDB erforderlich ist:

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

DasFlask(__name__) lädt das Anwendungsobjekt in die Variableapplication. Als Nächstes erstellt der Code die MongoDB-Verbindungszeichenfolge aus den Umgebungsvariablen mitos.environ. Wenn Sie dasapplication-Objekt an diePyMongo()-Methode übergeben, erhalten Sie dasmongo-Objekt, das wiederum dasdb-Objekt vonmongo.db ergibt.

Nun fügen Sie den Code hinzu, um eine Indexnachricht zu erstellen:

app/app.py

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

Das@application.route('/') definiert die/ GET-Route Ihrer API. Hier gibt Ihreindex()-Funktion eine JSON-Zeichenfolge mit derjsonify-Methode zurück.

Fügen Sie als Nächstes die Route von/todohinzu, um alle Aufgaben aufzulisten:

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
    )

Das@application.route('/todo') definiert die/todo GET-Route Ihrer API, die die Aufgaben in der Datenbank zurückgibt. Die Methodedb.todo.find() gibt alle Aufgaben in der Datenbank zurück. Als Nächstes iterieren Sie über_todos, um einitem zu erstellen, das nur dieid undtodo aus den Objekten enthält, die sie an eindata-Array anhängen, und kehrt schließlich zurück sie als JSON.

Fügen Sie als Nächstes den Code zum Erstellen der Aufgabe hinzu:

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

Das@application.route('/todo') definiert die/todo POST-Route Ihrer API, wodurch eine Aufgabennotiz in der Datenbank erstellt wird. Mitrequest.get_json(force=True) wird der JSON abgerufen, den Sie auf der Route veröffentlichen, und mititem wird der JSON erstellt, der in der Aufgabe gespeichert wird. Mitdb.todo.insert_one(item) wird ein Element in die Datenbank eingefügt. Nachdem die Aufgabe in der Datenbank gespeichert wurde, geben Sie eine JSON-Antwort mit dem Statuscode201 CREATED zurück.

Nun fügen Sie den Code hinzu, um die Anwendung auszuführen:

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)

Die Bedingung__name__ == "__main__" wird verwendet, um zu überprüfen, ob die globale Variable__name__ im Modul der Einstiegspunkt für Ihr Programm ist,"__main__" ist, und dann die Anwendung auszuführen. Wenn__name__ gleich"__main__" ist, führt der Code im Blockif die App mit diesem Befehlapplication.run(host='0.0.0.0', port=ENVIRONMENT_PORT, debug=ENVIRONMENT_DEBUG) aus.

Als nächstes erhalten wir die Werte fürENVIRONMENT_DEBUG undENVIRONMENT_PORT aus den Umgebungsvariablen unter Verwendung vonos.environ.get(), wobei der Schlüssel als erster Parameter und der Standardwert als zweiter Parameter verwendet werden. Mitapplication.run() werden die Werte fürhost,port unddebug für die Anwendung festgelegt.

Die fertigeapp.py-Datei sieht folgendermaßen aus:

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)

Speichern Sie die Datei und beenden Sie den Editor.

Erstellen Sie als Nächstes die Dateiwsgi.py im Verzeichnisapp.

nano app/wsgi.py

Die Dateiwsgi.pyerstellt ein Anwendungsobjekt (oder ein aufrufbares Objekt), damit der Server es verwenden kann. Bei jeder Anforderung verwendet der Server dieses Anwendungsobjekt, um die Anforderungshandler der Anwendung beim Parsen der URL auszuführen.

Fügen Sie den folgenden Inhalt in die Dateiwsgi.pyein, speichern Sie die Datei und beenden Sie den Texteditor:

app/wsgi.py

from app import application

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

Diesewsgi.py-Datei importiert das Anwendungsobjekt aus derapp.py-Datei und erstellt ein Anwendungsobjekt für den Gunicorn-Server.

Die Aufgaben-App ist nun vorhanden, sodass Sie die Anwendung in Containern ausführen können.

[[Schritt 5 - Erstellen und Ausführen der Container] == Schritt 5 - Erstellen und Ausführen der Container

Nachdem Sie alle Dienste in Ihrerdocker-compose.yml-Datei und deren Konfigurationen definiert haben, können Sie die Container starten.

Da die Dienste in einer einzelnen Datei definiert sind, müssen Sie einen einzelnen Befehl absetzen, um die Container zu starten, die Volumes zu erstellen und die Netzwerke einzurichten. Dieser Befehl erstellt auch das Image für Ihre Flask-Anwendung und den Nginx-Webserver. Führen Sie den folgenden Befehl aus, um die Container zu erstellen:

docker-compose up -d

Wenn Sie den Befehl zum ersten Mal ausführen, werden alle erforderlichen Docker-Images heruntergeladen. Dies kann einige Zeit dauern. Sobald die Bilder heruntergeladen und auf Ihrem lokalen Computer gespeichert wurden, erstelltdocker-compose Ihre Container. Das-d-Flag dämonisiert den Prozess, sodass er als Hintergrundprozess ausgeführt werden kann.

Verwenden Sie den folgenden Befehl, um die ausgeführten Container aufzulisten, sobald der Erstellungsprozess abgeschlossen ist:

docker ps

Sie sehen eine Ausgabe ähnlich der folgenden:

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

CONTAINER ID ist eine eindeutige Kennung, die für den Zugriff auf Container verwendet wird. DasIMAGE definiert den Bildnamen für den angegebenen Container. Das FeldNAMES ist der Dienstname, unter dem Container erstellt werden, ähnlich wieCONTAINER ID, mit denen auf Container zugegriffen werden kann. Schließlich liefertSTATUS Informationen zum Status des Containers, unabhängig davon, ob er ausgeführt, neu gestartet oder gestoppt wird.

Sie haben den Befehldocker-composeverwendet, um Ihre Container aus Ihren Konfigurationsdateien zu erstellen. Im nächsten Schritt erstellen Sie einen MongoDB-Benutzer für Ihre Anwendung.

[[Schritt 6 - Erstellen eines Benutzers für Ihre Mongodb-Datenbank] == Schritt 6 - Erstellen eines Benutzers für Ihre MongoDB-Datenbank

Standardmäßig ermöglicht MongoDB Benutzern die Anmeldung ohne Anmeldeinformationen und gewährt unbegrenzte Berechtigungen. In diesem Schritt sichern Sie Ihre MongoDB-Datenbank, indem Sie einen dedizierten Benutzer für den Zugriff darauf erstellen.

Dazu benötigen Sie den Root-Benutzernamen und das Kennwort, die Sie in den DateiumgebungsvariablenMONGO_INITDB_ROOT_USERNAME undMONGO_INITDB_ROOT_PASSWORD für den Dienstmongodbfestgelegt haben. Im Allgemeinen ist es besser, bei der Interaktion mit der Datenbank die Verwendung des root-Administratorkontos zu vermeiden. Stattdessen erstellen Sie einen dedizierten Datenbankbenutzer für Ihre Flask-Anwendung sowie eine neue Datenbank, auf die die Flask-App zugreifen darf.

Um einen neuen Benutzer zu erstellen, starten Sie zunächst eine interaktive Shell im Containermongodb:

docker exec -it mongodb bash

Sie verwenden den Befehldocker exec, um einen Befehl in einem laufenden Container auszuführen, zusammen mit dem Flag-it, um eine interaktive Shell im Container auszuführen.

Melden Sie sich im Container beim Administratorkonto von MongoDBrootan:

mongo -u mongodbuser -p

Sie werden aufgefordert, das Kennwort einzugeben, das Sie als Wert für die VariableMONGO_INITDB_ROOT_PASSWORD in der Dateidocker-compose.yml eingegeben haben. Das Kennwort kann geändert werden, indem im Dienstmongodb ein neuer Wert fürMONGO_INITDB_ROOT_PASSWORD festgelegt wird. In diesem Fall müssen Sie den Befehldocker-compose up -d erneut ausführen.

Führen Sie den Befehlshow dbs; aus, um alle Datenbanken aufzulisten:

show dbs;

Sie werden die folgende Ausgabe sehen:

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

Dieadmin-Datenbank ist eine spezielle Datenbank, die Benutzern Administratorrechte erteilt. Wenn ein Benutzer Lesezugriff auf dieadmin-Datenbank hat, verfügt er über Lese- und Schreibberechtigungen für alle anderen Datenbanken. Da in der Ausgabe die Datenbankadminaufgelistet ist, hat der Benutzer Zugriff auf diese Datenbank und kann daher alle anderen Datenbanken lesen und in diese schreiben.

Wenn Sie die erste Notiz speichern, werden automatischcreate the MongoDB database angezeigt. Mit MongoDB können Sie mit dem Befehluse database zu einer Datenbank wechseln, die nicht vorhanden ist. Es erstellt eine Datenbank, wenn ein Dokument in einer Sammlung gespeichert wird. Daher wird die Datenbank hier nicht angelegt. Dies geschieht, wenn Sie Ihre erste Notiz in der Datenbank über die API speichern. Führen Sie den Befehluse aus, um zur Datenbankflaskdb zu wechseln:

use flaskdb

Erstellen Sie als Nächstes einen neuen Benutzer, der auf diese Datenbank zugreifen darf:

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

Dieser Befehl erstellt einen Benutzer mit dem Namenflaskuser mitreadWrite Zugriff auf die Datenbankflaskdb. Stellen Sie sicher, dass Sie im Feldpwdein sicheres Kennwort verwenden. Dieuser undpwd hier sind die Werte, die Sie in der Dateidocker-compose.yml im Abschnitt Umgebungsvariablen für den Dienstflaskdefiniert haben.

Melden Sie sich mit dem folgenden Befehl bei der authentifizierten Datenbank an:

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

Nachdem Sie den Benutzer hinzugefügt haben, melden Sie sich von der Datenbank ab.

exit

Und zum Schluss verlassen Sie den Container:

exit

Sie haben jetzt eine dedizierte Datenbank und ein Benutzerkonto für Ihre Flask-Anwendung konfiguriert. Die Datenbankkomponenten sind bereit, sodass Sie jetzt mit der Ausführung der Flask-Aufgaben-App fortfahren können.

[[Schritt-7 - Ausführen der Flasche-zu-tun-App]] == Schritt 7 - Ausführen der Flasche-zu-tun-App

Nachdem Ihre Dienste konfiguriert und ausgeführt wurden, können Sie Ihre Anwendung testen, indem Sie in einem Browser zuhttp://your_server_ip navigieren. Darüber hinaus können Siecurl ausführen, um die JSON-Antwort von Flask anzuzeigen:

curl -i http://your_server_ip

Sie erhalten folgende Antwort:

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

Die Konfiguration für die Flask-Anwendung wird aus der Dateidocker-compose.ymlan die Anwendung übergeben. Die Konfiguration für die Datenbankverbindung wird mithilfe der VariablenMONGODB_*festgelegt, die im Abschnittenvironmentdes Dienstesflaskdefiniert sind.

Um alles zu testen, erstellen Sie mithilfe der Flask-API eine Aufgabennotiz. Sie können dies mit einer Curl-Anforderung vonPOSTan die Route von/todotun:

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

Diese Anforderung führt zu einer Antwort mit dem Statuscode201 CREATED, wenn das zu erledigende Element in MongoDB gespeichert wird:

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

Sie können alle Aufgabenhinweise von MongoDB mit einer GET-Anforderung an die Route von/todoauflisten:

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

Damit haben Sie eine Flask-API angedockt, auf der ein MongoDB-Backend mit Nginx als Reverse-Proxy ausgeführt wird, der auf Ihren Servern bereitgestellt wird. In einer Produktionsumgebung können Siesudo systemctl enable docker verwenden, um sicherzustellen, dass Ihr Docker-Dienst zur Laufzeit automatisch gestartet wird.

Fazit

In diesem Lernprogramm haben Sie eine Flask-Anwendung mit Docker, MongoDB, Nginx und Gunicorn bereitgestellt. Sie haben jetzt eine funktionierende moderne zustandslose API-Anwendung, die skaliert werden kann. Obwohl Sie dieses Ergebnis mit einem Befehl wiedocker container run erzielen können, vereinfachtdocker-compose.yml Ihre Arbeit, da dieser Stapel in die Versionskontrolle gestellt und bei Bedarf aktualisiert werden kann.

Von hier aus können Sie auch einen Blick auf unsere weiterenPython Framework tutorialswerfen.

Related