39,90 €
Praktische Anleitungen und Vorlagen zum Bauen erfolgreicher Kubernetes-Anwendungen - Bewährte Vorgehensweisen aus dem Arbeitsalltag von Kubernetes-Mitbegründer Brendan Burns - Praxisnaher Überblick über viele aktuelle und zentrale Themen - Mit Codebeispielen aus erfolgreichen Kubernetes-Projekten In diesem praktischen Leitfaden teilen Brendan Burns und drei weitere Kubernetes-Experten ihre bewährten Vorgehensweisen beim Erstellen von Anwendungen mit Kubernetes mit Ihnen. Sie bündeln jahrzehntelange Erfahrungen aus den unterschiedlichsten Anwendungsbereichen und von Unternehmen, die Kubernetes erfolgreich in der Produktion einsetzen. Konkrete Codebeispiele sowie Strategien zur Fehlervermeidung und -behebung helfen Ihnen bei der Umsetzung in die Praxis. Dieses Buch ist die Übersetzung der zweiten englischen Auflage. Es ist ideal für alle, die mit den grundlegenden Kubernetes-Konzepten vertraut sind und die neuesten Best Practices lernen möchten. Aus dem Inhalt: - Patterns für die Überwachung und Sicherung Ihrer Systeme sowie Verwaltung von Upgrades, Rollouts und Rollbacks - Integration von Diensten und Legacy-Anwendungen - Ausführen von Workloads für Machine Learning in Kubernetes - Sicherstellen der Pod- und Container-Sicherheit - Verstehen von Themen, die für die erfolgreiche Implementierung von Kubernetes immer wichtiger werden, wie z. B. Chaos Engineering/Testing, GitOps, Service Mesh und Observability
Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:
Seitenzahl: 451
Brendan Burns ist ein angesehener Engineer bei Microsoft Azure und Mitbegründer des Open-Source-Projekts Kubernetes. Er entwickelt seit mehr als einem Jahrzehnt Cloud-Anwendungen.
Eddie Villalba ist Engineering Manager und Application Platform Practice Lead für Nordamerika bei Google Cloud. Er leitet ein Team von Ingenieuren, das sich darauf konzentriert, Kunden beim Aufbau containeroptimierter Plattformen für skalierbare, zuverlässige verteilte Anwendungen zu unterstützen.
Dave Strebel ist Global Cloud Native Architect bei Microsoft Azure mit Schwerpunkt auf Open Source Cloud und Kubernetes. Er ist stark in das Open-Source-Projekt Kubernetes involviert, unterstützt das Kubernetes-Release-Team und leitet die SIG-Azure.
Lachlan Evenson ist Principal Program Manager im Container Compute Team bei Microsoft Azure. Er hat zahlreichen Menschen beim Einstieg in Kubernetes geholfen, sowohl durch praxisnahe Schulungen als auch mit seinen Vorträgen auf Konferenzen.
Brendan Burns · Eddie Villalba · Dave Strebel · Lachlan Evenson
Praktische Anleitungen und Vorlagen zu Grundlagen und fortgeschrittenen Themen
Übersetzung der 2. englischen Auflage
Brendan Burns
Eddie Villalba
Dave Strebel
Lachlan Evenson
Übersetzung: Rainer G. Haselier
Lektorat: Sandra Bollenbacher, Alissa Melitzer
Copy-Editing: Petra Heubach-Erdmann, Düsseldorf
Satz: inpunkt[w]o, Wilnsdorf, www.inpunktwo.de
Herstellung: Stefanie Weidner
Umschlaggestaltung: Eva Hepper, Silke Braun
Bibliografische Information der Deutschen Nationalbibliothek
Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.
ISBN:
Print 978-3-98889-027-6
PDF 978-3-98890-199-6
ePub 978-3-98890-200-9
1. Auflage 2025
Translation Copyright für die deutschsprachige Ausgabe © 2025
dpunkt.verlag GmbH
Wieblinger Weg 17
69123 Heidelberg
Authorized German translation of the English edition of Kubernetes Best Practices, 2EISBN 9781098142162 © 2024 Brendan Burns, Eddie Villalba, Dave Strebel, and Lachlan Evenson.
This translation is published and sold by permission of O'Reilly Media, Inc., which owns or controls all rights to publish and sell the same.
Schreiben Sie uns:
Falls Sie Anregungen, Wünsche und Kommentare haben, lassen Sie es uns wissen: [email protected].
Die vorliegende Publikation ist urheberrechtlich geschützt. Alle Rechte vorbehalten. Die Verwendung der Texte und Abbildungen, auch auszugsweise, ist ohne die schriftliche Zustimmung des Verlags urheberrechtswidrig und daher strafbar. Dies gilt insbesondere für die Vervielfältigung, Übersetzung oder die Verwendung in elektronischen Systemen.
Es wird darauf hingewiesen, dass die im Buch verwendeten Soft- und Hardware-Bezeichnungen sowie Markennamen und Produktbezeichnungen der jeweiligen Firmen im Allgemeinen warenzeichen-, marken- oder patentrechtlichem Schutz unterliegen.
Alle Angaben und Programme in diesem Buch wurden mit größter Sorgfalt kontrolliert. Weder Autoren noch Verlag noch Übersetzer können jedoch für Schäden haftbar gemacht werden, die in Zusammenhang mit der Verwendung dieses Buches stehen.
Die Autoren von Kubernetes Best Practices sind Koryphäen im Bereich Cloud Native. Das Buch ist ein Meisterkurs in der Verwaltung von Container-Orchestrierung im großen Maßstab. Mit seinem Themenspektrum von der Einrichtung bis zur Sicherheit ist das Buch eine umfassende Ressource, die nicht nur Wissen vermittelt, sondern auch empowert. Halbieren Sie Ihre Lernkurve und erstellen Sie mit den in dieser Pflichtlektüre vorgestellten bewährten Strategien schneller bessere Anwendungen.
– Joseph Sandoval, Principal Product Manager, Adobe Inc.
Nur weil wir etwas tun können, heißt das nicht, dass wir es auch tun sollten. Cloud Native ist ein umfangreiches Thema und daher gibt es zahlreiche Gelegenheiten, etwas falsch zu machen. Diese Experten fokussieren ihr tiefes Wissen auf die wichtigsten Rezepte, die Ihnen dabei helfen, dass Ihre Kubernetes-Deliveries nicht entgleisen.
– Jonathan Johnson, Cloud Native-Architekt, Vortragsredner, Trainer
Ihr Leitfaden für die Entwicklung erfolgreicher Anwendungen mit Kubernetes, gespickt mit Expertenwissen und Best Practices aus der Praxis.
– Dr. Roland Huß, Senior Principal Software Developer, Red Hat
Eine Fundgrube an Weisheiten über Container-Management von wahren Experten. Zitieren Sie dieses Buch in Meetings! Es geht nicht darum, Ideen zu stehlen – sie wollen, dass Sie dieses Buch lesen.
– Jess Males, DevOps Engineer, TriumphPay
Das Kubernetes-Ökosystem hat sich im Laufe der Zeit erheblich erweitert. Die in diesem exzellenten Leitfaden beschriebenen spezifischen, umsetzbaren Empfehlungen machen die aktuelle Komplexität für die wachsende Community greifbar.
– Bridget Kromhout, Principal Product Manager, Microsoft
Copyright und Urheberrechte:
Die durch die dpunkt.verlag GmbH vertriebenen digitalen Inhalte sind urheberrechtlich geschützt. Der Nutzer verpflichtet sich, die Urheberrechte anzuerkennen und einzuhalten. Es werden keine Urheber-, Nutzungs- und sonstigen Schutzrechte an den Inhalten auf den Nutzer übertragen. Der Nutzer ist nur berechtigt, den abgerufenen Inhalt zu eigenen Zwecken zu nutzen. Er ist nicht berechtigt, den Inhalt im Internet, in Intranets, in Extranets oder sonst wie Dritten zur Verwertung zur Verfügung zu stellen. Eine öffentliche Wiedergabe oder sonstige Weiterveröffentlichung und eine gewerbliche Vervielfältigung der Inhalte wird ausdrücklich ausgeschlossen. Der Nutzer darf Urheberrechtsvermerke, Markenzeichen und andere Rechtsvorbehalte im abgerufenen Inhalt nicht entfernen.
Einleitung
1Einen einfachen Service einrichten
1.1Die Anwendung im Überblick
1.2Konfigurationsdateien verwalten
1.3Mit Deployments einen replizierten Service erstellen
1.3.1Best Practices für die Verwaltung von Images
1.3.2Replizierte Anwendung erstellen
1.4Externen Ingress für HTTP-Verkehr einrichten
1.5Anwendung mit ConfigMaps konfigurieren
1.6Authentifizierung mit Secrets verwalten
1.7Einfache zustandsbehaftete Datenbank bereitstellen
1.8TCP-Load-Balancer mithilfe von Services erstellen
1.9Ingress zur Weiterleitung des Datenverkehrs an einen statischen Dateiserver verwenden
1.10Ihre Anwendung mit Helm parametrisieren
1.11Best Practices für die Bereitstellung von Services
1.12Zusammenfassung
2Workflows für Entwickler
2.1Ziele
2.2Aufbau eines Entwicklungsclusters
2.3Einen gemeinsam nutzbaren Cluster für mehrere Entwickler einrichten
2.3.1Onboarding von Benutzern
2.3.2Namespace erstellen und absichern
2.3.3Namespaces verwalten
2.3.4Services auf Clusterebene
2.4Entwickler-Workflows ermöglichen
2.4.1Anfängliches Setup
2.4.2Aktive Entwicklung ermöglichen
2.4.3Testen und Debuggen ermöglichen
2.5Best Practices für das Einrichten einer Entwicklungsumgebung
2.6Zusammenfassung
3Monitoring und Protokollierung in Kubernetes
3.1Metriken vs. Protokolle
3.2Monitoringtechniken
3.3Monitoringmuster
3.4Kubernetes-Metriken im Überblick
3.4.1cAdvisor
3.4.2Metrics-Server
3.4.3kube-state-metrics
3.5Welche Metriken muss ich monitoren?
3.6Monitoringtools
3.7Kubernetes mit Prometheus überwachen
3.8Protokollierung im Überblick
3.9Tools für die Protokollierung
3.10Protokollierung mit einem Loki-Stack
3.11Warnmeldungen
3.12Best Practices für Monitoring, Protokollierung und Alarmierung
3.12.1Monitoring
3.12.2Protokollierung
3.12.3Warnmeldungen
3.13Zusammenfassung
4Konfiguration, Secrets und RBAC
4.1Konfiguration durch ConfigMaps und Secrets
4.1.1ConfigMaps
4.1.2Secrets
4.2Gemeinsame Best Practices für die APIs ConfigMap und Secrets
4.3Best Practices speziell für Secrets
4.4RBAC
4.4.1RBAC-Einmaleins
4.5Best Practices für RBAC
4.6Zusammenfassung
5Continuous Integration, Testen und Bereitstellung
5.1Versionsverwaltung
5.2Continuous Integration
5.3Testen
5.4Container-Builds
5.5Tagging von Container-Images
5.6Continuous Deployment
5.7Bereitstellungsstrategien
5.8Tests in der Produktivumgebung
5.9Einrichten einer Pipeline und Durchführen eines Chaos-Experiments
5.9.1CI einrichten
5.9.2CD einrichten
5.9.3Rolling Upgrade durchführen
5.9.4Ein einfaches Chaos-Experiment
5.10Best Practices für CI/CD
5.11Zusammenfassung
6Versionierung, Releases und Rollouts
6.1Versionierung
6.2Releases
6.3Rollouts
6.4Alles zusammenfügen
6.5Best Practices für Versionierung, Releases und Rollouts
6.6Zusammenfassung
7Weltweite Distribution und Staging von Anwendungen
7.1Ihr Image distribuieren
7.2Parametrisierung Ihres Deployments
7.3Lastausgleich für weltweiten Datenverkehr
7.4Zuverlässiger weltweiter Rollout von Software
7.4.1Validierung vor dem Rollout
7.4.2Canary-Region
7.4.3Typen von Regionen identifizieren
7.4.4Globales Rollout planen
7.5Wenn etwas schiefgeht
7.6Best Practices für ein globales Rollout
7.7Zusammenfassung
8Ressourcenverwaltung
8.1Kubernetes-Scheduler
8.1.1Predicates
8.1.2Prioritäten
8.2Fortgeschrittene Scheduling-Techniken
8.2.1Pod-Affinity und Anti-Affinity
8.2.2nodeSelector
8.2.3Taints und Tolerations
8.3Pod-Ressourcenverwaltung
8.3.1Ressourcenanforderung
8.3.2Ressourcenobergrenzen und die Quality of Service von Pods
8.3.3PodDisruptionBudgets
8.3.4Ressourcen mit Namespaces verwalten
8.3.5ResourceQuota
8.3.6LimitRange
8.3.7Skalierung von Clustern
8.3.8Anwendungsskalierung
8.3.9Skalierung mit Horizontal Pod Autoscaler (HPA)
8.3.10HPA mit benutzerdefinierten Metriken
8.3.11Vertical Pod Autoscaler (VPA)
8.4Best Practices der Ressourcenverwaltung
8.5Zusammenfassung
9Vernetzung, Netzwerksicherheit und Service Meshes
9.1Grundsätze des Kubernetes-Netzwerks
9.2Netzwerk-Plug-ins
9.2.1Kubenet
9.2.2Best Practices für Kubenet
9.2.3Das CNI-Plug-in
9.2.4Best Practices für CNI
9.3Services in Kubernetes
9.3.1Service-Typ ClusterIP
9.3.2Service-Typ NodePort
9.3.3Service-Typ ExternalName
9.3.4Service-Typ LoadBalancer
9.3.5Ingress und Ingress-Controller
9.3.6Gateway-API
9.3.7Best Practices für Services und Ingress-Controller
9.4Netzwerksicherheitsrichtlinien
9.5Best Practices für Netzwerksicherheitsrichtlinien
9.6Service Meshes
9.7Best Practices für Service Meshes
9.8Zusammenfassung
10Pod- und Container-Sicherheit
10.1Pod Security Admission Controller
10.1.1Pod Security Admission Controller aktivieren
10.1.2Pod-Sicherheitsstandards
10.1.3Pod-Sicherheit mit Namespace-Labels aktivieren
10.2Workload-Isolierung und RuntimeClass
10.2.1RuntimeClass verwenden
10.2.2Laufzeit-Implementierungen
10.2.3Best Practices für Workload-Isolierung und RuntimeClass
10.3Weitere Überlegungen zur Pod- und Container-Sicherheit
10.3.1Admission Controller
10.3.2Werkzeuge zur Erkennung von Angriffen und Anomalien
10.4Zusammenfassung
11Policy und Governance für Ihren Cluster
11.1Warum Policy und Governance wichtig sind
11.2Was ist an dieser Policy anders?
11.2.1Cloud Native Policy Engine
11.3Einführung in Gatekeeper
11.3.1Beispielrichtlinien
11.3.2Gatekeeper-Terminologie
11.3.3Einschränkungsvorlagen definieren
11.3.4Constraints definieren
11.3.5Datenreplikation
11.3.6UX
11.4Durchsetzungsmaßnahmen und Audits verwenden
11.4.1Mutation
11.4.2Richtlinien testen
11.4.3Sich mit Gatekeeper vertraut machen
11.5Best Practices für Policy und Governance
11.6Zusammenfassung
12Verwaltung mehrerer Cluster
12.1Warum mehrere Cluster?
12.2Herausforderungen beim Multi-Cluster-Design
12.3Multi-Cluster-Bereitstellungen verwalten
12.4Deployment- und Managementmuster
12.5Der GitOps-Ansatz zur Verwaltung von Clustern
12.6Tools für das Multi-Cluster-Management
12.7Kubernetes Federation
12.8Best Practices für die Verwaltung mehrerer Cluster
12.9Zusammenfassung
13Externe Services in Kubernetes integrieren
13.1Services in Kubernetes importieren
13.1.1Services ohne Selektor für stabile IP-Adressen
13.1.2CNAME-basierte Dienste für stabile DNS-Namen
13.1.3Aktive Controller-basierte Ansätze
13.2Services aus Kubernetes exportieren
13.2.1Dienste mit internen Load Balancern exportieren
13.2.2Services auf NodePorts exportieren
13.2.3Integration von externen Maschinen und Kubernetes
13.3Gemeinsame Nutzung von Services zwischen Kubernetes-Clustern
13.4Tools von Drittanbietern
13.5Best Practices für die Verbindung von Cluster und externen Services
13.6Zusammenfassung
14Maschinelles Lernen in Kubernetes ausführen
14.1Warum eignet sich Kubernetes hervorragend für maschinelles Lernen?
14.2Workflow für maschinelles Lernen
14.3Maschinelles Lernen für Kubernetes-Cluster-Administratoren
14.3.1Modell auf Kubernetes trainieren
14.3.2Verteiltes Training auf Kubernetes
14.3.3Ressourcenbeschränkungen
14.3.4Spezialisierte Hardware
14.3.5Bibliotheken, Treiber und Kernel-Module
14.3.6Storage
14.3.7Vernetzung
14.3.8Spezialisierte Protokolle
14.4Tools für Datenwissenschaftler
14.5Best Practices für maschinelles Lernen auf Kubernetes
14.6Zusammenfassung
15Auf Basis von Kubernetes übergeordnete Anwendungs-Patterns erstellen
15.1Ansätze zur Entwicklung von Abstraktionen auf höherer Ebene
15.2Kubernetes erweitern
15.2.1Kubernetes-Cluster erweitern
15.2.2Kubernetes-User Experience erweitern
15.2.3Containerisierte Entwicklung einfacher machen
15.2.4Eine »Push-to-Deploy«-Erfahrung entwickeln
15.3Designüberlegungen beim Erstellen von Plattformen
15.3.1Unterstützung für den Export in ein Container-Image
15.3.2Bestehende Mechanismen für Service und Service Discovery unterstützen
15.4Best Practices für den Aufbau von Anwendungsplattformen
15.5Zusammenfassung
16Status und zustandsbehaftete Anwendungen verwalten
16.1Volumes und Volume Mounts
16.2Best Practices für Volumes
16.3Kubernetes-Datenspeicher
16.3.1PersistentVolume
16.3.2PersistentVolumeClaims
16.3.3StorageClass
16.3.4Best Practices für Kubernetes-Datenspeicher
16.4Zustandsbehaftete Anwendungen
16.4.1StatefulSets
16.4.2Operatoren
16.4.3Best Practices für StatefulSets und Operatoren
16.5Zusammenfassung
17Zugangskontrolle und Autorisierung
17.1Zugangskontrolle
17.1.1Was sind Admission Controller?
17.1.2Warum sind Admission Controller wichtig?
17.1.3Arten von Zugangscontrollern
17.1.4Konfigurieren von Zugangs-Webhooks
17.1.5Best Practices für die Zugangskontrolle
17.2Autorisierung
17.2.1Autorisierungsmodule
17.2.2ABAC
17.2.3RBAC
17.2.4Webhook
17.2.5Best Practices bei der Autorisierung
17.3Zusammenfassung
18GitOps und Bereitstellung
18.1Was ist GitOps?
18.2Warum GitOps?
18.3GitOps Repo-Struktur
18.4Secrets verwalten
18.5Flux einrichten
18.6GitOps-Tools
18.7Best Practices für GitOps
18.8Zusammenfassung
19Sicherheit
19.1Clustersicherheit
19.1.1Zugriff auf etcd
19.1.2Authentifizierung
19.1.3Autorisierung
19.1.4TLS
19.1.5Kubelet und Zugriff auf Cloud-Metadaten
19.1.6Secrets
19.1.7Protokollierung und Überwachung
19.1.8Tools für Cloud Security Posture Management (CSPM)
19.2Best Practices für die Clustersicherheit
19.3Container-Sicherheit auf der Workload-Ebene
19.3.1Pod-Sicherheit
19.3.2Seccomp, AppArmor und SELinux
19.3.3Admission Controller
19.3.4Operatoren
19.3.5Netzwerk Policy
19.3.6Sicherheit der Laufzeitumgebung
19.3.7Best Practices für die Sicherheit von Workload-Containern
19.4Codesicherheit
19.4.1Non-Root und Distroless-Container
19.4.2Container auf Schwachstellen scannen
19.4.3Sicherheit des Code-Repositorys
19.4.4Best Practices für die Codesicherheit
19.5Zusammenfassung
20Chaos Engineering, Lasttests und Experimente
20.1Chaos Engineering
20.1.1Ziele für Chaos Engineering
20.1.2Voraussetzungen für Chaos Engineering
20.1.3Chaosexperiment für die Kommunikation Ihrer Anwendung
20.1.4Chaosexperiment für den Betrieb Ihrer Anwendung
20.1.5Fuzz-Testing Ihrer Anwendung für Sicherheit und Ausfallsicherheit
20.1.6Zusammenfassung
20.2Lasttests
20.2.1Ziele für Lasttests
20.2.2Voraussetzungen für Lasttests
20.2.3Realistischen Traffic generieren
20.2.4Lasttest Ihrer Anwendung
20.2.5Optimieren Sie mit Lasttests Ihre Anwendung
20.2.6Zusammenfassung
20.3Experimente
20.3.1Ziele für Experimente
20.3.2Voraussetzungen für ein Experiment
20.3.3Aufbau eines Experiments
20.3.4Zusammenfassung
20.4Zusammenfassung
21Einen Operator implementieren
21.1Schlüsselkomponenten von Operatoren
21.2Custom Resource Definitions
21.3Unsere API erstellen
21.4Reconciliation: Ist- und Soll-Zustand abgleichen
21.5Ressourcen-Validierung
21.6Controller-Implementierung
21.7Lebenszyklus des Operators
21.7.1Versionsupgrades
21.7.2Best Practices für Operatoren
21.8Zusammenfassung
22Schlussfolgerung
Index
Kubernetes ist der De-facto-Standard für die cloudnative Entwicklung. Es ist ein leistungsstarkes Tool, mit dem sich Ihre nächste Anwendung einfacher entwickeln, schneller bereitstellen und zuverlässiger betreiben lässt. Um die Leistungsfähigkeit von Kubernetes auszuschöpfen, muss es jedoch richtig eingesetzt werden. Dieses Buch richtet sich an alle, die reale Anwendungen auf Kubernetes bereitstellen und daran interessiert sind, Muster und Praktiken zu lernen, die sie für Anwendungen nutzen können, die sich auf Kubernetes aufbauen.
Wichtig ist: Dieses Buch ist keine Einführung in Kubernetes. Wir gehen davon aus, dass Sie mit der Kubernetes-API und den Tools vertraut sind und wissen, wie man einen Kubernetes-Cluster erstellt und mit ihm interagiert. Wenn Sie Kubernetes erlernen möchten, gibt es zahlreiche hervorragende Ressourcen, wie beispielsweise das Buch Kubernetes – Eine kompakte Einführung (ebenfalls im dpunkt.verlag erschienen), das genau das ist, was der Titel verspricht, nämlich eine gute und kompakte Einführung in Kubernetes.
Stattdessen ist dieses Buch eine Ressource für alle, die tiefer in die Bereitstellung bestimmter Anwendungen und Workloads auf Kubernetes eintauchen möchten. Das Buch sollte nützlich für Sie sein, gleichgültig, ob Sie gerade dabei sind, Ihre erste Anwendung auf Kubernetes zu implementieren, oder ob Sie Kubernetes bereits seit Jahren verwenden.
Wir vier haben viel Erfahrung bei der Unterstützung einer Vielzahl von Benutzern, die ihre Anwendungen auf Kubernetes bereitstellen wollen. Wir haben gesehen, wo Menschen Schwierigkeiten haben, und wir haben ihnen geholfen, ihren Weg zum Erfolg zu finden. Beim Schreiben dieses Buches haben wir versucht, diese Erfahrungen festzuhalten, damit noch mehr Menschen Nutzen aus den Lektionen ziehen können, die wir aus diesen realen Erfahrungen gelernt haben. Wir hoffen, dass wir durch die schriftliche Fixierung unserer Erfahrungen unser Wissen skalieren können, damit Sie Ihre Anwendung auf Kubernetes selbstständig erfolgreich bereitstellen und verwalten können.
Obwohl Sie dieses Buch in einer einzigen Sitzung von vorne bis hinten durchlesen könnten, entspricht dies nicht dem, was wir im Hinterkopf hatten. Stattdessen haben wir dieses Buch als Sammlung von einzelnen Kapiteln konzipiert. Jedes Kapitel gibt einen vollständigen Überblick über eine bestimmte Aufgabe, die Sie möglicherweise mit Kubernetes erledigen müssen. Wir gehen davon aus, dass die Leser in das Buch eintauchen, um etwas über ein bestimmtes Thema oder Interesse zu erfahren, dann das Buch in Ruhe lassen, um erst zurückzukehren, wenn ein neues Thema mit neuen Fragen auftaucht.
Trotz dieses eigenständigen Ansatzes ziehen sich einige Themen durch das gesamte Buch. Es gibt mehrere Kapitel über die Entwicklung von Anwendungen auf Kubernetes. Kapitel 2 behandelt Entwickler-Workflows. Kapitel 5 befasst sich mit kontinuierlicher Integration und Tests. Kapitel 15 befasst sich mit dem Aufbau übergeordneter Plattformen auf Kubernetes, und Kapitel 16 behandelt die Verwaltung von Zuständen und zustandsbehafteten Anwendungen. Neben der Entwicklung von Anwendungen gibt es auch mehrere Kapitel zum Betrieb von Services in Kubernetes. Kapitel 1 befasst sich mit der Einrichtung eines grundlegenden Service, und Kapitel 3 behandelt die Überwachung und Metriken. Kapitel 4 befasst sich mit der Konfigurationsverwaltung und Kapitel 6 mit der Versionierung und den Releases. Kapitel 7 befasst sich mit der globalen Bereitstellung Ihrer Anwendung.
Es gibt auch mehrere Kapitel über die Clusterverwaltung, darunter Kapitel 8 über die Ressourcenverwaltung, Kapitel 9 über die Vernetzung, Kapitel 10 über die Pod-Sicherheit, Kapitel 11 über Policy und Governance, Kapitel 12 über die Verwaltung mehrerer Cluster und Kapitel 17 über die Zugangskontrolle und Autorisierung. Schließlich sind einige Kapitel wirklich unabhängig; diese behandeln maschinelles Lernen (Kap. 14) und die Integration mit externen Services (Kap. 13).
Obwohl es nützlich sein kann, alle Kapitel zu lesen, bevor Sie das Thema in der Praxis anwenden, hoffen wir, dass Sie dieses Buch als Nachschlagewerk betrachten. Es soll Ihnen als Leitfaden dienen, wenn Sie diese Themen in der Praxis umsetzen.
Wir haben die erste Auflage dieses Buches um vier neue Kapitel erweitert, die sich mit neuen Tools und Mustern befassen, während sich Kubernetes weiterentwickelt. Diese neuen Kapitel sind Kapitel 18 über GitOps, Kapitel 19 über Sicherheit, Kapitel 20 über Chaosexperimente und andere Testverfahren sowie Kapitel 21, in dem es um die Implementierung eines Operators geht.
Die folgenden typografischen Konventionen werden in diesem Buch genutzt:
Kursiv
Für neue Begriffe, URLs, E-Mail-Adressen, Dateinamen und Dateierweiterungen.
Nichtproportionalschrift
Für Programmlistings, aber auch für Codefragmente in Absätzen, wie etwa Variablen- oder Funktionsnamen, Datenbanken, Datentypen, Umgebungsvariablen, Anweisungen und Schlüsselwörter.
fette Nichtproportionalschrift
Für Befehle und anderen Text, der genau so vom Benutzer eingegeben werden sollte.
kursive Nichtproportionalschrift
Für Text, der vom Benutzer durch eigene Werte ersetzt werden sollte.
Tipp
Dieses Element steht für einen Tipp oder eine Anregung.
Hinweis
Dieses Element kennzeichnet einen allgemeinen Hinweis.
Achtung
Dieses Element kennzeichnet eine Warnung oder einen Warnhinweis.
Zusätzliches Material (Codebeispiele, Übungen usw.) steht zum Herunterladen unter https://oreil.ly/KBPsample bereit.
Dieses Buch ist dazu da, Ihnen beim Erledigen Ihrer Arbeit zu helfen. Im Allgemeinen dürfen Sie die Codebeispiele aus diesem Buch in Ihren eigenen Programmen und der dazugehörigen Dokumentation verwenden. Sie müssen uns dazu nicht um Erlaubnis fragen, solange Sie nicht einen beträchtlichen Teil des Codes reproduzieren. Beispielsweise benötigen Sie keine Erlaubnis, um ein Programm zu schreiben, in dem mehrere Codefragmente aus diesem Buch vorkommen. Wollen Sie dagegen einen Datenträger mit Beispielen aus Büchern von der dpunkt.verlag GmbH verkaufen oder verteilen, benötigen Sie eine Erlaubnis. Eine Frage zu beantworten, indem Sie aus diesem Buch zitieren und ein Codebeispiel wiedergeben, benötigt keine Erlaubnis. Eine beträchtliche Menge Beispielcode aus diesem Buch in die Dokumentation Ihres Produkts aufzunehmen, bedarf hingegen einer Erlaubnis.
Wir freuen uns über Zitate, verlangen diese aber nicht. Ein Zitat enthält Titel, Autor, Verlag und ISBN. Beispiel: »Kubernetes Best Practices von Brendan Burns, Eddie Villalba, Dave Strebel und Lachlan Evenson. Copyright 2024 dpunkt.verlag GmbH, 978-3-98889-027-6.«
Wenn Sie glauben, dass Ihre Verwendung von Codebeispielen über die übliche Nutzung hinausgeht oder außerhalb der oben beschriebenen Nutzungsbedingungen liegt, kontaktieren Sie uns bitte unter [email protected].
Mit Anmerkungen, Fragen oder Verbesserungsvorschlägen zu diesem Buch können Sie sich jederzeit an den Verlag wenden:
Bitte beachten Sie, dass über unsere E-Mail-Adresse kein Software-Support angeboten wird.
Brendan möchte sich bei seiner wunderbaren Familie, Robin, Julia und Ethan, für die Liebe und Unterstützung bei allem, was er tut, bedanken; bei der Kubernetes-Community, ohne die nichts von alledem möglich wäre; und bei seinen fabelhaften Co-Autoren, ohne die dieses Buch nicht existieren würde.
Dave möchte sich bei seiner wunderschönen Frau Jen und seinen drei Kindern Max, Maddie und Mason für ihre Unterstützung bedanken. Er möchte sich auch bei der Kubernetes-Community für all die Ratschläge und die Hilfe bedanken, die sie im Laufe der Jahre geleistet haben. Schließlich möchte er seinen Mitautoren dafür danken, dass sie dieses Abenteuer in die Tat umgesetzt haben.
Lachlan möchte sich bei seiner Frau und seinen drei Kindern für ihre Liebe und Unterstützung bedanken. Er möchte sich auch bei allen Mitgliedern der Kubernetes-Community bedanken, einschließlich der wunderbaren Menschen, die sich die Zeit genommen haben, ihn im Laufe der Jahre zu unterrichten. Ein besonderer Dank geht an Joseph Sandoval für seine Mentorenschaft. Und schließlich möchte er seinen fantastischen Mitautoren dafür danken, dass sie dieses Buch möglich gemacht haben.
Eddie möchte sich bei seiner Frau Sandra für ihre unermüdliche Unterstützung, Liebe und Ermutigung während des Schreibprozesses bedanken. Er möchte auch seiner Tochter Giavanna dafür danken, dass sie ihn motiviert hat, ein Vermächtnis zu hinterlassen, damit sie stolz auf ihren Vater sein kann. Schließlich möchte er sich bei der Kubernetes-Community und seinen Co-Autoren bedanken, die ihm auf seinem Weg, cloudnativ zu werden, immer ein Wegweiser waren.
Wir alle möchten Virginia Wilson für ihre Arbeit bei der Entwicklung des Manuskripts und ihre Hilfe bei der Zusammenführung all unserer Ideen danken und Jill Leonard für ihre Anleitung bei der 2. Ausgabe. Schließlich möchten wir Bridget Kromhout, Bilgin Ibryam, Roland Huß, Justin Domingus, Jess Males und Jonathan Johnson für ihre Aufmerksamkeit beim Feinschliff danken.
Dieses Kapitel beschreibt das Verfahren zum Einrichten einer einfachen mehrschichtigen Anwendung in Kubernetes. Das Beispiel, das wir durchgehen werden, besteht aus zwei Schichten: einer einfachen Webanwendung und einer Datenbank. Auch wenn es sich hierbei nicht um die komplizierteste Anwendung handelt, ist es ein guter Ausgangspunkt, um die Verwaltung einer Anwendung in Kubernetes zu erlernen.
Die Anwendung für unser Beispiel ist recht simpel. Es handelt sich um einen einfachen Journaldienst, der folgende Details aufweist:
Die Anwendung hat einen separaten statischen Dateiserver, der NGINX verwendet.
Sie verfügt über ein RESTful Application Programming Interface (API)
https://some-host-name.io/api
im Pfad
/api
.
Sie besitzt auf der Haupt-URL
https://some-host-name.io
einen Dateiserver.
Sie verwendet den Dienst Let’s Encrypt (
https://letsencrypt.org
) für die Verwaltung von Secure Sockets Layer (SSL).
Abbildung 1–1 zeigt ein Diagramm dieser Anwendung. Machen Sie sich keine Sorgen, wenn Sie nicht auf Anhieb alle einzelnen Teile verstehen; sie werden im Laufe des Kapitels detaillierter erklärt. Wir werden den Aufbau dieser Anwendung Schritt für Schritt durchgehen, und verwenden zunächst YAML-Konfigurationsdateien und dann Helm-Charts.
Abb. 1–1Ein Diagramm unseres Journaldienstes, wie er in Kubernetes implementiert ist
Bevor wir uns im Detail mit dem Aufbau dieser Anwendung in Kubernetes befassen, sollten wir beschreiben, wie wir die Konfigurationen selbst verwalten. Bei Kubernetes wird alles deklarativ repräsentiert. Das bedeutet, dass Sie den gewünschten Zustand der Anwendung im Cluster aufschreiben (im Allgemeinen in YAML- oder JSON-Dateien). Diese deklarierten gewünschten Zustände definieren alle Teile Ihrer Anwendung. Dieser deklarative Ansatz ist einem imperativen Ansatz vorzuziehen, bei dem der Zustand Ihres Clusters die Summe einer Reihe von Änderungen am Cluster ist. Wenn ein Cluster imperativ konfiguriert ist, ist es schwierig zu verstehen und zu reproduzieren, wie er in diesen Zustand gekommen ist. Hierdurch ist es dann auch schwierig, Probleme mit Ihrer Anwendung zu verstehen oder zu beheben.
Bei der Deklaration des Zustands Ihrer Anwendung wird in der Regel YAML gegenüber JSON bevorzugt, obwohl Kubernetes beide Formate unterstützt. Der Grund dafür ist, dass YAML etwas weniger ausführlich ist und von Menschen besser bearbeitet werden kann als JSON. Es ist jedoch zu beachten, dass YAML auf Einrückungen reagiert; häufig lassen sich Fehler in Kubernetes-Konfigurationen auf eine falsche Einrückung in YAML zurückführen. Wenn sich die Dinge nicht wie erwartet verhalten, ist die Überprüfung der Einrückungen ein guter Ansatzpunkt für die Fehlersuche. Die meisten Editoren unterstützen die Syntaxhervorhebung sowohl für JSON als auch für YAML. Wenn Sie mit diesen Dateien arbeiten, ist es eine gute Idee, solche Tools zu installieren, um in Ihren Konfigurationen sowohl Autoren- als auch Dateifehler leichter zu finden. Es gibt auch eine hervorragende Erweiterung für Visual Studio Code, die eine umfassendere Fehlerprüfung für Kubernetes-Dateien unterstützt.
Da der deklarative Zustand, der in diesen YAML-Dateien enthalten ist, als Source of Truth für Ihre Anwendung dient, ist die korrekte Verwaltung dieses Zustands entscheidend für den Erfolg Ihrer Anwendung. Wenn Sie den gewünschten Zustand Ihrer Anwendung ändern, müssen Sie in der Lage sein, die Änderungen zu verwalten, zu überprüfen, ob sie korrekt sind, zu kontrollieren, wer die Änderungen vorgenommen hat, und im Falle eines Fehlers die Änderungen zurückzunehmen. Glücklicherweise haben wir im Rahmen der Softwareentwicklung bereits die notwendigen Werkzeuge entwickelt, um sowohl Änderungen am deklarativen Zustand als auch die Prüfung und das Rollback zu verwalten. Die bewährten Verfahren zur Versionsverwaltung und für Code-Reviews lassen sich direkt auf die Verwaltung des deklarativen Zustands Ihrer Anwendung anwenden.
Heutzutage speichern die meisten Leute ihre Kubernetes-Konfigurationen in Git. Auch wenn die spezifischen Details des Versionskontrollsystems unwichtig sind, erwarten viele Tools im Kubernetes-Ökosystem Dateien in einem Git-Repository. Beim Code-Review gibt es eine viel größere Heterogenität; obwohl GitHub offensichtlich sehr beliebt ist, verwenden andere On-Premises-Tools oder Dienste für das Code-Review. Unabhängig davon, wie Sie das Code-Review für die Konfiguration Ihrer Anwendung implementieren, sollten Sie sie mit der gleichen Sorgfalt und Konzentration behandeln, die Sie für die Verwaltung des Quellcodes anwenden.
Wenn es darum geht, das Dateisystem für Ihre Anwendung einzurichten, lohnt es sich, die mit dem Dateisystem gelieferte Verzeichnisstruktur zu verwenden, um Ihre Komponenten zu organisieren. In der Regel wird ein einzelnes Verzeichnis genutzt, um einen Anwendungsdienst zusammenzufassen. Die Definition dessen, was einen Anwendungsdienst ausmacht, kann von Team zu Team unterschiedlich sein, aber im Allgemeinen handelt es sich um einen Service, der von einem Team aus 8 bis 12 Personen entwickelt wird. Innerhalb dieses Verzeichnisses werden Unterverzeichnisse für Unterkomponenten der Anwendung eingesetzt.
Für unsere Anwendung legen wir die Verzeichnisstruktur für die Dateien wie folgt an:
journal/
frontend/
redis/
fileserver/
In jedem Verzeichnis befinden sich die konkreten YAML-Dateien, die zur Definition des Service benötigt werden. Wie Sie später sehen werden, wird dieses Dateilayout komplizierter, wenn wir beginnen, unsere Anwendung in mehreren verschiedenen Regionen oder Clustern einzusetzen.
Um unsere Anwendung zu beschreiben, beginnen wir mit dem Frontend und arbeiten uns nach unten vor. Die Frontend-Anwendung für das Journal ist eine in TypeScript implementierte Node.js-Anwendung. Die komplette Anwendung ist zu umfangreich, um sie in das Buch aufzunehmen, daher haben wir sie auf unseren GitHub hochgeladen (https://github.com/brendandburns/kbp-sample). Dort finden Sie auch den Code für zukünftige Beispiele, es lohnt sich also, für diese URL ein Lesezeichen zu setzen. Die Anwendung stellt einen HTTP-Dienst auf Port 8080 bereit, der Anfragen an den Pfad /api/* weiterleitet und das Redis-Backend verwendet, um die aktuellen Journaleinträge hinzuzufügen, zu löschen oder zurückzugeben. Wenn Sie die folgenden YAML-Beispiele auf Ihrem lokalen Rechner durcharbeiten möchten, sollten Sie diese Anwendung mithilfe des Dockerfiles in ein Container-Image erstellen und in Ihr eigenes Image-Repository pushen. Anstatt den Namen unserer Beispieldatei zu nehmen, sollten Sie dann in Ihren Code den Namen Ihres Container-Image aufnehmen.
Obwohl die Erstellung und Pflege von Container-Images im Allgemeinen den Rahmen dieses Buches sprengen würde, lohnt es sich, einige allgemeine Best Practices für die Erstellung und Benennung von Images aufzuzeigen. Im Allgemeinen kann der Image-Erstellungsprozess für »Supply-Chain-Angriffe« anfällig sein. Bei solchen Angriffen injiziert ein böswilliger Benutzer Code oder Binärdateien in eine Abhängigkeit von einer vertrauenswürdigen Quelle, die dann in Ihre Anwendung eingebaut wird. Aufgrund des Risikos solcher Angriffe ist es wichtig, dass Sie bei der Erstellung Ihrer Images nur auf bekannte und vertrauenswürdige Image-Anbieter zurückgreifen. Alternativ können Sie auch alle Ihre Images von Grund auf neu erstellen. Bei einigen Sprachen (z. B. Go), die statische Binärdateien erstellen können, ist die Erstellung von Grund auf einfach, bei interpretierten Sprachen wie Python, JavaScript oder Ruby ist dies jedoch wesentlich komplizierter.
Die anderen Best Practices für Images beziehen sich auf die Namensgebung. Obwohl die Version eines Container-Images in einer Image-Registry theoretisch veränderbar ist, sollten Sie das Versions-Tag als unveränderbar behandeln. Insbesondere ist eine Kombination aus der semantischen Version und dem SHA-Hash des Commits, in dem das Image erstellt wurde, eine gute Praxis für die Benennung von Images (z. B. v1.0.1-bfeda01f). Wenn Sie keine Image-Version angeben, wird standardmäßig latest verwendet. Obwohl dies in der Entwicklung praktisch sein kann, ist es eine schlechte Idee für den produktiven Einsatz, da latest jedes Mal, wenn ein neues Image erstellt wird, verändert wird.
Unsere Frontend-Anwendung ist zustandslos; sie verlässt sich in Bezug auf ihren Zustand vollständig auf das Redis-Backend. Daher können wir sie beliebig replizieren, ohne den Datenverkehr zu beeinträchtigen. Obwohl es unwahrscheinlich ist, dass unsere Anwendung in großem Umfang genutzt wird, ist es dennoch eine gute Idee, mit mindestens zwei Replikaten zu arbeiten, damit Sie einen unerwarteten Absturz bewältigen oder eine neue Version der Anwendung ohne Ausfallzeiten ausrollen können.
In Kubernetes ist die ReplicaSet-Ressource diejenige, die direkt die Replikation einer bestimmten Version Ihrer containerisierten Anwendung verwaltet. Da sich die Version aller Anwendungen im Laufe der Zeit ändert, wenn Sie den Code ändern, ist es keine optimale Methode, direkt ein ReplicaSet zu verwenden. Stattdessen nutzen Sie die Ressource Deployment. Ein Deployment kombiniert die Replikationsfunktionen von ReplicaSet mit der Versionierung und der Möglichkeit, ein gestuftes Rollout durchzuführen. Durch ein Deployment können Sie die in Kubernetes integrierten Werkzeuge nutzen, um von einer Version der Anwendung zur nächsten zu wechseln.
Die Kubernetes-Deployment-Ressource für unsere Anwendung sieht wie folgt aus:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
# Alle Pods im Deployment verwenden dieses Label
app: frontend
name: frontend
namespace: default
spec:
# Wir sollten aus Gründen der Zuverlässigkeit immer mindestens zwei Replikate einsetzen
replicas: 2
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- image: my-repo/journal-server:v1-abcde
imagePullPolicy: IfNotPresent
name: frontend
# TODO: Ermitteln, wie hoch der tatsächliche Ressourcenbedarf ist
resources:
request:
cpu:"1.0"
memory:"1G"
limits:
cpu:"1.0"
memory:"1G"
Bei diesem Deployment sind mehrere Dinge zu beachten. Erstens verwenden wir Labels, um das Deployment sowie die ReplicaSets und die Pods zu identifizieren, die das Deployment erstellt. Wir haben das Label app: frontend zu all diesen Ressourcen hinzugefügt, damit wir alle Ressourcen für eine bestimmte Schicht in einer einzigen Anfrage untersuchen können. Sie werden sehen, dass wir beim Hinzufügen weiterer Ressourcen genauso vorgehen werden.
Außerdem haben wir an einigen Stellen in das YAML Kommentare eingefügt. Obwohl diese Kommentare nicht in die auf dem Server gespeicherte Kubernetes-Ressource einfließen, dienen sie, genau wie Kommentare im Code, als Orientierungshilfe für Personen, die sich diese Konfiguration zum ersten Mal ansehen.
Sie sollten weiterhin beachten, dass wir für die Container im Deployment für die Ressourcenanforderungen sowohl Request als auch Limit angegeben und für Request und Limit die gleichen Werte verwendet haben. Wenn eine Anwendung ausgeführt wird, ist Request (Anforderung) die Reservierung, die auf dem Host-Rechner, auf dem sie läuft, garantiert wird. Limit (Obergrenze) ist die maximale Ressourcennutzung, die dem Container zugestanden wird. Wenn Sie am Anfang Request gleich Limit setzen, führt dies zu einem möglichst vorhersehbaren Verhalten Ihrer Anwendung. Diese Vorhersagbarkeit geht jedoch zulasten der Ressourcennutzung. Da die Einstellung Request gleich Limit Ihre Anwendungen daran hindert, zu viel Zeit einzuplanen oder zu viele ungenutzte Ressourcen zu verbrauchen, werden Sie nicht in der Lage sein, eine maximale Auslastung zu erreichen, es sei denn, Sie stimmen Request und Limit sehr, sehr sorgfältig aufeinander ab. Wenn Sie das Kubernetes-Ressourcen-modell besser verstehen, können Sie Request und Limit für Ihre Anwendung unabhängig voneinander anpassen. Die meisten Benutzer vertreten jedoch die Auffassung, dass eine stabile Vorhersagbarkeit die geringere Ressourcenausnutzung wert ist.
Wie unser Kommentar andeutet, ist es oft schwierig, die richtigen Werte für diese Ressourcenobergrenzen zu ermitteln. Ein guter Ansatz ist, die Schätzungen zunächst zu hoch anzusetzen und sie dann mithilfe des Monitorings auf die richtigen Werte abzustimmen. Wenn Sie jedoch einen neuen Service einführen, sollten Sie bedenken, dass Ihr Ressourcenbedarf beim ersten großen Datenverkehr wahrscheinlich erheblich ansteigen wird. Darüber hinaus gibt es einige Sprachen, insbesondere Sprachen mit Garbage Collection, die munter den gesamten verfügbaren Speicher verbrauchen, was es schwierig machen kann, das richtige Minimum für den Speicher zu bestimmen. In diesem Fall kann eine Form der binären Suche notwendig sein, aber denken Sie daran, dies in einer Testumgebung zu tun, damit es sich nicht auf Ihre Produktivumgebung auswirkt!
Nachdem wir nun die Deployment-Ressource definiert haben, checken wir sie in die Versionskontrolle ein und stellen sie in Kubernetes bereit:
git add frontend/deployment.yaml
git commit -m "Deployment hinzugefügt" frontend/deployment.yaml
kubectl apply -f frontend/deployment.yaml
Es ist ebenfalls eine Best Practice, sicherzustellen, dass der Inhalt Ihres Clusters genau mit dem Inhalt Ihrer Quellcodeverwaltung übereinstimmt. Das beste Muster dafür ist die Verwendung eines GitOps-Ansatzes und die Bereitstellung für die Produktion nur von einem bestimmten Zweig Ihrer Versionsverwaltung aus vorzunehmen. Hierzu nutzen Sie die Automatisierung der kontinuierlichen Integration/kontinuierlichen Bereitstellung (CI/CD). So ist gewährleistet, dass Quellcodeverwaltung und Produktion übereinstimmen. Obwohl eine vollständige CI/CD-Pipeline für eine einfache Anwendung übertrieben erscheinen mag, ist die Automatisierung an sich, unabhängig von der Zuverlässigkeit, die sie bietet, in der Regel den Zeitaufwand für ihre Einrichtung wert. Außerdem lässt sich CI/CD im Nachhinein nur sehr schwer in eine bestehende, imperativ bereitgestellte Anwendung einbauen.
Auf diese mit YAML erstellte Anwendungsbeschreibung werden wir in späteren Abschnitten zurückkommen, um weitere Elemente wie die ConfigMap und Secrets-Volumes sowie die Pod Quality of Service zu untersuchen.
Die Container für unsere Anwendung sind zwar jetzt bereitgestellt, aber es ist derzeit noch nicht möglich, dass jemand auf die Anwendung zugreift. Standardmäßig sind die Cluster-Ressourcen nur innerhalb des Clusters selbst verfügbar. Um unsere Anwendung der Außenwelt zugänglich zu machen, müssen wir einen Service und einen Load Balancer erstellen, um eine externe IP-Adresse bereitzustellen und den Datenverkehr zu unseren Containern zu leiten. Für die externe Bereitstellung werden wir zwei Kubernetes-Ressourcen verwenden.
Die erste Ressource ist ein Service, der für einen Lastausgleich des Datenverkehrs des Transmission Control Protocol (TCP) oder des User Datagram Protocol (UDP) sorgt. In unserem Fall nutzen wir das TCP-Protokoll. Die zweite ist eine Ingress-Ressource, die einen HTTP(S)-Lastausgleich mit intelligentem Routing von Anfragen auf der Grundlage von HTTP-Pfaden und Hosts bietet. Bei einer einfachen Anwendung wie dieser werden Sie sich vielleicht fragen, warum wir die komplexere Ingress-Ressource verwenden, aber wie Sie in späteren Abschnitten sehen werden, werden selbst bei dieser einfachen Anwendung HTTP-Anfragen von zwei verschiedenen Diensten bedient. Darüber hinaus bietet ein Ingress an der Schnittstelle nach außen die Flexibilität, die für zukünftige Erweiterungen unseres Dienstes erforderlich ist.
Hinweis
Die Ingress-Ressource ist eine der älteren Ressourcen in Kubernetes, und im Laufe der Jahre wurden zahlreiche Probleme mit der Art und Weise, wie sie den HTTP-Zugriff auf Microservices modelliert, gelöst. Dies hat zur Entwicklung der Gateway-API für Kubernetes geführt. Die Gateway-API wurde als Erweiterung für Kubernetes entwickelt und erfordert in Ihrem Cluster die Installation zusätzlicher Komponenten. Wenn Sie feststellen, dass Ingress Ihre Anforderungen nicht erfüllt, sollten Sie in Erwägung ziehen, zur Gateway-API zu wechseln.
Bevor die Ingress-Ressource definiert werden kann, muss ein Kubernetes-Service vorhanden sein, auf den der Ingress verweisen kann. Wir werden Labels verwenden, um den Dienst zu den Pods zu leiten, die wir im vorherigen Abschnitt erstellt haben. Der Service ist wesentlich einfacher zu definieren als das Deployment und sieht wie folgt aus:
apiVersion: v1
kind: Service
metadata:
labels:
app: frontend
name: frontend
namespace: default
spec:
ports:
- port: 8080
protocol: TCP
targetPort: 8080
selector:
app: frontend
type: ClusterIP
Nachdem Sie den Service definiert haben, können Sie eine Ingress-Ressource definieren. Im Gegensatz zu Service-Ressourcen muss für Ingress im Cluster ein Ingress-Controller-Container ausgeführt werden. Sie haben die Wahl zwischen verschiedenen Implementierungen, die entweder von Ihrem Cloud-Provider angeboten oder mit Open-Source-Servern implementiert werden. Wenn Sie sich für die Installation eines Open-Source-Ingress-Anbieters entscheiden, ist es eine gute Idee, den Helm-Paketmanager (https://helm.sh) zu verwenden, um ihn zu installieren und zu warten. Die Ingress-Provider nginx oder haproxy sind beliebte Optionen:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: frontend-ingress
spec:
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test
port:
number: 8080
Nachdem unsere Ingress-Ressource erstellt wurde, ist unsere Anwendung bereit, den Datenverkehr von Webbrowsern aus der ganzen Welt zu bedienen. Als Nächstes werden wir uns ansehen, wie Sie Ihre Anwendung für eine einfache Konfiguration und Anpassung einrichten können.
Jede Anwendung benötigt ein gewisses Maß an Konfiguration. Dabei kann es sich um die Anzahl der pro Seite anzuzeigenden Journaleinträge, die Farbe eines bestimmten Hintergrunds, eine spezielle Darstellung bei Feiertagen oder viele andere Arten der Konfiguration handeln. In der Regel ist es am besten, solche Konfigurationsinformationen von der Anwendung selbst zu trennen.
Für diese Trennung gibt es mehrere gute Gründe. Der erste Grund ist, dass Sie vielleicht dieselbe Binärdatei der Anwendung je nach Umgebung unterschiedlich konfigurieren möchten. In Europa möchten Sie vielleicht ein Osterspecial hervorheben, während Sie in China vielleicht ein Special zum chinesischen Neujahr anzeigen möchten. Neben dieser Spezialisierung auf bestimmte Umgebungen gibt es auch im Hinblick auf die Agilität Gründe für diese Trennung. Wenn Sie diese Funktionen per Code aktivieren, besteht die einzige Möglichkeit, die aktiven Funktionen zu ändern, darin, eine neue Binärdatei zu erstellen und zu veröffentlichen, was ein teurer und langsamer Prozess sein kann.
Die Verwendung der Konfiguration zur Aktivierung einer Reihe von Funktionen bedeutet, dass Sie Funktionen schnell (und sogar dynamisch) aktivieren und deaktivieren können, um auf Benutzeranforderungen oder Fehler im Anwendungscode zu reagieren. Features können so einzeln ein- und wieder ausgeschaltet werden. Diese Flexibilität stellt sicher, dass Sie mit den meisten Features kontinuierlich Fortschritte machen, selbst wenn einige zurückgesetzt werden müssen, um Leistungsprobleme oder Fehler zu beheben.
In Kubernetes wird diese Art von Konfiguration durch eine Ressource namens ConfigMap repräsentiert. Eine ConfigMap enthält mehrere Schlüssel/Wert-Paare, die Konfigurationsinformationen oder eine Datei darstellen. Diese Konfigurationsinformationen können einem Container in einem Pod entweder über Dateien oder Umgebungsvariablen zur Verfügung gestellt werden. Stellen Sie sich vor, Sie möchten, dass Ihre Online-Journalanwendung eine konfigurierbare Anzahl von Journaleinträgen pro Seite anzeigt. Um dies zu erreichen, können Sie eine ConfigMap wie folgt definieren:
kubectl create configmap frontend-config --from-literal=journalEntries=10
Um Ihre Anwendung zu konfigurieren, legen Sie die Konfigurationsinformationen als Umgebungsvariable in der Anwendung selbst offen. Dazu können Sie der Container-Ressource im Deployment, das Sie zuvor definiert haben, Folgendes hinzufügen:
...
# Das Array Containers im PodTemplate innerhalb des Deployments
containers:
- name: frontend
...
env:
- name: JOURNAL_ENTRIES
valueFrom:
configMapKeyRef:
name: frontend-config
key: journalEntries
...
Obwohl hier gezeigt wird, wie Sie eine ConfigMap zur Konfiguration Ihrer Anwendung verwenden können, werden Sie in der realen Welt von Deployments regelmäßige Änderungen an dieser Konfiguration vornehmen wollen, zumindest wöchentlich. Es mag verlockend sein, diese Änderungen einfach in der ConfigMap selbst vorzunehmen, aber das ist aus verschiedenen Gründen keine Best Practice: Erstens löst eine Änderung der Konfiguration keine Aktualisierung der bestehenden Pods aus. Die Konfiguration wird erst beim Neustart des Pods übernommen. Daher ist der Rollout nicht auf bestimmte Dienstprobleme hin ausgerichtet und kann ad hoc oder zufällig erfolgen. Ein weiterer Grund ist, dass die einzige Versionierung für die ConfigMap in Ihrer Versionsverwaltung liegt und es sehr schwierig sein kann, ein Rollback durchzuführen.
Ein besserer Ansatz ist es, in den Namen der ConfigMap eine Versionsnummer aufzunehmen. Anstatt die Map frontend-config zu nennen, nennen Sie sie frontend-config-v1. Wenn Sie eine Änderung vornehmen möchten, erstellen Sie eine neue ConfigMap v2 und aktualisieren die Deployment-Ressource, um diese Konfiguration zu verwenden, anstatt die ConfigMap selbst zu aktualisieren. Wenn Sie dies tun, wird automatisch ein Deployment-Rollout ausgelöst, bei dem die entsprechenden Zustandsprüfungen und Pausen zwischen den Änderungen durchgeführt werden. Sollten Sie jemals ein Rollback durchführen müssen, befindet sich die v1-Konfiguration im Cluster, und das Rollback ist einfach, da Sie dann lediglich eine erneute Aktualisierung des Deployments durchführen müssen.
Bisher ging es noch nicht wirklich um den Redis-Dienst, mit dem unser Frontend verbunden ist. Aber in jeder echten Anwendung müssen wir die Verbindungen zwischen unseren Diensten sichern. Zum einen, um den Schutz der Benutzer und ihrer Daten zu gewährleisten, und zum anderen ist es wichtig, Fehler zu vermeiden, wie z. B. ein Entwicklungs-Frontend mit einer Produktivdatenbank zu verbinden.
Die Redis-Datenbank wird mit einem einfachen Passwort authentifiziert. Es mag bequem erscheinen, dieses Passwort im Quellcode Ihrer Anwendung oder in einer Datei in Ihrem Image zu speichern, aber beides ist aus verschiedenen Gründen keine gute Idee. Der erste Grund ist, dass Sie Ihr Secret (das Kennwort) in einer Umgebung öffentlich zugänglich machen, in der Sie nicht unbedingt an Zugriffssteuerung denken. Wenn Sie ein Kennwort in Ihre Quellcodeverwaltung einbauen, dann hat jeder, der Zugriff auf Ihren Quellcode hat, gleichzeitig auch Zugriff auf alle Secrets. Das ist nicht die beste Vorgehensweise, da Sie wahrscheinlich eine größere Anzahl von Benutzern haben werden, die auf Ihren Quellcode zugreifen können, als die, die Zugriff auf Ihre Redis-Instanz haben sollten. Außerdem sollte jemand, der Zugriff auf Ihr Container-Image hat, nicht unbedingt Zugriff auf Ihre Produktivdatenbank haben.
Neben den Bedenken hinsichtlich der Zugriffskontrolle ist ein weiterer Grund, Secrets nicht an die Versionsverwaltung und/oder Images zu binden, die Parametrisierung. Sie möchten in der Lage sein, denselben Quellcode und dieselben Images in verschiedenen Umgebungen (z. B. Entwicklung, Canary und Produktion) zu verwenden. Wenn die Secrets im Quellcode oder in einem Image fest verdrahtet sind, benötigen Sie für jede Umgebung ein anderes Image (oder einen anderen Code).
Nachdem Sie im vorherigen Abschnitt die ConfigMaps kennengelernt haben, könnten Sie sofort denken, dass das Kennwort als Konfiguration gespeichert und dann als anwendungsspezifische Konfiguration in die Anwendung eingefügt werden könnte. Sie haben völlig recht, wenn Sie glauben, dass die Trennung von Konfiguration und Anwendung dasselbe ist wie die Trennung von Secrets und Anwendung. Aber in Wahrheit ist ein Secret ein wichtiges Konzept für sich. Wahrscheinlich möchten Sie die Zugriffskontrolle, die Handhabung und die Aktualisierung von Secrets anders handhaben als die einer Konfiguration. Noch wichtiger ist, dass Sie möchten, dass Ihre Entwickler beim Zugriff auf Secrets anders denken als beim Zugriff auf Konfigurationen. Aus diesen Gründen verfügt Kubernetes über eine integrierte Secret-Ressource für die Verwaltung von geheimen, vertraulichen Daten.
Sie können für Ihre Redis-Datenbank wie folgt ein geheimes Kennwort erstellen:
kubectl create secret generic redis-passwd --from-literal=passwd=${RANDOM}
Es liegt auf der Hand, dass Sie für Ihr Kennwort etwas anderes als eine Zufallszahl nehmen möchten. Außerdem möchten Sie wahrscheinlich einen Service zur Verwaltung von Geheimnissen/Schlüsseln einsetzen, entweder über Ihren Cloud-Anbieter, wie Microsoft Azure Key Vault, oder ein Open-Source-Projekt, wie Vault von Hashi-Corp. Ein Schlüsselverwaltungsdienst verfügt in der Regel über eine engere Integration mit Kubernetes-Secrets.
Nachdem Sie das Redis-Kennwort als Secret in Kubernetes gespeichert haben, müssen Sie dieses Secret an die laufende Anwendung binden, wenn diese in Kubernetes bereitgestellt wird. Hierfür können Sie ein Kubernetes-Volume verwenden. Ein Volume ist eine Datei oder ein Verzeichnis, das in einen laufenden Container an einem benutzerdefinierten Ort eingebunden werden kann. Im Falle von Secrets wird das Volume als tmpfs-RAM-gestütztes Dateisystem erstellt und dann in den Container eingehängt. Dadurch ist es für einen Angreifer sehr viel schwieriger, an die Secrets heranzukommen, selbst wenn der Rechner physisch kompromittiert wird (was in der Cloud recht unwahrscheinlich, im Rechenzentrum jedoch möglich ist).
Hinweis
In Kubernetes werden Secrets standardmäßig unverschlüsselt gespeichert. Wenn Sie Secrets verschlüsselt speichern möchten, können Sie mit einem Schlüsselanbieter zusammenarbeiten, der Ihnen einen Schlüssel liefert, den Kubernetes zur Verschlüsselung aller Secrets im Cluster verwendet. Beachten Sie, dass dies zwar die Schlüssel gegen direkte Angriffe auf die etcd-Datenbank schützt, der Zugriff über den Kubernetes-API-Server aber dennoch ordnungsgemäß gesichert sein muss.
Um einem Deployment ein Volume für Secrets hinzuzufügen, müssen Sie zwei neue Einträge in der YAML für das Deployment angeben. Der erste ist ein Volume-Eintrag für den Pod, der das Volume zum Pod hinzufügt:
...
volumes:
- name: passwd-volume
secret:
secretName: redis-passwd
Mit CSI-Treibern (Container Storage Interface) können Sie Schlüsselverwaltungssysteme (Key Management System, KMS) verwenden, die sich außerhalb Ihres Kubernetes-Clusters befinden. Dies ist häufig eine Anforderung für die Einhaltung von Vorschriften und die Sicherheit in großen oder regulierten Organisationen. Wenn Sie einen dieser CSI-Treiber verwenden, würde Ihr Volume stattdessen wie folgt aussehen:
...
volumes:
- name: passwd-volume
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass:"azure-sync"
...
Unabhängig von der Methode müssen Sie das im Pod definierte Volume in einen bestimmten Container mounten. Dies geschieht über das Feld volumeMounts in der Container-Beschreibung:
...
volumeMounts:
- name: passwd-volume
readOnly: true
mountPath:"/etc/redis-passwd"
...
Dadurch wird das Volume mit den Secrets in das Verzeichnis redis-passwd eingebunden und der Client-Code kann dann darauf zugreifen. Baut man dies alles zusammen, erhält man das folgende vollständige Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: frontend
name: frontend
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- image: my-repo/journal-server:v1-abcde
imagePullPolicy: IfNotPresent
name: frontend
volumeMounts:
- name: passwd-volume
readOnly: true
mountPath:"/etc/redis-passwd"
resources:
request:
cpu:"1.0"
memory:"1G"
limits:
cpu:"1.0"
memory:"1G"
volumes:
- name: passwd-volume
secret:
secretName: redis-passwd
An diesem Punkt haben wir die Clientanwendung so konfiguriert, dass ihr für die Authentifizierung gegenüber dem Redis-Dienst ein Secret zur Verfügung steht. Die Konfiguration von Redis für die Verwendung dieses Kennworts ist ähnlich; wir binden es in den Redis-Pod ein und laden das Passwort aus der Datei.
Obwohl die Bereitstellung einer zustandsbehafteten Anwendung konzeptionell ähnlich ist wie die Bereitstellung eines Clients wie unseres Frontends, macht die Tatsache, dass wir es mit einem zustandsbehafteten Service zu tun haben, die Dinge komplizierter. Die erste Komplikation besteht darin, dass in Kubernetes ein Pod aus verschiedenen Gründen verschoben werden kann, z. B. wegen des Zustands des Knotens, eines Upgrades oder wenn die Last neu verteilt wird. In diesem Fall wird der Pod möglicherweise auf eine andere Maschine verschoben. Wenn sich die mit der Redis-Instanz verbundenen Daten auf einer bestimmten Maschine oder im Container selbst befinden, gehen diese Daten verloren, wenn der Container migriert oder neu gestartet wird. Um dies zu verhindern, ist es bei der Ausführung zustandsbehafteter Workloads in Kubernetes wichtig, entfernte persistente Volumes zu verwenden, um den mit der Anwendung verbundenen Zustand zu verwalten.
Es gibt eine Vielzahl von Implementierungen von PersistentVolumes-Objekten in Kubernetes, aber alle besitzen gemeinsame Merkmale. Wie die zuvor beschriebenen Secret-Volumes sind sie mit einem Pod verbunden und werden an einem bestimmten Ort in einen Container eingebunden. Im Gegensatz zu Secrets handelt es sich bei PersistentVolumes in der Regel um Remote-Speicher, der über eine Art Netzwerkprotokoll eingebunden wird, entweder dateibasiert (z. B. Network File System, NFS) oder Server Message Block (SMB) oder blockbasiert (iSCSI, cloudbasierte Festplatten usw.). Für Anwendungen wie Datenbanken sind blockbasierte Festplatten in der Regel vorzuziehen, da sie eine bessere Leistung bieten, aber wenn die Leistung weniger wichtig ist, bieten dateibasierte Datenträger manchmal eine größere Flexibilität.
Hinweis
Die Verwaltung von Zuständen ist im Allgemeinen kompliziert, und Kubernetes ist da keine Ausnahme. Wenn Sie in einer Umgebung arbeiten, die zustandsbehaftete Dienste unterstützt (z. B. MySQL as a Service, Redis as a Service), ist es im Allgemeinen eine gute Idee, diese zustandsbehafteten Dienste zu verwenden. Auf den ersten Blick mögen die Mehrkosten für zustandsbehaftete Software as a Service (SaaS) hoch erscheinen, aber wenn man alle betrieblichen Anforderungen des Zustands (Backup, Datenlokalisierung, Redundanz usw.) und die Tatsache berücksichtigt, dass das Vorhandensein des Zustands in einem Kubernetes-Cluster das Verschieben von Anwendungen zwischen Clustern erschwert, wird klar, dass Storage-SaaS in den meisten Fällen den Preisaufschlag wert ist. In lokalen Umgebungen, in denen Storage-SaaS nicht verfügbar ist, ist es definitiv besser, ein spezielles Team mit der Bereitstellung von Storage als Service für das gesamte Unternehmen zu beauftragen, als jedem Team zu erlauben, es selbst aufzubauen.
Für die Bereitstellung unseres Redis-Dienstes verwenden wir eine StatefulSet-Ressource. StatefulSets wurden nach der ersten Kubernetes-Version als Ergänzung zu ReplicaSet-Ressourcen hinzugefügt und bieten etwas bessere Garantien wie konsistente Namen (keine zufälligen Hashes!) und eine definierte Reihenfolge für das Hochskalieren und Herunterskalieren. Wenn Sie ein Singleton bereitstellen, ist dies weniger wichtig, aber wenn Sie einen replizierten Zustand bereitstellen möchten, sind diese Attribute sehr praktisch.
Um ein PersistentVolume für unser Redis zu erhalten, verwenden wir einen PersistentVolumeClaim. Man kann sich einen Claim als eine »Anfrage nach Ressourcen« vorstellen. Unser Redis erklärt abstrakt, dass er 50 GB Speicherplatz benötigt, und der Kubernetes-Cluster bestimmt, wie er ein entsprechendes PersistentVolume bereitstellen kann. Hierfür gibt es zwei Gründe. Der erste ist, dass wir ein StatefulSet schreiben können, das zwischen verschiedenen Clouds und vor Ort portabel ist, wo die Details der Festplatten unterschiedlich sein können. Der andere Grund ist, dass viele PersistentVolume-Typen zwar nur auf einen einzigen Pod gemountet werden können, wir aber mithilfe von Volume-Claims eine Vorlage schreiben können, die repliziert werden kann und dennoch jedem Pod sein eigenes spezifisches PersistentVolume zuweist.
Das folgende Beispiel zeigt ein Redis StatefulSet mit PersistentVolumes:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName:"redis"
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:5-alpine
ports:
- containerPort: 6379
name: redis
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
Damit wird eine einzelne Instanz Ihres Redis-Dienstes bereitgestellt. Nehmen Sie jedoch an, dass Sie den Redis-Cluster replizieren möchten, um Lesevorgänge zu skalieren und Ausfallsicherheit zu gewährleisten. Dazu müssen Sie natürlich die Anzahl der Replikate auf drei erhöhen, aber Sie müssen auch sicherstellen, dass die beiden neuen Replikate mit dem Schreib-Master für Redis verbunden sind. Wie Sie diese Verbindung herstellen, erfahren Sie im folgenden Abschnitt.
Wenn Sie den headless Service für das Redis StatefulSet erstellen, wird ein DNS-Eintrag redis-0.redis erstellt; dies ist die IP-Adresse des ersten Replikats. Damit können Sie ein einfaches Skript erstellen, das in allen Containern gestartet werden kann:
Sie können dieses Skript als ConfigMap erstellen:
kubectl create configmap redis-config --from-file=./launch.sh
Diese ConfigMap fügen Sie dann zu Ihrem StatefulSet hinzu und nutzen sie als Befehl für den Container. Lassen Sie uns außerdem das Kennwort für die Authentifizierung hinzufügen, das wir weiter vorne im Kapitel erstellt haben.
Das vollständige Redis-System mit drei Replikaten sieht wie folgt aus:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName:"redis"
replicas: 3
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:5-alpine
ports:
- containerPort: 6379
name: redis
volumeMounts:
- name: data
mountPath: /data
- name: script
mountPath: /script/launch.sh
subPath: launch.sh
- name: passwd-volume
mountPath: /etc/redis-passwd
command:
- sh
- -c
- /script/launch.sh
volumes:
- name: script
configMap:
name: redis-config
defaultMode: 0777
- name: passwd-volume
secret:
secretName: redis-passwd
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
Jetzt ist Ihr Redis für Fehlertoleranz geclustert. Wenn eines der drei Redis-Replikate aus irgendeinem Grund ausfällt, kann Ihre Anwendung mit den beiden verbleibenden Replikaten weiterlaufen, bis das dritte Replikat wiederhergestellt ist.
Nachdem wir den zustandsbehafteten Redis-Service bereitgestellt haben, müssen wir ihn für unser Frontend verfügbar machen. Zu diesem Zweck erstellen wir zwei verschiedene Kubernetes-Services. Der erste ist der Service, der Daten aus Redis liest. Da Redis die Daten an alle drei Mitglieder des StatefulSet repliziert, ist es uns egal, an welches Replikat unsere Leseanfrage geht. Daher verwenden wir für die Lesevorgänge einen Basis-Service:
apiVersion: v1
kind: Service
metadata:
labels:
app: redis
name: redis
namespace: default
spec:
ports:
- port: 6379
protocol: TCP
targetPort: 6379
selector:
app: redis
sessionAffinity: None
type: ClusterIP
Um Schreibvorgänge zu ermöglichen, müssen Sie den Redis-Master (Replikat Nr. 0) anvisieren. Dazu erstellen Sie einen headless Service. Ein headless Service hat keine Cluster-IP-Adresse, sondern programmiert einen DNS-Eintrag für jeden Pod im StatefulSet. Das bedeutet, dass wir auf unseren Master über den DNS-Namen redis-0.redis zugreifen können:
apiVersion: v1
kind: Service
metadata:
labels:
app: redis-write
name: redis-write
spec:
clusterIP: None
ports:
- port: 6379
selector:
app: redis
Wenn wir uns also mit Redis für Schreibvorgänge oder transaktionale Lese-/Schreibpaare verbinden wollen, können wir einen separaten Schreib-Client erstellen, der mit dem redis-0.redis-write-Server verbunden ist.
Die letzte Komponente unserer Anwendung ist ein statischer Dateiserver. Der Server für statische Dateien ist für die Bereitstellung von HTML-, CSS-, JavaScript- und Bilddateien zuständig. Es ist sowohl effizienter als auch zielgerichteter für uns, die Bereitstellung statischer Dateien von unserem zuvor beschriebenen API-Serving-Frontend zu trennen. Wir können problemlos einen hochleistungsfähigen statischen Standarddateiserver wie NGINX für die Bereitstellung von Dateien verwenden, während sich unsere Entwicklungsteams auf den für die Implementierung unserer API erforderlichen Code konzentrieren können.
Glücklicherweise macht die Ingress-Ressource diese Art von Mini-Microservice-Architektur sehr einfach. Genau wie beim Frontend können wir eine Deployment-Ressource verwenden, um einen replizierten NGINX-Server zu beschreiben. Lassen Sie uns die statischen Images in den NGINX-Container einbauen und diese an jedes Replikat verteilen. Die Deployment-Ressource sieht wie folgt aus:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: fileserver
name: fileserver
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: fileserver
template:
metadata:
labels:
app: fileserver
spec:
containers:
# Dieses Image ist als Beispiel gedacht, ersetzen Sie es durch Ihre eigene
# statische Image-Datei.
- image: my-repo/static-files:v1-abcde
imagePullPolicy: Always
name: fileserver
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
resources:
requests:
cpu:"1.0"
memory:"1G"
limits:
cpu:"1.0"
memory:"1G"
dnsPolicy: ClusterFirst
restartPolicy: Always
Nachdem nun ein replizierter statischer Webserver eingerichtet und in Betrieb ist, erstellen Sie ebenfalls eine Service-Ressource, die als Lastausgleich fungiert:
apiVersion: v1
kind: Service
metadata:
labels:
app: fileserver
name: fileserver
namespace: default
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: fileserver
sessionAffinity: None
type: ClusterIP
Da Sie nun einen Service für Ihren statischen Dateiserver haben, erweitern Sie die Ingress-Ressource um den neuen Pfad. Es ist wichtig, dass Sie den Pfad / nach dem Pfad /api platzieren, sonst würde er /api subsumieren und API-Anfragen an den statischen Dateiserver leiten. Der neue Ingress sieht wie folgt aus:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: frontend-ingress
spec:
rules:
- http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: fileserver
port:
number: 8080
# HINWEIS: Dies sollte nach /api kommen, da es sonst Anfragen abfängt.
- path: /
pathType: Prefix
backend:
service:
name: fileserver
port:
number: 80
Da Sie nun, zusätzlich zum Ingress für die API, eine Ingress-Ressource für Ihren Dateiserver eingerichtet haben, ist die Benutzeroberfläche der Anwendung einsatzbereit. Die meisten modernen Anwendungen kombinieren statische Dateien, normalerweise HTML und JavaScript, mit einem dynamischen API-Server, der in einer serverseitigen Programmiersprache wie Java, .NET oder Go implementiert ist.
Alles, was wir bisher beschrieben haben, konzentriert sich auf die Bereitstellung einer einzelnen Instanz unseres Dienstes in einem einzelnen Cluster. In der Realität wird jedoch fast jeder Service und jedes Serviceteam in mehreren Umgebungen bereitgestellt werden müssen (selbst wenn sie sich einen Cluster teilen). Selbst wenn Sie nur ein einzelner Entwickler sind, der an einer einzigen Anwendung arbeitet, möchten Sie wahrscheinlich mindestens eine Entwicklungs- und eine Produktivversion Ihrer Anwendung haben, damit Sie iterieren und entwickeln können, ohne die Nutzer der Produktivumgebung zu beeinträchtigen. Wenn Sie Integrationstests und CI/CD in Betracht ziehen, ist es wahrscheinlich, dass Sie selbst bei einem einzigen Service und einer Handvoll Entwickler mindestens drei verschiedene Umgebungen bereitstellen möchten, möglicherweise sogar mehr, wenn Sie den Umgang mit Ausfällen auf Rechenzentrumsebene berücksichtigen. Lassen Sie uns ein paar Optionen für die Bereitstellung untersuchen.
Ein anfänglicher Fehler vieler Teams besteht darin, die Dateien einfach von einem Cluster in einen anderen zu kopieren. Statt eines einzigen Verzeichnisses frontend/gibt es dann zwei Verzeichnisse: frontend-production/ und frontend-development/. Dies ist zwar eine praktikable Option, aber auch gefährlich, da Sie nun dafür sorgen müssen, dass diese Dateien miteinander synchronisiert bleiben. Wenn sie völlig identisch sein sollten, wäre dies einfach, aber es ist zu erwarten, dass es zu einer gewissen Abweichung zwischen Entwicklung und Produktion kommt, da Sie neue Funktionen entwickeln werden. Es ist von entscheidender Bedeutung, dass diese Abweichung sowohl beabsichtigt als auch leicht zu handhaben ist.
Eine andere Möglichkeit, dies zu erreichen, wäre die Verwendung von Zweigen und Versionsverwaltungssystemen, wobei die Produktions- und Entwicklungszweige von einem zentralen Repository ausgehen und die Unterschiede zwischen den Zweigen deutlich sichtbar sind. Dies kann für einige Teams eine praktikable Option sein, aber die Mechanik des Wechsels zwischen Zweigen ist eine Herausforderung, wenn Sie Software gleichzeitig in verschiedenen Umgebungen bereitstellen möchten (z. B. ein CI/CD-System, das in verschiedenen Cloud-Regionen bereitgestellt wird).
Daher verwenden die meisten Anwender ein Templating-System. Ein Templating-System kombiniert Templates, die das zentrale Rückgrat der Anwendungskonfiguration bilden, mit Parametern, die das Template auf eine bestimmte Umgebungskonfiguration spezialisieren