One place for hosting & domains

      Bereitstellen einer skalierbaren und sicheren Django-Anwendung mit Kubernetes


      Einführung

      In diesem Tutorial stellen Sie eine containerisierte Django-Umfrageanwendung in einem Kubernetes-Cluster bereit.

      Django ist ein leistungsfähiges Web-Framework, das Ihnen dabei helfen kann, Ihre Python-Anwendung schnell bereitzustellen. Es enthält mehrere praktische Funktionen wie einen objektrelationalen Mapper, eine Benutzerauthentifizierung und eine anpassbare Verwaltungsoberfläche für Ihre Anwendung. Es beinhaltet auch ein Caching-Framework und fördert ein sauberes App-Design durch seinen URL Dispatcher und das Vorlagensystem.

      In Erstellen einer Django- und Gunicorn-Anwendung mit Docker wurde die Django Tutorial Umfrageanwendung gemäß der Zwölf-Faktor-Methodik zur Erstellung skalierbarer Cloud-nativer Web-Apps modifiziert. Diese containerisierte Einrichtung wurde mit einem Nginx Reverse-Proxy und Let’s Encrypt-signierten TLS-Zertifikaten in Skalieren und Sichern einer Django-Anwendung mit Docker, Nginx und Let’s Encrypt skaliert und gesichert. In diesem letzten Tutorial der Reihe Von Containern zu Kubernetes mit Django wird die modernisierte Django-Umfrageanwendung in einem Kubernetes-Cluster bereitgestellt.

      Kubernetes ist ein leistungsstarker Open-Source-Container-Orchestrator, der die Bereitstellung, das Skalieren und die Verwaltung von containerisierten Anwendungen automatisiert. Mit Kubernetes-Objekten wie ConfigMaps und Secrets können Sie die Konfiguration zentralisieren und von Ihren Containern entkoppeln, während Controller wie Deployments fehlgeschlagene Container automatisch neu starten und eine schnelle Skalierung von Container-Replikaten ermöglichen. Die TLS-Verschlüsselung wird mit einem Ingress-Objekt und dem Open-Source Ingress-Controller ingress-nginx aktiviert. Das Kubernetes Add-on cert-manager erneuert und stellt Zertifikate mit der kostenlosen Zertifizierungsstelle Let’s Encrypt aus.

      Voraussetzungen

      Um dieser Anleitung zu folgen, benötigen Sie:

      • Einen Kubernetes 1.15+-Cluster mit aktivierter rollenbasierter Zugriffskontrolle (RBAC). In diesem Setup wird ein DigitalOcean Kubernetes-Cluster verwendet, doch Sie können einen Cluster auch mit einer anderen Methode erstellen.
      • Das Befehlszeilen-Tool kubectl, das auf Ihrem lokalen Rechner installiert und für die Verbindung mit Ihrem Cluster konfiguriert ist. Weitere Informationen zur Installation von kubectl finden Sie in der offiziellen Dokumentation. Wenn Sie einen DigitalOcean Kubernetes-Cluster verwenden, lesen Sie bitte Verbinden mit einem DigitalOcean Kubernetes-Cluster, um zu erfahren, wie Sie sich mit kubectl mit Ihrem Cluster verbinden können.
      • Einen registrierten Domänennamen. In diesem Tutorial wird durchgängig your_domain.com verwendet. Einen Domänennamen können Sie kostenlos bei Freenom erhalten oder Sie nutzen eine Domänenregistrierungsstelle Ihrer Wahl.
      • Einen ingress-nginx Ingress-Controller und den TLS-Zertifizierungsmanager cert-manager, die in Ihrem Cluster installiert und zur Ausgabe von TLS-Zertifikaten konfiguriert sind. Um zu erfahren, wie Sie einen Ingress mit cert-manager installieren und konfigurieren, konsultieren Sie bitte Einrichten eines Nginx Ingress mit Cert-Manager in DigitalOcean Kubernetes.
      • Einen A-DNS-Datensatz mit your_domain.com, der auf die öffentliche IP-Adresse des Ingress Load Balancers verweist. Wenn Sie DigitalOcean zum Verwalten der DNS-Datensätze Ihrer Domäne verwenden, konsultieren Sie bitte Verwalten von DNS-Datensätzen, um zu erfahren, wie Sie A-Datensätze erstellen.
      • Einen S3-Objektspeicher-Bucket wie beispielsweise einen DigitalOcean Space zur Speicherung der statischen Dateien Ihres Django-Projekts und einen Satz von Zugriffsschlüsseln für diesen Space. Um zu erfahren, wie Sie einen Space erstellen können, lesen Sie die Produktdokumentation Erstellen von Spaces. Um zu erfahren, wie Sie Zugriffsschlüssel für Spaces erstellen können, lesen Sie Zugriff auf Spaces mit Zugriffsschlüsseln gemeinsam nutzen. Mit geringfügigen Änderungen können Sie jeden Objektspeicherdienst verwenden, der das Plugin django-storages verwendet.
      • Eine PostgreSQL-Server-Instanz, Datenbank und Benutzer für Ihre Django-Anwendung. Mit geringfügigen Änderungen können Sie jede Datenbank verwenden, die Django unterstützt.
      • Ein Docker Hub-Konto und ein öffentliches Repository. Weitere Informationen zu deren Erstellung finden Sie in Repositories in der Docker-Dokumentation.
      • Die auf Ihrem lokalen Rechner installierte Docker-Engine. Weitere Informationen finden Sie unter Installieren und Verwenden von Docker unter Ubuntu 18.04.

      Sobald Sie diese Komponenten eingerichtet haben, können Sie mit diesem Leitfaden beginnen.

      Schritt 1 — Klonen und Konfigurieren der Anwendung

      In diesem Schritt klonen wir den Anwendungscode von GitHub und konfigurieren Einstellungen wie Datenbankzugangsdaten und Objektspeicherschlüssel.

      Der Anwendungscode und das Dockerfile befinden sich im Zweig polls-docker der Django Tutorial Umfrageanwendung GitHub-Repository. Dieses Repository enthält Code für die Beispiel-Umfrageanwendung aus der Django-Dokumentation, in der Sie lernen, wie Sie eine Umfrageanwendung von Grund auf erstellen.

      Der Zweig polls-docker enthält eine dockerisierte Version der Umfrageanwendung. Um zu erfahren, wie die Umfrageanwendung modifiziert wurde, um effektiv in einer containerisierten Umgebung zu arbeiten, lesen Sie bitte Erstellen einer Django- und Gunicorn-Anwendung mit Docker.

      Beginnen Sie mit der Verwendung von git zum Klonen des Zweigs polls-docker der Django Tutorial Umfrageanwendung GitHub-Repository auf Ihren lokalen Rechner:

      • git clone --single-branch --branch polls-docker https://github.com/do-community/django-polls.git

      Navigieren Sie in das Verzeichnis django-polls:

      Dieses Verzeichnis enthält den Python-Code der Django-Anwendung, ein Dockerfile, das Docker zum Erstellen des Container-Images verwendet, sowie eine Datei env, die eine Liste von Umgebungsvariablen enthält, die an die laufende Umgebung des Containers übergeben werden müssen. Prüfen Sie das Dockerfile:

      Output

      FROM python:3.7.4-alpine3.10 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 CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "mysite.wsgi"]

      Dieses Dockerfile verwendet das offizielle Python 3.7.4 Docker-Image als Basis und installiert die Python-Paketanforderungen von Django und Gunicorn, wie sie in der Datei django-polls/requirements.txt definiert sind. Anschließend entfernt es einige unnötige Builddateien, kopiert den Anwendungscode in das Image und legt den Ausführungspfad PATH fest. Schließlich gibt es an, dass Port 8000 verwendet wird, um eingehende Container-Verbindungen zu akzeptieren und gunicorn mit 3 Workern ausgeführt wird, die Port 8000 abhören.

      Um mehr über die einzelnen Schritte in diesem Dockerfile zu erfahren, lesen Sie bitte Schritt 6 von Erstellen einer Django- und Gunicorn-Anwendung mit Docker.

      Erstellen Sie nun das Image mit docker build:

      Wir benennen das Image polls mit dem Flag -t und übergeben im aktuellen Verzeichnis als Build-Kontext den Satz von Daten, auf den beim Erstellen des Images verwiesen werden soll.

      Nachdem Docker das Image erstellt und mit Tags versehen hat, listen wir die verfügbaren Images mit docker images auf:

      Sie sollten die polls-Images aufgelistet sehen:

      OutputREPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
      polls               latest              80ec4f33aae1        2 weeks ago         197MB
      python              3.7.4-alpine3.10    f309434dea3a        8 months ago        98.7MB
      

      Bevor wir den Django-Container ausführen, müssen wir seine Betriebsumgebung mithilfe der im aktuellen Verzeichnis vorhandenen Datei env konfigurieren. Diese Datei wird an den Befehl docker run übergeben, der zum Ausführen des Containers verwendet wird, und Docker injiziert die konfigurierten Umgebungsvariablen in die Betriebsumgebung des Containers.

      Öffnen Sie die Datei env mit nano oder Ihrem bevorzugten Editor:

      django-polls/env

      DJANGO_SECRET_KEY=
      DEBUG=True
      DJANGO_ALLOWED_HOSTS=
      DATABASE_ENGINE=postgresql_psycopg2
      DATABASE_NAME=polls
      DATABASE_USERNAME=
      DATABASE_PASSWORD=
      DATABASE_HOST=
      DATABASE_PORT=
      STATIC_ACCESS_KEY_ID=
      STATIC_SECRET_KEY=
      STATIC_BUCKET_NAME=
      STATIC_ENDPOINT_URL=
      DJANGO_LOGLEVEL=info
      

      Geben Sie die fehlenden Werte für die folgenden Schlüssel ein:

      • DJANGO_SECRET_KEY: Setzen Sie diesen auf einen eindeutigen, nicht vorhersagbaren Wert, wie in den Django-Dokumentationen beschrieben. Eine Methode zur Generierung dieses Wertes wird in Anpassen der Anwendungseinstellungen in dem Tutorial Skalierbare Django-Anwendung angeboten.
      • DJANGO_ALLOWED_HOSTS: Diese Variable sichert die Anwendung und verhindert HTTP-Host-Header-Angriffe. Setzen Sie diese Variable für Testzwecke auf *, einen Platzhalter, der auf alle Hosts zutrifft. In der Produktion sollten Sie diese Variable auf your_domain.com setzen. Um mehr über diese Django-Einstellungen zu erfahren, konsultieren Sie die Core-Einstellungen der Django-Dokumentation.
      • DATABASE_USERNAME: Setzen Sie diesen auf den in den vorbereitenden Schritten erstellten PostgreSQL Datenbankbenutzer.
      • DATABASE_NAME: Setzen Sie diesen auf polls oder den in den vorbereitenden Schritten erstellten Namen der PostgreSQL-Datenbank.
      • DATABASE_PASSWORD: Setzen Sie dieses auf das in den vorbereitenden Schritten erstellte Passwort für den PostgreSQL Benutzer.
      • DATABASE_HOST: Setzen Sie diesen Wert auf den Hostnamen Ihrer Datenbank.
      • DATABASE_PORT: Setzen Sie diesen Wert auf den Port Ihrer Datenbank.
      • STATIC_ACCESS_KEY_ID: Setzen Sie dies auf den Zugriffsschlüssel Ihres Space oder Objektspeichers.
      • STATIC_SECRET_KEY: Setzen Sie dies auf den Zugriffsschlüssel Ihres Space- oder Objektspeicher-Secret.
      • STATIC_BUCKET_NAME: Setzen Sie dies auf den Namen Ihres Space- oder Objektspeicher-Buckets.
      • STATIC_ENDPOINT_URL: Setzen Sie diese auf die entsprechende Endpunkt-URL des Space oder Objektspeichers, z. B. https://space-name.nyc3.digitaloceanspaces.com, wenn sich Ihr Space in der Region nyc3 befindet.

      Wenn Sie die Bearbeitung abgeschlossen haben, speichern und schließen Sie die Datei.

      Im nächsten Schritt führen wir den konfigurierten Container lokal aus und erstellen das Datenbankschema. Wir laden ebenfalls statische Assets wie Stylesheets und Images in den Objektspeicher hoch.

      Schritt 2 — Erstellen des Datenbankschemas und Hochladen von Assets in den Objektspeicher

      Nachdem der Container erstellt und konfiguriert ist, verwenden wir nun docker run, um den CMD-Satz in dem Dockerfile zu überschreiben und das Datenbankschema mit den Befehlen manage.py makemigrations und manage.py migrate zu erstellen:

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

      Wir führen das Container-Image polls:latest aus, übergeben die von uns gerade modifizierte Umgebungsvariablendatei und überschreiben den Dockerfile-Befehl mit sh -c "python manage.py makemigrations && python manage.py migrate", wodurch das durch den Anwendungscode definierte Datenbankschema erstellt wird.

      Wenn Sie dies zum ersten Mal ausführen, sollten Sie Folgendes sehen:

      Output

      No 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.

      Wenn Sie migrate zu einem späteren Zeitpunkt ausführen, führt Django eine Nulloperation durch, es sei denn, das Datenbankschema wurde geändert.

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

      • docker run -i -t --env-file env polls sh

      Dadurch erhalten Sie eine Shell-Eingabeaufforderung innerhalb des laufenden Containers, die Sie zum Erstellen des Django-Benutzers verwenden können:

      • python manage.py createsuperuser

      Geben Sie einen Benutzernamen, eine E-Mail-Adresse und ein Passwort für Ihren Benutzer ein. Drücken Sie nach dem Erstellen des Benutzers STRG+D, um den Container zu verlassen und zu beenden.

      Schließlich generieren wir die statischen Dateien für die Anwendung und laden sie mit collectstatic in den DigitalOcean Space hoch. Beachten Sie, dass dies möglicherweise einige Zeit dauern kann.

      • docker run --env-file env polls sh -c "python manage.py collectstatic --noinput"

      Nachdem diese Dateien generiert und hochgeladen sind, erhalten Sie folgende Ausgabe.

      Output

      121 static files copied.

      Wir können die Anwendung nun ausführen:

      • docker run --env-file env -p 80:8000 polls

      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 dem Dockerfile definierten Standardbefehl gunicorn ---bind :8000 --workers 3 mysite.wsgi:application aus und geben den Container-Port 8000 frei, sodass Port 80 auf Ihrem lokalen Rechner dem Port 8000 des Containers polls zugeordnet wird.

      Sie sollten nun über Ihren Webbrowser zu der Anwendung polls navigieren können, indem Sie http://localhost in die URL-Leiste eingeben. Da für den Pfad / keine Route definiert ist, erhalten Sie wahrscheinlich einen 404 Page Not Found-Fehler, der zu erwarten ist.

      Navigieren Sie zu http://localhost/polls, um die Benutzeroberfläche der Umfrageanwendung zu sehen:

      Oberfläche der Umfrageanwendung

      Um die administrative Oberfläche anzuzeigen, besuchen Sie http://localhost/admin. Sie sollten das Authentifizierungsfenster für den Administrator der Umfrageanwendung sehen:

      Authentifizierungsseite für Polls-Administrator

      Geben Sie den administrativen Benutzernamen und das Passwort ein, das Sie mit dem Befehl createsuperuser erstellt haben.

      Nach der Authentifizierung können Sie auf die administrative Oberfläche der Umfrageanwendung zugreifen:

      Administrative Hauptoberfläche von Polls

      Beachten Sie, dass statische Assets für die Anwendungen admin und polls direkt aus dem Objektspeicher bereitgestellt werden. Um dies zu bestätigen, konsultieren Sie Prüfen der statischen Dateizustellung von Spaces.

      Wenn Sie die Erkundung abgeschlossen haben, drücken Sie STRG+C im Terminalfenster, in dem der Docker-Container ausgeführt wird, um den Container zu beenden.

      Nachdem das Docker-Image der Django-Anwendung getestet, die statischen Assets in den Objektspeicher hochgeladen und das Datenbankschema konfiguriert und für die Verwendung mit Ihrer Anwendung bereit ist/sind, können Sie Ihr Django-App-Image in eine Bildregistrierung wie Docker Hub hochladen.

      Schritt 3 — Verschieben des Django-App-Images zu Docker Hub

      Um Ihre Anwendung unter Kubernetes bereitzustellen, muss Ihr App-Image in eine Registrierung wie Docker Hub hochgeladen werden. Kubernetes zieht das App-Image aus seinem Repository und stellt es dann in Ihren Cluster bereit.

      Sie können eine private Docker-Registrierung verwenden, wie die DigitalOcean Container Registry, die derzeit kostenlos in Early Access ist, oder eine öffentliche Docker-Registrierung wie Docker Hub. Mit Docker Hub können Sie auch private Docker-Repositorys erstellen. Ein öffentliches Repository erlaubt jedem, die Container-Images zu sehen und abzurufen, während Sie mit einem privaten Repository den Zugriff auf Sie und Ihre Teammitglieder beschränken können.

      In diesem Tutorial verschieben wir das Django-Image in das öffentliche Docker Hub-Repository, das in den Voraussetzungen erstellt wurde. Sie können Ihr Image auch in ein privates Repository verschieben, aber das Abrufen von Images aus einem privaten Repository liegt außerhalb des Rahmens dieses Artikels. Um mehr über die Authentifizierung von Kubernetes mit Docker Hub und das Abrufen von privaten Images zu erfahren, lesen Sie bitte Abrufen eines Images aus einer privaten Registrierung aus den Kubernetes-Dokumenten.

      Beginnen Sie mit der Anmeldung an Docker Hub auf Ihrem lokalen Rechner:

      Output

      Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one. Username:

      Geben Sie Ihren Docker Hub-Benutzernamen und Ihr Passwort ein, um sich anzumelden.

      Das Django-Image hat derzeit die Markierung polls:latest. Um es in Ihr Docker Hub-Repository zu verschieben, markieren Sie das Image erneut mit Ihrem Docker Hub-Benutzernamen und dem Repository-Namen:

      • docker tag polls:latest your_dockerhub_username/your_dockerhub_repo_name:latest

      Verschieben Sie das Image in das Repository:

      • docker push sammy/sammy-django:latest

      In diesem Tutorial ist der Docker Hub-Benutzername sammy und der Repository-Name ist sammy-django. Sie sollten diese Werte durch Ihren eigenen Docker Hub-Benutzernamen und Repository-Namen ersetzen.

      Sie sehen eine Ausgabe, die sich aktualisiert, wenn Image-Schichten in Docker Hub verschoben werden.

      Nachdem Ihr Image nun für Kubernetes unter Docker Hub verfügbar ist, können Sie es in Ihrem Cluster bereitstellen.

      Schritt 4 — Einrichten der ConfigMap

      Als wir den Django-Container lokal ausgeführt haben, haben wir die Datei env an docker run übergeben, um Konfigurationsvariablen in die Laufzeitumgebung zu injizieren. Bei Kubernetes können Konfigurationsvariablen mit ConfigMaps und Secrets injiziert werden.

      ConfigMaps sollte verwendet werden, um nicht vertrauliche Konfigurationsdaten wie App-Einstellungen zu speichern, und Secrets sollte für sensible Informationen wie API-Schlüssel und Datenbank-Zugangsdaten verwendet werden. Sie werden beide auf ähnliche Weise in Container injiziert, aber Secrets haben zusätzliche Zugriffskontrolle und Sicherheitsfunktionen wie Verschlüsselung im Ruhezustand. Secrets speichern außerdem Daten in base64, während ConfigMaps Daten im Klartext speichern.

      Erstellen Sie zunächst ein Verzeichnis namens yaml, in dem wir unsere Kubernetes-Manifeste speichern werden. Navigieren Sie in das Verzeichnis.

      Öffnen Sie eine Datei namens polls-configmap.yaml in nano oder Ihrem bevorzugten Texteditor:

      • nano polls-configmap.yaml

      Fügen Sie das folgende ConfigMap-Manifest ein:

      polls-configmap.yaml

      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: polls-config
      data:
        DJANGO_ALLOWED_HOSTS: "*"
        STATIC_ENDPOINT_URL: "https://your_space_name.space_region.digitaloceanspaces.com"
        STATIC_BUCKET_NAME: "your_space_name"
        DJANGO_LOGLEVEL: "info"
        DEBUG: "True"
        DATABASE_ENGINE: "postgresql_psycopg2"
      

      Wir haben die nicht sensible Konfiguration aus der in Schritt 1 geänderten Datei env extrahiert und in ein ConfigMap-Manifest eingefügt. Das ConfigMap-Objekt wird polls-config genannt. Kopieren Sie die gleichen Werte hinein, die Sie im vorherigen Schritt in die Datei env eingegeben haben.

      Lassen Sie für Testzwecke DJANGO_ALLOWED_HOSTS auf * stehen, um die Host-Header-basierte Filterung zu deaktivieren. In einer Produktionsumgebung sollten Sie dies auf die Domäne Ihrer Anwendung setzen.

      Wenn Sie mit der Bearbeitung der Datei fertig sind, speichern und schließen Sie sie.

      Erstellen Sie die ConfigMap in Ihrem Cluster mit kubectl apply:

      • kubectl apply -f polls-configmap.yaml

      Output

      configmap/polls-config created

      Nachdem die ConfigMap erstellt wurde, erstellen wir im nächsten Schritt das von unserer Anwendung verwendete Secret.

      Schritt 5 — Einrichten des Secret

      Secret-Werte müssen base64-kodiert sein, d. h. das Erstellen von Secret-Objekten in Ihrem Cluster ist etwas aufwendiger als das Erstellen von ConfigMaps. Sie können den Vorgang aus dem vorherigen Schritt wiederholen, indem Sie Secret-Werte manuell base64-kodieren und in eine Manifestdatei einfügen. Sie können sie auch mit einer Umgebungsvariablendatei kubectl create und dem Flag --from-env-file erstellen, was wir in diesem Schritt tun werden.

      Wir verwenden erneut die Datei env von Schritt 1 und entfernen die in die ConfigMap eingefügten Variablen. Erstellen Sie eine Kopie der Datei env mit dem Namen polls-secrets im Verzeichnis yaml:

      • cp ../env ./polls-secrets

      Bearbeiten Sie die Datei in Ihrem bevorzugten Editor:

      polls-secrets

      DJANGO_SECRET_KEY=
      DEBUG=True
      DJANGO_ALLOWED_HOSTS=
      DATABASE_ENGINE=postgresql_psycopg2
      DATABASE_NAME=polls
      DATABASE_USERNAME=
      DATABASE_PASSWORD=
      DATABASE_HOST=
      DATABASE_PORT=
      STATIC_ACCESS_KEY_ID=
      STATIC_SECRET_KEY=
      STATIC_BUCKET_NAME=
      STATIC_ENDPOINT_URL=
      DJANGO_LOGLEVEL=info
      

      Löschen Sie alle in das ConfigMap-Manifest eingefügten Variablen. Wenn Sie fertig sind, sollte die Datei wie folgt aussehen:

      polls-secrets

      DJANGO_SECRET_KEY=your_secret_key
      DATABASE_NAME=polls
      DATABASE_USERNAME=your_django_db_user
      DATABASE_PASSWORD=your_django_db_user_password
      DATABASE_HOST=your_db_host
      DATABASE_PORT=your_db_port
      STATIC_ACCESS_KEY_ID=your_space_access_key
      STATIC_SECRET_KEY=your_space_access_key_secret
      

      Stellen Sie sicher, dass Sie die gleichen Werte wie in Schritt 1 verwenden. Wenn Sie fertig sind, speichern und schließen Sie die Datei.

      Erstellen Sie das Secret in Ihrem Cluster mit kubectl create secret:

      • kubectl create secret generic polls-secret --from-env-file=poll-secrets

      Output

      secret/polls-secret created

      Hier erstellen wir ein Secret-Objekt namens polls-secret und übergeben die soeben erstellte Secrets-Datei.

      Sie können das Secret mit kubectl describe inspizieren:

      • kubectl describe secret polls-secret

      Output

      Name: polls-secret Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== DATABASE_PASSWORD: 8 bytes DATABASE_PORT: 5 bytes DATABASE_USERNAME: 5 bytes DJANGO_SECRET_KEY: 14 bytes STATIC_ACCESS_KEY_ID: 20 bytes STATIC_SECRET_KEY: 43 bytes DATABASE_HOST: 47 bytes DATABASE_NAME: 5 bytes

      Zu diesem Zeitpunkt haben Sie die Konfiguration Ihrer Anwendung in Ihrem Kubernetes-Cluster mithilfe der Objekttypen Secret und ConfigMap gespeichert. Wir sind nun bereit, die Anwendung in dem Cluster bereitzustellen.

      Schritt 6 — Bereitstellen der Django-Anwendung mithilfe eines Deployment

      In diesem Schritt erstellen Sie ein Deployment für Ihre Django-Anwendung. Eine Kubernetes Deployment ist ein Controller, der zur Verwaltung von zustandslosen Anwendungen in Ihrem Cluster verwendet werden kann. Ein Controller ist eine Kontrollschleife, die Arbeitslasten reguliert, indem er sie herauf- oder herunterskaliert. Controller starten auch fehlgeschlagene Container neu und leeren sie aus.

      Deployments steuern ein oder mehrere Pods, die kleinste einsetzbare Einheit in einem Kubernetes-Cluster. Pods umschließen einen oder mehrere Container. Um mehr über die verschiedenen Arten von Arbeistlasten zu erfahren, die Sie starten können, lesen Sie bitte Eine Einführung in Kubernetes.

      Öffnen Sie zunächst eine Datei namens polls-deployment.yaml in Ihrem bevorzugten Editor:

      • nano polls-deployment.yaml

      Fügen Sie das folgende Deployment-Manifest ein:

      polls-deployment.yaml

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: polls-app
        labels:
          app: polls
      spec:
          replicas: 2
        selector:
          matchLabels:
            app: polls
        template:
          metadata:
            labels:
              app: polls
          spec:
            containers:
              - image: your_dockerhub_username/app_repo_name:latest
                name: polls
                envFrom:
                - secretRef:
                    name: polls-secret
                - configMapRef:
                    name: polls-config
                ports:
                  - containerPort: 8000
                    name: gunicorn
      

      Geben Sie den entsprechenden Container-Image-Namen ein und verweisen Sie dabei auf das Django Umfrage-Image, das Sie in Schritt 2 in Docker Hub verschoben haben.

      Hier definieren wir eine Kubernetes Deployment namens polls-app und kennzeichnen es mit dem Schlüsselwertpaar app: polls. Wir geben an, dass wir zwei Replikate des Pods ausführen möchten, der unterhalb des Felds template definiert ist.

      Durch die Verwendung von envFrom mit secretRef und configMapRef legen wir fest, dass alle Daten aus dem Secret polls-secret und der ConfigMap polls-config als Umgebungsvariablen in die Container injiziert werden sollen. Die Schlüssel ConfigMap und Secret werden zu den Namen der Umgebungsvariablen.

      Abschließend geben wir containerPort 8000 frei und nennen ihn gunicorn.

      Um mehr über die Konfiguration von Kubernetes Deployments zu erfahren, konsultieren Sie bitte Deployments aus der Kubernetes-Dokumentation.

      Wenn Sie mit der Bearbeitung der Datei fertig sind, speichern und schließen Sie sie.

      Erstellen Sie das Deployment in Ihrem Cluster mit kubectl apply -f:

      • kubectl apply -f polls-deployment.yaml
      • deployment.apps/polls-app created

      Überprüfen Sie mit kubectl get, ob das Deployment korrekt bereitgestellt wurde:

      • kubectl get deploy polls-app

      Output

      NAME READY UP-TO-DATE AVAILABLE AGE polls-app 2/2 2 2 6m38s

      Wenn Sie auf einen Fehler stoßen, oder etwas nicht so ganz funktioniert, können Sie kubectl describe verwenden, um das fehlgeschlagene Deployment zu inspizieren:

      Sie können die beiden Pods mit kubectl get pod inspizieren:

      Output

      NAME READY STATUS RESTARTS AGE polls-app-847f8ccbf4-2stf7 1/1 Running 0 6m42s polls-app-847f8ccbf4-tqpwm 1/1 Running 0 6m57s

      Zwei Replikate Ihrer Django-Anwendung sind nun im Cluster in Betrieb. Um auf die Anwendung zuzugreifen, müssen Sie einen Kubernetes-Dienst erstellen, was wir als Nächstes tun.

      Schritt 7 — Erlauben des externen Zugriffs unter Verwendung eines Dienstes

      In diesem Schritt erstellen Sie einen Dienst für Ihre Django-Anwendung. Ein Kubernetes-Dienst ist eine Abstraktion, die es Ihnen ermöglicht, einen Satz laufender Pods als Netzwerkdienst bereitzustellen. Mit einem Dienst können Sie einen stabilen Endpunkt für Ihre Anwendung erstellen, der sich nicht ändert, wenn Pods sterben und neu erstellt werden.

      Es gibt mehrere Dienstarten, einschließlich ClusterIP-Dienste, die den Dienst auf einer clusterinternen IP-Adresse bereitstellen, NodePort-Dienste, die den Dienst auf jedem Knoten an einem statischen Port, dem Nodeport, bereitstellen, und LoadBalancer-Dienste, die einen Cloud-Load-Balancer bereitstellen, um externen Datenverkehr zu den Pods in Ihrem Cluster zu leiten (über NodePorts, die er automatisch erstellt). Um mehr über diese zu erfahren, lesen Sie bitte Service in den Kubernetes-Dokumenten.

      In unserer endgültigen Einrichtung verwenden wir einen ClusterIP-Dienst, der über einen Ingress und den in den Voraussetzungen für diesen Leitfaden eingerichteten Ingress-Controller freigegeben wird. Um zu testen, ob alles korrekt funktioniert, erstellen wir zunächst einen temporären NodePort-Dienst, um auf die Django-Anwendung zuzugreifen.

      Beginnen Sie mit dem Erstellen einer Datei namens polls-svc.yaml mit Ihrem bevorzugten Editor:

      Fügen Sie das folgende Dienst-Manifest ein:

      polls-svc.yaml

      apiVersion: v1
      kind: Service
      metadata:
        name: polls
        labels:
          app: polls
      spec:
        type: NodePort
        selector:
          app: polls
        ports:
          - port: 8000
            targetPort: 8000
      

      Hier erstellen wir einen NodePort-Dienst namens polls und geben ihm die Kennzeichnung app: polls. Dann wählen wir Backend-Pods mit der Kennzeichnung app: polls, und zielen auf deren Ports 8000.

      Wenn Sie mit der Bearbeitung der Datei fertig sind, speichern und schließen Sie sie.

      Stellen Sie den Dienst mit kubectl apply bereit:

      • kubectl apply -f polls-svc.yaml

      Output

      service/polls created

      Bestätigen Sie mit kubectl get svc, dass Ihr Dienst erstellt wurde:

      Output

      NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE polls NodePort 10.245.197.189 <none> 8000:32654/TCP 59s

      Diese Ausgabe zeigt die clusterinterne IP des Dienstes und den NodePort (32654). Um eine Verbindung mit dem Dienst herzustellen, benötigen wir die externen IP-Adressen für unsere Cluster-Knoten:

      Output

      NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME pool-7no0qd9e0-364fd Ready <none> 27h v1.18.8 10.118.0.5 203.0.113.1 Debian GNU/Linux 10 (buster) 4.19.0-10-cloud-amd64 docker://18.9.9 pool-7no0qd9e0-364fi Ready <none> 27h v1.18.8 10.118.0.4 203.0.113.2 Debian GNU/Linux 10 (buster) 4.19.0-10-cloud-amd64 docker://18.9.9 pool-7no0qd9e0-364fv Ready <none> 27h v1.18.8 10.118.0.3 203.0.113.3 Debian GNU/Linux 10 (buster) 4.19.0-10-cloud-amd64 docker://18.9.9

      Rufen Sie in Ihrem Webbrowser Ihre Umfrageanwendung mit der externen IP-Adresse eines beliebigen Knotens und dem NodePort auf. Mit der vorstehenden Ausgabe würde die URL der Anwendung lauten: http://203.0.113.1:32654/polls.

      Sie sollten die gleiche Oberfläche der Umfrageanwendung sehen, auf die Sie in Schritt 1 lokal zugegriffen haben:

      Oberfläche der Umfrageanwendung

      Sie können den gleichen Test mit der Route /admin wiederholen: http://203.0.113.1:32654/admin. Sie sollten die gleiche Admin-Oberfläche wie zuvor sehen:

      Authentifizierungsseite für Polls-Administrator

      Zu diesem Zeitpunkt haben Sie zwei Replikate des Django Umfrageanwendungs-Containers mithilfe eines Deployments bereitgestellt. Außerdem haben Sie einen stabilen Netzwerk-Endpunkt für diese beiden Replikate erstellt und ihn mit einem NodePort-Dienst von außen zugänglich gemacht.

      Der letzte Schritt in diesem Tutorial ist die Sicherung des externen Datenverkehrs zu Ihrer Anwendung mit HTTPS. Dazu verwenden wir den in den Voraussetzungen installierten Ingress-Controller ingress-nginx und erstellen ein Ingress-Objekt, um den externen Verkehr in den Kubernetes-Dienst polls zu leiten.

      Schritt 8 — Konfigurieren von HTTPS unter Verwendung von Nginx Ingress und cert-manager

      Mit Ingresses von Kubernetes können Sie den Datenverkehr von außerhalb Ihres Kubernetes-Clusters flexibel an Dienste innerhalb Ihres Clusters leiten. Dies geschieht mit der Verwendung von Ingress-Objekten, die Regeln für das Routing von HTTP- und HTTPS-Verkehr zu Kubernetes-Diensten definieren, und Ingress-Controllern, die die Regeln umsetzen, indem sie den Verkehr durch Lastverteilung an die entsprechenden Backend-Dienste weiterleiten.

      In den Voraussetzungen haben den Ingress-Controller ingress-nginx und das TLS-Zertifizierungsautomatisierungs-Add-on cert-manager installiert. Außerdem haben Sie die Staging- und Produktions-ClusterIssuers für Ihre Domäne unter Verwendung der Zertifizierungsstelle Let’s Encrypt eingerichtet und einen Ingress erstellt, um die Ausstellung von Zertifikaten und die TLS-Verschlüsselung für zwei Dummy-Backend-Dienste zu testen. Bevor Sie mit diesem Schritt fortfahren, sollten Sie den in dem Voraussetzungs-Tutorial erstellten Ingress echo-ingress löschen:

      • kubectl delete ingress echo-ingress

      Wenn Sie möchten, können Sie auch die Dummy-Dienste und -Deployments mit kubectl delete svc und kubectl delete delploy löschen, aber dies ist für die Durchführung des Tutorials nicht unbedingt erforderlich.

      Sie sollten auch einen DNS-A-Datensatz mit your_domain.com erstellt haben, der auf die öffentliche IP-Adresse des Ingress Load Balancers verweist. Wenn Sie einen DigitalOcean Load Balancer verwenden, finden Sie diese IP-Adresse im Abschnitt Load Balancers des Bedienfelds. Wenn Sie DigitalOcean auch für die Verwaltung der DNS-Datensätze Ihrer Domäne verwenden, konsultieren Sie bitte Verwalten von DNS-Datensätzen, um zu erfahren, wie Sie A-Datensätze erstellen.

      Wenn Sie DigitalOcean Kubernetes verwenden, stellen Sie außerdem sicher, dass Sie die in Schritt 5 von Einrichten eines Nginx-Ingress mit Cert-Manager unter DigitalOcean Kubernetes beschriebene Problemumgehung umgesetzt haben.

      Sobald Sie einen A-Datensatz haben, der auf den Ingress Controller Load Balancer verweist, können Sie einen Ingress für your_domain.com und den Dienst polls erstellen.

      Öffnen Sie eine Datei namens polls-ingress.yaml mit Ihrem bevorzugten Editor:

      Fügen Sie das folgende Ingress-Manifest ein:

      [polls-ingress.yaml]
      apiVersion: networking.k8s.io/v1beta1
      kind: Ingress
      metadata:
        name: polls-ingress
        annotations:
          kubernetes.io/ingress.class: "nginx"
          cert-manager.io/cluster-issuer: "letsencrypt-staging"
      spec:
        tls:
        - hosts:
          - your_domain.com
          secretName: polls-tls
        rules:
        - host: your_domain.com
          http:
            paths:
            - backend:
                serviceName: polls
                servicePort: 8000
      

      Wir erstellen ein Ingress-Objekt namens polls-ingress und annotieren es, um die Steuerungsebene anzuweisen, den Ingress-Controller ingress-nginx und den ClusterIssuer „staging“ zu verwenden. Außerdem aktivieren wir TLS für your_domain.com und speichern das Zertifikat und den privaten Schlüssel in einem Secret namens polls-tls. Schließlich definieren wir eine Regel, um den Verkehr für den Host your_domain.com an den Dienst polls auf Port 8000 zu leiten.

      Wenn Sie mit der Bearbeitung der Datei fertig sind, speichern und schließen Sie sie.

      Erstellen Sie den Ingress in Ihrem Cluster mit kubectl apply:

      • kubectl apply -f polls-ingress.yaml

      Output

      ingress.networking.k8s.io/polls-ingress created

      Sie können kubectl describe verwenden, um den Status des soeben erstellten Ingress zu verfolgen:

      • kubectl describe ingress polls-ingress

      Output

      Name: polls-ingress Namespace: default Address: workaround.your_domain.com Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>) TLS: polls-tls terminates your_domain.com Rules: Host Path Backends ---- ---- -------- your_domain.com polls:8000 (10.244.0.207:8000,10.244.0.53:8000) Annotations: cert-manager.io/cluster-issuer: letsencrypt-staging kubernetes.io/ingress.class: nginx Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal CREATE 51s nginx-ingress-controller Ingress default/polls-ingress Normal CreateCertificate 51s cert-manager Successfully created Certificate "polls-tls" Normal UPDATE 25s nginx-ingress-controller Ingress default/polls-ingress

      Sie können auch describe für das Zertifikat polls-tls ausführen, um dessen erfolgreiche Erstellung weiter zu bestätigen:

      • kubectl describe certificate polls-tls

      Output

      . . . Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Issuing 3m33s cert-manager Issuing certificate as Secret does not exist Normal Generated 3m32s cert-manager Stored new private key in temporary Secret resource "polls-tls-v9lv9" Normal Requested 3m32s cert-manager Created new CertificateRequest resource "polls-tls-drx9c" Normal Issuing 2m58s cert-manager The certificate has been successfully issued

      Dies bestätigt, dass das TLS-Zertifikat erfolgreich ausgestellt wurde und die HTTPS-Verschlüsselung nun für your_domain.com aktiv ist.

      Da wir den Staging-ClusterIssuer verwendet haben, werden die meisten Webbrowser dem gefälschten Let’s Encrypt-Zertifikat nicht vertrauen, das es ausgestellt hat, sodass die Navigation zu your_domain.com Sie zu einer Fehlerseite führt.

      Um eine Testanfrage zu senden, verwenden wir wget von der Befehlszeile aus:

      • wget -O - http://your_domain.com/polls

      Output

      . . . ERROR: cannot verify your_domain.com's certificate, issued by ‘CN=Fake LE Intermediate X1’: Unable to locally verify the issuer's authority. To connect to your_domain.com insecurely, use `--no-check-certificate'.

      Wir verwenden das vorgeschlagene Flag --no-check-certificate, um die Zertifikatsprüfung zu umgehen:

      • wget --no-check-certificate -q -O - http://your_domain.com/polls

      Output

      <link rel="stylesheet" type="text/css" href="https://your_space.nyc3.digitaloceanspaces.com/django-polls/static/polls/style.css"> <p>No polls are available.</p>

      Diese Ausgabe zeigt das HTML für die Benutzeroberflächenseite /polls und bestätigt auch, dass das Stylesheet aus dem Objektspeicher bedient wird.

      Nachdem Sie nun die Zertifikatausstellung mit dem Staging-ClusterIssuer erfolgreich getestet haben, können Sie den Ingress ändern, um den ClusterIssuer, zu verwenden.

      Öffnen Sie polls-ingress.yaml zur erneuten Bearbeitung:

      Ändern Sie die Annotation cluster-issuer:

      [polls-ingress.yaml]
      apiVersion: networking.k8s.io/v1beta1
      kind: Ingress
      metadata:
        name: polls-ingress
        annotations:
          kubernetes.io/ingress.class: "nginx"
          cert-manager.io/cluster-issuer: "letsencrypt-prod"
      spec:
        tls:
        - hosts:
          - your_domain.com
          secretName: polls-tls
        rules:
        - host: your_domain.com
          http:
            paths:
            - backend:
                serviceName: polls
                servicePort: 8000
      

      Wenn Sie fertig sind, speichern und schließen Sie die Datei. Aktualisieren Sie den Ingress mit kubectl apply:

      • kubectl apply -f polls-ingress.yaml

      Output

      ingress.networking.k8s.io/polls-ingress configured

      Um den Status der Zertifikatausstellung zu verfolgen, können Sie kubectl describe certificate polls-tls und kubectl describe ingress polls-ingress verwenden:

      • kubectl describe ingress polls-ingress

      Output

      . . . Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal CREATE 23m nginx-ingress-controller Ingress default/polls-ingress Normal CreateCertificate 23m cert-manager Successfully created Certificate "polls-tls" Normal UPDATE 76s (x2 over 22m) nginx-ingress-controller Ingress default/polls-ingress Normal UpdateCertificate 76s cert-manager Successfully updated Certificate "polls-tls"

      Die obige Ausgabe bestätigt, dass das neue Produktionszertifikat erfolgreich ausgestellt und im Secret polls-tls gespeichert wurde.

      Navigieren Sie in Ihrem Webbrowser zu your_domain.com/polls, um zu bestätigen, dass die HTTPS-Verschlüsselung aktiviert ist und alles wie erwartet funktioniert. Sie sollten die Oberfläche der Umfrageanwendung sehen:

      Oberfläche der Umfrageanwendung

      Überprüfen Sie, ob die HTTPS-Verschlüsselung in Ihrem Webbrowser aktiv ist. Wenn Sie Google Chrome verwenden, bestätigt die Ankunft auf der obigen Seite ohne Fehler, dass alles korrekt funktioniert. Außerdem sollten Sie ein Vorhängeschloss in der URL-Leiste sehen. Durch Klicken auf das Vorhängeschloss können Sie die Details des Let’s Encrypt-Zertifikats einsehen.

      Als letzte Bereinigungsaufgabe können Sie optional den Typ des Dienstes polls von NodePort auf den nut internen Typ ClusterIP umstellen.

      Ändern Sie polls-svc.yaml mit Ihrem Editor:

      Ändern Sie den type von NodePort auf ClusterIP:

      polls-svc.yaml

      apiVersion: v1
      kind: Service
      metadata:
        name: polls
        labels:
          app: polls
      spec:
        type: ClusterIP
        selector:
          app: polls
        ports:
          - port: 8000
            targetPort: 8000
      

      Wenn Sie mit der Bearbeitung der Datei fertig sind, speichern und schließen Sie sie.

      Stellen Sie die Änderungen mit kubectl apply bereit:

      • kubectl apply -f polls-svc.yaml --force

      Output

      service/polls configured

      Bestätigen Sie mit kubectl get svc, dass Ihr Dienst geändert wurde:

      Output

      NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE polls ClusterIP 10.245.203.186 <none> 8000/TCP 22s

      Diese Ausgabe zeigt, dass der Diensttyp nun ClusterIP ist. Der einzige Weg darauf zuzugreifen, ist über Ihre Domäne und den in diesem Schritt erstellten Ingress.

      Zusammenfassung

      In diesem Tutorial haben Sie eine skalierbare HTTPS-gesicherte Django-Anwendung in einem Kubernetes-Cluster bereitgestellt. Statische Inhalte werden direkt aus dem Objektspeicher bedient, und die Anzahl der laufenden Pods kann über das Feld replicas in dem Deployment-Manifest polls-app schnell herauf- oder herunterskaliert werden.

      Wenn Sie einen DigitalOcean Space verwenden, können Sie auch die Bereitstellung statischer Assets über ein Content Delivery-Netzwerk aktivieren und eine benutzerdefinierte Subdomäne für Ihren Space erstellen. Lesen Sie bitte Aktivieren von CDN aus Einrichten einer skalierbaren Django-Anwendung mit von DigitalOcean verwalteten Datenbanken und Spaces, um mehr zu erfahren.

      Um den Rest der zu lesen, besuchen Sie bitte unsere Seite Von Containern zu Kubernetes mit Django.



      Source link

      Verwenden von SSH zum Herstellen einer Verbindung mit einem Remoteserver


      Einführung

      Ein wesentliches Tool, das ein Systemadministrator beherrschen sollte, ist SSH.

      SSH oder Secure Shell ist ein Protokoll, das zur sicheren Anmeldung bei Remotesystemen verwendet wird. Es ist die häufigste Methode für den Zugriff auf Remote-Linux-Server.

      In diesem Leitfaden diskutieren wir, wie SSH zur Herstellung einer Verbindung mit einem Remotesystem genutzt werden kann.

      Grundlegende Syntax

      Um per SSH eine Verbindung mit einem Remotesystem herzustellen, verwenden wir den Befehl ssh. Die grundlegendste Form des Befehls ist:

      Der remote_host in diesem Beispiel ist die IP-Adresse oder der Domänenname, mit der oder dem Sie eine Verbindung herstellen möchten.

      Dieser Befehl geht davon aus, dass Ihr Benutzername im Remotesystem der gleiche wie Ihr Benutzername in Ihrem lokalen System ist.

      Wenn Ihr Benutzername im Remotesystem anders ist, können Sie ihn durch Verwendung dieser Syntax angeben:

      • ssh remote_username@remote_host

      Sobald Sie mit dem Server verbunden sind, werden Sie möglicherweise aufgefordert, Ihre Identität durch Angabe eines Passworts zu verifizieren. Später werden wir auf die Erstellung von Schlüsseln zur Verwendung anstelle von Passwörtern eingehen.

      Um die SSH-Sitzung zu beenden und zu Ihrer lokalen Shell-Sitzung zurückzukehren, geben Sie Folgendes ein:

      Wie funktioniert SSH?

      SSH arbeitet, indem ein Clientprogramm mit einem SSH-Server namens sshd verbunden wird.

      Im vorherigen Abschnitt war ssh das Clientprogramm. Der SSH-Server wird bereits auf dem von uns angegebenen remote_host ausgeführt.

      Auf Ihrem Server sollte der sshd-Server bereits ausgeführt werden. Wenn dies nicht der Fall ist, müssen Sie möglicherweise über eine webbasierte Konsole oder eine lokale serielle Konsole auf Ihren Server zugreifen.

      Der Prozess, der zum Starten eines SHH-Servers erforderlich ist, hängt von der Linux-Distribution ab, die Sie verwenden.

      Unter Ubuntu können Sie den SSH-Server durch folgende Eingabe starten:

      Damit sollte der sshd-Server starten und Sie können sich remote anmelden.

      Konfigurieren von SSH

      Wenn Sie die Konfiguration von SSH ändern, ändern Sie die Einstellungen des sshd-Servers.

      In Ubuntu befindet sich die Hauptkonfigurationsdatei von sshd unter /etc/ssh/sshd_config.

      Sichern Sie vor der Bearbeitung die aktuelle Version dieser Datei:

      • sudo cp /etc/ssh/sshd_config{,.bak}

      Öffnen Sie sie mit einem Texteditor:

      • sudo nano /etc/ssh/sshd_config

      Die meisten Optionen in dieser Datei werden Sie nicht verändern wollen. Es gibt jedoch einige Optionen, auf die Sie möglicherweise einen Blick werfen möchten:

      /etc/ssh/sshd_config

      Port 22
      

      Die Portdeklaration gibt an, an welchem Port der sshd-Server nach Verbindungen lauschen wird. Standardmäßig ist dies Port 22. Sie sollten diese Einstellung wahrscheinlich unverändert lassen, es sei denn, Sie haben spezifische Gründe für ein anderes Vorgehen. Wenn Sie Ihren Port tatsächlich ändern, zeigen wir Ihnen später, wie Sie eine Verbindung mit dem neuen Port herstellen können.

      /etc/ssh/sshd_config

      HostKey /etc/ssh/ssh_host_rsa_key
      HostKey /etc/ssh/ssh_host_dsa_key
      HostKey /etc/ssh/ssh_host_ecdsa_key
      

      Die Hostschlüsseldeklarationen geben an, wo nach globalen Hostschlüsseln gesucht werden soll. Wir werden später erörtern, was ein Hostschlüssel ist.

      /etc/ssh/sshd_config

      SyslogFacility AUTH
      LogLevel INFO
      

      Diese beiden Elemente zeigen die Protokollierungsstufe an, die ausgeführt werden sollte.

      Wenn Sie Probleme mit SSH haben, ist die Erhöhung der Protokollierungsstufe ggf. eine gute Möglichkeit, um zu ermitteln, was das Problem ist.

      /etc/ssh/sshd_config

      LoginGraceTime 120
      PermitRootLogin yes
      StrictModes yes
      

      Diese Parameter geben einige der Anmeldedaten an.

      LoginGraceTime gibt an, wie viele Sekunden die Verbindung ohne eine erfolgreiche Anmeldung aufrechterhalten wird.

      Es ist ggf. eine gute Idee, diese Zeit nur etwas höher als die Zeit anzusetzen, die Sie normalerweise zum Anmelden benötigen.

      PermitRootLogin gibt an, ob sich der root-Benutzer anmelden darf.

      In den meisten Fällen sollte dies in no geändert werden, wenn Sie ein Benutzerkonto erstellt haben, das Zugriff auf erhöhte Berechtigungen hat (über su oder sudo) und sich über SSH anmelden kann.

      strictModes ist ein Sicherheitswächter, der Anmeldeversuche verweigert, wenn die Authentifizierungsdateien für alle lesbar sind.

      Dadurch werden Anmeldeversuche verhindert, wenn Konfigurationsdateien nicht sicher sind.

      /etc/ssh/sshd_config

      X11Forwarding yes
      X11DisplayOffset 10
      

      Diese Parameter konfigurieren eine Funktion namens X11 Forwarding. Dadurch können Sie die grafische Benutzeroberfläche (GUI) eines Remotesystems im lokalen System anzeigen.

      Diese Option muss auf dem Server aktiviert und mit dem SSH-Client während der Verbindung mit der Option -X übergeben werden.

      Speichern und schließen Sie nach dem Vornehmen Ihrer Änderungen die Datei durch Eingabe von STRG+X und Y gefolgt von ENTER.

      Wenn Sie Einstellungen in /etc/ssh/sshd_config geändert haben, stellen Sie sicher, dass Sie Ihren sshd-Server zur Implementierung der Änderungen neu laden:

      • sudo systemctl reload ssh

      Sie sollten Ihre Änderungen sorgfältig testen, um sicherzustellen, dass sie wie erwartet funktionieren.

      Es ist möglicherweise eine gute Idee, beim Vornehmen von Änderungen einige aktive Sitzungen zu haben. Dadurch können Sie die Konfiguration bei Bedarf zurücksetzen.

      Anmelden bei SSH mit Schlüsseln

      Es ist zwar nützlich, sich mit Passwörtern bei einem Remotesystem anmelden zu können, doch ist es eine wesentlich bessere Idee, schlüsselbasierte Authentifizierung einzurichten.

      Wie funktioniert schlüsselbasierte Authentifizierung?

      Schlüsselbasierte Authentifizierung funktioniert durch Erstellen eines Schlüsselpaars, bestehend aus einem privaten Schlüssel und einem öffentlichen Schlüssel.

      Der private Schlüssel befindet sich auf dem Clientrechner, ist gesichert und wird geheim gehalten.

      Der öffentliche Schlüssel kann an beliebige Personen weitergegeben oder auf jedem Server platziert werden, auf den Sie zugreifen möchten.

      Wenn Sie versuchen, eine Verbindung mit einem Schlüsselpaar herzustellen, verwendet der Server den öffentlichen Schlüssel zur Erstellung einer Nachricht für den Clientcomputer, die nur mit dem privaten Schlüssel gelesen werden kann.

      Der Clientcomputer sendet dann die entsprechende Antwort zurück an den Server und der Server weiß, dass der Client legitim ist.

      Das gesamte Verfahren erfolgt nach Einrichtung der Schlüssel automatisch.

      Erstellen von SSH-Schlüsseln

      SSH-Schlüssel sollten auf dem Computer erstellt werden, von dem aus Sie sich anmelden möchten. Dies ist normalerweise Ihr lokaler Rechner.

      Geben Sie Folgendes in die Befehlszeile ein:

      Drücken Sie die Eingabetaste zum Akzeptieren der Standardeinstellungen. Ihre Schlüssel werden unter ~/.ssh/id_rsa.pub und ~/.ssh/id_rsa erstellt.

      Wechseln Sie durch folgende Eingabe zum Verzeichnis .ssh:

      Sehen Sie sich die Berechtigungen der Dateien an:

      Output

      -rw-r--r-- 1 demo demo 807 Sep 9 22:15 authorized_keys -rw------- 1 demo demo 1679 Sep 9 23:13 id_rsa -rw-r--r-- 1 demo demo 396 Sep 9 23:13 id_rsa.pub

      Wie Sie sehen können, ist die Datei id_rsa nur für den Besitzer lesbar und beschreibbar. So sollte es sein, um sie geheim zu halten.

      Die Datei id_rsa.pub kann jedoch weitergegeben werden und verfügt über entsprechende Berechtigungen.

      Übertragen Ihres öffentlichen Schlüssels auf den Server

      Wenn Sie derzeit über passwortbasierten Zugriff auf einen Server verfügen, können Sie durch Eingabe des folgenden Befehls Ihren öffentlichen Schlüssel auf den Server kopieren:

      Dadurch wird eine SSH-Sitzung gestartet. Nach der Eingabe Ihres Passworts wird Ihr öffentlicher Schlüssel in die autorisierte Schlüsseldatei des Servers kopiert, sodass Sie sich beim nächsten Mal ohne das Passwort anmelden können.

      Optionen auf der Clientseite

      Es gibt eine Reihe von optionalen Flags, die Sie beim Verbinden über SSH wählen können.

      Einige davon könnten erforderlich sein, um mit den Einstellungen in der sshd-Konfiguration des Remote-Hosts übereinzustimmen.

      Wenn Sie beispielsweise die Portnummer in Ihrer sshd-Konfiguration geändert haben, müssen Sie durch folgende Angabe eine Anpassung an den Port auf der Clientseite vornehmen:

      • ssh -p port_number remote_host

      Wenn Sie nur einen einzigen Befehl in einem Remotesystem ausführen möchten, können Sie ihn nach dem Host wie folgt angeben:

      • ssh remote_host command_to_run

      Sie werden eine Verbindung mit dem Remoterechner herstellen und sich authentifizieren und der Befehl wird ausgeführt.

      Wie bereits gesagt: Wenn auf beiden Computern X11 Forwarding aktiviert ist, können Sie durch folgende Eingabe auf diese Funktion zugreifen:

      Wenn Sie über die entsprechenden Tools auf Ihrem Computer verfügen, öffnen die GUI-Programme, die Sie im Remotesystem verwenden, ihre Fenster nun auf Ihrem lokalen System.

      Deaktivieren der Passwortauthentifizierung

      Wenn Sie SSH-Schlüssel erstellt haben, können Sie durch Deaktivieren der ausschließlich passwortbasierten Authentifizierung die Sicherheit Ihres Servers erhöhen. Abgesehen von der Konsole ist die einzige Möglichkeit zur Anmeldung bei Ihrem Server der private Schlüssel, der zum öffentlichen Schlüssel passt, den Sie auf dem Server installiert haben.

      Warnung: Bevor Sie mit diesem Schritt fortfahren, stellen Sie sicher, dass Sie einen öffentlichen Schlüssel auf Ihrem Server installiert haben. Andernfalls werden Sie ausgesperrt!

      Öffnen Sie als root-Benutzer oder Benutzer mit sudo-Berechtigungen die sshd-Konfigurationsdatei:

      • sudo nano /etc/ssh/sshd_config

      Suchen Sie die Zeile, in der Password Authentication steht, und heben Sie die Kommentierung auf, indem Sie das führende #-Zeichen entfernen. Sie können dann den Wert in no ändern:

      /etc/ssh/sshd_config

      PasswordAuthentication no
      

      Zwei weitere Einstellungen, die nicht geändert werden sollten (sofern Sie diese Datei noch nicht geändert haben), sind PubkeyAuthentication und ChallengeResponseAuthentication. Sie werden standardmäßig gesetzt und sollten wie folgt aussehen:

      /etc/ssh/sshd_config

      PubkeyAuthentication yes
      ChallengeResponseAuthentication no
      

      Speichern und schließen Sie die Datei nach Vornahme Ihrer Änderungen.

      Sie können den SSH-Daemon nun neu laden:

      • sudo systemctl reload ssh

      Die Passwortauthentifizierung sollte nun deaktiviert und Ihr Server nur per SSH-Schlüsselauthentifizierung zugänglich sein.

      Zusammenfassung

      Sich mit SSH vertraut zu machen, ist eine lohnenswerte Aufgabe, schon weil sie so häufig erforderlich ist.

      Bei Verwendung der verschiedenen Optionen werden Sie erweiterte Funktionen entdecken, die Ihr Leben erleichtern können. SSH ist noch immer beliebt, da das Protokoll in verschiedenen Situationen sicher, leicht und hilfreich ist.



      Source link

      Erstellen einer benutzerdefinierten Paginierung mit React


      Einführung

      Wir sind oft an der Erstellung von Webanwendungen beteiligt, bei denen wir große Mengen von Datensätzen von einem Remote-Server, einer API oder einer Datenbank abrufen müssen. Wenn Sie beispielsweise ein Zahlungssystem entwickeln, könnten Tausende von Transaktionen abgerufen werden. Wenn es sich um eine Social Media-Anwendung handelt, könnten viele Benutzerkommentare, -profile oder -aktivitäten abgerufen werden. Was auch immer der Fall sein mag, es gibt verschiedene Lösungen, um Daten so darzustellen, dass der Endbenutzer, der mit der Anwendung interagiert, nicht überfordert wird.

      Eine Methode zur Handhabung großer Datensätze ist die Verwendung der Paginierung. Die Paginierung funktioniert effektiv, wenn Sie die Größe des Datensatzes (die Gesamtzahl der Datensätze im Datensatz) bereits kennen. Zweitens laden Sie nur den erforderlichen Datenblock aus dem Gesamtdatensatz basierend auf der Interaktion des Endbenutzers mit der Paginierungssteuerung. Diese Technik wird bei der Anzeige der Suchergebnisse in der Google-Suche verwendet.

      In diesem Tutorial lernen Sie, wie Sie mit React eine benutzerdefinierte Paginierungskomponente für die Paginierung großer Datensätze erstellen. Sie werden eine paginierte Ansicht der Länder der Welt erstellen – einen Datensatz mit einer bekannten Größe.

      Hier ist eine Demo davon, was Sie in diesem Tutorial erstellen werden:

      Screenshot der Demo-Anwendung — zeigt die Länder der Welt

      Voraussetzungen

      Um diesem Tutorial zu folgen, benötigen Sie:

      • Auf Ihrem Rechner nodejsinstalliertes [Node]. Die Schritte hierfür finden Sie unter Installieren von Node.js und Erstellen einer lokalen Entwicklungsumgebung.
      • Das Befehlszeilen-Paket [create-react-app][`create-react-app] zur Erstellung des Boilerplate-Codes für Ihre React-Anwendung. Wenn Sienpm < 5.2verwenden, müssen Siecreate-react-app` möglicherweise als globale Abhängigkeit installieren.
      • Schließlich geht dieses Tutorial davon aus, dass Sie bereits mit React vertraut sind. Sollte dies nicht der Fall sein, können Sie in der Reihe Codieren in React.js nachlesen, um mehr über React zu erfahren.

      Dieses Tutorial wurde mit Node v14.2.0, nmp v6.14.4, react v16.13.1 und react-scripts v3.4.1 verifiziert.

      Schritt 1 — Einrichten des Projekts

      Starten Sie eine neue React-Anwendung unter Verwendung des Befehls create-react-app. Sie können diese Anwendung beliebig benennen. In diesem Tutorial wird die Anwendung react-pagination genannt:

      • npx create-react-app react-pagination

      Als Nächstes installieren Sie die für Ihre Anwendung erforderlichen Abhängigkeiten. Verwenden Sie zunächst das Terminalfenster zur Navigation zum Projektverzeichnis:

      Führen Sie den folgenden Befehl aus, um die erforderlichen Abhängigkeiten zu installieren:

      • npm install bootstrap@4.1.0 prop-types@15.6.1 react-flags@0.1.13 countries-api@2.0.1 node-sass@4.14.1

      Dadurch werden bootstrap, prop-types, react-flags, countries-api und node-sass installiert.

      Sie haben das Paket bootstrap als Abhängigkeit für Ihre Anwendung installiert, da Sie etwas Standardgestaltung benötigen. Sie verwenden auch Stile aus der pagination-Komponente von Bootstrap verwenden.

      Um Bootstrap in die Anwendung einzubinden, bearbeiten Sie die Datei src/index.js:

      Und fügen Sie die folgende Zeile vor den anderen import-Anweisungen hinzu:

      src/index.js

      import "bootstrap/dist/css/bootstrap.min.css";
      

      Jetzt ist Bootstrap-Styling in Ihrer gesamten Anwendung verfügbar.

      Außerdem haben Sie react-flags als Abhängigkeit für Ihre Anwendung installiert. Um Zugriff auf die Flaggen-Symbole aus Ihrer Anwendung zu erhalten, müssen Sie die Symbolbilder in das Verzeichnis public Ihrer Anwendung kopieren.

      Erstellen Sie in Ihrem Verzeichnis public ein Verzeichnis img:

      Kopieren Sie die Bilddateien in flags zu img:

      • cp -R node_modules/react-flags/vendor/flags public/img

      Damit stellen Sie Ihrer Anwendung eine Kopie aller Bilder von react-flag bereit.

      Nachdem Sie nun einige Abhängigkeiten eingebunden haben, starten Sie die Anwendung, indem Sie den folgenden Befehl mit npm aus dem Projektverzeichnis react-pagination ausführen:

      Nachdem Sie die Anwendung gestartet haben, kann die Entwicklung beginnen. Beachten Sie, dass eine Browser-Registerkarte mit einer Live Neuladefunktionalität für Sie geöffnet wurde, um während der Entwicklung mit der Anwendung synchron zu bleiben.

      Zu diesem Zeitpunkt sollte die Ansicht der Anwendung wie im folgenden Screenshot dargestellt aussehen:

      Anfangsansicht – Willkommen bei React-Bildschirm

      Sie sind nun bereit, mit der Erstellung von Komponenten zu beginnen.

      Schritt 2 — Erstellen der Komponente CountryCard

      In diesem Schritt erstellen Sie die Komponente CountryCard. Die Komponente CountryCard gibt den Namen, die Region und die Flagge eines bestimmten Landes wieder.

      Zuerst erstellen wir ein Verzeichnis components im Verzeichnis src:

      Anschließend erstellen wir eine neue Datei CountryCard.js im Verzeichnis src/works:

      • nano src/components/CountryCard.js

      Und fügen den folgenden Code-Ausschnitt hinzu:

      src/components/CountryCard.js

      import React from 'react';
      import PropTypes from 'prop-types';
      import Flag from 'react-flags';
      
      const CountryCard = props => {
        const {
          cca2: code2 = '', region = null, name = {}
        } = props.country || {};
      
        return (
          <div className="col-sm-6 col-md-4 country-card">
            <div className="country-card-container border-gray rounded border mx-2 my-3 d-flex flex-row align-items-center p-0 bg-light">
              <div className="h-100 position-relative border-gray border-right px-2 bg-white rounded-left">
                <Flag country={code2} format="png" pngSize={64} basePath="./img/flags" className="d-block h-100" />
              </div>
              <div className="px-3">
                <span className="country-name text-dark d-block font-weight-bold">{ name.common }</span>
                <span className="country-region text-secondary text-uppercase">{ region }</span>
              </div>
            </div>
          </div>
        )
      }
      
      CountryCard.propTypes = {
        country: PropTypes.shape({
          cca2: PropTypes.string.isRequired,
          region: PropTypes.string.isRequired,
          name: PropTypes.shape({
            common: PropTypes.string.isRequired
          }).isRequired
        }).isRequired
      };
      
      export default CountryCard;
      

      Die Komponente CountryCard erfordert eine country-Prop, die die Daten über das wiederzugebende Land enthält. Wie in den propTypes für die Komponente CountryCard zu sehen ist, muss das Prop-Objekt country die folgenden Daten enthalten:

      • cca2 – 2-stelliger Ländercode
      • region – die Länderregion (z. B. „Afrika“)
      • name.common – der gebräuchliche Name des Landes (z. B. „Nigeria“)

      Hier ist ein Beispiel für ein Länderobjekt:

      {
        cca2: "NG",
        region: "Africa",
        name: {
          common: "Nigeria"
        }
      }
      

      Beachten Sie auch, wie Sie die Länderflagge mit dem Paket react-flags wiedergeben können. In der Dokumentation zu react-flags erfahren Sie mehr über die benötigten Props und die Verwendung des Pakets.

      Sie haben nun eine einzelne Komponente CountryCard fertiggestellt. Letztendlich werden Sie CountryCards mehrfach verwenden, um verschiedene Flaggen und Länderinformationen in Ihrer Anwendung anzuzeigen.

      In diesem Schritt erstellen Sie die Komponente Pagination. Die Komponente Pagination enthält die Logik für das Erstellen, Rendern und Wechseln der Seiten auf der Paginierungssteuerung.

      Erstellen Sie eine neue Datei Pagination.js im Verzeichnis src/components:

      • nano src/components/Pagination.js

      Und fügen den folgenden Code-Ausschnitt hinzu:

      src/components/Pagination.js

      import React, { Component, Fragment } from 'react';
      import PropTypes from 'prop-types';
      
      class Pagination extends Component {
        constructor(props) {
          super(props);
          const { totalRecords = null, pageLimit = 30, pageNeighbours = 0 } = props;
      
          this.pageLimit = typeof pageLimit === 'number' ? pageLimit : 30;
          this.totalRecords = typeof totalRecords === 'number' ? totalRecords : 0;
      
          // pageNeighbours can be: 0, 1 or 2
          this.pageNeighbours = typeof pageNeighbours === 'number'
            ? Math.max(0, Math.min(pageNeighbours, 2))
            : 0;
      
          this.totalPages = Math.ceil(this.totalRecords / this.pageLimit);
      
          this.state = { currentPage: 1 };
        }
      }
      
      Pagination.propTypes = {
        totalRecords: PropTypes.number.isRequired,
        pageLimit: PropTypes.number,
        pageNeighbours: PropTypes.number,
        onPageChanged: PropTypes.func
      };
      
      export default Pagination;
      

      Die Komponente Pagination kann vier spezielle Props aufnehmen, wie im Objekt propTypes angegeben.

      • onPageChanged ist eine Funktion, die nur dann mit Daten des aktuellen Paginierungsstatus aufgerufen wird, wenn sich die aktuelle Seite ändert.
      • totalRecords gibt die Gesamtzahl der zu paginierenden Datensätze an. Es ist erforderlich.
      • pageLimit gibt die Anzahl der Datensätze an, die pro Seite angezeigt werden sollen. Wenn sie nicht angegeben wird, ist sie gemäß der Definition in der constructor() auf 30 voreingestellt.
      • pageNeighbours gibt die Anzahl der zusätzlichen Seitennummern an, die auf jeder Seite der aktuellen Seite angezeigt werden sollen. Der Mindestwert ist 0, und der maximale Wert ist 2. Wir hier nichts angegeben, wird der Standardwert 0 gemäß der Definition in der constructor() verwendet.

      Das folgende Bild veranschaulicht die Wirkung verschiedener Werte der Prop pageNeighbours:

      Darstellung der PageNeighbours

      In der Funktion constructor() berechnen Sie die Gesamtseiten wie folgt:

      this.totalPages = Math.ceil(this.totalRecords / this.pageLimit);
      

      Beachten Sie, dass Sie hier Math.ceil() verwenden, um sicherzustellen, dass Sie einen ganzzahligen Wert für die Gesamtzahl der Seiten erhalten. Dadurch wird auch sichergestellt, dass die überschüssigen Datensätze auf der letzten Seite erfasst werden, insbesondere in Fällen, in denen die Anzahl der überschüssigen Datensätze geringer ist als die Anzahl der pro Seite anzuzeigenden Datensätze.

      Schließlich haben Sie den Status mit der Eigenschaft currentPage auf 1 initialisiert. Sie benötigen diese Statuseigenschaft, um intern den Überblick über die aktuell aktive Seite zu behalten.

      Als Nächstes erstellen Sie die Methode zur Erzeugung der Seitennummern.

      Fügen Sie nach import aber vor der Klasse Pagination die folgenden Konstanten und die Funktion range hinzu:

      src/components/Pagination.js

      // ...
      
      const LEFT_PAGE = 'LEFT';
      const RIGHT_PAGE = 'RIGHT';
      
      /**
       * Helper method for creating a range of numbers
       * range(1, 5) => [1, 2, 3, 4, 5]
       */
      const range = (from, to, step = 1) => {
        let i = from;
        const range = [];
      
        while (i <= to) {
          range.push(i);
          i += step;
        }
      
        return range;
      }
      

      Fügen Sie in der Klasse Pagination nach dem constructor die folgende Methode fetchPageNumbers hinzu:

      src/components/Pagination.js

      class Pagination extends Component {
        // ...
      
        /**
         * Let's say we have 10 pages and we set pageNeighbours to 2
         * Given that the current page is 6
         * The pagination control will look like the following:
         *
         * (1) < {4 5} [6] {7 8} > (10)
         *
         * (x) => terminal pages: first and last page(always visible)
         * [x] => represents current page
         * {...x} => represents page neighbours
         */
        fetchPageNumbers = () => {
          const totalPages = this.totalPages;
          const currentPage = this.state.currentPage;
          const pageNeighbours = this.pageNeighbours;
      
          /**
           * totalNumbers: the total page numbers to show on the control
           * totalBlocks: totalNumbers + 2 to cover for the left(<) and right(>) controls
           */
          const totalNumbers = (this.pageNeighbours * 2) + 3;
          const totalBlocks = totalNumbers + 2;
      
          if (totalPages > totalBlocks) {
            const startPage = Math.max(2, currentPage - pageNeighbours);
            const endPage = Math.min(totalPages - 1, currentPage + pageNeighbours);
            let pages = range(startPage, endPage);
      
            /**
             * hasLeftSpill: has hidden pages to the left
             * hasRightSpill: has hidden pages to the right
             * spillOffset: number of hidden pages either to the left or to the right
             */
            const hasLeftSpill = startPage > 2;
            const hasRightSpill = (totalPages - endPage) > 1;
            const spillOffset = totalNumbers - (pages.length + 1);
      
            switch (true) {
              // handle: (1) < {5 6} [7] {8 9} (10)
              case (hasLeftSpill && !hasRightSpill): {
                const extraPages = range(startPage - spillOffset, startPage - 1);
                pages = [LEFT_PAGE, ...extraPages, ...pages];
                break;
              }
      
              // handle: (1) {2 3} [4] {5 6} > (10)
              case (!hasLeftSpill && hasRightSpill): {
                const extraPages = range(endPage + 1, endPage + spillOffset);
                pages = [...pages, ...extraPages, RIGHT_PAGE];
                break;
              }
      
              // handle: (1) < {4 5} [6] {7 8} > (10)
              case (hasLeftSpill && hasRightSpill):
              default: {
                pages = [LEFT_PAGE, ...pages, RIGHT_PAGE];
                break;
              }
            }
      
            return [1, ...pages, totalPages];
          }
      
          return range(1, totalPages);
        }
      }
      

      Hier definieren Sie zunächst zwei Konstanten: LEFT_PAGE und RIGHT_PAGE. Diese Konstanten werden verwendet, um Punkte anzugeben, an denen Sie Seitenkontrollen für das Verschieben nach links bzw. rechts haben.

      Außerdem haben Sie eine Hilfsfunktion range() definiert, die Ihnen bei der Erstellung von Nummernbereichen helfen kann.

      Anmerkung: Wenn Sie eine Dienstprogrammbibliothek wie Lodash in Ihrem Projekt verwenden, können Sie stattdessen die Funktion .range() von Lodash bereitgestellt. Der folgende Code-Ausschnitt zeigt den Unterschied zwischen der gerade definierten Funktion range() und der von Lodash:

      range(1, 5); // returns [1, 2, 3, 4, 5]
      _.range(1, 5); // returns [1, 2, 3, 4]
      

      Als Nächstes haben Sie die Methode fetchPageNumbers() in der Klasse Pagination definiert. Diese Methode handhabt die Kernlogik für die Erstellung der Seitennummern, die auf der Paginierungssteuerung angezeigt werden sollen. Sie möchten, dass die erste Seite und die letzte Seite immer sichtbar sind.

      Zuerst haben Sie einige Variablen definiert. totalNumbers stellt die Gesamtzahl der Seitennummern dar, die auf der Steuerung angezeigt werden. totalBlocks steht für die Gesamtseitennummern, die angezeigt werden sollen, plus zwei zusätzliche Blöcke für die Indikatoren der linken und der rechten Seite.

      Wenn totalPages nicht größer als totalBlocks ist, geben Sie einen Zahlenbereich von 1 bis totalPages zurück. Andernfalls geben Sie den Bereich der Seitennummern mit LEFT_PAGE und RIGHT_PAGE an Punkten zurück, an denen die Seiten nach links bzw. rechts verschoben werden.

      Beachten Sie jedoch, dass Ihre Paginierungssteuerung sicherstellt, dass die erste Seite und die letzte Seite immer sichtbar sind. Die Steuerungen für die linke und rechte Seite erscheinen nach innen.

      Jetzt fügen Sie die Methode render() hinzu, mit der Sie die Paginierungssteuerung rendern können.

      Fügen Sie in der Klasse Pagination nach der Methode constructor und fetchPageNumbers die folgende Methode render hinzu:

      src/components/Pagination.js

      class Pagination extends Component {
        // ...
      
        render() {
          if (!this.totalRecords || this.totalPages === 1) return null;
      
          const { currentPage } = this.state;
          const pages = this.fetchPageNumbers();
      
          return (
            <Fragment>
              <nav aria-label="Countries Pagination">
                <ul className="pagination">
                  { pages.map((page, index) => {
      
                    if (page === LEFT_PAGE) return (
                      <li key={index} className="page-item">
                        <a className="page-link" href="https://www.digitalocean.com/community/tutorials/#" aria-label="Previous" onClick={this.handleMoveLeft}>
                          <span aria-hidden="true">&laquo;</span>
                          <span className="sr-only">Previous</span>
                        </a>
                      </li>
                    );
      
                    if (page === RIGHT_PAGE) return (
                      <li key={index} className="page-item">
                        <a className="page-link" href="https://www.digitalocean.com/community/tutorials/#" aria-label="Next" onClick={this.handleMoveRight}>
                          <span aria-hidden="true">&raquo;</span>
                          <span className="sr-only">Next</span>
                        </a>
                      </li>
                    );
      
                    return (
                      <li key={index} className={`page-item${ currentPage === page ? ' active' : ''}`}>
                        <a className="page-link" href="https://www.digitalocean.com/community/tutorials/#" onClick={ this.handleClick(page) }>{ page }</a>
                      </li>
                    );
      
                  }) }
      
                </ul>
              </nav>
            </Fragment>
          );
        }
      }
      

      Hier generieren Sie das Array der Seitennummern durch Aufruf der zuvor erstellten Methode fetchPageNumbers(). Dann rendern Sie jede Seitennummer mit Array.prototype.map(). Beachten Sie, dass Sie Click-Event-Handler für jede erstellte Seitennummer registrieren, um Klicks zu verarbeiten.

      Beachten Sie auch, dass die Paginierungssteuerung nicht gerendert wird, wenn das Prop totalRecords nicht korrekt an die Komponente Pagination übergeben wurde oder in Fällen, in denen nur 1 Seite vorhanden ist.

      Schließlich definieren Sie die Methoden für die Ereignishandler.

      Fügen Sie in der Klasse Pagination nach der Methode constructor und fetchPageNumbers und der Methode render Folgendes hinzu:

      src/components/Pagination.js

      class Pagination extends Component {
        // ...
      
        componentDidMount() {
          this.gotoPage(1);
        }
      
        gotoPage = page => {
          const { onPageChanged = f => f } = this.props;
          const currentPage = Math.max(0, Math.min(page, this.totalPages));
          const paginationData = {
            currentPage,
            totalPages: this.totalPages,
            pageLimit: this.pageLimit,
            totalRecords: this.totalRecords
          };
      
          this.setState({ currentPage }, () => onPageChanged(paginationData));
        }
      
        handleClick = page => evt => {
          evt.preventDefault();
          this.gotoPage(page);
        }
      
        handleMoveLeft = evt => {
          evt.preventDefault();
          this.gotoPage(this.state.currentPage - (this.pageNeighbours * 2) - 1);
        }
      
        handleMoveRight = evt => {
          evt.preventDefault();
          this.gotoPage(this.state.currentPage + (this.pageNeighbours * 2) + 1);
        }
      }
      

      Sie definieren die Methode gotoPage(), die den Status ändert und die currentPage auf die angegebene Seite setzt. Sie stellt sicher, dass das Argument page einen Mindestwert von 1 und einen maximalen Wert der Gesamtzahl der Seiten hat. Schließlich ruft sie die Funktion onPageChanged() auf, die als Prop übergeben wurde, wobei die Daten den neuen Paginierungsstatus angeben.

      Wenn die Komponente gemountet wird, gehen Sie zur ersten Seite, indem Sie this.gotoPage(1) aufrufen, wie in der Lebenszyklus-Methode this.gotoPage(1) gezeigt.

      Beachten Sie, wie Sie (this.pageNeighbours * 2) in handleMoveLeft() und handleMoveRight() verwenden, um die Seitennummern basierend auf der aktuellen Seitennummer nach links bzw. rechts zu verschieben.

      Hier ist eine Demonstration der Interaktion der Bewegung von links nach rechts.

      Links-rechts-Bewegung der Interaktion

      Sie haben nun die Komponente Pagination abgeschlossen. Benutzer können mit der Navigationssteuerung in dieser Komponente interagieren, um verschiedene Seiten von Flaggen anzuzeigen.

      Schritt 4 — Erstellen der Komponente App

      Nachdem Sie nun eine Komponente CountryCard und Pagination haben, können Sie sie in Ihrer Komponente App verwenden.

      Ändern Sie die Datei App.js im Verzeichnis src:

      Ersetzen Sie den Inhalt von App.js durch die folgenden Codezeilen:

      src/App.js

      import React, { Component } from 'react';
      import Countries from 'countries-api';
      import './App.css';
      import Pagination from './components/Pagination';
      import CountryCard from './components/CountryCard';
      
      class App extends Component {
        state = { allCountries: [], currentCountries: [], currentPage: null, totalPages: null }
      
        componentDidMount() {
          const { data: allCountries = [] } = Countries.findAll();
          this.setState({ allCountries });
        }
      
        onPageChanged = data => {
          const { allCountries } = this.state;
          const { currentPage, totalPages, pageLimit } = data;
          const offset = (currentPage - 1) * pageLimit;
          const currentCountries = allCountries.slice(offset, offset + pageLimit);
      
          this.setState({ currentPage, currentCountries, totalPages });
        }
      }
      
      export default App;
      

      Hier initialisieren Sie den Status der Komponente App mit den folgenden Attributen:

      • allCountries – dies ist ein Array aller Länder in Ihrer Anwendung. Initialisiert auf ein leeres Array ([]).
      • currentCountries – dies ist ein Array aller Länder, die auf der aktuell aktiven Seite angezeigt werden sollen. Initialisiert auf ein leeres Array ([]).
      • currentPage – die Seitennummer der aktuell aktiven Seite. Initialisiert auf null.
      • totalPages – die Gesamtzahl der Seiten für alle Ländereinträge. Initialisiert auf null.

      Als Nächstes rufen Sie in der Lebenszyklusmethode componentDidMount() alle Länder der Welt mit dem Paket countries-api ab, indem Sie Countries.findAll() abrufen. Dann aktualisieren Sie den Status der Anwendung, indem Sie allCountries so einstellen, dass es alle Länder der Welt enthält. Um mehr über das Paket countries-apizu erfahren, können Sie die [Dokumentation countries-api] einsehen.

      Schließlich haben Sie die Methode onPageChanged() definiert, die jedes Mal aufgerufen wird, wenn Sie über die Paginierungssteuerung zu einer neuen Seite navigieren. Diese Methode wird an das Prop onPageChanged der Komponente Pagination übergeben.

      Es gibt zwei Zeilen, die bei dieser Methode beachtet werden sollten. Die erste ist diese Zeile:

      const offset = (currentPage - 1) * pageLimit;
      

      Der Wert offset gibt den Startindex zum Abrufen der Datensätze für die aktuelle Seite an. Die Verwendung von (currentPage - 1) stellt sicher, dass der Offset null ist. Nehmen wir zum Beispiel an, dass Sie 25 Datensätze pro Seite anzeigen, und Sie betrachten derzeit Seite 5. Dann ist der offset ((5 - 1) * 25 = 100).

      Wenn Sie beispielsweise Datensätze bei Bedarf aus einer Datenbank abrufen, ist dies eine Beispiel-SQL-Abfrage, die Ihnen zeigt, wie Offset verwendet werden kann:

      SELECT * FROM `countries` LIMIT 100, 25
      

      Da Sie keine Datensätze aus einer Datenbank oder einer externen Quelle abrufen, benötigen Sie eine Möglichkeit, den erforderlichen Teil der Datensätze zu extrahieren, der für die aktuelle Seite angezeigt werden soll.

      Die zweite ist diese Zeile:

      const currentCountries = allCountries.slice(offset, offset + pageLimit);
      

      Hier verwenden Sie die Methode Array.prototype.slice(), um den erforderlichen Teil der Datensätze aus allCountries zu extrahieren, indem Sie offset als Startindex für Slice und (offset + pageLimit) als den Index, vor dem Slice beendet werden soll, übergeben.

      Anmerkung: In diesem Tutorial haben Sie keine Datensätze aus einer externen Quelle abgerufen. In einer realen Anwendung werden Sie wahrscheinlich Datensätze aus einer Datenbank oder einer API abrufen. Die Logik zum Abrufen der Datensätze kann in die Methode onPageChanged() der Komponente App eingebunden werden.

      Nehmen wir an, Sie haben einen fiktiven API-Endpunkt /api/countries?page={current_page}&limit={page_limit}. Der folgende Ausschnitt zeigt, wie Sie mithilfe des HTTaxiosP-Pakets [axios] Länder bei Bedarf von der API abrufen können:

      onPageChanged = data => {
        const { currentPage, totalPages, pageLimit } = data;
      
        axios.get(`/api/countries?page=${currentPage}&limit=${pageLimit}`)
          .then(response => {
            const currentCountries = response.data.countries;
            this.setState({ currentPage, currentCountries, totalPages });
          });
      }
      

      Jetzt können Sie die Komponente App beenden, indem Sie die Methode render() hinzufügen.

      Fügen Sie in der Klasse App, jedoch nach componentDidMount und onPageChanged, die folgende Methode render hinzu:

      src/App.js

      class App extends Component {
        // ... other methods here ...
      
        render() {
          const { allCountries, currentCountries, currentPage, totalPages } = this.state;
          const totalCountries = allCountries.length;
      
          if (totalCountries === 0) return null;
      
          const headerClass = ['text-dark py-2 pr-4 m-0', currentPage ? 'border-gray border-right' : ''].join(' ').trim();
      
          return (
            <div className="container mb-5">
              <div className="row d-flex flex-row py-5">
                <div className="w-100 px-4 py-5 d-flex flex-row flex-wrap align-items-center justify-content-between">
                  <div className="d-flex flex-row align-items-center">
                    <h2 className={headerClass}>
                      <strong className="text-secondary">{totalCountries}</strong> Countries
                    </h2>
                    { currentPage && (
                      <span className="current-page d-inline-block h-100 pl-4 text-secondary">
                        Page <span className="font-weight-bold">{ currentPage }</span> / <span className="font-weight-bold">{ totalPages }</span>
                      </span>
                    ) }
                  </div>
                  <div className="d-flex flex-row py-4 align-items-center">
                    <Pagination totalRecords={totalCountries} pageLimit={18} pageNeighbours={1} onPageChanged={this.onPageChanged} />
                  </div>
                </div>
                { currentCountries.map(country => <CountryCard key={country.cca3} country={country} />) }
              </div>
            </div>
          );
        }
      }
      

      In der Methode render() rendern Sie die Gesamtzahl der Länder, die aktuelle Seite, die Gesamtzahl der Seiten, die Steuerung <Pagination> und dann die <CountryCard> für jedes Land auf der aktuellen Seite.

      Beachten Sie, dass Sie die zuvor definierte Methode onPageChanged() an das Prop onPageChanged der Steuerung <Pagination> übergeben haben. Dies ist sehr wichtig für die Erfassung der Seitenänderungen aus der Komponente Pagination. Außerdem zeigen Sie 18 Länder pro Seite an.

      Zu diesem Zeitpunkt sieht die Anwendung wie der folgende Screenshot aus:

      Screenshot der Anwendung mit 248 aufgelisteten Ländern und Seitennummern oben, um durch die einzelnen Seiten zu gehen

      Sie haben nun eine Komponente App, die mehrere Komponenten CountryCard anzeigt, und eine Komponente Pagination, die den Inhalt in separate Seiten aufteilt. Als Nächstes werden Sie das Styling Ihrer Anwendung erkunden.

      Schritt 5 — Hinzufügen von benutzerdefinierten Stilen

      Vielleicht haben Sie bemerkt, dass Sie den zuvor erstellten Komponenten einige benutzerdefinierte Klassen hinzugefügt haben. Lassen Sie uns einige Stil-Regeln für diese Klassen in der Datei src/App.scss definieren.

      Die Datei App.scss wird wie der folgende Ausschnitt aussehen:

      src/App.scss

      /* Declare some variables */
      $base-color: #ced4da;
      $light-background: lighten(desaturate($base-color, 50%), 12.5%);
      
      .current-page {
        font-size: 1.5rem;
        vertical-align: middle;
      }
      
      .country-card-container {
        height: 60px;
        cursor: pointer;
        position: relative;
        overflow: hidden;
      }
      
      .country-name {
        font-size: 0.9rem;
      }
      
      .country-region {
        font-size: 0.7rem;
      }
      
      .current-page,
      .country-name,
      .country-region {
        line-height: 1;
      }
      
      // Override some Bootstrap pagination styles
      ul.pagination {
        margin-top: 0;
        margin-bottom: 0;
        box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
      
        li.page-item.active {
          a.page-link {
            color: saturate(darken($base-color, 50%), 5%) !important;
            background-color: saturate(lighten($base-color, 7.5%), 2.5%) !important;
            border-color: $base-color !important;
          }
        }
      
        a.page-link {
          padding: 0.75rem 1rem;
          min-width: 3.5rem;
          text-align: center;
          box-shadow: none !important;
          border-color: $base-color !important;
          color: saturate(darken($base-color, 30%), 10%);
          font-weight: 900;
          font-size: 1rem;
      
          &:hover {
            background-color: $light-background;
          }
        }
      }
      

      Ändern Sie Ihre Datei App.js so, dass sie auf App.scss anstelle von App.css verweist.

      Anmerkung: Weitere Informationen hierzu finden Sie in der Dokumentation zu Create React App.

      src/App.js

      import React, { Component } from 'react';
      import Countries from 'countries-api';
      import './App.scss';
      import Pagination from './components/Pagination';
      import CountryCard from './components/CountryCard';
      

      Nach dem Hinzufügen der Stile wird die Anwendung nun wie der folgende Screenshot aussehen:

      Screenshot der Anwendung, Seite 1 von 14, mit Stilen

      Sie haben nun eine vollständige Anwendung mit zusätzlichem benutzerdefiniertem Styling. Sie können benutzerdefinierte Stile verwenden, um alle Standardstile, die von Bibliotheken wie Bootstrap bereitgestellt werden, zu ändern und zu verbessern.

      Zusammenfassung

      In diesem Tutorial haben Sie ein benutzerdefiniertes Paginierungs-Widget in Ihrer React-Anwendung erstellt. Obwohl Sie in diesem Tutorial keine Aufrufe an eine API getätigt oder mit einem Datenbank-Backend interagiert haben, kann Ihre Anwendung solche Interaktionen erfordern. Sie sind in keiner Weise auf den in diesem Tutorial verwendeten Ansatz beschränkt. Sie können ihn beliebig erweitern, um den Anforderungen Ihrer Anwendung gerecht zu werden.

      Den vollständigen Quellcode dieses Tutorials finden Sie im Repository [build-react-pagination-demo][pagination-demo ]auf GitHub. Außerdem können Sie eine Live-Demo dieses Tutorials auf Code-Sandbox erhalten.

      Wenn Sie mehr über React erfahren möchten, sehen Sie sich unsere Reihe Codieren in React.js an oder besuchen Sie unsere React-Themenseite für Übungen und Programmierprojekte.



      Source link