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:
-
Ein Benutzer ohne Rootberechtigung mit
sudo
-Berechtigungen, der gemäß den Schritten im Lernprogramm fürInitial Server Setupkonfiguriert wurde. -
Docker installiert mit den Anweisungen aus Schritt 1 und Schritt 2 vonHow To Install and Use Docker.
-
Docker Compose installiert mit den Anweisungen aus Schritt 1 vonHow To Install Docker Compose.
[[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.yml
kö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_name
definieren 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-stopped
besteht 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 Eigenschaftvolumes
definiert die Volumes, die der Dienst verwendet. In Ihrem Fall wird das Volumeappdata
im Container im Verzeichnis/var/www
bereitgestellt. Die Eigenschaftdepends_on
definiert einen Dienst, von dem Flask abhängt, um ordnungsgemäß zu funktionieren. In diesem Fall hängt der Dienst vonflask
vonmongodb
ab, da der Dienst vonmongodb
als Datenbank für Ihre Anwendung fungiert. depends_on
stellt sicher, dass der Dienstflask
nur ausgeführt wird, wenn der Dienstmongodb
ausgeführt wird.
Die Eigenschaftnetworks
gibtfrontend
undbackend
als die Netzwerke an, auf die der Dienstflask
Zugriff hat.
Wenn der Dienstflask
definiert 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 Eigenschaftcommand
definieren 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 DienstmongoDB
macht keine Ports verfügbar, sodass auf den Dienst nur über das Netzwerkbackend
zugegriffen 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 Eigenschaftports
wird der Nginx-Dienst so konfiguriert, dass er über:80
und:443
öffentlich zugänglich ist, undvolumes
stellt dasnginxdata
-Volumen im Container bei/var/log/nginx
bereit. 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.yml
hinzu, um die Volumes zu konfigurieren:
docker-compose.yml
. . .
volumes:
mongodbdata:
driver: local
appdata:
driver: local
nginxdata:
driver: local
Der Abschnittvolumes
de 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.yml
definiert. 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 Verzeichnisapp
fü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 von1000
icher zuweisen. Außerdem können Sie die UID / GID ändern, indem Sie dieGROUP_ID
undUSER_ID
entsprechend Ihren Anforderungen aktualisieren.
Die DirektiveWORKDIR
definiert das Arbeitsverzeichnis für den Container. Stellen Sie sicher, dass Sie das FeldLABEL MAINTAINER
durch 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 Verzeichnisapp
in das Verzeichnis/var/www
im Container zu kopieren. Als Nächstes verwendet die Docker-Datei die AnweisungRUN
, um Gunicorn und die in der Dateirequirements.txt
angegebenen 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/www
haben.
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 Benutzerwww
verwenden. 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 NginxDockerfile
verwendet 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.conf
in 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.conf
ein:
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 Blockserver
definiert. Die Anweisunglisten
definiert 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.txt
Folgendes 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 Paketos
wird zum Importieren der Umgebungsvariablen verwendet. Aus der Bibliothekflask
haben Sie die ObjekteFlask
,request
undjsonify
importiert, 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/todo
hinzu, 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.py
erstellt 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.py
ein, 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-compose
verwendet, 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 Dienstmongodb
festgelegt 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 Datenbankadmin
aufgelistet 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 Feldpwd
ein sicheres Kennwort verwenden. Dieuser
undpwd
hier sind die Werte, die Sie in der Dateidocker-compose.yml
im Abschnitt Umgebungsvariablen für den Dienstflask
definiert 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.yml
an die Anwendung übergeben. Die Konfiguration für die Datenbankverbindung wird mithilfe der VariablenMONGODB_*
festgelegt, die im Abschnittenvironment
des Dienstesflask
definiert sind.
Um alles zu testen, erstellen Sie mithilfe der Flask-API eine Aufgabennotiz. Sie können dies mit einer Curl-Anforderung vonPOST
an die Route von/todo
tun:
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/todo
auflisten:
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.