Kapitel 1. Einführung

Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com

Kubernetes ist ein Open-Source-Orchestrator für die Bereitstellung von containerisierten Anwendungen. Er wurde ursprünglich von Google entwickelt, inspiriert durch ein Jahrzehnt Erfahrung in der Bereitstellung von skalierbaren, zuverlässigen Systemen in Containern über anwendungsorientierte APIs.1

Seit seiner Einführung im Jahr 2014 ist Kubernetes zu einem der größten und beliebtesten Open-Source-Projekte der Welt geworden. Es hat sich zur Standard-API für die Entwicklung von Cloud-nativen Anwendungen entwickelt und ist in fast jeder öffentlichen Cloud vertreten. Kubernetes ist eine bewährte Infrastruktur für verteilte Systeme, die sich für Cloud Native-Entwickler aller Größenordnungen eignet, von einem Cluster aus Raspberry-Pi-Computern bis hin zu einem Rechenzentrum voller modernster Maschinen. Es bietet die Software, die für den erfolgreichen Aufbau und die Bereitstellung zuverlässiger, skalierbarer verteilter Systeme erforderlich ist.

Du fragst dich vielleicht, was wir meinen, wenn wir von "zuverlässigen, skalierbaren verteilten Systemen" sprechen. Immer mehr Dienste werden über APIs im Netzwerk bereitgestellt. Diese APIs werden oft von einem verteilten System bereitgestellt, wobei die verschiedenen Teile, die die API implementieren, auf verschiedenen Rechnern laufen, die über das Netzwerk verbunden sind und ihre Aktionen über die Netzwerkkommunikation koordinieren. Da wir uns in allen Bereichen unseres täglichen Lebens zunehmend auf diese APIs verlassen (z. B. bei der Suche nach dem Weg zum nächsten Krankenhaus), müssen diese Systeme äußerst zuverlässig sein. Sie dürfen nicht fehlschlagen, selbst wenn ein Teil des Systems abstürzt oder aus anderen Gründen nicht mehr funktioniert. Ebenso müssen sie auch bei Software-Rollouts oder anderen Wartungsarbeiten verfügbar bleiben. Da immer mehr Menschen auf der Welt online gehen und solche Dienste nutzen, müssen sie hochskalierbar sein, damit sie mit der steigenden Nutzung Schritt halten können, ohne dass das verteilte System, das die Dienste implementiert, radikal umgestaltet werden muss. In vielen Fällen bedeutet das auch, dass die Kapazität automatisch wächst (und schrumpft), damit deine Anwendung so effizient wie möglich arbeiten kann.

Je nachdem, wann und warum du dieses Buch in die Hand nimmst, hast du vielleicht schon mehr oder weniger Erfahrung mit Containern, verteilten Systemen und Kubernetes. Vielleicht planst du, deine Anwendung auf einer öffentlichen Cloud-Infrastruktur, in privaten Rechenzentren oder in einer hybriden Umgebung zu entwickeln. Unabhängig von deiner Erfahrung sollte dieses Buch dich in die Lage versetzen, das Beste aus Kubernetes herauszuholen.

Es gibt viele Gründe, warum Menschen Container und Container-APIs wie Kubernetes nutzen, aber wir glauben, dass sie sich alle auf einen dieser Vorteile zurückführen lassen:

  • Geschwindigkeit der Entwicklung

  • Skalierung (sowohl der Software als auch der Teams)

  • Abstrahieren deiner Infrastruktur

  • Effizienz

  • Cloud-natives Ökosystem

In den folgenden Abschnitten beschreiben wir, wie Kubernetes dabei helfen kann, jede dieser Funktionen bereitzustellen.

Geschwindigkeit

Schnelligkeit ist heute die Schlüsselkomponente in fast jeder Softwareentwicklung. Die Softwarebranche hat sich von der Auslieferung von Produkten in Form von verpackten CDs oder DVDs zu Software entwickelt, die über webbasierte Dienste, die stündlich aktualisiert werden, über das Netzwerk bereitgestellt wird. Diese sich verändernde Landschaft bedeutet, dass der Unterschied zwischen dir und deinen Konkurrenten oft in der Geschwindigkeit liegt, mit der du neue Komponenten und Funktionen entwickeln und einsetzen kannst, oder in der Geschwindigkeit, mit der du auf Innovationen anderer reagieren kannst.

Es ist jedoch wichtig zu wissen, dass sich Geschwindigkeit nicht nur auf die reine Geschwindigkeit bezieht. Deine Nutzerinnen und Nutzer sind zwar immer auf der Suche nach iterativen Verbesserungen, aber sie sind eher an einem äußerst zuverlässigen Service interessiert. Früher war es in Ordnung, wenn ein Dienst jede Nacht um Mitternacht wegen Wartungsarbeiten ausfiel. Aber heute erwarten alle Nutzer/innen eine konstante Betriebszeit, auch wenn sich die Software, die sie benutzen, ständig ändert.

Die Geschwindigkeit wird also nicht an der Anzahl der Funktionen gemessen, die du pro Stunde oder Tag ausliefern kannst, sondern an der Anzahl der Dinge, die du ausliefern kannst, während du einen hochverfügbaren Service aufrechterhältst.

Auf diese Weise können Container und Kubernetes die Werkzeuge bereitstellen, die du brauchst, um schnell voranzukommen und gleichzeitig verfügbar zu bleiben. Die Kernkonzepte, die dies ermöglichen, sind:

  • Unveränderlichkeit

  • Deklarative Konfiguration

  • Selbstheilende Online-Systeme

  • Gemeinsame wiederverwendbare Bibliotheken und Werkzeuge

All diese Ideen spielen zusammen, um die Geschwindigkeit, mit der du neue Software zuverlässig einsetzen kannst, radikal zu verbessern.

Der Wert der Unveränderlichkeit

Container und Kubernetes ermutigen Entwickler dazu, verteilte Systeme zu bauen, die den Prinzipien einer unveränderlichen Infrastruktur folgen. Bei einer unveränderlichenInfrastruktur wird ein Artefakt, das einmal im System erstellt wurde, nicht mehr durch Benutzeränderungen verändert.

Traditionell werden Computer und Softwaresysteme als veränderliche Infrastruktur behandelt. Bei einer veränderlichen Infrastruktur werden Änderungen in Form von inkrementellen Updates auf ein bestehendes System angewendet. Diese Aktualisierungen können auf einmal erfolgen oder über einen längeren Zeitraum verteilt werden. Ein System-Upgrade über das Tool apt-get update ist ein gutes Beispiel für eine Aktualisierung eines veränderlichen Systems. Wenn apt ausgeführt wird, werden alle aktualisierten Binärdateien nacheinander heruntergeladen, über ältere Binärdateien kopiert und die Konfigurationsdateien schrittweise aktualisiert. Bei einem veränderlichen System wird der aktuelle Zustand der Infrastruktur nicht als ein einzelnes Artefakt dargestellt, sondern als eine Ansammlung von inkrementellen Aktualisierungen und Änderungen im Laufe der Zeit. Bei vielen Systemen stammen diese inkrementellen Aktualisierungen nicht nur von System-Upgrades, sondern auch von Änderungen durch die Betreiber. Außerdem ist es bei einem System, das von einem großen Team betrieben wird, sehr wahrscheinlich, dass diese Änderungen von vielen verschiedenen Personen vorgenommen wurden und in vielen Fällen nirgendwo aufgezeichnet wurden.

Im Gegensatz dazu wird in einem unveränderlichen System statt einer Reihe von inkrementellen Aktualisierungen und Änderungen ein völlig neues, komplettes Abbild erstellt, bei dem die Aktualisierung einfach das gesamte Abbild durch das neuere Abbild in einem einzigen Vorgang ersetzt. Es gibt keine inkrementellen Änderungen. Wie du dir vorstellen kannst, ist dies ein bedeutender Unterschied zur traditionellen Welt des Konfigurationsmanagements.

Um dies in der Welt der Container zu verdeutlichen, betrachte zwei verschiedene Möglichkeiten, deine Software zu aktualisieren:

  • Du kannst dich in einem Container anmelden, einen Befehl zum Herunterladen deiner neuen Software ausführen, den alten Server beenden und den neuen starten.

  • Du kannst ein neues Container-Image erstellen, es in eine Container-Registry stellen, den bestehenden Container löschen und einen neuen starten.

Auf den ersten Blick scheinen diese beiden Ansätze kaum voneinander zu unterscheiden zu sein. Was ist es also, das beim Bau eines neuen Containers die Zuverlässigkeit erhöht?

Das wichtigste Unterscheidungsmerkmal ist das Artefakt, das du erstellst, und die Aufzeichnung, wie du es erstellt hast. Diese Aufzeichnungen machen es einfach, die Unterschiede in einer neuen Version genau zu verstehen und, wenn etwas schief geht, festzustellen, was sich geändert hat und wie man es beheben kann.

Wenn du ein neues Image erstellst, anstatt ein bestehendes zu ändern, ist das alte Image immer noch vorhanden und kann im Falle eines Fehlers schnell für ein Rollback verwendet werden. Wenn du dagegen dein neues Binary über ein bestehendes Binary kopierst, ist ein solches Rollback fast unmöglich.

Unveränderliche Container-Images sind der Kern von allem, was du in Kubernetes aufbauen wirst. Es ist zwar möglich, laufende Container zwangsweise zu ändern, aber das ist ein Antipattern, das nur in extremen Fällen verwendet werden sollte, wenn es keine anderen Optionen gibt (z. B. wenn es die einzige Möglichkeit ist, ein geschäftskritisches Produktionssystem vorübergehend zu reparieren). Und selbst dann müssen die Änderungen durch eine deklarative Konfigurationsaktualisierung zu einem späteren Zeitpunkt aufgezeichnet werden, nachdem das Feuer gelöscht wurde.

Deklarative Konfiguration

Die Unveränderlichkeit erstreckt sich nicht nur auf die Container, die in deinem Cluster laufen, sondern auch auf die Art und Weise, wie du deine Anwendung in Kubernetes beschreibst. Alles in Kubernetes ist ein deklaratives Konfigurationsobjekt, das den gewünschten Zustand des Systems darstellt. Kubernetes hat die Aufgabe, dafür zu sorgen, dass der tatsächliche Zustand der Welt mit dem gewünschten Zustand übereinstimmt.

Ähnlich wie bei der Gegenüberstellung von veränderlicher und unveränderlicher Infrastruktur ist die deklarative Konfiguration eine Alternative zur imperativen Konfiguration, bei der der Zustand der Welt durch die Ausführung einer Reihe von Befehlen definiert wird und nicht durch eine Deklaration des gewünschten Zustands der Welt. Während imperative Befehle Aktionen festlegen, definieren deklarative Konfigurationen den Zustand.

Um diese beiden Ansätze zu verstehen, betrachte die Aufgabe, drei Replikate einer Software zu erstellen. Bei einem imperativen Ansatz würde die Konfiguration lauten: "Führe A, B und C aus". Die entsprechende deklarative Konfiguration würde lauten: "Replikate sind gleich drei".

Weil sie den Zustand der Welt beschreibt, muss die deklarative Konfiguration nicht ausgeführt werden, um verstanden zu werden. Ihre Auswirkungen werden konkret deklariert. Da die Auswirkungen der deklarativen Konfiguration verstanden werden können, bevor sie ausgeführt werden, ist die deklarative Konfiguration weit weniger fehleranfällig. Außerdem können die traditionellen Werkzeuge der Softwareentwicklung, wie Quellcodekontrolle, Codeüberprüfung und Unit-Tests, bei der deklarativen Konfiguration in einer Weise eingesetzt werden, die bei imperativen Anweisungen unmöglich ist. Die Idee, deklarative Konfiguration in der Versionskontrolle zu speichern, wird oft als "Infrastruktur als Code" bezeichnet.

In letzter Zeit hat die Idee von GitOps begonnen, die Praxis der Infrastruktur als Code mit der Quellcodekontrolle als Quelle der Wahrheit zu formalisieren. Wenn du GitOps einführst, werden Änderungen in der Produktion ausschließlich über Pushs in ein Git-Repository vorgenommen, die dann durch Automatisierung in deinen Cluster übertragen werden. Dein produktiver Kubernetes-Cluster wird sozusagen als reine Leseumgebung betrachtet. Darüber hinaus wird GitOps zunehmend in Kubernetes-Dienste integriert, da es die einfachste Möglichkeit ist, deine Cloud-Infrastruktur deklarativ zu verwalten.

Die Kombination aus dem deklarativen Zustand, der in einem Versionskontrollsystem gespeichert ist, und der Fähigkeit von Kubernetes, die Realität mit diesem deklarativen Zustand in Einklang zu bringen, macht das Rollback einer Änderung trivial einfach. Dabei wird einfach der vorherige deklarative Zustand des Systems wiederhergestellt. Bei imperativen Systemen ist das in der Regel nicht möglich, denn obwohl die imperativen Anweisungen beschreiben, wie man von Punkt A nach Punkt B kommt, enthalten sie selten die umgekehrten Anweisungen, mit denen man wieder zurückbekommt.

Selbstheilende Systeme

Kubernetes ist ein Online-System, das sich selbst repariert. Wenn es eine gewünschte Zustandskonfiguration erhält, führt es nicht einfach eine Reihe von Maßnahmen durch, um den aktuellen Zustand einmalig an den gewünschten Zustand anzupassen. Es ergreift kontinuierlich Maßnahmen, um sicherzustellen, dass der aktuelle Zustand mit dem gewünschten Zustand übereinstimmt. Das bedeutet, dass Kubernetes dein System nicht nur initialisiert, sondern es auch vor Ausfällen oder Störungen schützt, die das System destabilisieren und die Zuverlässigkeit beeinträchtigen könnten.

Eine herkömmliche Operator-Reparatur umfasst eine Reihe von manuellen Schritten zur Schadensbegrenzung oder ein menschliches Eingreifen, das als Reaktion auf eine Warnung durchgeführt wird. Eine solche dringende Reparatur ist teurer (da in der Regel ein Operator auf Abruf verfügbar sein muss, um die Reparatur durchzuführen). Außerdem ist sie in der Regel langsamer, da ein Mensch oft aufwachen und sich einloggen muss, um zu reagieren. Außerdem ist sie weniger zuverlässig, da die zwingende Reihe von Reparaturvorgängen unter allen im vorherigen Abschnitt beschriebenen Problemen der zwingenden Verwaltung leidet. Selbstheilende Systeme wie Kubernetes verringern sowohl die Belastung der Bediener/innen als auch die Gesamtzuverlässigkeit des Systems, indem sie zuverlässige Reparaturen schneller durchführen.

Ein konkretes Beispiel für dieses selbstheilende Verhalten: Wenn du Kubernetes einen gewünschten Zustand von drei Replikaten vorgibst, erstellt es nicht nur drei Replikate, sondern stellt kontinuierlich sicher, dass es genau drei Replikate gibt. Wenn du manuell ein viertes Replikat anlegst, zerstört Kubernetes eines, um die Anzahl wieder auf drei zu bringen. Wenn du ein Replikat manuell zerstörst, erstellt Kubernetes ein neues, um den gewünschten Zustand wiederherzustellen.

Selbstheilende Online-Systeme erhöhen die Entwicklungsgeschwindigkeit, da die Zeit und Energie, die du sonst für Betrieb und Wartung aufwenden müsstest, stattdessen für die Entwicklung und das Testen neuer Funktionen genutzt werden kann.

Eine fortgeschrittene Form der Selbstheilung ist das Operator-Paradigma für Kubernetes, an dem in letzter Zeit viel gearbeitet wurde. Bei Operatoren wird die erweiterte Logik, die für die Wartung, Skalierung und Heilung einer bestimmten Software (z. B. MySQL) erforderlich ist, in einer Operator-Anwendung kodiert, die als Container im Cluster läuft. Der Code im Operator ist für eine gezieltere und fortschrittlichere Zustandserkennung und -heilung verantwortlich, als dies mit der generischen Selbstheilung von Kubernetes möglich ist. Oft wird dies in "Operatoren" verpackt, die in Kapitel 17 behandelt werden.

Skalierung deines Dienstes und deiner Teams

Wenn dein Produkt wächst, ist es unvermeidlich, dass du sowohl deine Software als auch die Teams, die sie entwickeln, skalieren musst. Zum Glück kann Kubernetes bei beiden Zielen helfen. Kubernetes erreicht Skalierbarkeit, indem es entkoppelte Architekturen bevorzugt.

Entkopplung

In einer entkoppelten Architektur wird jede Komponente durch definierte APIs und Lastverteiler von anderen Komponenten getrennt. APIs und Lastverteiler isolieren jeden Teil des Systems von den anderen. APIs bieten einen Puffer zwischen Implementierer und Verbraucher, und Load Balancer bieten einen Puffer zwischen den laufenden Instanzen der einzelnen Dienste.

Die Entkopplung der Komponenten über Load Balancer macht es einfach, die Programme, aus denen dein Dienst besteht, zu skalieren, weil die Größe (und damit die Kapazität) des Programms erhöht werden kann, ohne dass eine der anderen Schichten deines Dienstes angepasst oder neu konfiguriert werden muss.

Die Entkopplung von Servern über APIs erleichtert die Skalierung der Entwicklungsteams, da sich jedes Team auf einen einzelnen, kleineren Microservice mit einer überschaubaren Oberfläche konzentrieren kann. Knackige APIs zwischen Microservices begrenzen den Kommunikationsaufwand zwischen den Teams, der für die Erstellung und Bereitstellung von Software erforderlich ist. Dieser Kommunikationsaufwand ist oft der größte Hemmschuh bei der Skalierung von Teams.

Einfache Skalierung für Anwendungen und Cluster

Wenn du deinen Dienst skalieren musst, macht die unveränderliche, deklarative Natur von Kubernetes diese Skalierung trivial zu implementieren. Da deine Container unveränderlich sind und die Anzahl der Replikate lediglich eine Zahl in einer deklarativen Konfigurationsdatei ist, musst du nur eine Zahl in einer Konfigurationsdatei ändern, Kubernetes diesen neuen deklarativen Zustand mitteilen und es den Rest erledigen lassen. Alternativ kannst du auch eine automatische Skalierung einrichten und Kubernetes das für dich erledigen lassen.

Diese Art der Skalierung setzt natürlich voraus, dass in deinem Cluster Ressourcen vorhanden sind, die du nutzen kannst. Manchmal musst du auch den Cluster selbst skalieren. Auch diese Aufgabe wird durch Kubernetes vereinfacht. Da viele Maschinen in einem Cluster völlig identisch mit anderen Maschinen in diesem Set sind und die Anwendungen selbst durch Container von den Details der Maschine entkoppelt sind, ist das Hinzufügen zusätzlicher Ressourcen zum Cluster einfach eine Sache des Imagings einer neuen Maschine der gleichen Klasse und des Beitritts zum Cluster. Dies kann über ein paar einfache Befehle oder über ein vorgefertigtes Maschinenimage erfolgen.

Eine der Herausforderungen bei der Skalierung von Maschinenressourcen ist die Vorhersage ihrer Nutzung. Wenn du eine physische Infrastruktur nutzt, dauert es Tage oder Wochen, bis du eine neue Maschine bekommst. Sowohl bei physischen als auch bei Cloud-Infrastrukturen ist es schwierig, die zukünftigen Kosten vorherzusagen, weil das Wachstum und der Skalierungsbedarf bestimmter Anwendungen schwer vorherzusagen sind.

Kubernetes kann die Vorhersage zukünftiger Rechenkosten vereinfachen. Um zu verstehen, warum das so ist, betrachte die Skalierung von drei Teams: A, B und C. In der Vergangenheit hast du festgestellt, dass das Wachstum jedes Teams sehr unterschiedlich ist und daher schwer vorherzusagen ist. Wenn du für jeden Dienst einzelne Maschinen bereitstellst, hast du keine andere Wahl, als das maximal zu erwartende Wachstum für jeden Dienst zu prognostizieren, da Maschinen, die für ein Team bestimmt sind, nicht für ein anderes Team verwendet werden können. Wenn du stattdessen Kubernetes verwendest, um die Teams von den spezifischen Maschinen, die sie nutzen, zu entkoppeln, kannst du das Wachstum auf der Grundlage des Gesamtwachstums aller drei Dienste prognostizieren. Die Kombination von drei variablen Wachstumsraten zu einer einzigen Wachstumsrate reduziert das statistische Rauschen und führt zu einer zuverlässigeren Prognose des erwarteten Wachstums. Darüber hinaus bedeutet die Entkopplung der Teams von den spezifischen Maschinen, dass sich die Teams Teile der Maschinen des jeweils anderen Teams teilen können, was den mit der Prognose des Wachstums der Rechenressourcen verbundenen Aufwand noch weiter reduziert.

Schließlich ermöglicht Kubernetes eine automatische Skalierung (sowohl nach oben als auch nach unten) der Ressourcen. Vor allem in einer Cloud-Umgebung, in der neue Maschinen über APIs erstellt werden können, bedeutet die Kombination von Kubernetes mit der automatischen Skalierung sowohl für die Anwendungen als auch für die Cluster selbst, dass du deine Kosten immer an die aktuelle Last anpassen kannst.

Skalierung von Entwicklungsteams mit Microservices

Wie in verschiedenen Untersuchungen festgestellt wurde, ist die ideale Teamgröße das "Zwei-Pizza-Team", also etwa sechs bis acht Personen. Diese Gruppengröße führt oft zu einem guten Wissensaustausch, einer schnellen Entscheidungsfindung und einem gemeinsamen Zielbewusstsein. Größere Teams neigen zu Hierarchieproblemen, schlechter Sichtbarkeit und internen Streitigkeiten, die Agilität und Erfolg behindern.

Viele Projekte benötigen jedoch deutlich mehr Ressourcen, um erfolgreich zu sein und ihre Ziele zu erreichen. Folglich gibt es ein Spannungsverhältnis zwischen der idealen Teamgröße für Agilität und der notwendigen Teamgröße für die Endziele des Produkts.

Die gängige Lösung für dieses Spannungsverhältnis ist die Entwicklung von entkoppelten, serviceorientierten Teams, die jeweils einen einzelnen Microservice entwickeln. Jedes kleine Team ist für die Entwicklung und Bereitstellung eines Dienstes verantwortlich, der von anderen kleinen Teams genutzt wird. Die Gesamtheit all dieser Dienste bildet schließlich die Oberfläche des Gesamtprodukts.

Kubernetes bietet zahlreiche Abstraktionen und APIs, die den Aufbau dieser entkoppelten Microservice-Architekturen erleichtern:

  • Pods, also Gruppen von Containern, können Container-Images, die von verschiedenen Teams entwickelt wurden, zu einer einsatzfähigen Einheit zusammenfassen.

  • Kubernetes-Dienste bieten Lastausgleich, Namensgebung und Discovery, um einen Microservice von einem anderen zu isolieren.

  • Namensräume bieten Isolation und Zugriffskontrolle, so dass jeder Microservice kontrollieren kann, inwieweit andere Dienste mit ihm interagieren.

  • Ingress-Objekte bieten ein benutzerfreundliches Frontend, das mehrere Microservices zu einer einzigen externalisierten API-Fläche zusammenfassen kann.

Schließlich bedeutet die Entkopplung von Container-Image und Maschine, dass verschiedene Microservices auf derselben Maschine untergebracht werden können, ohne sich gegenseitig zu behindern, was den Overhead und die Kosten von Microservice-Architekturen reduziert. Die Health-Checking- und Rollout-Funktionen von Kubernetes garantieren einen konsistenten Ansatz für den Rollout und die Zuverlässigkeit von Anwendungen, wodurch sichergestellt wird, dass die Zunahme von Microservice-Teams nicht auch zu einer Zunahme unterschiedlicher Ansätze für den Lebenszyklus und den Betrieb von Services führt.

Trennung der Belange für Konsistenz und Skalierung

Neben der Konsistenz, die Kubernetes für den Betrieb mit sich bringt, führt die Entkopplung und Trennung von Belangen durch den Kubernetes-Stack zu einer deutlich größeren Konsistenz auf den unteren Ebenen deiner Infrastruktur. So kannst du den Infrastrukturbetrieb skalieren und viele Maschinen mit einem einzigen kleinen, konzentrierten Team verwalten. Wir haben bereits ausführlich über die Entkopplung von Anwendungscontainer und Maschine/Betriebssystem (OS) gesprochen, aber ein wichtiger Aspekt dieser Entkopplung ist, dass die Container-Orchestrierungs-API zu einem klaren Vertrag wird, der die Verantwortlichkeiten des Anwendungsoperators vom Cluster-Orchestrierungsoperator trennt. Der Anwendungsentwickler verlässt sich auf das Service Level Agreement (SLA), das von der Container-Orchestrierungs-API bereitgestellt wird, ohne sich um die Details zu kümmern, wie dieses SLA erreicht wird. Ebenso konzentriert sich der Ingenieur für die Zuverlässigkeit der Container-Orchestrierungs-API darauf, die SLA der Orchestrierungs-API zu erfüllen, ohne sich um die Anwendungen zu kümmern, die darauf laufen.

Die Entkopplung von Belangen bedeutet, dass ein kleines Team, das einen Kubernetes-Cluster betreibt, für die Unterstützung von Hunderten oder sogar Tausenden von Teams verantwortlich sein kann, die Anwendungen innerhalb dieses Clusters ausführen(Abbildung 1-1). Genauso kann ein kleines Team für Dutzende (oder mehr) von Clustern auf der ganzen Welt verantwortlich sein. Durch die Entkopplung von Containern und Betriebssystemen können sich die Ingenieure für die Zuverlässigkeit des Betriebssystems auf die SLAs der einzelnen Rechner konzentrieren. Die Kubernetes-Betreiber verlassen sich auf das SLA des Betriebssystems, und die Betriebssystem-Betreiber kümmern sich ausschließlich um die Einhaltung dieses SLAs. Auf diese Weise kannst du ein kleines Team von Betriebssystemexperten auf eine Flotte von Tausenden von Maschinen ausweiten.

kur3 0101
Abbildung 1-1. Eine Illustration, wie verschiedene Betriebsteams mithilfe von APIs entkoppelt werden

Natürlich ist es für viele Unternehmen unmöglich, auch nur ein kleines Team mit der Verwaltung eines Betriebssystems zu beauftragen. In solchen Umgebungen ist ein verwalteter Kubernetes-as-a-Service (KaaS), der von einem Public Cloud-Provider bereitgestellt wird, eine gute Option. Mit der zunehmenden Verbreitung von Kubernetes ist auch die Verfügbarkeit von KaaS gestiegen, so dass es jetzt in fast jeder öffentlichen Cloud angeboten wird. Natürlich hat die Nutzung von KaaS einige Einschränkungen, da der Betreiber für dich entscheidet, wie die Kubernetes-Cluster aufgebaut und konfiguriert werden. Viele KaaS-Plattformen deaktivieren zum Beispiel Alpha-Funktionen, weil sie den verwalteten Cluster destabilisieren können.

Zusätzlich zu einem vollständig verwalteten Kubernetes-Service gibt es ein florierendes Ökosystem von Unternehmen und Projekten, die bei der Installation und Verwaltung von Kubernetes helfen. Es gibt ein breites Spektrum an Lösungen, von der "harten Arbeit" bis hin zu einem vollständig verwalteten Dienst.

Die Entscheidung, ob du KaaS nutzt oder selbst verwaltest (oder etwas dazwischen), muss daher jeder Nutzer je nach seinen Fähigkeiten und Anforderungen treffen. Für kleine Unternehmen ist KaaS oft eine einfach zu bedienende Lösung, die es ihnen ermöglicht, ihre Zeit und Energie auf die Entwicklung der Software zur Unterstützung ihrer Arbeit zu konzentrieren, anstatt einen Cluster zu verwalten. Für größere Organisationen, die sich ein eigenes Team für die Verwaltung ihres Kubernetes-Clusters leisten können, kann diese Art der Verwaltung sinnvoll sein, da sie eine größere Flexibilität in Bezug auf die Fähigkeiten und den Betrieb des Clusters ermöglicht.

Abstrahieren deiner Infrastruktur

Das Ziel der öffentlichen Cloud ist es,eine einfach zu bedienende Infrastruktur zur Verfügung zu stellen, die Entwickler/innen selbst nutzen können. Doch allzu oft orientieren sich die Cloud-APIs an der Infrastruktur, die die IT erwartet (z. B. "virtuelle Maschinen"), und nicht an den Konzepten (z. B. "Anwendungen"), die die Entwickler nutzen wollen. Außerdem bringt die Cloud in vielen Fällen bestimmte Implementierungsdetails oder Dienste mit sich, die für den Cloud-Provider spezifisch sind. Wenn du diese APIs direkt nutzt, ist es schwierig, deine Anwendung in mehreren Umgebungen zu betreiben oder zwischen Cloud- und physischen Umgebungen zu wechseln.

Der Wechsel zu anwendungsorientierten Container-APIs wie Kubernetes hat zwei konkrete Vorteile. Erstens trennt sie, wie bereits beschrieben, die Entwickler von bestimmten Maschinen. Das erleichtert die maschinennahe IT-Rolle, da Maschinen einfach hinzugefügt werden können, um den Cluster zu skalieren. Im Kontext der Cloud ermöglicht es außerdem ein hohes Maß an Portabilität, da die Entwickler eine übergeordnete API nutzen, die in Form der spezifischen Cloud-Infrastruktur-APIs implementiert ist.

Wenn deine Entwickler ihre Anwendungen in Form von Container-Images erstellen und sie mit Hilfe portabler Kubernetes-APIs bereitstellen, ist die Übertragung deiner Anwendung zwischen verschiedenen Umgebungen oder sogar in hybriden Umgebungen nur eine Frage der Übertragung der deklarativen Konfiguration an einen neuen Cluster. Kubernetes verfügt über eine Reihe von Plug-ins, die dich von einer bestimmten Cloud abstrahieren können. Die Kubernetes-Dienste wissen zum Beispiel, wie man Load Balancer in allen wichtigen öffentlichen Clouds sowie in verschiedenen privaten und physischen Infrastrukturen erstellt. Ebenso können Kubernetes PersistentVolumes und PersistentVolumeClaims verwendet werden, um deine Anwendungen von bestimmten Speicherimplementierungen zu trennen. Um diese Portabilität zu erreichen, musst du natürlich auf Cloud-verwaltete Dienste verzichten (z. B. Amazons DynamoDB, Azures Cosmos DB oder Googles Cloud Spanner), was bedeutet, dass du gezwungen bist, Open-Source-Speicherlösungen wie Cassandra, MySQL oder MongoDB einzusetzen und zu verwalten.

Zusammenfassend lässt sich sagen, dass die Entwicklung auf Basis der anwendungsorientierten Abstraktionen von Kubernetes sicherstellt, dass der Aufwand, den du in die Entwicklung, Bereitstellung und Verwaltung deiner Anwendung steckst, in einer Vielzahl von Umgebungen wirklich portabel ist.

Effizienz

Neben den Vorteilen für Entwickler und IT-Management, die Container und Kubernetes bieten, hat die Abstraktion auch einen konkreten wirtschaftlichen Nutzen. Da Entwickler nicht mehr in Maschinen denken, können ihre Anwendungen auf denselben Maschinen untergebracht werden, ohne dass dies Auswirkungen auf die Anwendungen selbst hat. Das bedeutet, dass Aufgaben von mehreren Nutzern auf weniger Maschinen verpackt werden können.

Die Effizienz lässt sich anhand des Verhältnisses zwischen der von einer Maschine oder einem Prozess geleisteten nützlichen Arbeit und der dafür aufgewendeten Gesamtenergie messen. Wenn es um die Bereitstellung und Verwaltung von Anwendungen geht, sind viele der verfügbaren Tools und Prozesse (z. B. Bash-Skripte, apt Updates oder zwingendes Konfigurationsmanagement) eher ineffizient. Wenn es um Effizienz geht, ist es oft hilfreich, sowohl die monetären Kosten für den Betrieb eines Servers als auch die Personalkosten zu berücksichtigen, die für die Verwaltung des Servers erforderlich sind.

Der Betrieb eines Servers verursacht Kosten, die sich aus dem Stromverbrauch, dem Kühlungsbedarf, dem Platz im Rechenzentrum und der reinen Rechenleistung ergeben. Sobald ein Server aufgestellt und eingeschaltet wird (oder angeklickt und hochgefahren wird), beginnt der Zähler buchstäblich zu laufen. Jede ungenutzte CPU-Zeit ist verschwendetes Geld. Daher gehört es zu den Aufgaben des Systemadministrators, die Auslastung auf einem akzeptablen Niveau zu halten, was eine kontinuierliche Verwaltung erfordert. An dieser Stelle kommen Container und der Kubernetes-Workflow ins Spiel. Kubernetes stellt Werkzeuge zur Verfügung, die die Verteilung von Anwendungen auf einen Cluster von Rechnern automatisieren und so eine höhere Auslastung sicherstellen, als dies mit herkömmlichen Werkzeugen möglich ist.

Eine weitere Effizienzsteigerung ergibt sich aus der Tatsache, dass die Testumgebung eines Entwicklers schnell und kostengünstig als eine Reihe von Containern erstellt werden kann, die in einer persönlichen Ansicht eines gemeinsam genutzten Kubernetes-Clusters laufen (unter Verwendung einer Funktion namens Namensräume). In der Vergangenheit hätte die Einrichtung eines Testclusters für einen Entwickler bedeuten können, dass er drei Maschinen aufstellen musste. Mit Kubernetes ist es ganz einfach, alle Entwickler/innen auf einen einzigen Testcluster zu verteilen und so die Anzahl der verwendeten Maschinen zu reduzieren. Die Verringerung der Gesamtzahl der genutzten Maschinen steigert wiederum die Effizienz jedes Systems: Da mehr Ressourcen (CPU, RAM usw.) auf jeder einzelnen Maschine genutzt werden, sind die Gesamtkosten jedes Containers viel niedriger.

Die Verringerung der Kosten für Entwicklungsinstanzen in deinem Stack ermöglicht Entwicklungspraktiken, die früher vielleicht zu kostspielig gewesen wären. Wenn deine Anwendung zum Beispiel über Kubernetes bereitgestellt wird, ist es möglich, jeden einzelnen Commit, den jeder Entwickler beigesteuert hat, in deinem gesamten Stack bereitzustellen und zu testen.

Wenn die Kosten für jede Bereitstellung in Form einer kleinen Anzahl von Containern statt mehrerer kompletter virtueller Maschinen (VMs) gemessen werden, sind die Kosten für solche Tests drastisch niedriger. Um auf den ursprünglichen Wert von Kubernetes zurückzukommen, erhöht dieses verstärkte Testen auch die Geschwindigkeit, da du starke Signale für die Zuverlässigkeit deines Codes und die nötige Detailgenauigkeit hast, um schnell zu erkennen, wo ein Problem aufgetreten sein könnte.

Wie in den vorangegangenen Abschnitten bereits erwähnt, kann die automatische Skalierung, die Ressourcen bei Bedarf hinzufügt und sie bei Bedarf wieder entfernt, auch dazu genutzt werden, die Gesamteffizienz deiner Anwendungen zu steigern und gleichzeitig die erforderlichen Leistungsmerkmale beizubehalten.

Cloud Native Ökosystem

Kubernetes wurde von Anfang an als erweiterbare Umgebung und für eine breite und aufgeschlossene Community konzipiert. Diese Ziele und die Allgegenwärtigkeit von Kubernetes in so vielen Rechenumgebungen haben zu einem lebendigen und großen Ökosystem von Tools und Diensten geführt, die sich um Kubernetes herum entwickelt haben. Nach dem Vorbild von Kubernetes (und Docker und Linux davor) sind die meisten dieser Projekte auch Open Source. Das bedeutet, dass ein Entwickler, der mit dem Aufbau beginnt, nicht bei Null anfangen muss. In den Jahren seit der Veröffentlichung wurden für Kubernetes Tools für fast jede Aufgabe entwickelt, vom maschinellen Lernen über die kontinuierliche Entwicklung bis hin zu serverlosen Programmiermodellen. Tatsächlich besteht die Herausforderung in vielen Fällen nicht darin, eine potenzielle Lösung zu finden, sondern zu entscheiden, welche der vielen Lösungen für die Aufgabe am besten geeignet ist. Die Fülle an Tools im Cloud-Native-Ökosystem ist für viele Menschen ein wichtiger Grund, Kubernetes zu übernehmen. Wenn du das Cloud-Native-Ökosystem nutzt, kannst du von der Community entwickelte und unterstützte Projekte für fast jeden Teil deines Systems verwenden, sodass du dich auf die Entwicklung der zentralen Geschäftslogik und der Dienste konzentrieren kannst, die einzigartig für dich sind.

Wie bei jedem Open-Source-Ökosystem besteht die größte Herausforderung in der Vielfalt der möglichen Lösungen und der Tatsache, dass es oft an einer durchgängigen Integration mangelt. Eine Möglichkeit, diese Komplexität zu bewältigen, ist die technische Anleitung durch die Cloud Native Computing Foundation (CNCF). Die CNCF fungiert als branchenneutrale Heimat für den Code und das geistige Eigentum von Cloud Native-Projekten. Sie verfügt über drei Projektreifegrade, die dir bei der Einführung von Cloud Native-Projekten helfen. Die meisten Projekte in der CNCF befinden sich in der Sandbox-Phase. Sandbox bedeutet, dass sich ein Projekt noch in einem frühen Entwicklungsstadium befindet. Es wird nicht empfohlen, das Projekt zu übernehmen, es sei denn, du bist ein Early Adopter und/oder daran interessiert, zur Entwicklung des Projekts beizutragen. Die nächste Reifestufe ist das Inkubationsstadium. Projekte, die sich in der Inkubationsphase befinden, haben ihren Nutzen und ihre Stabilität durch die Übernahme und den produktiven Einsatz bewiesen, entwickeln sich aber noch weiter und bauen ihre Gemeinschaften aus. Während es Hunderte von Sandbox-Projekten gibt, sind es kaum mehr als 20 Projekte in der Inkubationsphase. Die letzte Stufe der CNCF-Projekte ist die Graduierung. Diese Projekte sind vollständig ausgereift und weit verbreitet. Es gibt nur wenige graduierte Projekte, darunter Kubernetes selbst.

Eine weitere Möglichkeit, sich im Cloud Native Ecosystem zurechtzufinden, ist die Integration mit Kubernetes-as-a-Service. Derzeit bieten die meisten KaaS-Angebote auch zusätzliche Dienste über Open-Source-Projekte aus dem Cloud-Native-Ökosystem an. Da diese Dienste in Cloud-gestützte Produkte integriert sind, kannst du sicher sein, dass die Projekte ausgereift und produktionsreif sind.

Zusammenfassung

Kubernetes wurde entwickelt, um die Art und Weise, wie Anwendungen in der Cloud entwickelt und bereitgestellt werden, radikal zu verändern. Es wurde entwickelt, um Entwicklern mehr Geschwindigkeit, Effizienz und Agilität zu ermöglichen. Inzwischen laufen viele der Internetdienste und Anwendungen, die du täglich nutzt, auf Kubernetes. Wahrscheinlich bist du bereits ein Kubernetes-Nutzer, du wusstest es nur noch nicht! Wir hoffen, dass du in diesem Kapitel eine Vorstellung davon bekommen hast, warum du deine Anwendungen mit Kubernetes bereitstellen solltest. Jetzt, wo du davon überzeugt bist, wirst du in den folgenden Kapiteln lernen , wie du deine Anwendungen bereitstellen kannst.

1 Brendan Burns et al., "Borg, Omega, and Kubernetes: Lessons Learned from Three Container-Management Systems over a Decade," ACM Queue 14 (2016): 70-93, verfügbar unter https://oreil.ly/ltE1B.

Get Kubernetes: Up and Running, 3. Auflage now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.