Anwendungen für Kubernetes modernisieren

Einführung

Moderne zustandslose Anwendungen können in Software-Containern wie Docker ausgeführt und von Container-Clustern wie Kubernetes verwaltet werden. Sie werden mit den Prinzipien und Mustern Cloud Native und Twelve Factor entwickelt, um manuelle Eingriffe zu minimieren und zu maximieren Portabilität und Redundanz. Die Migration von Virtual-Machine- oder Bare-Metal-basierten Anwendungen in Container (so genannte Container-Anwendungen) und deren Bereitstellung innerhalb von Clustern erfordert häufig erhebliche Änderungen bei der Erstellung, Paketierung und Bereitstellung dieser Apps.

Aufbauend auf Architecting Applications for Kubernetes diskutieren wir in diesem konzeptionellen Handbuch allgemeine Schritte zur Modernisierung Ihrer Anwendungen mit dem Ziel ist es, sie in einem Kubernetes-Cluster auszuführen und zu verwalten. Obwohl Sie stateful-Anwendungen wie Datenbanken auf Kubernetes ausführen können, konzentriert sich dieses Handbuch auf die Migration und Modernisierung zustandsloser Anwendungen, wobei persistente Daten in einen externen Datenspeicher ausgelagert werden. Kubernetes bietet erweiterte Funktionen zum effizienten Verwalten und Skalieren zustandsloser Anwendungen. Außerdem werden die Änderungen an Anwendungen und Infrastrukturen untersucht, die zum Ausführen skalierbarer, beobachtbarer und portabler Apps auf Kubernetes erforderlich sind.

Vorbereiten der Anwendung für die Migration

Vor dem Containerisieren Ihrer Anwendung oder dem Schreiben von Konfigurationsdateien für Kubernetes Pod und Deployment sollten Sie Änderungen auf Anwendungsebene implementieren, um die Portabilität und Beobachtbarkeit Ihrer Anwendung in Kubernetes zu maximieren. Bei Kubernetes handelt es sich um eine hochautomatisierte Umgebung, in der fehlerhafte Anwendungscontainer automatisch bereitgestellt und neu gestartet werden können. Daher ist es wichtig, die entsprechende Anwendungslogik für die Kommunikation mit dem Container Orchestrator einzubauen und die App bei Bedarf automatisch zu skalieren.

Konfigurationsdaten extrahieren

Eine der ersten zu implementierenden Änderungen auf Anwendungsebene ist das Extrahieren der Anwendungskonfiguration aus dem Anwendungscode. Die Konfiguration besteht aus Informationen, die sich je nach Bereitstellung und Umgebung unterscheiden, z. B. Service-Endpunkte, Datenbankadressen, Anmeldeinformationen sowie verschiedene Parameter und Optionen. Wenn Sie beispielsweise zwei Umgebungen haben, z. B. "+ Staging " und " Produktion +", und jede eine separate Datenbank enthält, sollte Ihre Anwendung nicht über den Datenbankendpunkt und die explizit im Code deklarierten Anmeldeinformationen verfügen, sondern an einem separaten Speicherort gespeichert sein. Entweder als Variable in der laufenden Umgebung, als lokale Datei oder als externer Schlüsselwertspeicher, aus dem die Werte in die App eingelesen werden.

Das Hardcodieren dieser Parameter in Ihren Code stellt ein Sicherheitsrisiko dar, da diese Konfigurationsdaten häufig aus vertraulichen Informationen bestehen, die Sie dann in Ihr Versionskontrollsystem einchecken. Dies erhöht auch die Komplexität, da Sie jetzt mehrere Versionen Ihrer Anwendung verwalten müssen, von denen jede aus derselben Kernanwendungslogik besteht, die sich jedoch in der Konfiguration geringfügig unterscheiden. Wenn Anwendungen und ihre Konfigurationsdaten wachsen, wird das Hardcodieren der Konfiguration in Anwendungscode schnell unhandlich.

Indem Sie Konfigurationswerte aus Ihrem Anwendungscode extrahieren und stattdessen aus der laufenden Umgebung oder aus lokalen Dateien importieren, wird Ihre App zu einem generischen, portablen Paket, das in jeder Umgebung bereitgestellt werden kann, sofern Sie ihm die zugehörigen Konfigurationsdaten bereitstellen. Container-Software wie Docker und Cluster-Software wie Kubernetes basieren auf diesem Paradigma. Sie bieten Funktionen zum Verwalten von Konfigurationsdaten und zum Einfügen in Anwendungscontainer. Weitere Informationen zu diesen Funktionen finden Sie unter Containerizing und https://www.digitalocean.com/community / tutorials / modernisierung-anwendungen-für-kubernetes # einspeisen-von-konfigurationsdaten-mit-kubernetes [kubernetes] abschnitten.

Hier ist ein kurzes Beispiel, das zeigt, wie zwei Konfigurationswerte "+ DB_HOST " und " DB_USER +" aus dem Code einer einfachen Python-App (Flask externalisiert werden. Wir stellen sie in der laufenden Umgebung der App als Umgebungsvariablen zur Verfügung, aus denen die App sie liest:

hardcoded_config.py

from flask import Flask

DB_HOST = 'mydb.mycloud.com'
DB_USER = 'sammy'

app = Flask(__name__)

@app.route('/')
def print_config():
   output = 'DB_HOST: {} -- DB_USER: {}'.format(DB_HOST, DB_USER)
   return output

Wenn Sie diese einfache App ausführen (Informationen dazu finden Sie unter Flask Quickstart) und ihren Web-Endpunkt besuchen, wird eine Seite mit diesen beiden Konfigurationswerten angezeigt.

Im Folgenden sehen Sie dasselbe Beispiel mit den Konfigurationswerten, die in die Ausführungsumgebung der App ausgelagert wurden:

env_config.py

from flask import Flask

DB_HOST =
DB_USER =

app = Flask(__name__)

@app.route('/')
def print_config():
   output = 'DB_HOST: {} -- DB_USER: {}'.format(DB_HOST, DB_USER)
   return output

Vor dem Ausführen der App legen wir die erforderlichen Konfigurationsvariablen in der lokalen Umgebung fest:

export APP_DB_HOST=mydb.mycloud.com
export APP_DB_USER=sammy
flask run

Die angezeigte Webseite sollte den gleichen Text wie im ersten Beispiel enthalten, aber die Konfiguration der App kann jetzt unabhängig vom Anwendungscode geändert werden. Sie können auf ähnliche Weise Konfigurationsparameter aus einer lokalen Datei einlesen.

Im nächsten Abschnitt wird erläutert, wie der Anwendungsstatus außerhalb von Containern verschoben wird.

Anwendungsstatus entladen

Cloud Native-Anwendungen werden in Containern ausgeführt und von Clustersoftware wie Kubernetes oder Docker Swarm dynamisch orchestriert. Eine bestimmte App oder ein Dienst kann über mehrere Replikate verteilt werden, und jeder einzelne App-Container sollte ausfallen können, wobei der Dienst für die Clients nur minimal oder gar nicht unterbrochen wird. Um diese horizontale, redundante Skalierung zu ermöglichen, müssen Anwendungen zustandslos gestaltet werden. Dies bedeutet, dass sie auf Clientanforderungen antworten, ohne persistente Client- und Anwendungsdaten lokal zu speichern. Wenn der laufende App-Container zu einem beliebigen Zeitpunkt zerstört oder neu gestartet wird, gehen keine kritischen Daten verloren.

Wenn Sie beispielsweise eine Adressbuchanwendung ausführen und Ihre App Kontakte zu einem Adressbuch hinzufügt, daraus entfernt und daraus ändert, sollte der Adressbuchdatenspeicher eine externe Datenbank oder ein anderer Datenspeicher sein und die einzigen Daten im Containerspeicher sein kurzfristiger Natur und ohne kritischen Informationsverlust verfügbar. Daten, die über Benutzeraufrufe hinweg bestehen bleiben, wie z. B. Sitzungen, sollten auch in externe Datenspeicher wie Redis verschoben werden. Wann immer möglich, sollten Sie jeden Status von Ihrer App auf Dienste wie verwaltete Datenbanken oder Caches übertragen.

Für Stateful-Anwendungen, die einen persistenten Datenspeicher erfordern (wie eine replizierte MySQL-Datenbank), bietet Kubernetes Funktionen zum Anhängen persistenter Blockspeicher-Volumes an Container und Pods. Um sicherzustellen, dass ein Pod nach einem Neustart den Status beibehält und auf dasselbe beständige Volume zugreifen kann, muss die StatefulSet-Workload verwendet werden. StatefulSets eignen sich ideal für die Bereitstellung von Datenbanken und anderen Datenspeichern mit langer Laufzeit für Kubernetes.

Zustandslose Container ermöglichen maximale Portabilität und die vollständige Nutzung der verfügbaren Cloud-Ressourcen, sodass der Kubernetes-Scheduler Ihre App schnell skalieren und Pods starten kann, wo immer Ressourcen verfügbar sind. Wenn Sie die Stabilitäts- und Bestellgarantien des StatefulSet-Workloads nicht benötigen, sollten Sie den Deployment-Workload zum Verwalten und Skalieren Ihrer Anwendungen verwenden.

Weitere Informationen zum Design und zur Architektur von zustandslosen Cloud Native-Microservices finden Sie in unserem Kubernetes White Paper.

Integritätsprüfungen implementieren

Im Kubernetes-Modell kann auf die Cluster-Steuerebene zurückgegriffen werden, um eine fehlerhafte Anwendung oder einen fehlerhaften Dienst zu reparieren. Hierzu wird der Zustand der Anwendungs-Pods überprüft und fehlerhafte oder nicht reagierende Container neu gestartet oder neu geplant. Wenn Ihr Anwendungscontainer ausgeführt wird, sieht Kubernetes Ihren Pod standardmäßig als "fehlerfrei" an. In vielen Fällen ist dies ein zuverlässiger Indikator für den Zustand einer ausgeführten Anwendung. Wenn Ihre Anwendung jedoch blockiert ist und keine sinnvolle Arbeit leistet, werden der App-Prozess und der Container weiterhin auf unbestimmte Zeit ausgeführt, und Kubernetes behält standardmäßig den blockierten Container bei.

Um den Anwendungszustand ordnungsgemäß an die Kubernetes-Steuerebene zu übermitteln, sollten Sie benutzerdefinierte Anwendungszustandsüberprüfungen implementieren, die angeben, wann eine Anwendung ausgeführt wird und für den Empfang von Datenverkehr bereit ist. Die erste Art der Integritätsprüfung wird als * Bereitschaftsprüfung * bezeichnet und zeigt Kubernetes an, wann Ihre Anwendung für den Empfang von Datenverkehr bereit ist. Die zweite Art der Überprüfung wird als * Liveness Probe * bezeichnet und zeigt Kubernetes an, ob Ihre Anwendung fehlerfrei funktioniert und ausgeführt wird. Der Kubelet Node-Agent kann diese Tests mit drei verschiedenen Methoden auf laufenden Pods ausführen:

  • HTTP: Der Kubelet-Test führt eine HTTP-GET-Anforderung für einen Endpunkt aus (z. B. "+ / health +") und ist erfolgreich, wenn der Antwortstatus zwischen 200 und 399 liegt

  • Container-Befehl: Der Kubelet-Test führt einen Befehl im laufenden Container aus. Wenn der Exit-Code 0 ist, ist der Test erfolgreich.

  • TCP: Der Kubelet-Test versucht, über einen angegebenen Port eine Verbindung zu Ihrem Container herzustellen. Wenn eine TCP-Verbindung hergestellt werden kann, ist der Test erfolgreich.

Sie sollten die geeignete Methode in Abhängigkeit von den ausgeführten Anwendungen, der Programmiersprache und dem Framework auswählen. Die Bereitschafts- und Verfügbarkeitsprüfungen können dieselbe Prüfmethode verwenden und dieselbe Prüfung durchführen. Durch die Einbeziehung einer Bereitschaftsprüfung wird jedoch sichergestellt, dass der Pod erst dann Datenverkehr empfängt, wenn die Prüfung erfolgreich ist.

Wenn Sie planen und darüber nachdenken, Ihre Anwendung in Container zu packen und auf Kubernetes auszuführen, sollten Sie Planungszeit für die Definition der Bedeutung von "fehlerfrei" und "bereit" für Ihre bestimmte Anwendung und Entwicklungszeit für die Implementierung und das Testen der Endpunkte und / oder Prüfbefehle einplanen.

Hier ist ein minimaler Integritätsendpunkt für das oben genannte Flask-Beispiel:

env_config.py

. . .
@app.route('/')
def print_config():
   output = 'DB_HOST: {} -- DB_USER: {}'.format(DB_HOST, DB_USER)
   return output

Eine Kubernetes-Aktivitätssonde, die diesen Pfad überprüft, würde dann ungefähr so ​​aussehen:

pod_spec.yaml

. . .
 livenessProbe:
     httpGet:
       path: /health
       port: 80
     initialDelaySeconds: 5
     periodSeconds: 2

Das Feld "+ initialDelaySeconds " gibt an, dass Kubernetes (insbesondere das Node-Kubelet) den Endpunkt " / health " nach 5 Sekunden prüfen soll, und " periodSeconds " weist das Kubelet an, " / health +" alle 2 Sekunden zu prüfen.

Weitere Informationen zu Aktivitäts- und Bereitschaftsprüfungen finden Sie in der Kubernetes documentation.

Gerätecode für Protokollierung und Überwachung

Wenn Sie Ihre containerisierte Anwendung in einer Umgebung wie Kubernetes ausführen, ist es wichtig, Telemetrie- und Protokolldaten zu veröffentlichen, um die Leistung Ihrer Anwendung zu überwachen und zu debuggen. Durch die Integration von Funktionen zum Veröffentlichen von Leistungsmetriken wie Antwortdauer und Fehlerraten können Sie Ihre Anwendung überwachen und Sie warnen, wenn Ihre Anwendung fehlerhaft ist.

Ein Tool, mit dem Sie Ihre Dienste überwachen können, ist Prometheus, ​​ein Open-Source-Toolkit zur Systemüberwachung und -warnung, das von der Cloud Native Computing Foundation (CNCF) bereitgestellt wird. Prometheus bietet mehrere Client-Bibliotheken, mit denen Sie Ihren Code mit verschiedenen Metriktypen instrumentieren können, um Ereignisse und deren Dauer zu zählen. Wenn Sie beispielsweise das Flask Python-Framework verwenden, können Sie mit Prometheus Python client Dekoratoren zu Ihren Anforderungsverarbeitungsfunktionen hinzufügen, um den Zeitaufwand für die Verarbeitung von Anforderungen zu verfolgen. Diese Metriken können dann von Prometheus an einem HTTP-Endpunkt wie "+ / metrics +" erstellt werden.

Eine hilfreiche Methode zum Entwerfen der App-Instrumentierung ist die RED-Methode. Es besteht aus den folgenden drei Metriken für Schlüsselanforderungen:

  • Rate: Die Anzahl der Anfragen, die Ihre Bewerbung erhalten hat

  • Fehler: Die Anzahl der von Ihrer Anwendung ausgegebenen Fehler

  • Dauer: Die Zeit, die Ihre Anwendung benötigt, um eine Antwort zu liefern

Mit dieser Mindestanzahl von Metriken sollten Sie genügend Daten erhalten, um darauf aufmerksam zu machen, wenn sich die Leistung Ihrer Anwendung verschlechtert. Wenn Sie diese Instrumentierung zusammen mit den oben erläuterten Integritätsprüfungen implementieren, können Sie eine fehlerhafte Anwendung schnell erkennen und wiederherstellen.

Weitere Informationen zu Signalen zur Überwachung Ihrer Anwendungen finden Sie unter Monitoring Distributed Systems in Google Site Reliability Engineering-Buch.

Neben dem Überlegen und Entwerfen von Funktionen zum Veröffentlichen von Telemetriedaten sollten Sie auch planen, wie sich Ihre Anwendung in einer verteilten clusterbasierten Umgebung anmeldet. Sie sollten im Idealfall fest codierte Konfigurationsverweise auf lokale Protokolldateien und Protokollverzeichnisse entfernen und stattdessen direkt bei stdout und stderr protokollieren. Sie sollten Protokolle als fortlaufenden Ereignisstrom oder als Folge von zeitlich geordneten Ereignissen behandeln. Dieser Ausgabestream wird dann vom Container erfasst, der Ihre Anwendung einhüllt, und kann von dort an eine Protokollierungsschicht wie den EFK-Stapel (Elasticsearch, Fluentd und Kibana) weitergeleitet werden. Kubernetes bietet viel Flexibilität beim Entwerfen Ihrer Protokollierungsarchitektur, auf die wir im Folgenden näher eingehen werden.

Erstellen Sie die Administrationslogik in die API

Sobald Ihre Anwendung containerisiert ist und in einer Clusterumgebung wie Kubernetes ausgeführt wird, haben Sie möglicherweise keinen Shell-Zugriff mehr auf den Container, in dem Ihre App ausgeführt wird. Wenn Sie eine angemessene Integritätsprüfung, Protokollierung und Überwachung implementiert haben, können Sie schnell benachrichtigt und Probleme in der Produktion behoben werden. Es kann jedoch schwierig sein, Maßnahmen zu ergreifen, die über den Neustart und die erneute Bereitstellung von Containern hinausgehen. Für schnelle Betriebs- und Wartungskorrekturen wie das Leeren von Warteschlangen oder das Löschen eines Caches sollten Sie die entsprechenden API-Endpunkte implementieren, damit Sie diese Vorgänge ausführen können, ohne Container neu starten oder "+ exec +" in ausgeführten Containern ausführen und eine Reihe von Befehlen ausführen zu müssen. Container sollten als unveränderliche Objekte behandelt werden, und die manuelle Verwaltung sollte in einer Produktionsumgebung vermieden werden. Wenn Sie einmalige Verwaltungsaufgaben ausführen müssen, z. B. das Löschen von Caches, sollten Sie diese Funktionalität über die API bereitstellen.

Zusammenfassung

In diesen Abschnitten haben wir Änderungen auf Anwendungsebene besprochen, die Sie möglicherweise implementieren möchten, bevor Sie Ihre Anwendung containerisieren und auf Kubernetes verschieben. Weitere Informationen zum Erstellen von Cloud Native-Apps finden Sie unter Architecting Applications for Kubernetes.

Wir werden nun einige Überlegungen diskutieren, die Sie beim Erstellen von Containern für Ihre Apps berücksichtigen sollten.

Containerisierung Ihrer Anwendung

Nachdem Sie die App-Logik implementiert haben, um die Portabilität und Beobachtbarkeit in einer Cloud-basierten Umgebung zu maximieren, ist es an der Zeit, Ihre App in einem Container zu verpacken. Für die Zwecke dieses Handbuchs werden Docker-Container verwendet. Sie sollten jedoch die Containerimplementierung verwenden, die Ihren Produktionsanforderungen am besten entspricht.

Deklarieren Sie Abhängigkeiten explizit

Bevor Sie eine Docker-Datei für Ihre Anwendung erstellen, müssen Sie zunächst eine Bestandsaufnahme der Software- und Betriebssystemabhängigkeiten durchführen, die für die ordnungsgemäße Ausführung Ihrer Anwendung erforderlich sind. Mit Dockerfiles können Sie jede im Image installierte Software explizit versionieren. Sie sollten diese Funktion nutzen, indem Sie das übergeordnete Image, die Softwarebibliothek und die Programmiersprachenversionen explizit deklarieren.

Vermeiden Sie + latest + Tags und nicht versionierte Pakete so weit wie möglich, da diese sich verschieben können und möglicherweise Ihre Anwendung beschädigen. Möglicherweise möchten Sie eine private Registrierung oder einen privaten Spiegel einer öffentlichen Registrierung erstellen, um mehr Kontrolle über die Image-Versionierung zu haben und um zu verhindern, dass Änderungen im Upstream unbeabsichtigt Ihre Image-Builds beschädigen.

Weitere Informationen zum Einrichten einer Registrierung für private Bilder finden Sie unter Deploy a Registry Server in der offiziellen Dokumentation von Docker und in der https://www.digitalocean.com/community / tutorials / modernizing-applications-for-kubernetes #publish-image-to-a-registry Abschnitt [Registries] weiter unten.

Bildgrößen klein halten

Beim Bereitstellen und Abrufen von Containerabbildern können große Abbilder die Verarbeitung erheblich verlangsamen und Ihre Bandbreitenkosten erhöhen. Das Packen eines minimalen Satzes von Tools und Anwendungsdateien in ein Image bietet mehrere Vorteile:

  • Bildgrößen reduzieren

  • Beschleunigen Sie die Image-Erstellung

  • Behälterstartverzögerung reduzieren

  • Beschleunigen Sie die Bildübertragungszeiten

  • Verbessern Sie die Sicherheit, indem Sie die Angriffsfläche reduzieren

Einige Schritte, die Sie beim Erstellen Ihrer Bilder berücksichtigen können:

  • Verwenden Sie ein minimales Basis-OS-Image wie "+ alpine " oder erstellen Sie es von " scratch" anstelle eines voll funktionsfähigen Betriebssystems wie "+ ubuntu"

  • Bereinigen Sie nach der Installation der Software nicht benötigte Dateien und Artefakte

  • Verwenden Sie separate Build- und Runtime-Container, um die Container für Produktionsanwendungen klein zu halten

  • Ignorieren Sie unnötige Build-Artefakte und -Dateien, wenn Sie in große Verzeichnisse kopieren

Eine vollständige Anleitung zur Optimierung von Docker-Containern, einschließlich zahlreicher anschaulicher Beispiele, finden Sie unter Building Optimized Containers for Kubernetes.

Konfiguration injizieren

Docker bietet verschiedene hilfreiche Funktionen zum Einfügen von Konfigurationsdaten in die Ausführungsumgebung Ihrer App.

Eine Möglichkeit hierfür besteht darin, Umgebungsvariablen und ihre Werte in der Docker-Datei mit der Anweisung + ENV + anzugeben, damit Konfigurationsdaten in Images integriert werden:

Dockerfile

...
ENV MYSQL_USER=my_db_user
...

Ihre App kann diese Werte dann in der laufenden Umgebung analysieren und die Einstellungen entsprechend konfigurieren.

Sie können Umgebungsvariablen auch als Parameter übergeben, wenn Sie einen Container mit + docker run + und dem Flag + -e + starten:

docker run -e MYSQL_USER='my_db_user' IMAGE[:TAG]

Schließlich können Sie eine env-Datei verwenden, die eine Liste der Umgebungsvariablen und ihrer Werte enthält. Erstellen Sie dazu die Datei und übergeben Sie sie mit dem Parameter "+ - env-file" an den Befehl:

docker run --env-file var_list IMAGE[:TAG]

Wenn Sie Ihre Anwendung so modernisieren, dass sie mit einem Cluster-Manager wie Kubernetes ausgeführt wird, sollten Sie Ihre Konfiguration über das Image weiter externalisieren und die Konfiguration mithilfe von Kubernetes 'integriertem https://kubernetes.io/docs/tasks/configure- pod-container / configure-pod-configmap / [ConfigMap] - und Secrets -Objekte. Auf diese Weise können Sie die Konfiguration von Image-Manifesten trennen, sodass Sie sie separat von Ihrer Anwendung verwalten und versionieren können. Informationen zum Externalisieren der Konfiguration mithilfe von ConfigMaps und Secrets finden Sie im Abschnitt ConfigMaps and Secrets. unten.

Veröffentlichen Sie das Bild in einer Registrierung

Sobald Sie Ihre Anwendungsimages erstellt haben, sollten Sie sie in eine Container-Image-Registrierung hochladen, um sie Kubernetes zur Verfügung zu stellen. Öffentliche Registries wie https://hub.docker.com [Docker Hub] hosten die neuesten Docker-Images für beliebte Open Source-Projekte wie https://hub.docker.com//node/[Node.js] und https: / /hub.docker.com//nginx/[nginx]. Mit privaten Registern können Sie Ihre internen Anwendungsbilder veröffentlichen und sie Entwicklern und Infrastrukturen zur Verfügung stellen, nicht jedoch der ganzen Welt.

Sie können eine private Registrierung unter Verwendung Ihrer vorhandenen Infrastruktur bereitstellen (z. (zusätzlich zum Cloud-Objektspeicher), oder verwenden Sie optional eines von mehreren Docker-Registrierungsprodukten wie Quay.io oder kostenpflichtige Docker Hub-Pläne. Diese Registries können in gehostete Versionskontrolldienste wie GitHub integriert werden, sodass der Registrierungsdienst beim Aktualisieren und Übertragen einer Docker-Datei automatisch die neue Docker-Datei abruft, das Container-Image erstellt und das aktualisierte Image Ihren Diensten zur Verfügung stellt.

Um mehr Kontrolle über das Erstellen und Testen Ihrer Container-Images sowie deren Kennzeichnung und Veröffentlichung zu erhalten, können Sie eine CI-Pipeline (Continuous Integration) implementieren.

Implementieren Sie eine Build-Pipeline

Das manuelle Erstellen, Testen, Veröffentlichen und Bereitstellen Ihrer Images in der Produktion kann fehleranfällig sein und lässt sich nicht gut skalieren. Verwenden Sie eine Build-Pipeline, um Builds zu verwalten und Container mit den neuesten Codeänderungen fortlaufend in Ihrer Image-Registrierung zu veröffentlichen.

Die meisten Pipelines erfüllen die folgenden Kernfunktionen:

  • Beobachten Sie die Quellcode-Repositorys auf Änderungen

  • Führen Sie Rauch- und Komponententests mit geändertem Code durch

  • Erstellen Sie Containerbilder mit geändertem Code

  • Führen Sie weitere Integrationstests mit erstellten Container-Images durch

  • Wenn die Tests erfolgreich sind, markieren Sie die Bilder und veröffentlichen Sie sie in der Registrierung

  • (Optional, in fortlaufenden Bereitstellungs-Setups) Aktualisieren Sie Kubernetes Deployments und stellen Sie Images für Staging- / Produktions-Cluster bereit

Es gibt viele kostenpflichtige Continuous-Integration-Produkte, die über eine integrierte Integration mit gängigen Versionskontrolldiensten wie GitHub und Image-Registrys wie Docker Hub verfügen. Eine Alternative zu diesen Produkten ist Jenkins, ein kostenloser Open-Source-Build-Automatisierungsserver, der so konfiguriert werden kann, dass er alle oben beschriebenen Funktionen ausführt. Informationen zum Einrichten einer Jenkins-Pipeline für die kontinuierliche Integration finden Sie unter https://www.digitalocean.com/community/tutorials/how-to-set-up-continuous-integration-pipelines-injenkins-on-ubuntu-16 -04 [Einrichten von Continuous Integration Pipelines in Jenkins unter Ubuntu 16.04].

Implementieren Sie die Container-Protokollierung und -Überwachung

Wenn Sie mit Containern arbeiten, ist es wichtig, über die Protokollierungsinfrastruktur nachzudenken, die Sie zum Verwalten und Speichern von Protokollen für alle ausgeführten und angehaltenen Container verwenden. Es gibt mehrere Muster auf Containerebene, die Sie für die Protokollierung verwenden können, sowie mehrere Muster auf Kubernetes-Ebene.

In Kubernetes verwenden Container standardmäßig die JSON-Datei Docker logging driver, mit der die Streams stdout und stderr erfasst und in JSON geschrieben werden Dateien auf dem Knoten, auf dem der Container ausgeführt wird. Manchmal reicht die direkte Protokollierung in stderr und stdout für Ihren Anwendungscontainer möglicherweise nicht aus, und Sie möchten den Anwendungscontainer möglicherweise mit einem protokollierenden sidecar-Container in einem Kubernetes Pod koppeln. Dieser Sidecar-Container kann dann Protokolle aus dem Dateisystem, einem lokalen Socket oder dem systemd-Journal abrufen, was Ihnen ein wenig mehr Flexibilität bietet, als nur die Streams stderr und stdout zu verwenden. Dieser Container kann auch einige Verarbeitungsschritte ausführen und dann angereicherte Protokolle an stdout / stderr oder direkt an ein Protokollierungs-Backend streamen. Weitere Informationen zu Kubernetes-Protokollierungsmustern finden Sie in diesem Lernprogramm unter section.

Wie Ihre Anwendung auf Containerebene protokolliert, hängt von ihrer Komplexität ab. Für einfache Einzweck-Microservices wird empfohlen, sich direkt bei stdout / stderr anzumelden und Kubernetes diese Streams abholen zu lassen, da Sie dann den Befehl + kubectl logs + verwenden können, um auf Log-Streams aus Ihren von Kubernetes bereitgestellten Containern zuzugreifen.

Ähnlich wie bei der Protokollierung sollten Sie über die Überwachung in einer container- und clusterbasierten Umgebung nachdenken. Docker bietet den hilfreichen Befehl + docker stats + zum Abrufen von Standardmetriken wie CPU- und Speichernutzung für die Ausführung von Containern auf dem Host und stellt über Remote REST noch mehr Metriken zur Verfügung API. Darüber hinaus bietet das Open-Source-Tool cAdvisor (standardmäßig auf Kubernetes Nodes installiert) erweiterte Funktionen wie die Erfassung historischer Metriken, den Export von Metrikdaten und eine hilfreiche Web-Benutzeroberfläche zum Durchsuchen die Daten.

In einer Produktionsumgebung mit mehreren Knoten und Containern können jedoch komplexere Metrikstapel wie Prometheus und Grafana die Organisation und Überwachung Ihrer Container erleichtern. Leistungsdaten.

Zusammenfassung

In diesen Abschnitten wurden einige bewährte Methoden zum Erstellen von Containern, zum Einrichten einer CI / CD-Pipeline und eines Image-Registers sowie einige Überlegungen zur Verbesserung der Beobachtbarkeit Ihrer Container kurz erläutert.

Im nächsten Abschnitt werden die Kubernetes-Funktionen erläutert, mit denen Sie Ihre containerisierte App in einem Cluster ausführen und skalieren können.

Bereitstellung auf Kubernetes

Zu diesem Zeitpunkt haben Sie Ihre App containerisiert und Logik implementiert, um ihre Portabilität und Beobachtbarkeit in Cloud Native-Umgebungen zu maximieren. Wir werden nun Kubernetes-Funktionen untersuchen, die einfache Schnittstellen für die Verwaltung und Skalierung Ihrer Apps in einem Kubernetes-Cluster bieten.

Schreiben Sie Bereitstellungs- und Pod-Konfigurationsdateien

Nachdem Sie Ihre Anwendung in einen Container gestellt und in einer Registrierung veröffentlicht haben, können Sie sie jetzt mithilfe des Pod-Workloads in einem Kubernetes-Cluster bereitstellen. Die kleinste implementierbare Einheit in einem Kubernetes-Cluster ist kein Container, sondern ein Pod. Pods bestehen in der Regel aus einem Anwendungscontainer (wie einer containerisierten Flask-Web-App) oder einem App-Container und beliebigen Containern für Seitenwagen, die eine Hilfsfunktion wie Überwachung oder Protokollierung ausführen. Container in einem Pod teilen sich Speicherressourcen, einen Netzwerk-Namespace und einen Port-Space. Sie können miteinander unter Verwendung von "+ localhost +" kommunizieren und Daten unter Verwendung gemounteter Volumes gemeinsam nutzen. Darüber hinaus können Sie im Pod-Workload Init Containers definieren, mit denen Setup-Skripts oder Dienstprogramme ausgeführt werden, bevor der Haupt-App-Container ausgeführt wird.

Pods werden in der Regel mithilfe von Bereitstellungen bereitgestellt. Hierbei handelt es sich um Controller, die durch YAML-Dateien definiert werden, die einen bestimmten gewünschten Status deklarieren. Ein Anwendungsstatus könnte beispielsweise darin bestehen, drei Replikate des Flask-Webanwendungscontainers auszuführen und Port 8080 verfügbar zu machen. Nach der Erstellung stellt die Steuerebene den tatsächlichen Status des Clusters schrittweise auf den gewünschten Status ein, der in der Bereitstellung deklariert wurde, indem Container nach Bedarf auf Knoten eingeplant werden. Um die Anzahl der im Cluster ausgeführten Anwendungsreplikate zu skalieren, z. B. von 3 auf 5, aktualisieren Sie das Feld "+ replicas " der Konfigurationsdatei für die Bereitstellung und wenden dann " kubectl +" die neue Konfigurationsdatei an. Mithilfe dieser Konfigurationsdateien können Skalierungs- und Bereitstellungsvorgänge mithilfe Ihrer vorhandenen Versionsverwaltungsdienste und -integrationen nachverfolgt und versioniert werden.

Hier ist ein Beispiel für eine Konfigurationsdatei für Kubernetes Deployment für eine Flask-App:

flask_deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
 name: flask-app
 labels:
   app: flask-app
spec:
 replicas: 3
 selector:
   matchLabels:
     app: flask-app
 template:
   metadata:
     labels:
       app: flask-app
   spec:
     containers:
     - name: flask
       image: sammy/flask_app:1.0
       ports:
       - containerPort: 8080

Diese Bereitstellung startet 3 Pods, auf denen ein Container mit dem Namen "+ flask " ausgeführt wird. Dabei wird das Image " sammy / flask_app " (Version " 1.0 ") mit geöffnetem Port " 8080 " verwendet. Das Deployment heißt " flask-app".

Weitere Informationen zu Kubernetes Pods und Bereitstellungen finden Sie unter Pods und https://kubernetes.io/docs/concepts/workloads/controllers/deployment / Abschnitte [Deployments] der offiziellen Kubernetes-Dokumentation.

Konfigurieren Sie den Pod-Speicher

Kubernetes verwaltet die Pod-Speicherung mithilfe von Volumes, Persistent Volumes (PVs) und Persistent Volume Claims (PVCs). Volumes sind die Kubernetes-Abstraktion, die zum Verwalten des Pod-Speichers verwendet wird. Sie unterstützen die meisten Blockspeicherangebote von Cloud-Anbietern sowie den lokalen Speicher auf den Knoten, auf denen die ausgeführten Pods gehostet werden. Eine vollständige Liste der unterstützten Datenträgertypen finden Sie in der Dokumentation zu Kubernetes documentation.

Wenn Ihr Pod beispielsweise zwei NGINX-Container enthält, die Daten zwischen ihnen austauschen müssen (sagen wir, der erste mit dem Namen "+ nginx " liefert Webseiten, und der zweite mit dem Namen " nginx-sync " holt die Seiten von einem externen Speicherort und Aktualisiert die Seiten, die vom Container " nginx " bereitgestellt werden. Ihre Pod-Spezifikation würde ungefähr so ​​aussehen (hier verwenden wir https://kubernetes.io/docs/concepts/storage/volumes/#emptydir [` emptyDir +`] Volumentyp):

pod_volume.yaml

apiVersion: v1
kind: Pod
metadata:
 name: nginx
spec:
 containers:
 - name: nginx
   image: nginx
   volumeMounts:
   - name: nginx-web
     mountPath: /usr/share/nginx/html

 - name: nginx-sync
   image: nginx-sync
   volumeMounts:
   - name: nginx-web
     mountPath: /web-data

 volumes:
 - name: nginx-web
   emptyDir: {}

Wir verwenden ein "+ volumeMount " für jeden Container, um anzuzeigen, dass wir das " nginx-web " -Volume mit den Webseiten-Dateien unter " / usr / share / nginx / html " im " nginx " mounten möchten container und bei ` / web-data ` im ` nginx-sync ` container. Wir definieren auch ein " Volume" mit dem Namen "+ nginx-web" vom Typ "+ emptyDir".

Auf ähnliche Weise können Sie den Pod-Speicher mithilfe von Cloud-Blockspeicherprodukten konfigurieren, indem Sie den Typ "+ volume " von " emptyDir +" in den entsprechenden Typ des Cloud-Speicher-Volumes ändern.

Der Lebenszyklus eines Volumes ist an den Lebenszyklus des Pod gebunden, jedoch nicht an den eines Containers. Wenn ein Container in einem Pod stirbt, bleibt das Volume bestehen und der neu gestartete Container kann dasselbe Volume bereitstellen und auf seine Daten zugreifen. Wenn ein Pod neu gestartet wird oder abstürzt, werden auch die Volumes neu gestartet. Wenn die Volumes jedoch aus Cloud-Block-Speicher bestehen, werden sie einfach deaktiviert, und die Daten bleiben für zukünftige Pods verfügbar.

Um Daten über Neustarts und Aktualisierungen des Pod hinweg zu erhalten, müssen die Objekte PersistentVolume (PV) und PersistentVolumeClaim (PVC) verwendet werden.

PersistentVolumes sind Abstraktionen, die Teile des persistenten Speichers darstellen, z. B. Cloud-Block-Speicher-Volumes oder NFS-Speicher. Sie werden getrennt von PersistentVolumeClaims erstellt, bei denen es sich um Anforderungen für Speicher von Entwicklern handelt. In ihren Pod-Konfigurationen fordern Entwickler persistenten Speicher mit PVCs an, die Kubernetes mit den verfügbaren PV-Volumes abgleichen (wenn Kubernetes Cloud-Blockspeicher verwendet, können sie PersistentVolumes dynamisch erstellen, wenn PersistentVolumeClaims erstellt werden).

Wenn Ihre Anwendung ein persistentes Volume pro Replikat erfordert, wie dies bei vielen Datenbanken der Fall ist, sollten Sie keine Bereitstellungen verwenden, sondern den StatefulSet-Controller verwenden, der für Apps entwickelt wurde, für die stabile Netzwerkkennungen, stabiler persistenter Speicher und Bestellgarantien erforderlich sind. Bereitstellungen sollten für zustandslose Anwendungen verwendet werden. Wenn Sie PersistentVolumeClaim für die Verwendung in einer Bereitstellungskonfiguration definieren, wird diese PVC von allen Replikaten der Bereitstellung gemeinsam genutzt.

Weitere Informationen zum StatefulSet-Controller finden Sie unter Kubernetes documentation. Weitere Informationen zu PersistentVolumes- und PersistentVolume-Ansprüchen finden Sie im Kubernetes-Speicher unter documentation.

Konfigurationsdaten mit Kubernetes einspeisen

Ähnlich wie Docker bietet Kubernetes die Felder "+ env " und " envFrom " zum Festlegen von Umgebungsvariablen in Pod-Konfigurationsdateien. Hier ist ein Beispielausschnitt aus einer Pod-Konfigurationsdatei, in der die Umgebungsvariable " HOSTNAME " im ausgeführten Pod auf " mein_Hostname +" gesetzt ist:

sample_pod.yaml

...
   spec:
     containers:
     - name: nginx
       image: nginx:1.7.9
       ports:
       - containerPort: 80
       env:
       - name: HOSTNAME
         value: my_hostname
...

Auf diese Weise können Sie die Konfiguration aus den Docker-Dateien in die Pod- und Deployment-Konfigurationsdateien verschieben. Ein wesentlicher Vorteil der weiteren Externalisierung der Konfiguration aus Ihren Docker-Dateien besteht darin, dass Sie diese Kubernetes-Workload-Konfigurationen jetzt separat von Ihren Anwendungscontainer-Definitionen ändern können (z. B. durch Ändern des Werts "+ HOSTNAME " in " mein_Hostname_2 +"). Sobald Sie die Pod-Konfigurationsdatei geändert haben, können Sie den Pod in seiner neuen Umgebung erneut bereitstellen, während das zugrunde liegende Container-Image (definiert über seine Docker-Datei) nicht neu erstellt, getestet und in ein Repository verschoben werden muss. Sie können diese Pod- und Bereitstellungskonfigurationen auch separat von Ihren Docker-Dateien versionieren, sodass Sie schnell aktuelle Änderungen erkennen und Konfigurationsprobleme von Anwendungsfehlern trennen können.

Kubernetes bietet ein weiteres Konstrukt zur weiteren Externalisierung und Verwaltung von Konfigurationsdaten: ConfigMaps und Secrets.

ConfigMaps und Geheimnisse

Mit ConfigMaps können Sie Konfigurationsdaten als Objekte speichern, auf die Sie dann in Ihren Pod- und Bereitstellungskonfigurationsdateien verweisen, damit Sie das Hardcodieren von Konfigurationsdaten vermeiden und sie über Pods und Bereitstellungen hinweg wiederverwenden können.

Hier ist ein Beispiel für die Verwendung der Pod-Konfiguration von oben. Wir werden zuerst die Umgebungsvariable "+ HOSTNAME +" als ConfigMap speichern und dann in der Pod-Konfiguration darauf verweisen:

kubectl create configmap hostname --from-literal=HOSTNAME=my_host_name

Um in der Pod-Konfigurationsdatei darauf zu verweisen, verwenden wir die Konstrukte + valueFrom + und + configMapKeyRef +:

sample_pod_configmap.yaml

...
   spec:
     containers:
     - name: nginx
       image: nginx:1.7.9
       ports:
       - containerPort: 80
       env:
       - name: HOSTNAME
         valueFrom:
           configMapKeyRef:
             name: hostname
             key: HOSTNAME
...

Daher wurde der Wert der Umgebungsvariablen "+ HOSTNAME +" vollständig aus den Konfigurationsdateien ausgelagert. Wir können diese Variablen dann für alle Bereitstellungen und Pods aktualisieren, auf die sie verweisen, und die Pods neu starten, damit die Änderungen wirksam werden.

Wenn Ihre Anwendungen Konfigurationsdateien verwenden, können Sie diese Dateien in ConfigMaps zusätzlich als ConfigMap-Objekte speichern (mithilfe des Flags + - from-file +), die Sie dann als Konfigurationsdateien in Container einhängen können.

Secrets bieten die gleichen grundlegenden Funktionen wie ConfigMaps, sollten jedoch für vertrauliche Daten wie Datenbankanmeldeinformationen verwendet werden, da die Werte base64-codiert sind.

Um mehr über ConfigMaps und Secrets zu erfahren, konsultieren Sie Kubernetes documentation.

Erstellen Sie Services

Sobald Ihre Anwendung in Kubernetes ausgeführt wird, wird jedem Pod eine (interne) IP-Adresse zugewiesen, die von den Containern gemeinsam genutzt wird. Wenn einer dieser Pods entfernt wird oder stirbt, werden neu gestarteten Pods andere IP-Adressen zugewiesen.

Für Dienste mit langer Laufzeit, bei denen die Funktionalität für interne und / oder externe Clients verfügbar ist, möchten Sie möglicherweise einer Reihe von Pods, die dieselbe Funktion (oder Bereitstellung) ausführen, eine stabile IP-Adresse zuweisen, mit der Anforderungen über die Container verteilt werden. Dies können Sie mit einem Kubernetes-Dienst tun.

Kubernetes Services haben 4 Typen, die durch das Feld "+ type +" in der Service-Konfigurationsdatei angegeben werden:

  • + ClusterIP +: Dies ist der Standardtyp, mit dem dem Dienst eine stabile interne IP-Adresse zugewiesen wird, auf die von einer beliebigen Stelle innerhalb des Clusters aus zugegriffen werden kann.

  • + NodePort +: Hiermit wird Ihr Dienst auf jedem Knoten an einem statischen Port verfügbar gemacht, der standardmäßig zwischen 30000-32767 liegt. Wenn eine Anforderung einen Knoten an seiner Knoten-IP-Adresse und dem "+ NodePort +" für Ihren Dienst erreicht, wird die Anforderung lastausgeglichen und an die Anwendungscontainer für Ihren Dienst weitergeleitet.

  • + LoadBalancer +: Hiermit erstellen Sie einen Load Balancer mit dem Load Balancing-Produkt Ihres Cloud-Anbieters und konfigurieren einen NodePort und ein ClusterIP für Ihren Service, an den externe Anforderungen weitergeleitet werden.

  • + ExternalName +: Mit diesem Diensttyp können Sie einen Kubernetes-Dienst einem DNS-Eintrag zuordnen. Es kann verwendet werden, um über Kubernetes DNS von Ihren Pods auf externe Dienste zuzugreifen.

Beachten Sie, dass durch das Erstellen eines Dienstes vom Typ "+ LoadBalancer +" für jede in Ihrem Cluster ausgeführte Bereitstellung ein neuer Cloud-Lastenausgleich für jeden Dienst erstellt wird, der kostspielig werden kann. Um das Weiterleiten externer Anforderungen an mehrere Dienste mit einem einzigen Load Balancer zu verwalten, können Sie einen Ingress Controller verwenden. Ingress-Controller werden in diesem Artikel nicht behandelt. Weitere Informationen finden Sie in der Kubernetes-Website documentation. Ein beliebter einfacher Ingress Controller ist der NGINX Ingress Controller.

Hier ist eine einfache Service-Konfigurationsdatei für das in den Pods und Bereitstellungen verwendete Flask-Beispiel: https://www.digitalocean.com/community/tutorials/modernizing-applications-for-kubernetes#write-deployment-and-pod-configuration-files [ Abschnitt] dieses Handbuchs:

flask_app_svc.yaml

apiVersion: v1
kind: Service
metadata:
 name: flask-svc
spec:
 ports:
 - port: 80
   targetPort: 8080
 selector:
   app: flask-app
 type: LoadBalancer

Hier legen wir fest, dass die "+ flask-app " -Bereitstellung mit diesem " flask-svc " -Dienst verfügbar gemacht wird. Wir erstellen einen Cloud-Load-Balancer, der den Datenverkehr vom Load-Balancer-Port " 80 " zum exponierten Container-Port " 8080 +" weiterleitet.

Weitere Informationen zu Kubernetes Services finden Sie im Abschnitt Services der Kubernetes-Dokumentation.

Protokollierung und Überwachung

Das Parsen einzelner Container- und Pod-Protokolle mit "+ kubectl logs " und " docker logs +" kann mit zunehmender Anzahl laufender Anwendungen mühsam werden. Um Ihnen beim Debuggen von Anwendungs- oder Clusterproblemen zu helfen, sollten Sie die zentralisierte Protokollierung implementieren. Auf hoher Ebene besteht dies aus Agenten, die auf allen Arbeitsknoten ausgeführt werden, die Pod-Protokolldateien und -Streams verarbeiten, sie mit Metadaten anreichern und die Protokolle an ein Back-End wie Elasticsearch weiterleiten . Von dort aus können Protokolldaten mit einem Visualisierungstool wie Kibana visualisiert, gefiltert und organisiert werden.

Im Abschnitt zur Protokollierung auf Containerebene haben wir den empfohlenen Kubernetes-Ansatz erörtert, bei dem Anwendungen in Containern in den Streams stdout / stderr protokolliert werden. Wir haben auch kurz die Protokollierung von Sidecar-Containern erörtert, die Ihnen mehr Flexibilität beim Protokollieren von Ihrer Anwendung bieten können. Sie können auch Protokollierungsagenten direkt in Ihren Pods ausführen, die lokale Protokolldaten erfassen und diese direkt an Ihr Protokollierungs-Backend weiterleiten. Jeder Ansatz hat seine Vor- und Nachteile sowie Kompromisse bei der Ressourcennutzung (z. B. kann das Ausführen eines Protokollierungs-Agent-Containers in jedem Pod ressourcenintensiv werden und Ihr Protokollierungs-Backend schnell überfordern). Weitere Informationen zu verschiedenen Protokollierungsarchitekturen und deren Kompromissen finden Sie in der Kubernetes-Website documentation.

In einer Standardkonfiguration führt jeder Knoten einen Protokollagenten wie Filebeat oder Fluentd aus, der Containerprotokolle aufnimmt erstellt von Kubernetes. Denken Sie daran, dass Kubernetes JSON-Protokolldateien für Container auf dem Knoten erstellt (in den meisten Installationen finden Sie diese unter "+ / var / lib / docker / containers / +"). Diese sollten mit einem Werkzeug wie Logrotate gedreht werden. Der Knotenprotokollierungs-Agent sollte als DaemonSet Controller ausgeführt werden, eine Art Kubernetes-Workload, der sicherstellt, dass auf jedem Knoten eine Kopie des DaemonSet Pod ausgeführt wird. In diesem Fall würde der Pod den Protokollierungsagenten und seine Konfiguration enthalten, der Protokolle aus Dateien und Verzeichnissen verarbeitet, die im Protokollierungs-DaemonSet-Pod eingebunden sind.

Ähnlich wie bei der Verwendung von "+ kubectl logs " zum Debuggen von Containerproblemen müssen Sie möglicherweise eine robustere Option in Betracht ziehen, als einfach " kubectl top " und das Kubernetes Dashboard zum Überwachen der Nutzung von Pod-Ressourcen in Ihrem Cluster zu verwenden. Die Überwachung auf Cluster- und Anwendungsebene kann mithilfe des https://prometheus.io/[Prometheus] -Messsystems und der Zeitreihendatenbank sowie des https://github.com/grafana/grafana[Grafana] -Messdaten-Dashboards eingerichtet werden. Prometheus verwendet ein Pull-Modell, mit dem HTTP-Endpunkte (z. B. " / metrics / cadvisor " auf den Knoten oder die REST-API-Endpunkte der " / metrics +" -Anwendung) periodisch nach Metrikdaten durchsucht und gespeichert werden. Diese Daten können dann mithilfe des Grafana-Dashboards analysiert und visualisiert werden. Prometheus und Grafana können wie jeder andere Deployment and Service in einem Kubernetes-Cluster gestartet werden.

Zur Erhöhung der Ausfallsicherheit möchten Sie möglicherweise Ihre Protokollierungs- und Überwachungsinfrastruktur in einem separaten Kubernetes-Cluster ausführen oder externe Protokollierungs- und Metrikdienste verwenden.

Fazit

Das Migrieren und Modernisieren einer Anwendung, damit sie in einem Kubernetes-Cluster effizient ausgeführt werden kann, erfordert häufig einen nicht trivialen Planungs- und Architekturaufwand für Software- und Infrastrukturänderungen. Sobald diese Änderungen implementiert sind, können Servicebesitzer kontinuierlich neue Versionen ihrer Apps bereitstellen und diese bei Bedarf mit minimalem manuellen Aufwand einfach skalieren. Mithilfe von Schritten wie dem Externalisieren der Konfiguration aus Ihrer App, dem Einrichten der ordnungsgemäßen Protokollierung und Veröffentlichung von Metriken sowie dem Konfigurieren von Integritätsprüfungen können Sie das Cloud Native-Paradigma, für das Kubernetes entwickelt wurde, voll ausnutzen. Indem Sie tragbare Container erstellen und diese mithilfe von Kubernetes-Objekten wie Deployments und Services verwalten, können Sie die verfügbaren Recheninfrastruktur- und Entwicklungsressourcen vollständig nutzen.