Tutorial

Bedienen von Flask-Anwendungen mit Gunicorn und Nginx unter Ubuntu 20.04

NginxUbuntuPythonPython FrameworksUbuntu 20.04

Einführung

In diesem Leitfaden erstellen Sie eine Python-Anwendung mithilfe des Flask-Microframeworks unter Ubuntu 20.04. Im Großteil dieses Artikels geht es um die Einrichtung des Gunicorn Anwendungsservers und das Starten der Anwendung sowie um die Konfiguration von Nginx als Front-end-Reverse-Proxy.

Voraussetzungen

Bevor Sie mit diesem Leitfaden beginnen, sollten Sie folgende Voraussetzungen erfüllt haben:

Schritt 1 — Installieren der Komponenten aus den Ubuntu-Repositorys

Unser erster Schritt besteht darin, alle Elemente zu installieren, die aus den Ubuntu-Repositorys benötigt werden. Dazu gehört pip, der Python-Paketmanager, der unsere Python-Komponenten verwalten wird. Außerdem erhalten wir die zum Erstellen einiger Gunicorn-Komponenten benötigten Python-Entwicklungsdateien.

Wir aktualisieren zuerst den lokalen Paketindex und installieren die Pakete, mit denen wir unsere Python-Umgebung aufbauen können. Dazu gehören python3-pip sowie einige weitere Pakete und Entwicklungs-Tools, die für eine robuste Programmierumgebung erforderlich sind:

  • sudo apt update
  • sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools

Wenn diese Pakete eingerichtet sind, fahren wir mit der Erstellung einer virtuellen Umgebung für unser Projekt fort.

Schritt 2 — Erstellen einer virtuellen Python-Umgebung

Als Nächstes richten wir eine virtuelle Umgebung ein, um unsere Flask-Anwendung von anderen Python-Dateien auf dem System zu isolieren.

Beginnen Sie mit der Installation des python3-venv Pakets, womit das venv Modul installiert wird:

  • sudo apt install python3-venv

Als Nächstes erstellen wir ein übergeordnetes Verzeichnis für unser Flask-Projekt. Nachdem Sie es erstellt haben, wechseln Sie in das Verzeichnis:

  • mkdir ~/myproject
  • cd ~/myproject

Erstellen Sie eine virtuelle Umgebung, um die Python-Anforderungen Ihres Flask-Projekts zu speichern, indem Sie Folgendes eingeben:

  • python3 -m venv myprojectenv

Auf diese Weise wird eine lokale Kopie von Python und pip in ein Verzeichnis mit dem Namen myprojectenv innerhalb Ihres Projektverzeichnisses installiert.

Bevor Sie Anwendungen innerhalb der virtuellen Umgebung installieren, müssen Sie sie aktivieren. Geben Sie hierfür Folgendes ein:

  • source myprojectenv/bin/activate

Ihre Eingabeaufforderung ändert sich und zeigt an, dass Sie jetzt innerhalb der virtuellen Umgebung arbeiten. Sie sieht etwa wie folgt aus: (myprojectenv)user@host:~/myproject$.

Schritt 3 — Einrichten einer Flask-Anwendung

Sie befinden sich nun in Ihrer virtuellen Umgebung und können somit Flask und Gunicorn installieren und mit der Gestaltung Ihrer Anwendung beginnen.

Wir installieren zuerst wheel mit der lokalen Instanz von pip, um sicherzustellen, dass unsere Pakete installiert werden, selbst wenn wheel-Archive fehlen:

  • pip install wheel

Hinweis
Unabhängig davon, welche Version von Python Sie verwenden, wenn die virtuelle Umgebung aktiviert ist, sollten Sie den Befehl pip (nicht pip3) verwenden.

Als Nächstes installieren wir Flask und Gunicorn:

  • pip install gunicorn flask

Erstellen einer Beispiel-App

Nachdem Sie jetzt über Flask verfügen, können Sie eine einfache Anwendung erstellen. Flask ist ein Microframework. Es enthält nicht viele der Tools, die vollwertige Frameworks möglicherweise ausmachen, und besteht hauptsächlich als Modul, das Sie in Ihre Projekte importieren können, um Sie bei der Initialisierung einer Webanwendung zu unterstützen.

Während Ihre Anwendung möglicherweise komplexer ist, erstellen wir unsere Flask-App in einer einzigen Datei mit dem Namen myproject.py:

  • nano ~/myproject/myproject.py

Der Anwendungscode ist in dieser Datei enthalten. Er importiert Flask und instanziiert ein Flask-Objekt. Sie können damit die Funktionen definieren, die nach der Anfrage einer bestimmten Route ausgeführt werden sollen:

~/myproject/myproject.py
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1 style='color:blue'>Hello There!</h1>"

if __name__ == "__main__":
    app.run(host='0.0.0.0')

Dies definiert im Wesentlichen, welche Inhalte beim Zugriff auf die Stammdomäne gezeigt werden. Speichern und schließen Sie abschließend die Datei.

Wenn Sie den Leitfaden zur Servereinrichtung befolgt haben, sollte Ihre UFW-Firewall aktiviert sein. Um die Anwendung zu testen, müssen Sie den Zugriff auf Port 5000 zulassen:

  • sudo ufw allow 5000

Geben Sie Folgendes ein, um Ihre Flask-App zu testen:

  • python myproject.py

Sie sehen eine Ausgabe ähnlich wie die Folgende mit einer praktischen Warnung, die Sie darauf hinweist, diese Servereinrichtung nicht produktiv zu nutzen:

Output
* Serving Flask app "myproject" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: off * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

Gehen Sie auf die IP-Adresse Ihres Servers, gefolgt von :5000 in Ihrem Webbrowser:

http://your_server_ip:5000

Sie sollten in etwa Folgendes sehen:

Flask Beispiel-App

Wenn Sie fertig sind, betätigen Sie STRG-C in Ihrem Terminalfenster, um den Flask-Entwicklungsserver anzuhalten.

Erstellen des WSGI-Einstiegspunkts

Als Nächstes erstellen wir eine Datei, die als Einstiegspunkt für unsere Anwendung dient. Diese sagt dem Gunicorn-Server, wie die Interaktion mit der Anwendung erfolgt.

Wir wollen die Datei wsgi.py nennen:

  • nano ~/myproject/wsgi.py

In diese Datei importieren wir die Flask-Instanz aus unserer Anwendung und führen sie dann aus:

~/myproject/wsgi.py
from myproject import app

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

Wenn Sie fertig sind, speichern und schließen Sie die Datei.

Schritt 4 — Konfigurieren von Gunicorn

Ihre Anwendung ist jetzt mit einem bestehenden Einstiegspunkt geschrieben. Wir können jetzt mit der Konfiguration von Gunicorn fortfahren.

Bevor wir fortfahren, sollten wir überprüfen, ob Gunicorn die Anwendung korrekt bedienen kann.

Dazu geben wir einfach den Namen des Einstiegspunktes weiter. Dieser ist als der Name des Moduls (minus die Erweiterung .py) plus der Name des Callable innerhalb der Anwendung aufgebaut. In unserem Fall ist das wsgi:app.

Außerdem geben wir die Schnittstelle und den Port an, damit die Anwendung auf einer öffentlich verfügbaren Schnittstelle gestartet wird:

  • cd ~/myproject
  • gunicorn --bind 0.0.0.0:5000 wsgi:app

Sie sollten eine Ausgabe wie die folgende sehen:

Output
[2020-05-20 14:13:00 +0000] [46419] [INFO] Starting gunicorn 20.0.4 [2020-05-20 14:13:00 +0000] [46419] [INFO] Listening at: http://0.0.0.0:5000 (46419) [2020-05-20 14:13:00 +0000] [46419] [INFO] Using worker: sync [2020-05-20 14:13:00 +0000] [46421] [INFO] Booting worker with pid: 46421

Gehen Sie auf die IP-Adresse Ihres Servers mit :5000, das am Ende Ihres Webbrowsers erneut angehängt ist:

http://your_server_ip:5000

Sie sollten die Ausgabe Ihrer Anwendung sehen:

Flask Beispiel-App

Wenn Sie die ordnungsgemäße Funktionsweise bestätigt haben, betätigen Sie STRG-C in Ihrem Terminalfenster.

Wir sind jetzt mit unserer virtuellen Umgebung fertig und können sie deaktivieren:

  • deactivate

Alle Python-Befehle nutzen jetzt wieder die Python-Umgebung des Systems.

Als Nächstes erstellen wir die systemd-Diensteinheit-Datei. Das Erstellen einer systemd Unit-Datei ermöglicht, dass das Init-System von Ubuntu Gunicorn automatisch startet und die Flask-Anwendung bei jedem Hochfahren des Servers bedient wird.

Erstellen Sie zunächst eine Unit-Datei, die auf .service endet, innerhalb des Verzeichnisses /etc/systemd/system:

  • sudo nano /etc/systemd/system/myproject.service

Darin beginnen wir mit dem Abschnitt [Unit], mit dem Metadaten und Abhängigkeiten angegeben werden. Wir wollen hier eine Beschreibung unseres Dienstes eingeben und dem Init-System mitteilen, ihn nur zu starten, nachdem das Netzwerkziel erreicht wurde:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

Als Nächstes wollen wir den Abschnitt [Service] öffnen. Damit werden der Benutzer und die Gruppe angegeben, unter denen wir den Prozess ausführen möchten. Wir wollen unserem regulären Benutzerkonto die Prozessverantwortung übergeben, da es alle relevanten Dateien besitzt. Wir wollen außerdem der Gruppe www-data die Gruppenverantwortung übertragen, damit Nginx einfach mit den Gunicorn-Prozessen kommunizieren kann. Denken Sie daran, den Benutzername hier durch Ihren Benutzernamen zu ersetzen:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data

Als Nächstes wollen wir das Arbeitsverzeichnis darstellen und die Umgebungsvariable PATH setzen, damit das Init-System weiß, dass sich die ausführbaren Daten für den Prozess innerhalb unserer virtuellen Umgebung befinden. Wir wollen auch den Befehl zum Starten des Dienstes angeben. Dieser Befehl führt Folgendes aus:

  • Starten von 3 Worker-Prozessen (bei Bedarf anpassen)
  • Erstellen und binden an eine myproject.sock Unix Socket-Datei, innerhalb unseres Projektverzeichnisses. Wir setzen einen umask-Wert von 007 ein, um die Socket-Datei zu erstellen, die den Zugriff auf den Eigentümer und die Gruppe erteilt, aber den Zugriff anderer einschränkt.
  • Geben Sie den WSGI Einstiegspunkt-Dateinamen zusammen mit dem Python Callable innerhalb dieser Datei (wsgi:app) an.

Systemd erfordert, dass wir der ausführbaren Gunicorn den vollen Pfad geben, der innerhalb unserer virtuellen Umgebung installiert ist.

Denken Sie daran, den Benutzernamen und die Projektpfade durch Ihre eigenen Informationen zu ersetzen:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myproject
Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

Schließlich fügen wir einen [Install]-Abschnitt hinzu. Dies teilt systemd mit, womit dieser Dienst verknüpft werden soll, wenn wir festlegen, dass er während des Startvorgangs starten soll. Wir wollen, dass dieser Dienst startet, wenn das normale Mehrbenutzersystem arbeitet.

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myproject
Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

[Install]
WantedBy=multi-user.target

Damit ist unsere systemd-Dienstdatei fertiggestellt. Speichern und schließen Sie diese jetzt.

Wir können nun den Gunicorn-Dienst starten, den wir erstellt haben, und festlegen, dass er während des Startvorgangs startet:

  • sudo systemctl start myproject
  • sudo systemctl enable myproject

Wir wollen den Status überprüfen:

  • sudo systemctl status myproject

Sie sollten eine Ausgabe wie diese sehen:

Output
● myproject.service - Gunicorn instance to serve myproject Loaded: loaded (/etc/systemd/system/myproject.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2020-05-20 14:15:18 UTC; 1s ago Main PID: 46430 (gunicorn) Tasks: 4 (limit: 2344) Memory: 51.3M CGroup: /system.slice/myproject.service ├─46430 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─46449 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─46450 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app └─46451 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

Wenn Sie Fehler sehen, müssen Sie sie beheben, bevor Sie mit dem Tutorial fortfahren.

Schritt 5 — Konfigurieren von Nginx für Proxy-Anfragen

Unser Gunicorn-Anwendungsserver sollte nun ausgeführt werden und darauf warten, dass Anfragen an die Socket-Datei im Projektverzeichnis gestellt werden. Wir wollen nun konfigurieren, dass Nginx Webanfragen an diesen Socket übergibt, indem wir einige kleine Ergänzungen zu dieser Konfigurationsdatei hinzufügen.

Beginnen Sie mit der Erstellung einer neuen Serverblockkonfigurationsdatei im Nginx-Verzeichnis sites-available. Wir wollen es myproject nennen, um die Einheitlichkeit mit diesen Leitfaden zu wahren:

  • sudo nano /etc/nginx/sites-available/myproject

Öffnen Sie einen Serverblock und weisen Sie Nginx an, den Standardport 80 abzuhören. Wir wollen ihm außerdem mitteilen, diesen Block für Anfragen für den Domänennamen unseres Servers zu verwenden:

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name your_domain www.your_domain;
}

Als Nächstes fügen wir eineSpeicherort hinzu, der mit jeder Anfrage übereinstimmt. Innerhalb dieses Blocks schließen wir die Datei proxy_params ein, die einige allgemeine Proxying-Parameter angibt, die eingestellt werden müssen. Wir übergeben dann die Anfragen an das Socket, das wir mit der Anweisung proxy_pass definiert haben.

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name your_domain www.your_domain;

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/sammy/myproject/myproject.sock;
    }
}

Speichern und schließen Sie abschließend die Datei.

Um die Nginx-Serverblockkonfiguration zu aktivieren, die Sie gerade erstellt haben, verknüpfen Sie die Datei mit dem Verzeichnis sites-enabled:

  • sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

Mit der Datei in diesem Verzeichnis können Sie auf Syntaxfehler testen:

  • sudo nginx -t

Wenn eine Rückmeldung ohne Fehleranzeige erfolgt, starten Sie den Nginx-Prozess neu, um die neue Konfiguration auszulesen:

  • sudo systemctl restart nginx

Abschließend wollen wir noch einmal die Firewall anpassen. Wir brauchen keinen Zugriff über Port 5000, sodass wir diese Regel entfernen können. Wir können dann vollen Zugriff auf den Nginx-Server zulassen:

  • sudo ufw delete allow 5000
  • sudo ufw allow 'Nginx Full'

Sie sollten nun in Ihrem Webbrowser zu dem Domänennamen Ihres Servers navigieren können:

http://your_domain

Sie sollten die Ausgabe Ihrer Anwendung sehen:

Flask Beispiel-App

Wenn Sie Fehler feststellen, versuchen Sie, Folgendes zu überprüfen:

  • sudo less /var/log/nginx/error.log: überprüft die Nginx-Fehlerprotokolle.
  • sudo less /var/log/nginx/access.log: überprüft die Nginx-Zugriffsprotokolle.
  • sudo journalctl -u nginx: überprüft die Nginx-Prozessprotokolle.
  • sudo journalctl -u myproject: überprüft die Gunicorn-Protokolle Ihrer Flask App.

Schritt 6 – Sichern der Anwendung

Um sicherzustellen, dass der Datenverkehr zu Ihrem Server sicher bleibt, benötigen wir ein SSL-Zertifikat für Ihre Domäne. Um ein Zertifikat zu erhalten, gibt es mehrere Möglichkeiten. U. a. können Sie ein kostenloses Zertifikat von Let’s Encrypt anfordern, ein selbst signiertes Zertifikat generieren oder ein Zertifikat von einem anderen Anbieter erwerben. Konfigurieren Sie Nginx entsprechend, um das Zertifikat zu nutzen, indem Sie den Schritten 2 bis 6 von Erstellen eines selbst signierten SSL-Zertifikats für Nginx in Ubuntu 20.04 folgen. Wir nutzen aus praktischen Gründen die erste Option.

Installieren Sie das Nginx-Paket von Certbot mit apt:

  • sudo apt install python3-certbot-nginx

Certbot bietet eine Vielzahl von Möglichkeiten, um SSL-Zertifikate über Plugins zu erhalten. Das Nginx-Plugin übernimmt die Neukonfiguration von Nginx und das Neuladen der Konfiguration, wenn nötig. Geben Sie Folgendes ein, um dieses Plugin zu verwenden:

  • sudo certbot --nginx -d your_domain -d www.your_domain

Damit wird certbot mit dem --nginx Plugin ausgeführt, wobei über -d die Namen angegeben werden, für die das Zertifikat gültig sein soll.

Wenn Sie Certbot zum ersten Mal ausführen, werden Sie aufgefordert, eine E-Mail-Adresse einzugeben und die Bedingungen des Dienstes zu akzeptieren. Danach kommuniziert Certbot mit dem Let’s Encrypt-Server und stellt dann anhand einer Prüfung sicher, dass Sie die Domäne, für die Sie das Zertifikat anfordern, auch kontrollieren.

Nach erfolgreicher Überprüfung fragt certbot, wie Sie Ihre HTTPS-Einstellungen konfigurieren möchten.

Output
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. ------------------------------------------------------------------------------- 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. ------------------------------------------------------------------------------- Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

Treffen Sie Ihre Wahl und drücken Sie dann ENTER. Die Konfiguration wird aktualisiert und Nginx zum Abholen der neuen Einstellungen neu geladen. Certbot schließt mit einer Mitteilung, dass der Prozess erfolgreich war und wo Ihre Zertifikate gespeichert sind:

Output
IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/your_domain/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/your_domain/privkey.pem Your cert will expire on 2020-08-18. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal. - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le

Wenn Sie die Nginx-Installationsanweisungen in den Voraussetzungen befolgt haben, benötigen Sie die redundante HTTP-Profil-Zulassung nicht mehr:

  • sudo ufw delete allow 'Nginx HTTP'

Um die Konfiguration zu überprüfen, navigieren Sie mit https://: erneut in Ihre Domäne:

https://your_domain

Sie sollten wieder die Ausgabe Ihrer Anwendung sowie den Sicherheitsindikator Ihres Browsers sehen, der jetzt darauf hinweist, dass die Site sicher ist.

Zusammenfassung

In diesem Leitfaden haben Sie eine einfache Flask-Anwendung in einer virtuellen Python-Umgebung erstellt und gesichert. Sie haben einen WSGI Einstiegspunkt erstellt, sodass jeder beliebige WSGI-fähige Anwendungsserver sich damit verbinden kann, und dann den Gunicorn App-Server so konfiguriert, dass er die Funktion bereitstellt. Danach haben Sie eine systemd-Dienstdatei erstellt, um den Anwendungsserver automatisch beim Boot zu starten. Außerdem haben Sie einen Nginx-Serverblock erstellt, der den Web-Client-Verkehr an den Anwendungsserver übergibt und externe Anfragen und den gesicherten Datenverkehr mit Let’s Encrypt an Ihren Server weiterleitet.

Flask ist ein sehr einfaches, aber extrem flexibles Framework, das Ihren Anwendungen Funktionalitäten bereitstellt, ohne hinsichtlich Struktur und Gestaltung zu restriktiv zu sein. Sie können den in diesem Leitfaden beschriebenen allgemeinen Stack verwenden, um die von Ihnen gestalteten Flask-Anwendungen zu bedienen.

0 Comments

Creative Commons License