
Dockerize Python Queue Manager Projekt für einfache Bereitstellung
Letzten Monat habe ich Ihnen gezeigt, wie Sie eine SMS-basierte Anwendung zur Verwaltung von Warteschlangen mit Python und Flask. Diese Anwendung war gut geeignet, um ein grundlegendes Beispiel für die Verwendung der Nexmo SMS APIzu zeigen, aber sie war wirklich nur für die Entwicklung von Prototypen und lokalen Anwendungen geeignet. In diesem Beitrag zeige ich Ihnen einige Schritte, die Sie unternehmen können, um diese Anwendung produktionsreif zu machen, mit dem Endergebnis einer Dockerisierte Version der Anwendung, die Sie direkt auf Heroku.
Auf dem Weg dorthin werden wir ein wenig darüber sprechen:
Bewährte Praktiken für die Verwaltung von Geheimnissen
Container und warum sie nützlich sind
Arbeiten mit einem Anwendungsserver, der den Produktionsverkehr bewältigen kann
Voraussetzungen
A Heroku Account
Die Heroku CLI
ngrok (für lokale Tests)
Vonage API-Konto
Um dieses Tutorial durchzuführen, benötigen Sie ein Vonage API-Konto. Wenn Sie noch keines haben, können Sie sich noch heute anmelden und mit einem kostenlosen Guthaben beginnen. Sobald Sie ein Konto haben, finden Sie Ihren API-Schlüssel und Ihr API-Geheimnis oben auf dem Vonage-API-Dashboard.
In diesem Lernprogramm wird auch eine virtuelle Telefonnummer verwendet. Um eine zu erwerben, gehen Sie zu Rufnummern > Rufnummern kaufen und suchen Sie nach einer Nummer, die Ihren Anforderungen entspricht.
Wählen Sie einen Startpunkt
Wenn Sie meinen ersten Beitrag verfolgt haben ersten Beitrag über die Erstellung einer Warteschlangen-Verwaltungsanwendung verfolgt haben, brauchen Sie nur die oben aufgeführten Voraussetzungen zu erfüllen, um mit dieser Anwendung zu beginnen. Wenn Sie gerade erst einsteigen, können Sie die fertige App von vorher klonen:
git clone https://github.com/nexmo-community/sms-queue-notify.gitSie können auch mit der fertigen, gedockerten Version des Projekts beginnen:
git clone https://github.com/nexmo-community/docker-queue-manager.gitViele der Einrichtungsschritte und Build-Befehle in diesem Beitrag müssen weiterhin ausgeführt werden, aber Sie müssen keine Änderungen am Code vornehmen.
Wo auch immer Sie beginnen, vergewissern Sie sich, dass Sie sich gerade in Ihrem Projektverzeichnis befinden:
cd sms-queue-notifyoder
cd docker-queue-manager Geheimnisse verwalten
Zuvor hatte ich Sie gebeten, Ihren Nexmo-Schlüssel, Ihr Geheimnis und Ihre Telefonnummer direkt oben auf dem Bildschirm zu platzieren. main.py. Dies funktionierte zwar für die Demonstration einer einfachen App ohne viele bewegliche Teile, aber im Allgemeinen möchten Sie sicherstellen, dass Ihre Anmeldeinformationen von Ihrem Code getrennt sind. Die erste Änderung, die wir an main.py ist es, unsere Geheimnisse direkt aus den Umgebungsvariablen zu lesen. Ändern Sie die folgenden Zeilen:
NEXMO_KEY = <Your Nexmo Key>
NEXMO_SECRET = <Your Nexmo Secret>
NEXMO_NUMBER = <Your Nexmo Number>zu:
NEXMO_KEY = os.environ['NEXMO_KEY']
NEXMO_SECRET = os.environ['NEXMO_SECRET']
NEXMO_NUMBER = os.environ['NEXMO_NUMBER']Als Nächstes müssen Sie sicherstellen, dass Sie Ihre Geheimnisse nicht versehentlich irgendwo öffentlich machen. Wenn Sie noch keine haben, erstellen Sie eine .gitignore Datei und stellen Sie sicher, dass Sie .env aufgeführt. Wenn Sie schon dabei sind, erstellen Sie eine .dockerignore Datei mit folgendem Inhalt:
.env
.gitJetzt können Sie eine neue Datei mit dem Namen .env erstellen, die Ihre sensiblen Informationen enthält. Der Inhalt der Datei sollte lauten:
NEXMO_KEY=<Your Nexmo Key>
NEXMO_SECRET=<Your Nexmo Secret>
NEXMO_NUMBER=<Your Nexmo Number>Beachten Sie, dass die Formatierung hier wichtig ist. Um die Gleichheitszeichen herum gibt es keine Leerzeichen, und anders als bei den Werten in main.pyhatten, sollten sie nicht in Anführungszeichen gesetzt werden.
Wenn Sie Ihre Anwendung jetzt testen möchten, können Sie Folgendes im Terminal ausführen, um die Umgebungsvariablen auf der Grundlage der .env Datei zu setzen:
set -o allexport
source .env
set +o allexportWir werden Docker verwenden, um die Änderungen an unserer Anwendung zu testen, die direkt aus der .env Datei direkt lesen kann.
Änderungen der Konfiguration
Ganz am Ende von main.pysehen Sie Folgendes:
if __name__ == '__main__':
app.run(debug=True, threaded=True)Diese Konfiguration, mit debug=Trueeignet sich hervorragend zum Testen der Anwendung, da Änderungen vorgenommen werden können, ohne dass der Server jedes Mal neu gestartet werden muss. Dieser Debug-Modus ist nur für Entwicklungszwecke gedacht und sollte nicht in der Produktion verwendet werden.
Der andere Teil der Konfiguration, threaded=Truebezieht sich auf vom Server gesendete Ereignisse, für die Threading erforderlich ist, damit sie richtig funktionieren. Wie Sie in Kürze erfahren werden, werden wir die Anfragen mit einem separaten Anwendungsserver bearbeiten, so dass wir auch diesen Teil der Konfiguration entfernen können. Die aktualisierte main.py sollte wie folgt aussehen:
if __name__ == '__main__':
app.run() Eine Dockerdatei erstellen
Um die Bereitstellung unseres Projekts zu vereinfachen, werden wir alles in einen Docker-Container packen. Container sind eine leichtgewichtige Methode, um sicherzustellen, dass Ihre Anwendung über alle Ressourcen verfügt, die sie zum Ausführen benötigt, einschließlich des richtigen Betriebssystems und der Abhängigkeiten. Die Bündelung unserer Anwendung in einem Container ermöglicht die Bereitstellung auf einer Vielzahl von Plattformen, ohne dass wir uns Gedanken darüber machen müssen, welche anderen Prozesse, Konfigurationen und Software bereits vorhanden sind.
Docker erstellt Container-Images mithilfe eines so genannten Dockerfiles, das Zeile für Zeile die Schritte auflistet, die erforderlich sind, um die Umgebung zu erstellen, in der Ihre Anwendung ausgeführt werden soll. Es handelt sich um ein Rezept, das Docker mitteilt, wie die Einrichtung zu replizieren ist, von der Sie wissen, dass sie für die korrekte Ausführung Ihrer Anwendung erforderlich ist. Jede Zeile der Dockerdatei erstellt eine Schicht, und wenn Sie ein Docker-Image neu erstellen, werden nur die Schichten neu erstellt, die sich geändert haben. Das bedeutet, dass Sie mit der allgemeinsten Einrichtung an der Spitze beginnen sollten (Betriebssystem und erforderliche Pakete) und sich dann zu spezifischeren Anforderungen vorarbeiten.
Für unsere Anwendung beginnen wir mit einer neuen Datei namens Dockerfile die das Folgende enthält:
FROM python:3.6-slim-buster
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txtDamit wird Docker angewiesen, mit einem Basis-Image zu beginnen, das eine abgespeckte Version des Debian Buster Betriebssystems sowie die Python-Version 3.6 enthält. Dies ist ein Standard-Basis-Image, das von Docker Hubeiner öffentlichen Registrierung von Container-Images, verfügbar ist.
Die nächsten beiden Zeilen des Dockerfiles stellen sicher, dass die erforderlichen Abhängigkeiten für unsere Anwendung (Flask, Flask-SQLAlchemy und das Nexmo SDK) installiert sind. Sofern keine Änderungen an requirements.txt vorgenommen werden oder ein anderes Basis-Image verwendet wird, müssen diese Schritte nur einmal durchgeführt werden. Spätere Builds (z. B. für Änderungen am Code) können diese bereits vorhandenen Schichten verwenden.
Produktionsanwendungsserver
Flask hat einen eingebauten Webserver für Testzwecke, den wir im vorherigen Beitrag verwendet haben. Dieser Server ist nicht für den produktiven Einsatz gedacht - er ist in erster Linie dafür gedacht, jeweils eine Anfrage zu bearbeiten und wird nicht so skaliert, dass er den von einer Produktionsanwendung erwarteten Datenverkehr bewältigen kann.
In der Produktion sollten Sie sicherstellen, dass Sie einen eigenen Webserver haben und einen separaten Anwendungsserver, der die Kommunikation mit Ihrer Python-Anwendung übernimmt. Zwei gängige Lösungen sind Nginx und Gunicorn. Da wir die Bereitstellung auf Heroku planen, das einen Webserver für Sie bereitstellt, müssen wir nur Gunicorn (und auch gevent um Threading zu handhaben). Fügen Sie Folgendes zu Ihrem Dockerfile hinzu:
RUN pip install gunicorn gevent Einrichtung der Datenbank
Wenn Sie sich an den ersten Beitragerinnern, haben wir unsere Datenbank mit einigen Python-Befehlen direkt auf der Kommandozeile erstellt. Dies funktioniert nicht so gut, wenn Sie Ihre Anwendung in einem Container bereitstellen, da Sie keine manuellen Schritte zum Einrichten Ihrer Umgebung durchführen möchten. Um die Dinge zu automatisieren, erstellen Sie eine neue create_db.py Datei, die wie folgt aussieht:
from main import db
db.create_all()Das war einfach! Jetzt können wir unsere Dockerfile mit dem Folgenden abschließen:
COPY . /app
WORKDIR /app
CMD python create_db.py && gunicorn -k gevent -b 0.0.0.0:$PORT main:app
Diese letzten Schritte kopieren den Inhalt Ihres Projektverzeichnisses in einen Ordner namens /app in den Container, der dann als Arbeitsverzeichnis festgelegt wird. Die letzte Zeile teilt dem Container mit, welche Befehle er beim Starten ausführen soll: Zuerst wird die Datenbank erstellt, und danach wird ein Gunicorn-Server gestartet, um unsere Anwendung auszuführen. Die $PORT Umgebungsvariable wird von Heroku gesetzt, wenn der Container läuft.
Lokal mit Docker testen
Jetzt, da die Dockerdatei vollständig ist, ist es einfach zu testen, ob alles lokal funktioniert. Stellen Sie zunächst sicher, dass Docker auf Ihrem Computer ausgeführt wird. Führen Sie dann den folgenden Befehl in Ihrem Projektverzeichnis aus, um ein Docker-Image zu erstellen, indem Sie --tag um einen leicht zu referenzierenden Namen festzulegen:
docker build --tag queue_app .Wenn das Build erfolgreich war, können Sie den Container jetzt starten:
docker run -d -p 5000:5000 --env-file .env -e PORT=5000 queue_appBeachten Sie, dass wir unsere Geheimnisse aus der Datei .env Datei und setzen die PORT Umgebungsvariable selbst setzen.
Sobald Ihr Container läuft, sollten Sie in der Lage sein, einen Browser zu öffnen, zu localhost:5000aufrufen und Ihre Anwendung sehen!
Es gibt noch einen weiteren Schritt, wenn Sie Ihre Anwendung vollständig testen wollen. Im vorherigen Beitrag haben Sie ngrok so eingerichtet, dass Ihre Anwendung über das Web zugänglich ist. Dies müssen Sie erneut tun, wenn Sie das Senden einer SMS an die Anwendung testen möchten. Öffnen Sie ein neues Terminalfenster und führen Sie Folgendes aus:
ngrok http 5000Gehen Sie dann zum Nexmo Dashboard und kopieren Sie die Weiterleitungs-URL in die Einstellungen Ihrer Nummer in das Feld Inbound Webhook URL Feld, etwa so: https://<your ngrok ID>.ngrok.io/webhooks/inbound-sms (siehe vorherigen Beitrag für Details).
Jetzt sollten Sie in der Lage sein, mit Ihrer Anwendung über Text zu interagieren, genau wie zuvor! Wenn Sie nun Ihren Container anhalten und neu starten, wird die Datenbank vollständig zurückgesetzt.
Postgres-Datenbank
In unserer Entwicklungsversion der Anwendung haben wir eine SQLite Datenbank, um Informationen darüber zu speichern, wer in der Warteschlange wartete. SQLite erstellte die Datenbank als Datei im Projektverzeichnis, was die Einrichtung sehr einfach machte. In einem Container-basierten Setup funktioniert das nicht, da das Dateisystem des Containers nicht bestehen bleibt, wenn der Container neu gestartet werden muss. Außerdem ist es schwierig, die Anwendung über mehrere Container hinweg zu skalieren, da es keine gemeinsame Datenquelle gibt.
Glücklicherweise haben wir Flask-SQLAlchemy verwendet, um die Datenbankspezifika von unserem Code zu abstrahieren, so dass der Austausch von SQLite gegen ein von Heroku bereitgestelltes Postgres Datenbank auszutauschen ist unglaublich einfach. Die Postgres-Datenbank befindet sich außerhalb des Containers, sodass sie auch bei einem Neustart des Containers bestehen bleibt und von mehreren Containern genutzt werden kann.
Wenn Heroku eine Postgres-Datenbank erstellt, wird die Datenbank-URL in der DATABASE_URL Umgebungsvariable gespeichert. Die einzige Änderung, die wir an unserem Code vornehmen müssen, um von SQLite zur Heroku-Postgres-Datenbank zu wechseln, ist das Ersetzen dieser Zeile in main.py:
db_path = "sqlite:///queue.db"damit:
db_path = os.environ['DATABASE_URL']Dann müssen wir die Zeile in unserem Dockerfile die lautet:
RUN pip install gunicorn geventzu sagen:
RUN pip install gunicorn gevent psycopg2-binaryDas Paket psycopg2 ist ein Postgres-Datenbankadapter, der speziell für Python entwickelt wurde.
Der letzte Schritt besteht darin, eine Postgres-Datenbank auf Heroku zu erstellen. Dazu müssen Sie sich zunächst bei der Heroku-CLI anmelden und eine Heroku-Anwendung erstellen:
heroku login
heroku create <your application name>Als Nächstes erstellen Sie die Datenbank, wobei Sie darauf achten, dass der Name Ihrer Anwendung angegeben wird:
heroku addons:create heroku-postgresql:hobby-dev -a <your application name> Stellen Sie Ihren Container auf Heroku bereit
Nachdem Sie Ihre Anwendung und Datenbank auf Heroku initialisiert haben, sind nur noch wenige Schritte notwendig, um Ihre Docker-App zu implementieren. Zunächst müssen Sie Ihre Nexmo-Anmeldedaten als Heroku Config Vars festlegen, was auf dem Heroku-Dashboard unter "Einstellungen" erfolgt:
Config Vars interface in Heroku
Als Nächstes sollten Sie Ihren Docker-Container neu erstellen, um sicherzustellen, dass Sie die jüngsten Änderungen übernehmen:
docker build --tag queue_app .Anschließend müssen Sie sich bei der Heroku-Container-Registrierung anmelden:
heroku container:loginUnd zum Schluss pushen Sie Ihren Container und geben ihn an Heroku frei:
heroku container:push web -a <your application name>
heroku container:release web -a <your application name>Das war's! Nun, fast. Starten Sie Ihre Anwendung von Ihrem Heroku-Dashboard aus, um sicherzustellen, dass sie funktioniert, gehen Sie dann zu Ihrem Nexmo-Dashboard und aktualisieren Sie das Inbound Webhook URL Feld so, dass es wie folgt aussieht: https://<your application name>.herokuapp.com/webhooks/inbound-sms.
Sie haben es geschafft! Dank der Leistungsfähigkeit der Containerisierung haben Sie jetzt eine produktionsreife Anwendung, die leicht skalierbar und replizierbar ist.
Wenn Sie auf Probleme stoßen oder Fragen haben, wenden Sie sich an uns auf unserem Gemeinschaft Slack. Vielen Dank fürs Lesen!
