Erstellen einer Django und Gunicorn-Anwendung mit Docker

Einführung

Django ist ein leistungsstarkes Webframework, mit dem Sie Ihre Python-Anwendung schnell auf den Weg bringen können. Es enthält mehrere praktische Funktionen wieobject-relational mapper, Benutzerauthentifizierung und eine anpassbare Verwaltungsoberfläche für Ihre Anwendung. Es enthält auchcaching framework und fördert das saubere App-Design durchURL Dispatcher undTemplate system.

In diesem Tutorial erfahren Sie, wie Sie eine skalierbare und portable DjangoPolls-App mit Docker-Containern erstellen. Standardmäßig sind für eine Django-App mehrere Änderungen erforderlich, um effektiv in Containern ausgeführt zu werden, z. B. die Protokollierung in Standardausgabestreams und die Konfiguration über Umgebungsvariablen, die an den Container übergeben werden. Durch das Auslagern statischer Elemente wie JavaScript- und CSS-Stylesheets in den Objektspeicher können Sie die Verwaltung dieser Dateien in einer Umgebung mit mehreren Containern optimieren und zentralisieren.

Sie werden diese Änderungen - inspiriert von derTwelve-Factor-Methode zum Erstellen skalierbarer, Cloud-nativer Web-Apps - in einer Beispiel-App von DjangoPollsimplementieren. Anschließend erstellen Sie das Anwendungsimage und führen die containerisierte App mit Docker aus.

Am Ende dieses Tutorials haben Sie das Setup inHow to Set Up a Scalable Django App zusammengefasst. In den folgenden Tutorials dieser Reihe erfahren Sie, wie SieDocker Compose verwenden, um den Django-Container mit einem Nginx-Reverse-Proxy zu koppeln und diese Architektur in einem Kubernetes-Cluster bereitzustellen.

Es wird dringend empfohlen, das Lernprogramm durchzuarbeiten, um die Änderungen zu verstehen, die Sie an der App vornehmen. Wenn Sie jedoch fortfahren möchten, können Sie den geänderten Code aus dempolls-docker branch des GitHub-Repositorys der Polls-App abrufen.

Voraussetzungen

Um diesem Tutorial zu folgen, benötigen Sie:

  • Ein Ubuntu 18.04-Server, der nach diesenInitial Server Setup guide eingerichtet wurde.

  • Docker auf Ihrem Server installiert, folgen Sie den Schritten 1 und 2 vonHow To Install and Use Docker on Ubuntu 18.04. Stellen Sie sicher, dass Sie Ihren Benutzer zur Gruppedockerhinzufügen, wie in Schritt 2 beschrieben.

  • ADigitalOcean Space zum Speichern der statischen Dateien Ihres Django-Projekts und einer Reihe von Zugriffsschlüsseln für diesen Bereich. Informationen zum Erstellen eines Space finden Sie in der Produktdokumentation vonHow to Create Spaces. Informationen zum Erstellen von Zugriffsschlüsseln für Spaces finden Sie unterSharing Access to Spaces with Access Keys. Mit geringfügigen Änderungen können Sie jeden S3-kompatiblen Objektspeicherdienst verwenden.

  • Ein von DigitalOcean verwaltetes PostgreSQL-Cluster. Informationen zum Erstellen eines Clusters finden Sie in DigitalOceanManaged Databases product documentation. Mit geringfügigen Änderungen können Sie jede Datenbank verwenden, dieDjango supports.

[[Schritt-1 - Erstellen der Postgresql-Datenbank und des Benutzers]] == Schritt 1 - Erstellen der PostgreSQL-Datenbank und des Benutzers

Zunächst stellen wir über die Ubuntu-Instanz eine Verbindung zum PostgreSQL-Server her. Anschließend erstellen wir eine PostgreSQL-Datenbank und einen Benutzer für die Django-App und konfigurieren die Datenbank so, dass sie effektiv mit Django zusammenarbeitet.

Bevor wir von unserem Ubuntu-Computer (nicht vom App-Container) aus eine Verbindung zur Datenbank herstellen, müssen wir das Paketpostgresql-clientaus den Ubuntu-Repositorys installieren. Aktualisieren Sie zuerst den Paketindex des lokalenaptund laden Sie dann das Paket herunter und installieren Sie es:

sudo apt update
sudo apt install postgresql-client

Drücken SieY und dannENTER, wenn Sie aufgefordert werden, die Pakete herunterzuladen und zu installieren.

Nachdem Sie den Client installiert haben, erstellen wir damit eine Datenbank und einen Datenbankbenutzer für unsere Django-Anwendung.

Holen Sie sich zunächst dieConnection Parameters für Ihren Cluster, indem Sie vonCloud Control Panel zuDatabases navigieren und in Ihre Datenbank klicken. Sie sollten einConnection Details-Feld sehen, das einigeConnection parameters für Ihren Cluster enthält. Notieren Sie diese.

Melden Sie sich in der Befehlszeile mit diesen Anmeldeinformationen und dem soeben installierten PostgreSQL-Client vonpsqlbei Ihrem Cluster an:

psql -U username -h host -p port -d database -set=sslmode=require

Wenn Sie dazu aufgefordert werden, geben Sie das neben dem Postgres-Benutzernamen angezeigte Kennwort ein und drücken SieENTER.

Sie erhalten eine PostgreSQL-Eingabeaufforderung, über die Sie die Datenbank verwalten können.

Erstellen Sie zunächst eine Datenbank für Ihr Projekt mit dem Namenpolls:

CREATE DATABASE polls;

[.note] #Note: Jede Postgres-Anweisung muss mit einem Semikolon enden. Stellen Sie daher sicher, dass Ihr Befehl mit einem endet, wenn Probleme auftreten.
#

Wir können jetzt zur Datenbankpollswechseln:

\c polls;

Erstellen Sie als Nächstes einen Datenbankbenutzer für das Projekt. Stellen Sie sicher, dass Sie ein sicheres Passwort wählen:

CREATE USER sammy WITH PASSWORD 'password';

Wir werden nun einige der Verbindungsparameter für den soeben erstellten Benutzer ändern. Dies beschleunigt die Datenbankoperationen, sodass nicht bei jedem Verbindungsaufbau die richtigen Werte abgefragt und eingestellt werden müssen.

Wir setzen die Standardcodierung aufUTF-8, was Django erwartet. Wir setzen außerdem das Standard-Transaktionsisolationsschema auf "read commit", wodurch Lesevorgänge für nicht festgeschriebene Transaktionen blockiert werden. Zuletzt stellen wir die Zeitzone ein. Standardmäßig verwenden unsere Django-ProjekteUTC. Dies sind alles Empfehlungen vonthe Django project itself.

Geben Sie die folgenden Befehle an der PostgreSQL-Eingabeaufforderung ein:

ALTER ROLE sammy SET client_encoding TO 'utf8';
ALTER ROLE sammy SET default_transaction_isolation TO 'read committed';
ALTER ROLE sammy SET timezone TO 'UTC';

Jetzt können wir unserem neuen Benutzer den Zugriff auf die Verwaltung unserer neuen Datenbank gewähren:

GRANT ALL PRIVILEGES ON DATABASE polls TO sammy;

Wenn Sie fertig sind, beenden Sie die PostgreSQL-Eingabeaufforderung, indem Sie Folgendes eingeben:

\q

Eine ordnungsgemäß konfigurierte Django-App kann nun eine Verbindung zu dieser Datenbank herstellen und diese verwalten. Im nächsten Schritt klonen wir den Polls-App-Code von GitHub und definieren die Python-Paketabhängigkeiten explizit.

[[Schritt-2 - Klonen von App-Repository und Deklarieren von Abhängigkeiten] == Schritt 2 - Klonen von App-Repository und Deklarieren von Abhängigkeiten

Um mit dem Containerisieren unserer Django Polls-App zu beginnen, klonen wir zunächst dasdjango-polls-Repository, das den vollständigen Code für dietutorial Polls-App desDjango-Projekts enthält.

Melden Sie sich bei Ihrem Server an, erstellen Sie ein Verzeichnis mit dem Namenpolls-project und verwenden Siegit, um das Repo vondjango-polls von GitHub zu klonen:

mkdir polls-project
cd polls-project
git clone https://github.com/do-community/django-polls.git

Greifen Sie auf das Verzeichnisdjango-pollszu und listen Sie den Repository-Inhalt auf:

cd django-polls
ls
OutputLICENSE  README.md  manage.py  mysite  polls  templates

Sie sollten die folgenden Objekte sehen:

  • manage.py: Das Hauptbefehlszeilenprogramm zum Bearbeiten der App.

  • polls: Enthält den App-Code vonpolls.

  • mysite: Enthält Code und Einstellungen für den Django-Projektbereich.

  • templates: Enthält benutzerdefinierte Vorlagendateien für die Verwaltungsoberfläche.

Weitere Informationen zur Projektstruktur und zu den Dateien finden Sie inCreating a Project in der offiziellen Django-Dokumentation.

In diesem Verzeichnis erstellen wir auch eine Datei mit dem Namenrequirements.txt, die die Python-Abhängigkeiten der Django-App enthält.

Öffnen Sie eine Datei mit dem Namenrequirements.txt in einem Editor Ihrer Wahl und fügen Sie die folgenden Python-Abhängigkeiten ein:

polls-project/django-polls/requirements.txt

boto3==1.9.252
botocore==1.12.252
Django==2.2.6
django-storages==1.7.2
docutils==0.15.2
gunicorn==19.9.0
jmespath==0.9.4
psycopg2==2.8.3
python-dateutil==2.8.0
pytz==2019.3
s3transfer==0.2.1
six==1.12.0
sqlparse==0.3.0
urllib3==1.25.6

Hier installieren wir Django, das Plugindjango-storageszum Auslagern statischer Assets in den Objektspeicher, den WSGI-Servergunicorn, den PostgreSQL-Adapterpsycopg2owie einige zusätzliche Abhängigkeitspakete. Beachten Sie, dass wir jedes für unsere App erforderliche Python-Paket explizit auflisten und versionieren.

Speichern und schließen Sie die Datei.

Nachdem wir die App geklont und ihre Abhängigkeiten definiert haben, können wir sie aus Gründen der Portabilität weiter bearbeiten.

[[Schritt 3 - Django durch Umgebungsvariablen konfigurierbar machen]] == Schritt 3 - Django durch Umgebungsvariablen konfigurierbar machen

Eine der wichtigsten Empfehlungen aus derthe twelve-factor app-Methode ist das Extrahieren der fest codierten Konfiguration aus der Codebasis Ihrer Anwendung. Auf diese Weise können Sie das Verhalten Ihrer Anwendung zur Laufzeit einfach ändern, indem Sie Umgebungsvariablen ändern. Docker und Kubernetes schlagen beide diese Methode zur Konfiguration von Containern vor. Daher passen wir die Einstellungsdatei unserer Anwendung an, um dieses Muster zu verwenden.

Die Haupteinstellungsdatei für unser Django-Projekt (django-polls/mysite/settings.py) ist ein Python-Modul, das native Datenstrukturen zum Konfigurieren der Anwendung verwendet. Standardmäßig sind die meisten Werte in der Datei fest codiert. Dies bedeutet, dass Sie die Konfigurationsdatei bearbeiten müssen, um das Anwendungsverhalten zu ändern. Wir können diegetenv-Funktion von Python imos-Modul verwenden, um Django so zu konfigurieren, dass stattdessen Konfigurationsparameter aus lokalen Umgebungsvariablen gelesen werden.

Dazu gehen wirsettings.py durch und ersetzen die fest codierten Werte jeder der Variablen, die wir zur Laufzeit festlegen möchten, durch einen Aufruf vonos.getenv. Die Funktionos.getenv liest den Wert aus einem angegebenen Umgebungsvariablennamen. Optional können Sie einen zweiten Parameter mit einem Standardwert angeben, der verwendet wird, wenn die Umgebungsvariable nicht festgelegt ist.

Dies ermöglicht es uns, Variablen wie folgt festzulegen:

polls-project/django-polls/mysite/settings.py

. . .
SECRET_KEY = os.getenv('DJANGO_SECRET_KEY')
. . .
DEBUG = os.getenv('DJANGO_DEBUG', False)
. . .

FürSECRET_KEY sucht Django nach einer Umgebungsvariablen namensDJANGO_SECRET_KEY. Da dies nicht fest codiert sein sollte und auf allen Anwendungsservern gleich sein muss, möchten wir dies extern ohne Fallback-Wert festlegen. Wir möchten, dass die Anwendung fehlschlägt, wenn wir dies nicht bereitstellen, da dies zu Problemen führen kann, wenn verschiedene Kopien unserer Anwendung unterschiedliche Schlüssel verwenden.

FürDEBUG sucht Django nach einer Umgebungsvariablen namensDJANGO_DEBUG. Dieses Mal haben wir jedoch einen Standardwert bereitgestellt, der als Fallback verwendet wird, wenn die Variable nicht festgelegt ist. In diesem Fall haben wir uns dafür entschieden,DEBUG aufFalse zu setzen, wenn kein Wert angegeben wird, damit wir nicht versehentlich vertrauliche Informationen verlieren, es sei denn, die Variable ist absichtlich definiert und aufTrue gesetzt. .

Um diese Technik anzuwenden, öffnen Sie die Dateipolls-project/django-polls/mysite/settings.pyin einem Editor Ihrer Wahl und gehen Sie sie durch, wobei Sie die folgenden Variablen mit den angegebenen Standardwerten externalisieren:

  • SECRET_KEY = os.getenv('DJANGO_SECRET_KEY')

  • DEBUG = os.getenv('DEBUG', False)

  • ALLOWED_HOSTS = os.getenv('DJANGO_ALLOWED_HOSTS', '127.0.0.1').split(',')

FürALLOWED_HOSTS rufen wir die UmgebungsvariableDJANGO_ALLOWED_HOSTS ab und teilen sie mit, als Trennzeichen in eine Python-Liste auf. Wenn die Variable nicht gesetzt ist, wirdALLOWED_HOSTS auf127.0.0.1 gesetzt.

Nachdem Sie die obigen Variablen geändert haben, navigieren Sie zur VariablenDATABASESund konfigurieren Sie sie wie folgt:

polls-project/django-polls/mysite/settings.py

. . .
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.{}'.format(
             os.getenv('DATABASE_ENGINE', 'sqlite3')
         ),
         'NAME': os.getenv('DATABASE_NAME', 'polls'),
         'USER': os.getenv('DATABASE_USERNAME', 'myprojectuser'),
         'PASSWORD': os.getenv('DATABASE_PASSWORD', 'password'),
         'HOST': os.getenv('DATABASE_HOST', '127.0.0.1'),
         'PORT': os.getenv('DATABASE_PORT', 5432),
         'OPTIONS': json.loads(
             os.getenv('DATABASE_OPTIONS', '{}')
         ),
     }
 }
 . . .

Dadurch werden die Datenbankparameter vondefaultmithilfe von Umgebungsvariablen festgelegt.

FürDATABASES['default']['OPTIONS'] haben wirjson.loads verwendet, um ein JSON-Objekt zu deserialisieren, das über die UmgebungsvariableDATABASE_OPTIONSübergeben wurde. In den meisten Fällen ist die Übersetzung in Django-Einstellungen einfacher zu lesen, wenn Umgebungsvariablen als einfache Zeichenfolgen interpretiert werden. In diesem Fall ist es jedoch wertvoll, eine beliebige Datenstruktur übergeben zu können. Jedes Datenbankmodul verfügt über einen eindeutigen Satz gültiger Optionen. Die Möglichkeit, ein JSON-Objekt mit den entsprechenden Parametern zu codieren, erhöht die Flexibilität auf Kosten einer gewissen Lesbarkeit.

Um die Bibliothekjson zu verwenden, importieren Sie sie oben insettings.py:

polls-project/django-polls/mysite/settings.py

"""
Django settings for mysite project.

Generated by 'django-admin startproject' using Django 2.1.

For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/
"""

import os
import json
. . .

Der andere Bereich, der besondere Aufmerksamkeit erfordert, istDATABASES['default']['NAME']. Bei den meisten Datenbankmodulen ist dies der Datenbankname innerhalb des relationalen Datenbankverwaltungssystems. Wenn Sie dagegen SQLite verwenden, wirdNAME verwendet, um die Datenbankdatei anzugeben. Stellen Sie daher sicher, dass Sie diesen Parameter entsprechend festlegen.

Da es sich bei dersettings.py-Datei um Python-Code handelt, gibt es viele verschiedene Möglichkeiten, wie Sie Werte aus der Umgebung lesen können. Die hier verwendete Methode ist nur eine mögliche Technik zum Externalisieren der Konfiguration aus Ihrer Codebasis.

In diesem Schritt haben wir die wichtigsten Django-Einstellungsvariablen generisch und portabel konfiguriert, einschließlich der Datenbankparameter. Im folgenden Schritt werden die Einstellungen für statische Dateien wie Javascript und CSS-Stylesheets konfiguriert, die zentralisiert und an einen S3-kompatiblen Objektspeicherdienst übertragen werden.

[[Schritt-4 - Entladen statischer Assets]] == Schritt 4 - Entladen statischer Assets

Wenn Sie mehrere Django-Container in einer Produktionsumgebung ausführen, kann es schwierig sein, bestimmte Versionen statischer Assets und Dateien für die gesamte Flotte ausgeführter Container zu verwalten. Um diese Architektur zu rationalisieren, können wir alle gemeinsam genutzten Elemente und Zustände auf einen externen Speicher übertragen. Anstatt zu versuchen, diese Elemente über Replikate hinweg synchron zu halten oder Sicherungs- und Laderoutinen zu implementieren, um sicherzustellen, dass Daten lokal verfügbar sind, können wir den Zugriff auf diese Assets als über das Netzwerk zugängliche Dienste implementieren.

Im letzten Schritt haben wir Django so konfiguriert, dass Datenbankverbindungsparameter über Umgebungsvariablen übergeben werden können. In diesem Schritt machen wir dasselbe mit unserem Objektspeicherungsservice, mit dem wir statische Assets speichern, die von Django-Containern gemeinsam genutzt werden.

Das Paketdjango-storagesbietet Remotespeicher-Backends (einschließlich S3-kompatiblen Objektspeicher), mit denen Django Dateien auslagern kann. Wir konfigurieren die Polls-App so, dassdjango-storages zum Hochladen statischer Dateien in einen DigitalOcean Space verwendet werden, wie in Schritt 7 vonHow to Set Up a Scalable Django App with DigitalOcean Managed Databases and Spaces beschrieben. In diesem Handbuch werden DigitalOcean Spaces verwendet, Sie können jedoch jeden S3-kompatiblen Objektspeicheranbieter verwenden.

Zunächst nehmen wir einige Änderungen an derselbendjango-polls/mysite/settings.py-Datei vor, die wir in den vorherigen Schritten geändert haben.

Öffnen Sie zunächst die Dateimysite/settings.py, um die Appstorageszu bearbeiten und an Djangos Liste derINSTALLED_APPS anzuhängen:

polls-project/django-polls/mysite/settings.py

. . .
INSTALLED_APPS = [
    . . .
    'django.contrib.staticfiles',
    'storages',
]
. . .

Die Appstorages wird überdjango-storages in der in Schritt 1 definierten Dateirequirements.txt installiert.

Suchen Sie nun die VariableSTATIC_URL am Ende der Datei und ersetzen Sie sie durch den folgenden Block:

polls-project/django-polls/mysite/settings.py

. . .

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

# Moving static assets to DigitalOcean Spaces as per:
# https://www.digitalocean.com/community/tutorials/how-to-set-up-object-storage-with-django
AWS_ACCESS_KEY_ID = os.getenv('STATIC_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.getenv('STATIC_SECRET_KEY')

AWS_STORAGE_BUCKET_NAME = os.getenv('STATIC_BUCKET_NAME')
AWS_S3_ENDPOINT_URL = os.getenv('STATIC_ENDPOINT_URL')
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}
AWS_LOCATION = 'static'
AWS_DEFAULT_ACL = 'public-read'

STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

STATIC_URL = '{}/{}/'.format(AWS_S3_ENDPOINT_URL, AWS_LOCATION)
STATIC_ROOT = 'static/'

Wir codieren die folgenden Konfigurationsvariablen hart:

  • STATICFILES_STORAGE: Legt das Speicher-Backend fest, das Django zum Auslagern statischer Dateien verwendet. Das Backend diesesS3Boto3Storageollte mit jedem S3-kompatiblen Backend funktionieren, einschließlich DigitalOcean Spaces.

  • AWS_S3_OBJECT_PARAMETERS Legt die Cache-Steuerelement-Header für statische Dateien fest.

  • AWS_LOCATION: Definiert ein Verzeichnis mit dem Namenstatic im Objektspeicherbereich, in dem alle statischen Dateien abgelegt werden.

  • `+AWS_DEFAULT_ACL+: Definiert die Zugriffssteuerungsliste (ACL) für die statischen Dateien. Wenn Siepublic-read festlegen, wird sichergestellt, dass die Dateien für Endbenutzer öffentlich zugänglich sind.

  • STATIC_URL: Gibt die Basis-URL an, die Django beim Generieren von URLs für statische Dateien verwenden soll. Hier kombinieren wir die Endpunkt-URL und das Unterverzeichnis für statische Dateien, um eine Basis-URL für statische Dateien zu erstellen.

  • STATIC_ROOT: Gibt an, wo statische Dateien lokal erfasst werden sollen, bevor sie in den Objektspeicher kopiert werden.

Um Flexibilität und Portabilität zu gewährleisten, haben wir viele Parameter so eingerichtet, dass sie zur Laufzeit mithilfe von Umgebungsvariablen konfiguriert werden können, wie wir es zuvor getan haben. Diese schließen ein:

  • AWS_ACCESS_KEY_ID: Wird durch die UmgebungsvariableSTATIC_ACCESS_KEY_ID festgelegt. Die Kennung des DigitalOcean Spaces-Zugriffsschlüssels.

  • AWS_SECRET_ACCESS_KEY: Wird durchSTATIC_SECRET_KEY festgelegt. Der geheime Schlüssel von DigitalOcean Spaces.

  • AWS_STORAGE_BUCKET_NAME: Wird durchSTATIC_BUCKET_NAME festgelegt. Der Objektspeicher, in den Django Assets hochlädt.

  • AWS_S3_ENDPOINT_URL: Wird durchSTATIC_ENDPOINT_URL festgelegt. Die Endpunkt-URL, die für den Zugriff auf den Objektspeicherdienst verwendet wird. Bei DigitalOcean Spaces beträgt dies ungefährhttps://nyc3.digitaloceanspaces.com, abhängig von der Region, in der sich Ihr Spaces-Bucket befindet.

Wenn Sie alle Änderungen ansettings.pyvorgenommen haben, speichern und schließen Sie die Datei.

Wenn Sie von nun anmanage.py collectstatic ausführen, um die statischen Dateien Ihres Projekts zusammenzustellen, lädt Django diese in den Remote-Objektspeicher hoch. Django ist jetzt auch so konfiguriert, dass statische Assets von diesem Objektspeicherdienst bereitgestellt werden.

Wenn Sie zu diesem Zeitpunkt einen DigitalOcean Space verwenden, können Sie optional ein CDN für Ihren Space aktivieren, wodurch die Zustellung der statischen Dateien Ihres Django-Projekts beschleunigt wird, indem diese über ein geografisch verteiltes Netzwerk von Edgeservern zwischengespeichert werden. Optional können Sie auch eine benutzerdefinierte Unterdomäne für Ihren Space konfigurieren. Weitere Informationen zu CDNs finden Sie unterUsing a CDN to Speed Up Static Content Delivery. Das Konfigurieren eines CDN geht über den Rahmen dieses Lernprogramms hinaus, aber die Schritte stimmen sehr gut mit denen im AbschnittEnabling CDN vonHow to Set Up a Scalable Django App with DigitalOcean Managed Databases and Spaces überein.

Im nächsten Schritt werden wir eine letzte Reihe von Änderungen ansettings.py vornehmen, die es Django ermöglichen, sich bei STDOUT und STDERR anzumelden, damit diese Streams von der Docker Engine aufgenommen und mitdocker logs überprüft werden können.

[[Schritt-5 - Konfigurieren der Protokollierung]] == Schritt 5 - Konfigurieren der Protokollierung

Standardmäßig protokolliert Django Informationen in der Standardausgabe und im Standardfehler, wenn der Entwicklungs-HTTP-Server ausgeführt wird oder wenn die OptionDEBUG aufTrue gesetzt ist. Wenn jedochDEBUG aufFalse festgelegt ist oder wenn ein anderer HTTP-Server verwendet wird, was in Produktionsumgebungen wahrscheinlich der Fall ist, verwendet Django einen anderen Protokollierungsmechanismus. Anstatt alles mit der PrioritätINFO und höher in Standard-Streams zu protokollieren, werden Nachrichten mit der PrioritätERROR oderCRITICAL an ein administratives E-Mail-Konto gesendet.

Dies ist in vielen Situationen sinnvoll. In Kubernet- und Containerumgebungen wird jedoch die Protokollierung auf Standardausgabe und Standardfehler dringend empfohlen. Protokollnachrichten werden in einem zentralen Verzeichnis im Dateisystem des Knotens gesammelt und können interaktiv mit den Befehlenkubectl unddocker aufgerufen werden. Diese Aggregation auf Knotenebene erleichtert die Protokollerfassung, indem Betriebsteams einen Prozess auf jedem Knoten ausführen können, um Protokolle zu überwachen und weiterzuleiten. Um diese Architektur zu nutzen, muss die Anwendung ihre Protokolle in diese Standardsenken schreiben.

Glücklicherweise verwendet die Anmeldung in Django das hoch konfigurierbarelogging-Modul aus der Python-Standardbibliothek, sodass wir ein Wörterbuch definieren können, das anlogging.config.dictConfig übergeben wird, um die gewünschten Ausgaben und Formatierungen zu definieren. Weitere Informationen zu dieser und anderen Techniken zum Konfigurieren der Django-Protokollierung finden Sie unterDjango Logging, The Right Way.

Öffnen Sie erneutdjango-polls/mysite/settings.py in Ihrem Editor.

Wir werden zuerst eine zusätzlicheimport-Anweisung am Anfang der Datei hinzufügen, damit wir die Protokollierungskonfiguration bearbeiten können:

polls-project/django-polls/mysite/settings.py

import json
import os
import logging.config
. . .

Mit dem Import vonlogging.configkönnen wir das Standardprotokollierungsverhalten von Django überschreiben, indem wir ein Wörterbuch mit der neuen Protokollierungskonfiguration an die FunktiondictConfigübergeben.

Navigieren Sie nun zum Ende der Datei und fügen Sie den folgenden Block des Protokollierungskonfigurationscodes ein:

polls-project/django-polls/mysite/settings.py

. . .
# Logging Configuration

# Clear prev config
LOGGING_CONFIG = None

# Get loglevel from env
LOGLEVEL = os.getenv('DJANGO_LOGLEVEL', 'info').upper()

logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'console': {
            'format': '%(asctime)s %(levelname)s [%(name)s:%(lineno)s] %(module)s %(process)d %(thread)d %(message)s',
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'console',
        },
    },
    'loggers': {
        '': {
            'level': LOGLEVEL,
            'handlers': ['console',],
        },
    },
})

Hier setzen wirLOGGING_CONFIG aufNone, um die von Django bereitgestellte Standardprotokollierungskonfiguration zu deaktivieren. Wir setzenLOGLEVEL standardmäßig aufINFO, überprüfen jedoch die UmgebungsvariableDJANGO_LOGLEVEL, damit wir sie bei Bedarf überschreiben können.

Schließlich verwenden wir die FunktiondictConfig, um ein neues Konfigurationswörterbuch mit dem Modullogging.config festzulegen. Im Wörterbuch definieren wir das Textformat mitformatters, definieren die Ausgabe durch Einrichten vonhandlers und konfigurieren mitloggers, welche Nachrichten an jeden Handler gesendet werden sollen.

Dies ist eine ziemlich minimale Konfiguration, mit der Sie einen Schweregrad der Protokollierung mithilfe einer Umgebungsvariablen namensDJANGO_LOGLEVEL angeben und dann alle Nachrichten auf oder über dieser Ebene in Standard-Streams protokollieren können. Eine ausführliche Beschreibung der Django-Protokollierungsmechanismen finden Sie inLogging in den offiziellen Django-Dokumenten.

Bei dieser Konfiguration macht Docker diese Protokolle beim Containerisieren der Anwendung über den Befehldocker logsverfügbar. Ebenso erfasst Kubernetes die Ausgabe und macht sie über den Befehlkubectl logs verfügbar.

Damit sind unsere Code-Änderungen an der Django Polls-App abgeschlossen. Im nächsten Schritt beginnen wir den Containerisierungsprozess mit dem Schreiben der Docker-Datei der App.

[[Schritt-6 - Schreiben der Anwendungs-Docker-Datei]] == Schritt 6 - Schreiben der Anwendungs-Docker-Datei

In diesem Schritt definieren wir das Container-Image, auf dem unsere Django-App ausgeführt wird, und den Gunicorn-WSGI-Server, auf dem sie ausgeführt wird. Sie müssen ein Container-Image erstellen, indem Sie die Laufzeitumgebung definieren, die Anwendung und ihre Abhängigkeiten installieren und einige grundlegende Konfigurationsschritte ausführen. Während es viele Möglichkeiten gibt, eine Anwendung in ein Container-Image zu kapseln, führen die in diesem Schritt angewendeten Methoden zu einem schlanken, optimierten App-Image.

Auswahl eines geeigneten übergeordneten Bildes

Die erste wichtige Entscheidung, die Sie beim Erstellen eines Container-Images treffen müssen, ist die Grundlage, auf der Sie aufbauen können. Container-Images können entweder ausSCRATCH erstellt werden, die auf ein leeres Dateisystem hinweisen, oder aus einem vorhandenen Container-Image. Es stehen viele verschiedene Basiscontainer-Images zur Verfügung, die jeweils ein Dateisystem definieren und einen eindeutigen Satz vorinstallierter Pakete bereitstellen. Images, die auf Vanilla Linux-Distributionen wie Ubuntu 18.04 basieren, bieten eine allgemeine Betriebsumgebung, während speziellere Images häufig allgemeine Bibliotheken und Tools für bestimmte Programmiersprachen enthalten.

Wann immer möglich, ist es oft eine gute Idee, ein Bild von einem vonDocker’s official repositoriesals Basis zu verwenden. Diese Abbilder wurden von Docker nach bewährten Methoden überprüft und werden regelmäßig aktualisiert, um Sicherheitskorrekturen und Verbesserungen vorzunehmen.

Da unsere Anwendung mit Django erstellt wurde, bietet ein Image mit einer Standard-Python-Umgebung eine solide Grundlage und enthält viele der Tools, die wir für den Einstieg benötigen. Das offizielle Docker-Repository für Python bieteta wide selection of Python-based images, wobei jeweils eine Version von Python und einige gängige Tools auf einem Betriebssystem installiert werden.

Während die entsprechende Funktionalität von Ihrem Anwendungsfall abhängt, sind Bilder, die aufAlpine Linux basieren, häufig ein solider Ausgangspunkt. Alpine Linux bietet eine robuste, aber minimale Betriebsumgebung für die Ausführung von Anwendungen. Das Standarddateisystem ist sehr klein, enthält jedoch ein vollständiges Paketverwaltungssystem mit ziemlich umfangreichen Repositorys, um das Hinzufügen von Funktionen zu vereinfachen.

[.note] #Note: Möglicherweise haben Sie inlist of tags for Python images bemerkt, dass für jedes Bild mehrere Tags verfügbar sind. Docker-Tags sind veränderbar, und Betreuer können dasselbe Tag in Zukunft einem anderen Image zuweisen. Infolgedessen stellen viele Betreuer Sätze von Tags mit unterschiedlichen Spezifitätsgraden bereit, um unterschiedliche Anwendungsfälle zu ermöglichen. Beispielsweise wird das Tag3-alpine verwendet, um auf die neueste verfügbare Python 3-Version in der neuesten Alpine-Version zu verweisen, sodass es bei Veröffentlichung einer neuen Version von Python oder Alpine einem anderen Image zugewiesen wird. Um die Bildbildung deterministischer zu gestalten, verwenden Sie am besten die spezifischsten Tags, die Sie für das Bild finden, das Sie verwenden möchten.
#

In diesem Handbuch verwenden wir das als3.7.4-alpine3.10 gekennzeichnete Python-Image als übergeordnetes Image für unsere Django-Anwendung. Wir geben das Repository und das Tag des übergeordneten Bildes in unserenDockerfile mit der AnweisungFROM an.

Navigieren Sie zunächst aus dem Verzeichnisdjango-polls.

cd ..

Öffnen Sie dann eine Datei mit dem NamenDockerfile in einem Editor Ihrer Wahl. Fügen Sie die folgende übergeordnete Bilddefinition ein:

polls-project/Dockerfile

FROM python:3.7.4-alpine3.10

Dies definiert den Ausgangspunkt für das benutzerdefinierte Docker-Image, das wir für die Ausführung unserer Anwendung erstellen.

Hinzufügen von Anweisungen zum Einrichten der Anwendung

Sobald Sie ein übergeordnetes Image ausgewählt haben, können Sie Anweisungen hinzufügen, um Abhängigkeiten zu installieren, unsere Anwendungsdateien zu kopieren und die laufende Umgebung einzurichten. Dieser Prozess spiegelt im Allgemeinen die Schritte wider, die Sie zum Einrichten eines Servers für Ihre Anwendung ausführen würden, wobei einige wichtige Unterschiede für die Containerabstraktionen gelten.

Fügen Sie nach der ZeileFROMden folgenden Block des Dockerfile-Codes ein:

polls-project/Dockerfile

. . .

ADD django-polls/requirements.txt /app/requirements.txt

RUN set -ex \
    && apk add --no-cache --virtual .build-deps postgresql-dev build-base \
    && python -m venv /env \
    && /env/bin/pip install --upgrade pip \
    && /env/bin/pip install --no-cache-dir -r /app/requirements.txt \
    && runDeps="$(scanelf --needed --nobanner --recursive /env \
        | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
        | sort -u \
        | xargs -r apk info --installed \
        | sort -u)" \
    && apk add --virtual rundeps $runDeps \
    && apk del .build-deps

ADD django-polls /app
WORKDIR /app

ENV VIRTUAL_ENV /env
ENV PATH /env/bin:$PATH

EXPOSE 8000

Lassen Sie uns diese Anweisungen durchgehen, um einige der weniger offensichtlichen Optionen zu erläutern. Weitere Informationen zum Erstellen produktionsfähiger Docker-Dateien für Django-Apps finden Sie unterA Production-Ready Dockerfile for your Django App.

First Docker kopiert die Dateirequirements.txtnach/app/requirements.txt, damit die Abhängigkeiten unserer Anwendung im Dateisystem des Images verfügbar sind. Wir werden dies verwenden, um alle Python-Pakete zu installieren, die unsere Anwendung zum Ausführen benötigt. Wir kopieren die Abhängigkeitsdatei als separaten Schritt von der übrigen Codebasis, damit Docker die Bildebene mit der Abhängigkeitsdatei zwischenspeichern kann. Jedes Mal, wenn sich dierequirements.txt-Datei zwischen den Builds nicht ändert, kann Docker die zwischengespeicherte Ebene wiederverwenden, anstatt sie neu zu erstellen, was den Prozess beschleunigt.

Als nächstes haben wir eine einzelneRUN-Anweisung, die eine lange Liste von Befehlen ausführt, die jeweils mit dem Linux&&-Operator verkettet sind. Zusammenfassend sind diese Befehle:

  • Installieren Sie die PostgreSQL-Entwicklungsdateien und grundlegenden Build-Abhängigkeiten mit dem Paketmanagerapkvon Alpine

  • Erstellen Sie eine virtuelle Umgebung

  • Installieren Sie die inrequirements.txt aufgeführten Python-Abhängigkeiten mitpip

  • Stellen Sie eine Liste der zur Laufzeit benötigten Pakete zusammen, indem Sie die Anforderungen der installierten Python-Pakete analysieren

  • Deinstallieren Sie alle nicht benötigten Build-Abhängigkeiten

Wir verketten die Befehle miteinander, anstatt sie in einem separatenRUN-Schritt auszuführen, da Docker Bildebenen erstellt. Für jede AnweisungADD,COPY undRUN erstellt Docker eine neue Bildebene über dem vorhandenen Dateisystem, führt die Anweisung aus und speichert dann die resultierende Ebene. Dies bedeutet, dass das Komprimieren von Befehlen inRUN-Anweisungen zu weniger Bildebenen führt.

Sobald ein Objekt auf eine Bildebene geschrieben wurde, kann es in einer nachfolgenden Ebene nicht mehr entfernt werden, um die Bildgröße zu verringern. Wenn wir Build-Abhängigkeiten installieren, diese aber nach dem Einrichten der Anwendung entfernen möchten, müssen wir dies in derselben Anweisung tun, um die Bildgröße zu verringern. In diesem BefehlRUN installieren wir Build-Abhängigkeiten, verwenden sie zum Erstellen der App-Pakete und entfernen sie anschließend mitapk del.

Nach der AnweisungRUN verwenden wirADD, um den Anwendungscode zu kopieren, undWORKDIR, um das Arbeitsverzeichnis für das Bild in unser Codeverzeichnis zu setzen.

Anschließend verwenden wir die AnweisungENV, um zwei Umgebungsvariablen festzulegen, die in Containern verfügbar sind, die aus unserem Image hervorgehen. Der erste setztVIRTUAL_ENV auf/env und der zweite Befehl ändert die VariablePATH so, dass sie das Verzeichnis/env/bin enthält. Diese beiden Zeilen emulieren die Ergebnisse der Beschaffung des/env/bin/activate-Skripts, der traditionellen Methode zum Aktivieren einer virtuellen Umgebung.

Schließlich verwenden wirEXPOSE, um Docker darüber zu informieren, dass der Container zur Laufzeit Port8000 überwacht.

Zu diesem Zeitpunkt istDockerfile nahezu vollständig. Wir müssen nur den Standardbefehl definieren, der ausgeführt wird, wenn wir Container mit dem Image starten.

Definieren des Standardbefehls

Der Standardbefehl eines Docker-Images bestimmt, was passiert, wenn ein Container gestartet wird, ohne explizit einen auszuführenden Befehl bereitzustellen. Die Anweisungen vonENTRYPOINT undCMDkönnen unabhängig voneinander oder zusammen verwendet werden, um einen Standardbefehl innerhalb vonDockerfile zu definieren.

Wenn sowohlENTRYPOINT als auchCMD definiert sind, definiertENTRYPOINT die ausführbare Datei, die vom Container ausgeführt wird, undCMD repräsentiert die Standardargumentliste für diesen Befehl. Benutzer können die Standardargumentliste überschreiben, indem sie alternative Argumente an die Befehlszeile anhängen:docker run <image> <arguments>. In diesem Format können Benutzer den BefehlENTRYPOINTnicht einfach überschreiben. Daher wird der BefehlENTRYPOINThäufig auf ein Skript festgelegt, das die Umgebung einrichtet und basierend auf der empfangenen Argumentliste verschiedene Aktionen ausführt .

Bei alleiniger Verwendung konfiguriertENTRYPOINT die ausführbare Datei des Containers, definiert jedoch keine Standardargumentliste. Wenn nurCMD festgelegt ist, wird dies als Standardbefehls- und Argumentliste interpretiert, die zur Laufzeit überschrieben werden kann.

In unserem Image soll der Container unsere Anwendung standardmäßig mit dem Anwendungsservergunicornausführen. Die Argumentliste, die wir angunicorn übergeben, muss zur Laufzeit nicht konfigurierbar sein. Wir möchten jedoch die Möglichkeit haben, bei Bedarf problemlos andere Befehle auszuführen, um Debugging- oder Verwaltungsaufgaben auszuführen (z. B. statische Assets zu sammeln oder die Datenbank zu initialisieren). . Unter Berücksichtigung dieser Anforderungen ist es für uns sinnvoll,CMD zu verwenden, um einen Standardbefehl ohneENTRYPOINT zu definieren.

Die AnweisungCMDkann in einem der folgenden Formate definiert werden:

  • CMD ["argument 1", "argument 2", . . . ,"argument n"]: Das Argumentlistenformat (wird verwendet, um die Standardargumentliste fürENTRYPOINT zu definieren).

  • CMD ["command", "argument 1", "argument 2", . . . ,"argument n"]: Das Formatexec

  • CMD command "argument 1" "argument 2" . . . "argument n": Das Shell-Format

Das erste Format listet nur Argumente auf und wird in Verbindung mit einemENTRYPOINT verwendet. Die anderen beiden Formate geben Befehle und ihre Argumente mit ein paar wesentlichen Unterschieden an. Das empfohlene Formatexecführt den Befehl direkt aus und übergibt die Argumentliste ohne Shell-Verarbeitung. Das Shell-Format hingegen übergibt die gesamte Liste ansh -c. Dies ist erforderlich, wenn Sie beispielsweise den Wert einer Umgebungsvariablen in einem Befehl ersetzen müssen, dies jedoch im Allgemeinen als weniger vorhersehbar angesehen wird.

Für unsere Zwecke sieht die letzte Anweisung inDockerfilefolgendermaßen aus:

polls-project/Dockerfile

. . .
CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "mysite.wsgi:application"]

Standardmäßig führen Container, die dieses Image verwenden,gunicorn, die an den lokalen Host-Port8000 gebunden sind, mit 3 Workern aus und führen die Funktionapplication in der Dateiwsgi.py aus, die sich inmysitebefindet ) s Verzeichnis. Optional können Sie zur Laufzeit einen Befehl zum Ausführen eines anderen Prozesses anstelle vongunicorn bereitstellen.

Zu diesem Zeitpunkt können Siedocker build verwenden, um Ihr App-Image zu erstellen, unddocker run, um den Container auf Ihrem Computer auszuführen.

Erstellen des Docker-Image

Standardmäßig sucht der Befehldocker build im aktuellen Verzeichnis nachDockerfile, um die Build-Anweisungen zu finden. Es sendet auch den Build-Kontext, die lokale Dateisystemhierarchie, die während des Buildvorgangs verfügbar sein sollte, an den Docker-Dämon. Häufig wird das aktuelle Verzeichnis als Erstellungskontext festgelegt.

Führen Sie nach dem Zugriff auf das Verzeichnis mit IhrenDockerfiledocker build aus, übergeben Sie einen Bild- und Tag-Namen mit dem Flag-t und verwenden Sie das aktuelle Verzeichnis als Erstellungskontext. Hier benennen wir das Bilddjango-polls und kennzeichnen es mit Versionv0:

docker build -t django-polls:v0 .

Der Befehl übergibt dieDockerfile und das aktuelle Verzeichnis als Build-Kontext an den Docker-Daemon. Der Dämon erstellt Ihr Bild, indem er eine Reihe von Bildebenen erstellt, während er die Anweisungen vonDockerfileverarbeitet.

Wenndocker build abgeschlossen ist, sollte die folgende Ausgabe angezeigt werden:

OutputSuccessfully built 8260b58f5713
Successfully tagged django-polls:v0

Nachdem Sie das Image erfolgreich erstellt haben, können Sie den App-Container mitdocker run ausführen. Der Befehlrun wird hier jedoch höchstwahrscheinlich fehlschlagen, da wir die laufende Umgebung des Containers noch nicht konfiguriert haben. Externalisierte Variablen wieSECRET_KEY und Datenbankeinstellungen vonsettings.py sind entweder leer oder werden auf Standardwerte gesetzt.

Im letzten Schritt konfigurieren wir die Ausführungsumgebung des Containers mithilfe einer Umgebungsvariablendatei. Anschließend erstellen wir das Datenbankschema, generieren die statischen Dateien der App und laden sie in den Objektspeicher hoch. Anschließend testen wir die App.

[[Schritt-7 - Konfigurieren der laufenden Umgebung und Testen der App] == Schritt 7 - Konfigurieren der laufenden Umgebung und Testen der App

Docker bietetseveral methods zum Festlegen von Umgebungsvariablen im Container. Da wir alle in Schritt 1 ausgelagerten Variablen festlegen müssen, verwenden wir die Methode--env-file, mit der wir eine Datei übergeben können, die eine Liste der Umgebungsvariablen und ihrer Werte enthält.

Erstellen Sie eine Datei mit dem Namenenv im Verzeichnispolls-project und fügen Sie sie in die folgende Liste von Variablen ein:

polls-project/env

DJANGO_SECRET_KEY=your_secret_key
DEBUG=True
DJANGO_ALLOWED_HOSTS=your_server_IP_address
DATABASE_ENGINE=postgresql_psycopg2
DATABASE_NAME=polls
DATABASE_USERNAME=sammy
DATABASE_PASSWORD=your_database_password
DATABASE_HOST=your_database_host
DATABASE_PORT=your_database_port
STATIC_ACCESS_KEY_ID=your_space_access_key_id
STATIC_SECRET_KEY=your_space_secret_key
STATIC_BUCKET_NAME=your_space_name
STATIC_ENDPOINT_URL=https://nyc3.digitaloceanspaces.com
DJANGO_LOGLEVEL=info

Ersetzen Sie die folgenden Werte in dieser Datei:

  • DJANGO_SECRET_KEY: Setzen Sie diesen Wert auf einen eindeutigen, unvorhersehbaren Wert, wie inDjango docs beschrieben. Eine Methode zum Generieren dieses Schlüssels finden Sie inAdjusting the App Settings des LernprogrammsScalable Django App.

  • DJANGO_ALLOWED_HOSTS: Stellen Sie dies auf die IP-Adresse Ihres Ubuntu-Servers ein. Zu Testzwecken können Sie auch* festlegen, einen Platzhalter, der allen Hosts entspricht. Stellen Sie sicher, dass Sie diesen Wert richtig einstellen, wenn Sie Django in einer Produktionsumgebung ausführen.

  • DATABASE_USERNAME: Setzen Sie dies auf den im vorherigen Schritt erstellten Datenbankbenutzer.

  • DATABASE_PASSWORD: Setzen Sie dies auf das im vorherigen Schritt erstellte Benutzerkennwort.

  • DATABASE_HOST: Setzen Sie dies auf den Hostnamen Ihrer Datenbank.

  • DATABASE_PORT: Stellen Sie dies auf den Port Ihrer Datenbank ein.

  • STATIC_ACCESS_KEY_ID: Stellen Sie dies auf den Zugriffsschlüssel Ihres Space ein.

  • STATIC_SECRET_KEY: Stellen Sie dies auf den Zugriffsschlüssel Secret Ihres Space ein.

  • STATIC_BUCKET_NAME: Setzen Sie dies auf Ihren Space-Namen.

  • STATIC_ENDPOINT_URL: Setzen Sie dies auf die entsprechende Space-Endpunkt-URL.

Wenn Sie Django in der Produktion ausführen, müssen SieDEBUG aufFalse setzen und die Protokollstufe entsprechend Ihrer gewünschten Ausführlichkeit anpassen.

Speichern und schließen Sie die Datei.

Wir werden jetztdocker run verwenden, um die in der Docker-Datei festgelegtenCMD zu überschreiben und das Datenbankschema mit den Befehlenmanage.py makemigrations undmanage.py migrate zu erstellen:

docker run --env-file env django-polls:v0 sh -c "python manage.py makemigrations && python manage.py migrate"

Hier führen wir das Container-Image vondjango-polls:v0aus, übergeben die gerade erstellte Umgebungsvariablendatei und überschreiben den Dockerfile-Befehl mitsh -c "python manage.py makemigrations && python manage.py migrate", wodurch das durch den App-Code definierte Datenbankschema erstellt wird. Nach dem Ausführen des Befehls sollte Folgendes angezeigt werden:

OutputNo changes detected
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying polls.0001_initial... OK
  Applying sessions.0001_initial... OK

Dies zeigt an, dass das Datenbankschema erfolgreich erstellt wurde.

Als Nächstes führen wir eine weitere Instanz des App-Containers aus und verwenden eine interaktive Shell, um einen Administrator für das Django-Projekt zu erstellen.

docker run -i -t --env-file env django-polls:v0 sh

Dadurch erhalten Sie eine Shell-Eingabeaufforderung im laufenden Container, mit der Sie den Django-Benutzer erstellen können:

 python manage.py createsuperuser

Geben Sie einen Benutzernamen, eine E-Mail-Adresse und ein Kennwort für Ihren Benutzer ein und drücken Sie nach dem Erstellen des BenutzersCTRL+D, um den Container zu beenden und ihn zu beenden.

Schließlich generieren wir die statischen Dateien für die App und laden sie mitcollectstatic in den DigitalOcean Space hoch:

docker run --env-file env django-polls:v0 sh -c "python manage.py collectstatic --noinput"
Output
121 static files copied.

Wir können jetzt die App ausführen:

docker run --env-file env -p 80:8000 django-polls:v0
Output[2019-10-17 21:23:36 +0000] [1] [INFO] Starting gunicorn 19.9.0
[2019-10-17 21:23:36 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1)
[2019-10-17 21:23:36 +0000] [1] [INFO] Using worker: sync
[2019-10-17 21:23:36 +0000] [7] [INFO] Booting worker with pid: 7
[2019-10-17 21:23:36 +0000] [8] [INFO] Booting worker with pid: 8
[2019-10-17 21:23:36 +0000] [9] [INFO] Booting worker with pid: 9

Hier führen wir den in der Docker-Dateigunicorn --bind :8000 --workers 3 mysite.wsgi:application definierten Standardbefehl aus und legen den Container-Port8000 offen, sodass Port80 auf dem Ubuntu-Server Port8000 des zugeordnet wird django-polls:v0 Behälter.

Sie sollten nun in der Lage sein, mit Ihrem Webbrowser zurpolls-App zu navigieren, indem Siehttp://your_server_ip in die URL-Leiste eingeben. Da für den Pfad von/keine Route definiert ist, wird wahrscheinlich ein Fehler 404 Page Not Found angezeigt, der erwartet wird.

Navigieren Sie zuhttp://your_server_ip/polls, um die Benutzeroberfläche der Polls-App anzuzeigen:

Polls App Interface

Besuchen Siehttp://your_server_ip/admin, um die Administrationsoberfläche zu überprüfen. Das Fenster für die Administratorauthentifizierung der Polls-App sollte angezeigt werden:

Polls Admin Auth Page

Geben Sie den Administrator-Benutzernamen und das Kennwort ein, die Sie mit dem Befehlcreatesuperuser erstellt haben.

Nach der Authentifizierung können Sie auf die Verwaltungsoberfläche der Polls-App zugreifen:

Polls Admin Main Interface

Beachten Sie, dass statische Assets für die Appsadmin undpolls direkt aus dem Objektspeicher bereitgestellt werden. Um dies zu bestätigen, konsultieren SieTesting Spaces Static File Delivery.

Wenn Sie mit dem Erkunden fertig sind, drücken SieCTRL-C im Terminalfenster, in dem der Docker-Container ausgeführt wird, um den Container zu beenden.

Fazit

In diesem Lernprogramm haben Sie eine Django-Webanwendung so angepasst, dass sie effektiv in einer containergestützten, cloudbasierten Umgebung funktioniert. Anschließend haben Sie eine minimale Docker-Datei für das Container-Image geschrieben, diese lokal erstellt und mit Docker Engine ausgeführt. Sie könnendiff der Änderungen sehen, die Sie inpolls-docker branch des GitHub-Repositorys der Polls-App implementiert haben. Dieser Zweig enthält alle in diesem Tutorial beschriebenen Änderungen.

Von hier aus können Sie den Django / Gunicorn-Container mit einem Nginx-Reverse-Proxy-Container koppeln, um eingehende HTTP-Anforderungen zu verarbeiten und weiterzuleiten, und einenCertbot-Container, um TLS-Zertifikate zu erhalten. Sie können diese Multi-Container-Architektur mitDocker Compose verwalten. Dies wird in einem nachfolgenden Tutorial beschrieben.

Beachten Sie, dass dieses Setup wie es ist,not produktionsbereit ist, da Siealways Gunicorn hinter einem HTTP-Proxy ausführen sollten, um langsame Clients zu puffern. Andernfalls ist Ihre Django-Webanwendung für Denial-of-Service-Angriffe anfällig. Wir haben in diesem Tutorial auch 3 als willkürliche Anzahl von Gunicorn-Arbeitern ausgewählt. In der Produktion sollten Sie die Anzahl der Worker und Threads mithilfe von Leistungsbenchmarks festlegen.

In dieser Architektur haben wir uns für den Entwurf entschieden, statische Assets in den Objektspeicher zu verlagern, damit Container keine Version dieser Assets bündeln und sie mit Nginx bereitstellen müssen, was in Clusterumgebungen mit mehreren Containern wie Kubernetes schwierig zu verwalten sein kann . Abhängig von Ihrem Anwendungsfall ist dies möglicherweise kein effektives Design. Passen Sie die Schritte in diesem Lernprogramm entsprechend an.

Nachdem Sie die Django Polls-App vollständig containerisiert haben, können Sie das Image in eine Containerregistrierung wieDockerhub verschieben und für jedes System verfügbar machen, auf dem Docker verfügbar ist: Ubuntu-Server, virtuelle Maschinen und Containercluster wie Kubernetes.