Kapitel 4. Wolke

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

Nur wenige Technologietrends haben die IT-Branche so stark beeinflusst wie das Cloud Computing - außer vielleicht die Virtualisierung, die eine grundlegende Technologie für das Cloud Computing ist. Als Netzwerktechniker/in fragst du dich vielleicht: "Was hat Cloud Computing mit mir zu tun?" Manche Netzwerktechniker/innen lehnen Cloud Computing ab, weil sie glauben, dass ihre Fähigkeiten für Cloud-basierte Umgebungen nicht benötigt werden. Andere, darunter auch die Autoren, sehen das ganz anders.

Die Vernetzung ist "in der Cloud" genauso wichtig wie in einer On-Premises-Umgebung. Man könnte sogar sagen, dass die Vernetzung in der Cloud sogar noch wichtiger ist. Um die Vorteile des Cloud Computing - u.a. einfache Skalierung, On-Demand-Ressourcen und ephemere Umgebungen - wirklich nutzen zu können, musst du die Automatisierung nutzen. Es ist daher nur natürlich, dass ein Buch über Netzwerkautomatisierung und Programmierbarkeit die Rolle des Cloud Computing im Zusammenhang mit der Netzwerkautomatisierung diskutiert.

Über die verschiedenen Cloud-Provider, die von ihnen angebotenen Dienste, die Entwicklung von Lösungen für Cloud-Provider und vieles mehr wurden und werden wahrscheinlich auch in Zukunft ganze Bände geschrieben werden. Deshalb soll dieses Kapitel etwas kompakter sein und nur die wichtigsten Grundlagen vermitteln, die du als Netzwerkingenieur brauchst, um Cloud Computing in dein Arsenal von Werkzeugen zur Lösung der Probleme aufzunehmen, mit denen du täglich konfrontiert bist.

Am besten fängst du damit an, eine grundlegende Definition von Cloud Computing festzulegen.

Kurze Definition von Cloud Computing

Es gibt zwar viele Definitionen von Cloud Computing (man braucht nur im Internet nach "Was ist Cloud Computing" zu suchen), aber eine der wichtigsten Definitionen stammt vom Handelsministerium der Vereinigten Staaten, genauer gesagt vom National Institute of Standards and Technology (NIST). Die NIST-Definition von Cloud Computing, die in der Sonderveröffentlichung 800-145 enthalten ist, ist eine vielschichtige Definition, die sich mit den Hauptmerkmalen von Cloud Computing sowie mit Service- und Bereitstellungsmodellen befasst. Die NIST-Definition beschreibt all diese Dinge in einer anbieterunabhängigen Weise.

Das NIST definiert fünf wesentliche Merkmale von Cloud Computing:

Selbstbedienung auf Abruf

Die Nutzer (das NIST spricht von "Verbrauchern") sollten in der Lage sein, ihre eigenen Ressourcen beim Cloud-Provider nach Bedarf bereitzustellen, ohne dass ein Mensch eingreifen muss (mit anderen Worten: ohne ein Service-Ticket einzureichen!).

Breiter Netzwerkzugang

Cloud-Umgebungen sind von Natur aus vernetzte Umgebungen, die über das Netzwerk zugänglich sind. Daher stammt auch die Aussage, dass die Vernetzung in der Cloud genauso wichtig ist - ohne allgegenwärtigen Netzwerkzugang ist es kein Cloud Computing.

Ressourcenpooling

Die Ressourcen des Cloud-Providers werden gepoolt und automatisch an mehrere Mieter verteilt. Dies umfasst physische und virtuelle Ressourcen und gilt nicht nur für die Rechenkapazität, sondern auch für Speicherung und Vernetzung.

Schnelle Elastizität

Cloud-Ressourcen sollten "unendlich" skalierbar sein (aus Sicht des Nutzers scheinen sie unbegrenzt zu sein). Je nach Bedarf sollte eine Vergrößerung oder Verkleinerung möglich sein - in manchen Fällen sogar automatisch.

Gemessener Dienst

Cloud-Ressourcen werden gemessen, und den Nutzern wird das berechnet, was sie verbrauchen. Die Nutzung kann überwacht, kontrolliert und gemeldet werden.

Die NIST-Definition definiert außerdem drei Servicemodelle:

Software as a Service (SaaS)

Der "Dienst", der hier angeboten wird, ist der Zugang zu den Anwendungen eines Anbieters, die wahrscheinlich in einer Cloud-Infrastruktur laufen. Die Cloud-Infrastruktur wird den Nutzern jedoch nicht zugänglich gemacht, sondern nur die Anwendung und die Anwendungsschnittstelle(n). Beispiele für SaaS-Angebote sind Salesforce, Okta und Microsoft 365.

Plattform als Service (PaaS)

Bei PaaS haben die Nutzer Zugang zu einer Plattform - bestehend aus "Programmiersprachen, Bibliotheken, Diensten und Tools, die vom Anbieter unterstützt werden" (so die Formulierung des NIST) - auf der sie Anwendungen einsetzen können. Diese Anwendungen können von den Nutzern erworben (gekauft oder lizenziert) oder von ihnen selbst erstellt werden. Die Nutzer haben keinen Zugriff auf die Cloud-Infrastruktur dieser Plattform, sondern nur auf die Schnittstelle(n) der Plattform. Beispiele für PaaS-Angebote sind Heroku und AWS Elastic Beanstalk.

Infrastructure as a Service (IaaS)

Das letzte Servicemodell ist IaaS, bei dem die Nutzer Zugang zur Bereitstellung von Rechen-, Speicher-, Netzwerk-, Sicherheits- und anderen grundlegenden Ressourcen haben, um beliebige Software bis hin zu Betriebssysteminstanzen und Anwendungen einzusetzen. Die Nutzer/innen haben keinen direkten Zugriff auf die Cloud-Infrastruktur, können aber die Art und Weise, wie die grundlegenden Ressourcen bereitgestellt werden, kontrollieren. So haben die Nutzer/innen zum Beispiel keinen Zugriff auf die zugrunde liegenden Komponenten, die eine Amazon Virtual Private Cloud (VPC) ausmachen, können aber VPCs erstellen und konfigurieren. Beispiele für IaaS-Angebote aus der Praxis sind viele Aspekte von AWS, viele Dienste von Microsoft Azure und mehrere Dienste von Google Cloud.

Hinweis

Die Formulierung, die wir in unseren Beispielen für IaaS verwenden - "viele Aspekte von" oder "viele Dienste, die von" angeboten werden - ist Absicht. Alle - oder fast alle - Cloud-Provider bieten eine Vielzahl von Diensten an, die in verschiedene Servicemodelle fallen. Einige Angebote sind eindeutig IaaS, während andere als PaaS oder sogar SaaS eingestuft werden können.

Schließlich werden in der NIST-Definition auch Einsatzmodelle diskutiert:

Private Cloud

Bei einer privaten Cloud wird die Cloud-Infrastruktur, die vom NIST als die Sammlung von Hardware und Software definiert wird, die die fünf wesentlichen Merkmale von Cloud Computing ermöglicht, für die Nutzung durch eine einzelne Einheit, z. B. ein Unternehmen, bereitgestellt. Diese Infrastruktur kann sich auf dem Firmengelände oder außerhalb des Firmengeländes befinden; der Standort spielt keine Rolle. Ebenso kann sie von der Organisation oder einer anderen Person betrieben werden. Der wichtigste Unterschied ist die Zielgruppe, die nur die Organisation oder das Unternehmen ist.

Community-Cloud

Eine Community Cloud wird für die Nutzung durch eine gemeinsame Gemeinschaft von Nutzern bereitgestellt - zum Beispiel für eine Gruppe von Unternehmen mit gemeinsamen gesetzlichen Anforderungen oder für Organisationen, die ein gemeinsames Ziel verfolgen. Sie kann im Besitz einer oder mehrerer Organisationen der Gemeinschaft oder einer dritten Partei sein. Ebenso kann sie von einem oder mehreren Mitgliedern der Gemeinschaft oder einem Dritten betrieben werden und unterscheidet nicht zwischen innerhalb und außerhalb der Räumlichkeiten.

Öffentliche Wolke

Die Cloud-Infrastruktur einer Public Cloud wird für die Nutzung durch die Allgemeinheit zur Verfügung gestellt. Eigentümer, Verwalter und Betreiber der öffentlichen Cloud kann ein Unternehmen, eine staatliche Organisation, eine akademische Einrichtung oder eine Kombination aus diesen sein. Eine öffentliche Cloud befindet sich auf dem Gelände des Anbieters (der Organisation, die die Cloud-Infrastruktur besitzt, verwaltet und betreibt).

Hybride Wolke

Eine hybride Cloud ist einfach eine Zusammensetzung aus zwei oder mehr verschiedenen Cloud-Infrastrukturen, die auf irgendeine Weise miteinander verbunden sind, entweder durch standardisierte oder proprietäre Technologie, die ein gewisses Maß an Daten- und Anwendungsportabilität zwischen den Cloud-Infrastrukturen ermöglicht.

Wie du an diesen drei Aspekten - den wesentlichen Merkmalen von Cloud Computing, den Servicemodellen, mit denen es den Nutzern angeboten wird, und den Bereitstellungsmodellen, mit denen es für ein Publikum bereitgestellt wird - siehst, ist Cloud Computing sehr vielseitig.

In diesem Kapitel konzentrieren wir uns vor allem auf Public-Cloud-Bereitstellungsmodelle, die typischerweise zum IaaS-Dienstmodell gehören.

Hinweis

Wir sagen "typischerweise dem IaaS-Dienstmodell zuzuordnen", weil sich einige Serviceangebote nicht eindeutig als IaaS oder PaaS kategorisieren lassen. Die Branche hat sogar neue Begriffe geprägt, wie "Functions as a Service" (FaaS) oder "Database as a Service" (DBaaS), um Angebote zu beschreiben, die nicht eindeutig in eine der vom NIST definierten Kategorien fallen.

Nachdem wir die verschiedenen Formen des Cloud Computing definiert haben, ist es nun an der Zeit, dich mit den Grundlagen der Vernetzung in der Cloud zu befassen.

Grundlagen der Vernetzung in der Cloud

Auch wenn die Vernetzung in Cloud-Umgebungen etwas anders funktioniert, ist Cloud-Networking letzten Endes nichts anderes als Networking. Viele (die meisten?) der Fähigkeiten, die du als Netzwerktechniker/in entwickelt hast, gelten auch für die Vernetzung in Cloud-Umgebungen, und das hart erarbeitete Wissen und die Erfahrung, die du gesammelt hast, werden nicht umsonst sein.

Insbesondere musst du dich nicht mehr um die Details auf der unteren Ebene kümmern (d.h. du musst nicht wissen, wie die Netzwerkkonstrukte, wie z.B. virtuelle Netzwerke, implementiert sind - du musst nur die Abstraktionen verwenden), aber Dinge wie Routing-Protokolle, Routing-Topologien, Netzwerk-Topologie-Design und die Planung von IP-Adressen werden weiterhin benötigt. Werfen wir zunächst einen Blick auf einige der wichtigsten Bausteine für Cloud-Netzwerke.

Bausteine der Cloud-Vernetzung

Auch wenn die Details bei den einzelnen Cloud-Providern unterschiedlich sind, bieten alle großen Anbieter einige grundlegende (generische) Merkmale und Funktionen:

Logische Netzwerkisolierung

Genauso wie Kapselungsprotokolle wie VXLAN, Generic Routing Encapsulation (GRE) und andere Overlay-Netzwerke schaffen, die den logischen Netzwerkverkehr vom physischen Netzwerk isolieren, bieten alle großen Cloud-Provider Mechanismen zur Netzwerkisolierung an. Bei AWS handelt es sich dabei um eine Virtual Private Cloud (VPC), die wir in "Eine kleine Cloud-Netzwerk-Topologie" näher erläutern ; bei Azure ist es ein virtuelles Netzwerk (auch als VNet bekannt) und bei Google Cloud wird es einfach Netzwerk genannt. Unabhängig vom Namen ist der grundlegende Zweck derselbe: logische Netzwerke können isoliert und voneinander getrennt werden. Diese logischen Netzwerke sind vollständig von allen anderen logischen Netzwerken getrennt, können aber - wie du gleich sehen wirst - auf verschiedene Weise miteinander verbunden werden, um die gewünschten Ergebnisse zu erzielen.

Öffentliche und private Adressierung

Workloads können öffentliche (routingfähige) Adressen oder private (nicht routingfähige) Adressen zugewiesen werden. Siehe RFC 1918 und RFC 6598. Der Cloud-Provider stellt Mechanismen zur Verfügung, mit denen öffentliche und private Workloads mit dem Internet verbunden werden können. Dies geschieht in der Regel auf der logischen Netzwerkebene, d. h. jedes logische Netzwerk kann seine eigenen IP-Adressen zugewiesen bekommen. Sich überschneidende IP-Adressräume zwischen mehreren logischen Netzwerken sind zulässig.

Dauerhafte Adressierung

Netzwerkadressen können dem Konto eines Cloud-Provider-Kunden zugewiesen werden und dann einem Arbeitsvolumen zugewiesen werden. Später kann dieselbe Adresse von der Arbeitslast abgezogen und einer anderen Arbeitslast zugewiesen werden, aber die Adresse bleibt konstant und beständig, bis sie vom Konto des Kunden wieder an den Cloud-Provider freigegeben wird. Elastische IP-Adressen (EIPs) bei AWS oder öffentliche IP-Adressen bei Azure sind Beispiele dafür.

Komplexe Topologien

Die Bausteine der logischen Netzwerkisolierung (VPCs auf AWS, VNets auf Azure, Netzwerke auf Google Cloud) können auf komplexe Weise kombiniert werden, und Netzwerkingenieure haben die Kontrolle darüber, wie der Datenverkehr zwischen diesen Bausteinen weitergeleitet wird. Große Unternehmen, die umfangreiche Cloud-Ressourcen nutzen, verfügen unter Umständen über Hunderte von logischen Netzwerken sowie Site-to-Site-VPN-Verbindungen und Rückverbindungen zu (selbstverwalteten) Netzwerken vor Ort. Die Komplexität dieser Netzwerke kann selbst mit den umfangreichsten lokalen Netzwerken mithalten oder sie sogar übertreffen, und der Bedarf an erfahrenen Netzwerkingenieuren, die wissen, wie man diese Netzwerke entwirft und ihre Implementierung automatisiert, ist groß.

Lastausgleich

Cloud-Provider bieten Load-Balancing-Lösungen an, die nach Bedarf bereitgestellt werden und Layer 4 (TCP/UDP) Load-Balancing, Layer 7 (HTTP) Load-Balancing (mit oder ohne TLS-Terminierung), internes Load-Balancing (innerhalb des privaten/nicht routingfähigen Adressraums eines Kunden in einem VPC oder VNet) oder externes Load-Balancing (für Datenverkehr, der von internetbasierten Quellen stammt) bieten.

Hinweis

Für Netzwerktechniker/innen, die sich gerade erst mit Cloud-Netzwerken vertraut machen, ist Timothy McConnaughys The Hybrid Cloud Handbook for AWS (Carpe DMVPN) eine gute Einführung in die Grundlagen der AWS-Netzwerke.

Die großen Cloud-Provider bieten zahlreiche weitere Netzwerkdienste an, die hier nicht behandelt werden, darunter API-Gateways, Zugriffskontrollen auf Netzwerkebene, Zugriffskontrollen auf Instanzebene und sogar Unterstützung für Netzwerk-Appliances von Drittanbietern, die zusätzliche Funktionen ausführen. Außerdem sind alle diese Netzwerkdienste auf Abruf verfügbar. Die Nutzer müssen sich nur in ihre Cloud-Verwaltungskonsole einloggen, das CLI-Tool des Cloud-Providers verwenden, mit den APIs des Cloud-Providers interagieren oder eines der zahlreichen Cloud-Automatisierungstools verwenden, um einen oder mehrere dieser Netzwerkdienste zu instanziieren (wir gehen in Kapitel 12 näher auf diese Tools ein). Unser Ziel in diesem Kapitel ist es nicht, alle diese Möglichkeiten zu erklären, sondern dir zu zeigen, wie sich diese Komponenten in das Gesamtbild der Netzwerkprogrammierung und -automatisierung einfügen.

Mit diesem grundlegenden Verständnis der Bausteine für Cloud-Netzwerke können wir uns nun einigen Beispielen zuwenden, wie du diese Bausteine zusammensetzen kannst.

Topologien für Cloud-Netzwerke

Um die Konzepte hinter den relativ allgemeinen Konstrukten, die im vorherigen Abschnitt beschrieben wurden, zu verdeutlichen, beschränkt sich dieser Abschnitt auf die konkreten Angebote eines bestimmten Cloud-Providers: AWS. AWS ist der größte unter den Public Cloud-Providern, sowohl was den Umfang der angebotenen Dienste als auch was die Marktdurchdringung angeht (laut einem Gartner-Bericht vom Juni 2022 hatte AWS einen Anteil von 38,9 % am gesamten Cloud-Markt). Es ist also ein guter Ausgangspunkt. Ähnliche Konzepte gelten aber auch für andere Cloud-Provider, wie Microsoft Azure, Google Cloud Platform, Alibaba Cloud, Oracle Cloud, DigitalOcean und andere.

Tipp

Im Internet gibt es viele gute Ressourcen, die dir helfen, die bewährten Methoden der Cloud-Provider kennenzulernen. Bei AWS kannst du mit dem AWS Well-Architected Framework beginnen.

In diesem Abschnitt werden vier Szenarien besprochen:

  • Eine kleine Umgebung mit nur einem einzigen logischen Netzwerk

  • Eine mittelgroße Umgebung mit ein paar bis mehreren logischen Netzwerken

  • Eine größere Umgebung mit mehreren Dutzend logischen Netzwerken

  • Eine hybride Umgebung, die sowohl lokale als auch Cloud-basierte Netzwerke umfasst

Eine kleine Cloud-Netzwerk-Topologie

Unsere kleine Cloud-Netzwerk-Topologie nutzt nur einen einzigen VPC. Auf den ersten Blick mag das einschränkend erscheinen - kann ein einzelner VPC ausreichend skalieren? Ist diese Art von Topologie nur für die kleinsten Kunden geeignet, die nur wenige Workloads in der Public Cloud haben?

Hinweis

Das Konstrukt des virtuellen Netzwerks, wie AWS VPC, ist der wichtigste Baustein für Cloud-Netzwerke. Auch wenn jeder Cloud-Provider dieses Konstrukt etwas anders umsetzt, ist die Grundidee dieselbe: Ein virtuelles Netzwerk ist ein logisches Netzwerk (mit einigen Layer 2- und 3-Funktionen), mit dem Arbeitslasten voneinander getrennt werden können. Alle Cloud-Dienste sind an ein virtuelles Netzwerk angeschlossen, so dass sie miteinander kommunizieren können (vorausgesetzt, die entsprechenden Netzwerkzugriffskontrollen sind vorhanden).

Wenn du über Skalierbarkeit nachdenkst, solltest du diese Dimensionen berücksichtigen:

IP-Adressierung

Wenn du eine VPC erstellst, musst du einen CIDR-Block (Classless Inter-Domain Routing) angeben. Es wird empfohlen, Blöcke aus den Bereichen zu verwenden, die in RFC 1918 und RFC 6598 angegeben sind. Die Größe des Blocks kann von einem /16 (mit bis zu 65.536 IP-Adressen) bis zu einem /28 (mit nur 16 IP-Adressen) reichen. Außerdem kannst du CIDR-Blöcke hinzufügen - bis zu vier zusätzliche, sich nicht überschneidende Blöcke, also insgesamt fünf CIDR-Blöcke in einem VPC - um noch mehr verfügbare IP-Adressen bereitzustellen. Fünf /16 CIDR-Blöcke würden über 300.000 IP-Adressen bereitstellen.

Bandbreite

AWS bietet zwei Mechanismen, um Arbeitslasten mit dem Internet zu verbinden. Für Arbeitslasten in einem öffentlichen Subnetz verwendest du ein Internet-Gateway. AWS beschreibt diese Komponente als "eine horizontal skalierte, redundante und hochverfügbare VPC-Komponente", die "keine Verfügbarkeitsrisiken oder Bandbreitenbeschränkungen verursacht"; siehe die Amazon VPC-Dokumentation. Für Workloads in einem privaten Subnetz verwendest du in der Regel ein NAT-Gateway, das laut AWS bis zu 100 Gbit/s skaliert. NAT steht für Network Address Translation (Netzwerkadressübersetzung) und bezeichnet den Prozess, bei dem private (nicht routingfähige) IP-Adressen auf öffentliche (routingfähige) IP-Adressen abgebildet werden.

Hinweis

Für die Zwecke dieses Buches wird ein öffentliches Subnetz als ein Subnetz definiert, das mit einem Internet-Gateway verbunden ist oder eine Standardroute dorthin hat. In der Regel wird ein öffentliches Subnetz auch so konfiguriert, dass es den Ressourcen eine routingfähige IP-Adresse zuweist. Ein privates Subnetz ist definiert als ein Subnetz, das mit einem NAT-Gateway (oder einer selbstverwalteten NAT-Instanz) verbunden ist oder eine Standardroute dorthin hat. Ein privates Subnetz wird in der Regel auch so konfiguriert, dass es nur nicht routingfähige IP-Adressen (RFC 1918/6598) verwendet.

Verfügbarkeit

Eine einzelne VPC kann sich über mehrere Verfügbarkeitszonen erstrecken, aber nicht über mehrere Regionen. Eine Region ist in der AWS-Sprache eine Gruppierung von Rechenzentren. Eine Verfügbarkeitszone(AZ) innerhalb einer Region ist ein physisch getrennter, isolierter logischer Cluster von Rechenzentren. Jede Verfügbarkeitszone verfügt über eine unabhängige Stromversorgung, Kühlung und redundante Netzwerkverbindungen. Die AZs innerhalb einer Region verfügen über Hochgeschwindigkeitsverbindungen mit geringer Latenzzeit untereinander. Der Gedanke hinter der Überbrückung mehrerer AZs ist, dass ein Ausfall in einer AZ einer Region in der Regel keine Auswirkungen auf andere AZs in einer Region hat und du so in der Lage bist, Ausfälle besser zu überstehen. Ein Subnetz ist auf eine einzelne AZ beschränkt und kann keine AZs überspannen.

Sicherheit

AWS bietet Mechanismen zur Verkehrsfilterung auf Netzwerkebene (sogenannte Netzwerk-Zugriffskontrolllisten) und Mechanismen zur Verkehrsfilterung auf Host-Ebene (sogenannte Sicherheitsgruppen). Netzwerk-Zugriffskontrolllisten (Network ACLs oder NACLs) sind zustandslos und funktionieren auf der Netzwerkebene (genauer gesagt auf der Subnetzebene). NACL-Regeln werden in der Reihenfolge der ihnen zugewiesenen Nummer abgearbeitet (betrachte die Nummer als Priorität: Die Regel mit der niedrigsten Nummer entscheidet zuerst oder hat die höchste Priorität). Sicherheitsgruppen hingegen sind zustandsbehaftet, arbeiten auf Instanzebene und werten alle Regeln aus, bevor sie entscheiden, ob sie den Verkehr zulassen. NACLs und Sicherheitsgruppen können - und sollten - zusammen verwendet werden, um die stärkste Kontrolle über die Arten von Datenverkehr zu ermöglichen, die zwischen den Workloads in einer VPC erlaubt oder nicht erlaubt sind.

Keiner dieser Bereiche stellt eine nennenswerte Einschränkung dar, was beweist, dass selbst ein Cloud-Netzwerkdesign, das eine einzelne VPC nutzt, auf Tausende von Workloads skaliert werden kann und dabei ausreichend Bandbreite, Verfügbarkeit und Sicherheit für die darin enthaltenen Workloads bietet.

In Abbildung 4-1 bringen wir all diese Konzepte zusammen, um ein typisches Single-VPC-Design in AWS zu erstellen.

npa2 0401
Abbildung 4-1. Einzelne AWS VPC Netzwerktopologie

All diese Dinge könnten manuell erstellt und über die AWS Management Console miteinander verknüpft werden, aber die Chance für den Netzwerkingenieur besteht hier darin, diesen Prozess mit Hilfe von IaC-Tools und -Prozessen zu automatisieren. In "Netzwerkautomatisierung in der Cloud" besprechen wir die verfügbaren programmierbaren Optionen für die Verwaltung von Cloud-Ressourcen.

Eine mittlere Cloud-Netzwerk-Topologie

Obwohl eine einzelne VPC sehr skalierbar ist, gibt es Gründe, mehrere VPCs zu verwenden. Der offensichtlichste Grund ist, dass sich eine VPC nicht über eine AWS-Region erstrecken kann; jede VPC ist auf die Region beschränkt, in der sie erstellt wurde. Wenn du eine Netzwerktopologie entwerfen musst, die Arbeitslasten in mehreren Regionen aufnehmen kann, ist eine Multi-VPC-Topologie die richtige Wahl. Es gibt auch andere Gründe für die Verwendung mehrerer VPCs. Nur weil du Tausende von Arbeitslasten in einer einzigen VPC unterbringen kannst, heißt das nicht unbedingt, dass du das auch tun solltest.

Öffentliche Cloud-Provider konzipieren ihre Infrastruktur so, dass sie hochverfügbar ist, und drücken dies in Form von Verfügbarkeits- und Haltbarkeits-SLAs aus. Um über diese Angebote hinauszugehen, musst du deine eigenen Hochverfügbarkeitsarchitekturen schaffen, so wie wir es in unseren Rechenzentren vor Ort tun.

Sobald deine Topologie aus mehreren VPCs besteht, entstehen neue Herausforderungen für den Netzwerkingenieur:

Verwaltung des IP-Adressraums

Während jeder VPC identische, sich überschneidende CIDR-Blöcke haben kann, brauchst du, sobald du VPCs miteinander verbinden willst, nicht überschneidende CIDR-Blöcke. Du musst einen Plan erstellen, wie die IP-Adressen aufgeteilt und den VPCs in mehreren Regionen zugewiesen werden, und gleichzeitig zukünftiges Wachstum einplanen (in Bezug auf zukünftige Subnetze in bestehenden VPCs, zusätzliche VPCs in aktuellen Regionen und neue VPCs in ganz neuen Regionen).

Konnektivität

AWS bietet eine Möglichkeit, VPCs über VPC-Peering miteinander zu verbinden. VPC-Peering bietet nicht-transitive Konnektivität zwischen zwei VPCs und erfordert, dass die VPC-Routentabellen explizit mit Routen zu den Peer-VPCs aktualisiert werden. Wenn die Zahl der VPCs klein ist, ist die Nutzung von VPC-Peering überschaubar, aber wenn die Zahl der VPCs wächst, steigt auch die Zahl der Verbindungen und die Zahl der zu aktualisierenden und zu pflegenden Routentabellen.

Verbrauchsabhängige Preise

Auch wenn du als Netzwerkingenieur eine Topologie entwerfen kannst, die die notwendige Konnektivität zwischen allen Workloads in allen VPCs in allen Regionen gewährleistet, wird deine Topologie diese Konnektivität auf die kosteneffizienteste Weise bereitstellen, wenn die Nutzung steigt? Das bedeutet, dass du die Gebühren für den Verkehr zwischen den VPCs, die Gebühren für den Verkehr zwischen den Regionen und die Egress-Gebühren für internetbasierte Ziele berücksichtigen musst (um nur einige zu nennen).

All diese Überlegungen kommen zu dem hinzu, was bereits für kleinere (einzelne VPC) Cloud-Netzwerktopologien erforderlich ist.

Eine große Cloud-Netzwerk-Topologie

In einer großen Cloud-Netzwerktopologie hast du es mit mehreren Dutzend VPCs in mehreren Regionen weltweit zu tun. VPC-Peering ist keine brauchbare Option mehr. Es gibt einfach zu viele Peering-Verbindungen, die verwaltet werden müssen, und nichttransitive Verbindungen sorgen für noch mehr Komplexität.

Es ergeben sich neue Überlegungen zur Gestaltung:

Zentralisierte Konnektivität

Mit Tools wie Transit-VPCs und Transit-Gateways kannst du von der nicht-transitiven VPC-Peering-Konnektivität zu einer vollständig gerouteten Lösung wechseln. Mit diesen Tools kannst du auch zusätzliche Optionen wie AWS Direct Connect (für dedizierte Konnektivität zurück zu lokalen Netzwerken) oder VPN-Funktionen integrieren. Auch die Implementierung eines zentralen Egresses - bei dem die "Spoke"-VPCs NAT-Gateways in der "Hub"-VPC für die Internetanbindung nutzen - ist möglich. In all diesen Fällen musst du eine Routing-Strategie wie statisches Routing oder ein dynamisches Routing-Protokoll wie BGP festlegen.

Architekturen mit mehreren Konten

In so großen Topologien ist es viel wahrscheinlicher, dass du auf Situationen triffst, in denen ganze VPCs zu verschiedenen AWS-Konten gehören. Deine Netzwerktopologie muss nun berücksichtigen, wie du eine sichere Verbindung zwischen Ressourcen herstellen kannst, die verschiedenen Unternehmen gehören.

Mit den erwähnten zentralisierten Konnektivitäts-Tools können wir Cloud-Netzwerke erweitern, um andere Cloud-Provider und lokale Netzwerke zu erreichen.

Eine hybride Cloud-Netzwerk-Topologie

Mit der zunehmenden Verbreitung von Cloud-Diensten werden neue Verbindungsszenarien immer häufiger. Zu diesen Szenarien gehören die Verbindung verschiedener Cloud-Provider (öffentlich oder privat) und die Verbindung dieser Cloud-Provider mit lokalen Netzwerken. Laut Flexeras 2023 State of the Cloud Report nutzen 87 % der befragten Unternehmen mehr als einen Cloud-Provider. Außerdem werden die Grenzen zwischen diesen Umgebungen immer unschärfer, da Cloud-Dienste auch in den Rechenzentren der Kunden vor Ort laufen können (z. B. AWS Outposts Service). Die Branche verwendet den Begriff Hybrid Cloud, um diese Szenarien zu beschreiben.

Abgesehen von den Herausforderungen bei der Verwaltung und Orchestrierung der verschiedenen Infrastrukturdienste (und der darauf aufbauenden Anwendungen), besteht die erste offensichtliche Herausforderung darin, wie all diese Umgebungen miteinander verbunden werden können.

Für die Hybrid-Cloud-Konnektivität gibt es keine Einheitslösung. Alle Public Cloud-Provider bieten Mechanismen an, um ihre Dienste mit lokalen Netzwerken zu verbinden. Diese Mechanismen basieren in der Regel auf VPN-Verbindungen oder speziellen Konnektivitätsdiensten (z. B. AWS Direct Connect oder Azure ExpressRoute). Abbildung 4-2 zeigt ein Beispiel für eine hybride Cloud-Netzwerktopologie: Ein lokales Netzwerk ist über dedizierte Transportdienste mit den AWS- und Azure-Clouds verbunden, und die Routing-Informationen werden über BGP ausgetauscht. Du könntest beide Cloud-Provider auch über das lokale Netzwerk verbinden.

npa2 0402
Abbildung 4-2. Hybride Netzwerktopologie
Hinweis

Wenn du dein Netzwerk mit einem Cloud-Provider verbindest, kannst du deine eigenen Cloud-Workloads (z. B. deine Datenbanken und VMs), aber auch die allgemeinen Cloud-Dienste erreichen. Im Falle von AWS kannst du das Objektspeichersystem Amazon Simple Storage Service (S3) und viele andere erreichen.

Diese Konnektivitätsmechanismen sind jedoch nicht in der Lage, Cloud-Provider direkt miteinander zu verbinden. Um Cloud-Provider miteinander zu verbinden, musst du andere Lösungen nutzen, wie z. B. das Routing über ein lokales Netzwerk (siehe Abbildung 4-2 ) oder die Einrichtung eigener VPN-Verbindungen zwischen den Cloud-Providern, um ein Overlay-Netzwerk zu schaffen. Wenn dir die Verwaltung dieser Verbindungen zu mühsam wird, kannst du auf Drittanbieter zurückgreifen, die diese Verbindungen für dich orchestrieren. Zwei Beispiele für diese Anbieter sind Aviatrix und Alkira.

In jedem Fall musst du eine Netzwerktopologie entwerfen, die es ermöglicht, all diese Umgebungen mit einer einheitlichen Netzwerkadressierung und einem Routingplan zwischen den Netzwerksegmenten zu verbinden. Dies erfordert die Netzwerkkenntnisse, die du in deiner Karriere als Netzwerktechniker/in entwickelt hast.

Außerdem steigt mit zunehmender Komplexität auch der Bedarf an Automatisierung exponentiell an. Es gibt immer mehr "Dinge", die verwaltet und konfiguriert werden müssen, z. B. Transit-Gateway-Anhänge, VPN-Verbindungen oder separate Subnetze für bestimmte Arten von Verbindungen. Um sie effektiv zu verwalten, ist eine Form der Automatisierung erforderlich.

Netzwerkautomatisierung in der Cloud

Die Vernetzung in der Cloud ist im Gegensatz zu vielen Netzwerken vor Ort von Natur aus auf Automatisierung ausgelegt. Sie ist ein Nebenprodukt der Qualitäten des Cloud Computing gemäß der NIST-Definition in der "Kurzdefinition von Cloud Computing". Wir können die Netzwerkautomatisierung in der Cloud auf drei Arten angehen:

  • Direkte Nutzung der APIs des Cloud-Providers

  • Verwendung des CLI-Tools des Cloud-Providers

  • Verwendung eines Tools, das speziell für die Automatisierung der Cloud entwickelt wurde

Nutzung der APIs des Cloud-Providers

Cloud-Provider bieten API-first-Plattformen an. Sie verfügen über eine Vielzahl von APIs, mit denen die Bereitstellung von Cloud-Ressourcen automatisiert werden kann. Jede andere Methode (d.h. CLI-Tools oder eigens entwickelte Tools) ist nur ein Wrapper um diese APIs.

Allerdings wirst du die APIs in der Regel nicht direkt nutzen. Selbst wenn du direkt auf die APIs zugreifen willst, wirst du wahrscheinlich eine Programmiersprache verwenden, die einen Wrapper um die API herum implementiert (d. h. ein Software Development Kit oder SDK). AWS unterhält zum Beispiel viele SDKs für verschiedene Programmiersprachen, wie Boto3 für Python und AWS SDK für Go. Die Verwendung eines SDKs macht die Interaktion mit der API einfacher, da du dich nicht um die HTTP-Anfragen und -Antworten kümmern musst.

Tipp

Die APIs unterstützen verschiedene Versionen, um die Abwärtskompatibilität deines Codes zu gewährleisten. Das Gleiche gilt für die SDKs.

In den folgenden Kapiteln lernst du etwas über Programmiersprachen (Kapitel 6 und 7) sowie über HTTP-APIs(Kapitel 10). Deshalb werden wir hier keine Beispiele für die Nutzung von APIs behandeln. Beispiele für die Verwendung von REST-APIs, wie die von AWS, findest du in Kapitel 10. Dort lernst du, wie du mit verschiedenen Methoden wie cURL, Postman, Python und Go mit den APIs interagieren kannst. Auch wenn diese Beispiele nicht spezifisch für die Bereitstellung von Cloud-Diensten sind, gelten für alle APIs die gleichen Prinzipien, sodass du das Wissen auf den Anwendungsfall der Bereitstellung von Cloud-Diensten anwenden kannst.

Verwendung des CLI-Tools des Cloud-Providers

Die Befehlszeilenschnittstelle ist eine weitere Benutzeroberfläche, um mit den APIs des Cloud-Providers zu interagieren. Das CLI-Tool, das von vielen Cloud-Providern angeboten wird, ist oft nur ein Wrapper für die Standard-APIs des Anbieters, was die Interaktion mit ihnen erleichtert. Das AWS CLI-Tool nutzt zum Beispiel ein Python-SDK, um diese Benutzeroberfläche bereitzustellen.

Das CLI-Tool kann manuell von einem Betreiber verwendet werden, um die Cloud-Dienste zu verwalten, oder es kann auf automatisierte Weise eingesetzt werden. Zum Beispiel kannst du das CLI-Tool in einem Shell-Skript verwenden. In Kapitel 12 findest du Beispiele (mit der notwendigen Konfiguration) für die Verwendung des AWS CLI-Tools zur Interaktion mit der AWS-Cloud. In der Zwischenzeit zeigen wir dir anhand eines Beispiels aus dem AWS CLI-Repository, wie du das AWS CLI-Tool zur Erstellung einer VPC verwendest:

$ aws ec2 create-vpc \                                                  1
    --cidr-block 10.0.0.0/16 \                                          2
    --tag-specification ResourceType=vpc,Tags=[{Key=Name,Value=MyVpc}]` 3

{                                                                       4
    "Vpc": {
        "CidrBlock": "10.0.0.0/16",
        "DhcpOptionsId": "dopt-5EXAMPLE",
        "State": "pending",
        "VpcId": "vpc-0a60eb65b4EXAMPLE",
        "OwnerId": "123456789012",
        "InstanceTenancy": "default",
        "Ipv6CidrBlockAssociationSet": [],
        "CidrBlockAssociationSet": [
            {
                "AssociationId": "vpc-cidr-assoc-07501b79ecEXAMPLE",
                "CidrBlock": "10.0.0.0/16",
                "CidrBlockState": {
                    "State": "associated"
                }
            }
        ],
        "IsDefault": false,
        "Tags": [
            {
                "Key": "Name",
                "Value": MyVpc"
            }
        ]
    }
}
1

aws ec2 create-vpc ist der Befehl zum Erstellen einer VPC; ec2 ist der AWS-Servicetyp, und create-vpc ist die spezifische Aktion.

2

Jede AWS-Ressource hat obligatorische und optionale Argumente. Für die VPC ist nur der CIDR-Block obligatorisch.

3

Tagsoder Metadaten, ist ein gemeinsamer Parameter für alle Cloud-Ressourcen, um weitere Dimensionen hinzuzufügen und die Klassifizierung der Ressourcen zu verbessern.

4

Die Ausgabe des Befehls ist ein JSON-Objekt mit den Details der erstellten VPC, das direkt aus der AWS API-Antwort stammt.

Die vollständige Dokumentation des AWS CLI-Tools findest du online.

Verwendung eines Tools, das speziell für die Automatisierung der Cloud entwickelt wurde

Zusätzlich zu den APIs des Cloud-Providers wurden spezielle Tools entwickelt, um die Cloud zu automatisieren. Diese Tools werden in der Regel als Infrastruktur-als-Code-Tools bezeichnet, weil sie es dir ermöglichen, deine Infrastruktur als Code zu definieren (in einem deklarativen Ansatz) und sie dann entsprechend zu verwalten.

Du könntest die Cloud auch automatisieren, indem du einen imperativen Ansatz verwendest (z. B. mit Ansible), aber wir halten das für ein Antipattern für die Bereitstellung von Diensten und empfehlen es daher für das Konfigurationsmanagement. Beide Ansätze können nebeneinander bestehen, da du einige Dienste nach der Bereitstellung konfigurieren musst. In Kapitel 12 gehen wir näher auf dieses Thema ein.

Um die Tools, die einen deklarativen Ansatz umsetzen, zu klassifizieren, müssen wir zwei Dimensionen berücksichtigen: ob sie cloud-spezifisch oder multicloud sind und ob sie eine Programmiersprache oder eine domänenspezifische Sprache (DSL) verwenden, um die Infrastruktur zu definieren.

Hinweis

Eine DSL wurde entwickelt, um ein bestimmtes Problem zu lösen; im Gegensatz dazu ist eine Programmiersprache eine Allzwecksprache, die zur Lösung jedes Problems verwendet werden kann. DSLs sind dafür gedacht, von Nicht-Programmierern verwendet zu werden. Sie sind daher in der Regel leichter zu erlernen und zu verwenden als Programmiersprachen, aber sie sind weniger flexibel. Beispiele für DSLs sind Structured Query Language (SQL), HTML, die YAML-basierte Syntax in Ansible-Playbooks und die HashiCorp Configuration Language (HCL) in Terraform (mehr über YAML in Kapitel 8 und über Ansible und HCL/Terraform in Kapitel 12).

In Tabelle 4-1 verwenden wir diese beiden Dimensionen, um einige der beliebtesten Tools zu klassifizieren.

Tabelle 4-1. Speziell entwickelte Tools für die Automatisierung der Cloud
Programmiersprache Domänenspezifische Sprache

Einzelne Wolke

AWS Cloud Development Kit (CDK)

AWS CloudFormation, Azure Resource Manager

Multicloud

Pulumi, CDK für Terraform

Terraform

Du solltest das am besten geeignete Tool für deinen Kontext wählen. In Kapitel 12 erklären wir, wie du Terraform und seine DSL verwendest, denn es ist das beliebteste Tool für Multicloud-Umgebungen.

Nach dieser kurzen Einführung in die Cloud-Vernetzung gehen wir nun auf die Beziehung zwischen Containern und Cloud-Diensten ein.

Container

Wenn du nicht gerade nicht aufpasst, was in der Welt der Technologie passiert, hast du sicher schon von Containern gehört. Container sind letzten Endes nur Prozesse, die auf einer Betriebssysteminstanz laufen. Ein wichtiger Aspekt zum Verständnis von Containern ist, dass es so etwas wie einen Container gar nicht gibt. Was wir als Container bezeichnen, ist ein Prozess, der auf einer Betriebssysteminstanz läuft und bestimmte Funktionen des Betriebssystems nutzen kann oder auch nicht. Warum bekommen Container also so viel Aufmerksamkeit? Dafür gibt es einige sehr gute Gründe:

Isolierung

Ein Prozess, der in einem Container läuft - oder man könnte auch sagen, ein containerisierter Prozess - wird durch eine Reihe von Funktionen auf Betriebssystemebene, den sogenannten Namensräumen, von anderen Prozessen isoliert . Diese Namensräume können den Prozess auf verschiedene Art und Weise isolieren; zum Beispiel kann der Prozess glauben machen, dass er der einzige Prozess ist, der auf dem Betriebssystem läuft (über den PID-Namensraum), dass er seine eigenen Netzwerkschnittstellen hat (über den Netzwerk-Namensraum) oder dass er seine eigenen Benutzer und Gruppen hat (über den Benutzer-Namensraum). Welche Namensräume genau verwendet werden, hängt von der Container-Laufzeitumgebung (der Engine, die für die Einrichtung von Prozessen in einem Container zuständig ist), dem Vorhandensein oder Fehlen eines Container-Orchestrators (ein Tool, das für die Verwaltung des Lebenszyklus von Containern zuständig ist) und anderen Faktoren ab. Im Abschnitt über Kubernetes, einer beliebten Plattform für die Container-Orchestrierung, wirst du dies in Aktion sehen.

Vertrieb

Ein weiterer Aspekt von Containern ist, dass sie aus einem Container-Image erstellt werden. Ein Container-Image bündelt alle Abhängigkeiten, die der Prozess, der in einem Container läuft, benötigt. Dazu gehören alle ausführbaren Dateien, Systembibliotheken, Tools und sogar Umgebungsvariablen und andere erforderliche Konfigurationseinstellungen. Da ein Container-Image nicht den gesamten Kernel und das Betriebssystem enthält, sondern nur das, was für die Ausführung eines bestimmten Prozesses benötigt wird, sind die Images in der Regel viel kleiner als ein VM-Image. In Verbindung mit der Möglichkeit, Container-Images einfach in einer Registry zu veröffentlichen(Push) und von anderen Nutzern abzurufen(Pull), können Container-Images leicht verbreitet werden.

Nehmen wir ein Open-Source-Projekt wie den Caddy-Webserver. Die Betreuer eines Projekts wie Caddy können ihr Projekt und alle seine Abhängigkeiten bündeln und den Nutzern in einem Container-Image zur Verfügung stellen. Die Nutzer können das Container-Image von Caddy in wenigen Minuten mit ein paar Befehlen einsetzen.

Wiederverwendung

Container-Images sind nicht nur einfach zu erstellen, zu verteilen und zu nutzen, sondern auch für andere leicht wiederzuverwenden. Nehmen wir noch einmal den Caddy-Webserver. Wenn ein Nutzer Caddy zusammen mit benutzerdefinierten Webinhalten einsetzen möchte, ist es trivial, ein neues Container-Image zu erstellen, das auf dem Caddy Container-Image basiert. Jemand anderes könnte wiederum dein benutzerdefiniertes Image verwenden, um sein eigenes Image zu erstellen. Diese einfache Wiederverwendung könnte einer der wichtigsten und einflussreichsten Aspekte von Containern und Container-Images sein.

Geschwindigkeit

Ein Container läuft nicht schneller als ein "normaler" Prozess auf einem Host, aber Container selbst sind viel schneller zu starten als andere Isolationsmechanismen wie VMs. Die Größe eines Container-Images - manchmal nur ein paar Megabyte im Vergleich zu Hunderten oder Tausenden von Megabyte bei einem VM-Festplatten-Image - macht die Verteilung von Containern schneller. Schließlich können neue Container-Images oft innerhalb von Minuten erstellt werden, so dass der Zeitaufwand für die Erstellung eines neuen Container-Images deutlich geringer ist als der Zeitaufwand für die Erstellung eines VM-Images, selbst wenn Automatisierungswerkzeuge wie Packer für die Erstellung von VM-Images verwendet werden.

Container wurden 2013 mit der Einführung von Docker populär, aber Container gibt es schon viel länger. Bevor Docker aufkam, gab es Container in Form von LXC, Linux-VServer, OpenVZ, FreeBSD Jails, Oracle Solaris Containers (und Solaris Zones) und sogar schon bei der ursprünglichen Einführung von chroot. All dies waren Mechanismen auf Betriebssystemebene zur Isolierung von Prozessen auf einem Host. LXC gilt als die erste vollständige Implementierung von Containern, die sowohl Kontrollgruppen (cgroups) zur Begrenzung der Ressourcennutzung als auch Namensräume zur Isolierung von Prozessen nutzte , ohne dass ein angepasster Linux-Kernel erforderlich war.

Hinweis

Wenn du an weiteren Details zu Docker interessiert bist, empfehlen wir dir Using Docker von Adrian Mouat (O'Reilly).

Nach dem Aufstieg von Docker begannen sich Standards rund um Container zu entwickeln. Eine der wichtigsten Standardisierungsbemühungen ist die Open Container Initiative (OCI), die 2015 von Docker, CoreOS (jetzt Teil von Red Hat, das wiederum Teil von IBM ist) und anderen gegründet wurde. Die OCI hat dazu beigetragen, Standards für die Image-Spezifikation (wie Container-Images erstellt werden), die Runtime-Spezifikation (wie ein Container aus einem Container-Image instanziiert wird) und die Distributionsspezifikation (wie Container über eine Registry verteilt und gemeinsam genutzt werden) festzulegen. Zum Teil durch diese Standardisierung angetrieben (und in gewissem Maße dazu beigetragen, diese Standardisierung voranzutreiben), entstanden auch Standardimplementierungen: runc als Standardimplementierung für die Ausführung eines Containers gemäß der OCI-Spezifikation und containerd als Standardimplementierung einer Container-Laufzeitumgebung für den gesamten Lebenszyklus, die auf runc aufbaut.

Hinweis

Obwohl in den letzten Jahren große Fortschritte bei der Integration von Containerfunktionen in Windows gemacht wurden, sind Container auch heute noch größtenteils eine reine Linux-Sache. Die Diskussion über Container in diesem Kapitel konzentriert sich ausschließlich auf Linux-Container.

Da du nun besser verstehst, was Container sind und welche Vorteile sie bieten, wollen wir die Frage beantworten, warum wir Container in einem Kapitel über die Cloud behandeln, vor allem, wenn du bereits gesehen hast, dass Container in erster Linie ein Linux-Thema sind. Warum werden sie nicht im Linux-Kapitel und nicht im Cloud-Kapitel behandelt?

Was haben Container mit der Cloud zu tun?

An und für sich haben Container nichts mit Cloud Computing zu tun. Sie sind ein Konstrukt auf Betriebssystemebene und können sowohl auf deinem lokalen Laptop als auch auf einer VM, die auf einem Hypervisor in einem Rechenzentrum läuft, oder in einer Cloud-Instanz eingesetzt werden. Seit ihrer Einführung haben sich Container jedoch zu einem wichtigen Bestandteil des Cloud Native Computing entwickelt . Nach der Definition der Cloud Native Computing Foundation (CNCF) ermöglicht Cloud Native Computing "Unternehmen, skalierbare Anwendungen in modernen und dynamischen Umgebungen wie öffentlichen, privaten und hybriden Clouds zu entwickeln und auszuführen und dabei die Vorteile des Cloud Computing voll auszuschöpfen."

Angesichts der Bedeutung von Containern für das Cloud Native Computing und der weiten Verbreitung von Container-basierten Diensten, die heute von öffentlichen Cloud-Providern angeboten werden - wie AWS ECS, Google Cloud Run und Azure Container Instances (ACI) - halten wir es für angemessen, Container in diesem Cloud-Kapitel zu behandeln.

Was haben Container mit Netzwerken zu tun?

Die Platzierung von Containern in einem auf die Cloud fokussierten Kapitel ist hoffentlich sinnvoll, aber eine größere Frage muss noch beantwortet werden: Was haben Container mit Netzwerken zu tun? Wir sind der Meinung, dass es aus mehreren Gründen wichtig ist, Inhalte über Container in ein Buch über Netzwerkprogrammierung und -automatisierung aufzunehmen:

  • Die Art und Weise, wie Container-Netzwerke gehandhabt werden - insbesondere wenn Container in Verbindung mit der Container-Orchestrierungsplattform Kubernetes verwendet werden - unterscheidet sich so sehr von dem, was die meisten Netzwerktechniker bereits kennen und verstehen, dass es sich lohnt, diese Unterschiede hervorzuheben und eine Brücke zwischen Kubernetes-Konzepten und "typischen" Netzwerkkonstrukten zu schlagen.

  • Netzwerkingenieure möchten Container vielleicht in Verbindung mit anderen Tools verwenden. Ein Netzwerkingenieur könnte zum Beispiel ein Container-Image für "Netzwerk-Tools" erstellen, das alle wichtigen Netzwerk- und Netzwerkautomatisierungs-Tools (einschließlich aller notwendigen Abhängigkeiten) in einem einzigen Image bündelt, das sich leicht verteilen und auf jeder kompatiblen Plattform ausführen lässt. Container vereinfachen auch das Einrichten von Netzwerkentwicklungsumgebungen, wie du in Kapitel 5 erfahren wirst, wenn wir Containerlab behandeln.

  • Netzwerkanbieter können Container in ihren Plattformen einsetzen, insbesondere auf Linux-basierten Plattformen. Die Isolationsmechanismen von Containern in Kombination mit der einfachen Verteilung machen Container zu einer idealen Methode, um neue Funktionen für Netzwerkplattformen bereitzustellen.

Wir werden also die Grundlagen des Container-Netzwerks besprechen, einschließlich der Frage, wie Container voneinander isoliert sind und wie sie miteinander und mit der Außenwelt kommunizieren können.

Linux-Netzwerke für Container erweitern

Ein Grund, warum im Linux-Kapitel(Kapitel 3) so viel Zeit auf die grundlegenden Bausteine des Netzwerks verwendet wird, ist, dass Container (und Container-Orchestrierungssysteme wie Kubernetes, wie du in "Kubernetes" sehen wirst ) diese grundlegenden Bausteine erweitern, um Container-Netzwerke zu ermöglichen. Ein gutes Verständnis der Grundbausteine sorgt für die nötige Struktur, die es einfacher macht, container-spezifische Konzepte in dein Wissen aufzunehmen.

Für die Vernetzung der Container werden fünf Linux-Technologien eingesetzt:

  • Namensräume im Netzwerk

  • Virtuelle Ethernet (veth) Schnittstellen

  • Brücken

  • IP-Routing

  • IP-Masquerading

Brücken und IP-Routing - ein Begriff, der die Fähigkeit eines Linux-basierten Systems beschreibt, als Layer 3 (IP-basierter) Router zu fungieren - wurden in Kapitel 3 behandelt. Die übrigen drei Technologien werden in den folgenden Abschnitten behandelt. Wir beginnen mit den Linux-Netzwerk Namensräumen.

Linux Netzwerk Namensräume

Namensräume sind ein Isolationsmechanismus; sie schränken ein, was Prozesse in dem Namensraum "sehen" können. Der Linux-Kernel stellt mehrere Namensräume zur Verfügung; für die Netzwerkarbeit sind vor allem die Netzwerk-Namensräume von Bedeutung. Netzwerk-Namensräume können verwendet werden, um mehrere separate Routing-Tabellen oder mehrere separate iptables-Konfigurationen zu unterstützen oder um die Sichtbarkeit von Netzwerkschnittstellen einzuschränken. In mancher Hinsicht sind sie wahrscheinlich am ehesten mit VRF-Instanzen in der Netzwerkwelt verwandt.

Hinweis

Während Netzwerk-Namensräume zur Erstellung von VRF-Instanzen verwendet werden können, arbeitet die Linux-Kernel-Gemeinschaft derzeit daran, eine "richtige" VRF-Funktionalität in Linux zu integrieren. Diese vorgeschlagene VRF-Funktionalität würde eine zusätzliche logische Layer-3-Trennung innerhalb eines Namensraums ermöglichen. Es ist noch zu früh, um zu sehen, wohin das führen wird, aber wir möchten, dass du es trotzdem weißt.

Einige Anwendungsfälle für Linux-Netzwerk-Namensräume sind die folgenden:

Pro-Prozess-Routing

Wenn du einen Prozess in seinem eigenen Netzwerk-Namensraum laufen lässt, kannst du das Routing pro Prozess konfigurieren.

Aktivieren von VRF-Konfigurationen

Wir haben zu Beginn dieses Abschnitts erwähnt, dass Netzwerk-Namensräume wahrscheinlich am engsten mit VRF-Instanzen in der Netzwerkwelt verwandt sind.

Unterstützung für sich überschneidende IP-Adressräume

Du könntest auch Namensräume im Netzwerk verwenden, um überlappende IP-Adressräume zu unterstützen, in denen dieselbe Adresse (oder derselbe Adressbereich) für verschiedene Zwecke verwendet wird und unterschiedliche Bedeutungen hat. Im Großen und Ganzen müsstest du dies wahrscheinlich mit Overlay-Netzwerken und/oder NAT kombinieren, um einen solchen Anwendungsfall vollständig zu unterstützen.

Container-Vernetzung

Netzwerk Namensräume sind auch ein wichtiger Bestandteil der Art und Weise, wie Container mit einem Netzwerk verbunden sind. Container-Laufzeiten verwenden Netzwerk-Namensräume, um die Netzwerkschnittstellen und Routing-Tabellen einzuschränken, die der containerisierte Prozess sehen oder nutzen darf.

In dieser Diskussion über Netzwerk-Namensräume liegt der Schwerpunkt auf dem letzten Anwendungsfall, der Vernetzung von Containern, und darauf, wie Netzwerk-Namensräume verwendet werden. Außerdem wird nur erörtert, wie die Docker-Container-Laufzeitumgebung Netzwerk-Namensräume nutzt. Und schließlich werden wir, wie bereits erwähnt, nur Linux-Container besprechen und keine Container auf anderen Betriebssystemen.

Wenn du einen Container startest, erstellt Docker in der Regel einen Netzwerk-Namensraum für diesen Container. Du kannst dieses Verhalten beobachten, indem du den Befehl lsns verwendest. Wenn du zum Beispiel lsns -t net ausführst, um die Netzwerk-Namensräume auf einem Ubuntu-Linux-System anzuzeigen, auf dem keine Container laufen, siehst du nur einen einzigen Netzwerk-Namensraum.

Wenn du jedoch einen Docker-Container auf demselben System mit docker run --name nginxtest -p 8080:80 -d nginx startest, wird lsns -t net etwas anderes anzeigen. Hier ist die Ausgabe von lsns -t net -J, die die Namensräume des Netzwerks als JSON serialisiert auflistet:

{
  "namespaces": [
    {
      "ns": 4026531840,                                       1
      "type": "net",
      "nprocs": 132,
      "pid": 1,
      "user": "root",
      "netnsid": "unassigned",
      "nsfs": null,
      "command": "/sbin/init"
    },
    {
      "ns": 4026532233,                                       2
      "type": "net",
      "nprocs": 5,
      "pid": 15422,
      "user": "root",
      "netnsid": "0",
      "nsfs": "/run/docker/netns/b87b15b217e8",
      "command": "nginx: master process nginx -g daemon off;" 2
    }
  ]
}
1

Der Host- (oder Root- oder Standard-) Namensraum, der auf jedem Linux-System vorhanden ist. Wenn wir mit Netzwerkschnittstellen, Routing-Tabellen oder anderen Netzwerkkonfigurationen interagieren, arbeiten wir in der Regel mit dem Standard-Namensraum.

2

Der Namensraum, der von Docker erstellt wurde, als wir den Container ausgeführt (erstellt) haben. Es gibt mehrere Möglichkeiten, um festzustellen, ob es sich um den von Docker erstellten Namensraum handelt, aber in diesem Fall verrät es das Feld command.

In manchen Fällen - wie im vorangegangenen Beispiel - ist es einfach zu erkennen, welcher Netzwerk-Namensraum welcher ist. Für eine konkretere Bestimmung kannst du die folgenden Schritte verwenden:

  1. Verwende den Befehl docker container ls, um die Container-ID des betreffenden Containers zu erhalten.

  2. Verwende den docker container inspect container-id um die Docker-Laufzeitumgebung dazu zu bringen, detaillierte Informationen über den angegebenen Container zu liefern. Dabei wird eine Menge Ausgabe in JSON erzeugt; du kannst diese Informationen leichter verarbeiten, wenn du sie durch ein Dienstprogramm wie jq leitest.

  3. Suche nach der Eigenschaft SandboxKey. Sie gibt einen Pfad an, wie /var/run/docker/netns/b87b15b217e8. Vergleiche das mit der Ausgabe von lsns und seiner Eigenschaft nsfs. Mit Ausnahme des Präfixes /var stimmen die Werte überein, wenn du dir denselben Container ansiehst.

Hinweis

Wenn du einen Container nicht in einem Netzwerk-Namensraum starten willst, unterstützt Docker ein --network=host Flag, das den Container im Host-Namensraum (auch bekannt als Root- oder Standard-Namensraum) startet.

Der Befehl ip (aus dem Paket iproute2 ), der in Kapitel 3 ausführlich behandelt wird, verfügt auch über einen Unterbefehl ip netns, mit dem der Benutzer Netzwerk-Namensräume erstellen, manipulieren und löschen kann. Die von Docker und anderen Container-Laufzeiten erstellten Netzwerk-Namensräume sind jedoch in der Regel für den Benutzer nicht sichtbar, selbst wenn er mit sudo erweiterte Rechte erhält. Der in diesem Abschnitt verwendete Befehl lsns zeigt jedoch Netzwerk-Namensräume an , die von Docker oder einer anderen Container-Laufzeitumgebung erstellt wurden. (Obwohl wir hier nur von Netzwerk-Namensräumen sprechen, kann lsns alle Arten von Namensräumen anzeigen, nicht nur Netzwerk-Namensräume).

Netzwerk-Namensräume können nicht nur in Containern verwendet werden. Einen detaillierteren Einblick in die Arbeit mit Netzwerk-Namensräumen, die nicht in Containern verwendet werden, findest du in den zusätzlichen Online-Inhalten zu diesem Buch. Dort findest du Beispiele dafür, wie du mit dem Befehl ip netns die Netzwerkkonfiguration eines Linux-Hosts manipulieren kannst, z. B. indem du Schnittstellen zu einem Netzwerk-Namensraum hinzufügst oder daraus entfernst.

Linux-Namensräume sind eine wichtige Komponente für die Vernetzung von Containern, aber ohne einen Mechanismus, der das Verhalten einer echten Netzwerkschnittstelle simuliert, wären die Möglichkeiten unserer Systeme, Container mit dem Netzwerk zu verbinden, stark eingeschränkt. Tatsächlich könnten wir nur so viele Container betreiben, wie wir physische Netzwerkschnittstellen im Host unterbringen können. Virtuelle Ethernet-Schnittstellen sind die Lösung für diese Einschränkung.

Virtuelle Ethernet-Schnittstellen

So wie der Linux-Kernel mit Hilfe von Namensräumen eine Netzwerkisolierung ermöglicht, kannst du mit virtuellen Ethernet-Schnittstellen (auch Veth-Schnittstellen genannt) diese Isolierung absichtlich durchbrechen. Veth-Schnittstellen sind logische Schnittstellen; sie entsprechen keiner physischen Netzwerkkarte oder anderen Hardwarekomponenten. Daher kannst du viel mehr Veth-Schnittstellen haben als physische Schnittstellen.

Außerdem gibt es Veth-Schnittstellen immer in Paaren: Der Verkehr, der an einer Schnittstelle des Paares eingeht, kommt an der anderen Schnittstelle des Paares wieder heraus. Aus diesem Grund werden sie auch als Veth-Paare bezeichnet. Wie andere Arten von Netzwerkschnittstellen kann auch eine Veth-Schnittstelle einem Netzwerk-Namensraum zugewiesen werden. Wenn eine Veth-Schnittstelle - ein Mitglied eines Veth-Paars - einer Netzwerkschnittstelle zugewiesen wird, hast du gerade zwei Netzwerk-Namensräume miteinander verbunden (weil der Verkehr, der in eine Veth-Schnittstelle in einem Namensraum eintritt, die andere Veth-Schnittstelle im anderen Namensraum verlässt).

Dieses Verhalten - ein Mitglied eines Veth-Paares in einen Netzwerk-Namensraum zu legen und die andere Schnittstelle im Host- (oder Standard-) Namensraum zu belassen - ist der Schlüssel zum Container-Netzwerk. So funktionieren alle grundlegenden Container-Netzwerke.

Mit dem gleichen Docker-Container, der im vorherigen Abschnitt erstellt wurde (ein einfacher nginx-Container, der mit dem Befehl docker run --name nginxtest -p 8080:80 -d nginx gestartet wurde), wollen wir uns nun ansehen, wie das Netzwerk dieses Containers gehandhabt wird.

Zunächst kennst du bereits den Netzwerk-Namensraum, den der nginx-Container verwendet; du hast ihn mit lsns -t net und docker container inspect ermittelt und die Eigenschaften nsfs und SandboxKey der beiden Befehle abgeglichen.

Mit diesen Informationen kannst du nun den Befehl nsenter verwenden, um andere Befehle innerhalb des Namensraums des nginx-Docker-Containers auszuführen. (Ausführliche Informationen über den Befehl nsenter findest du unter man nsenter.) Das Flag nsenter, mit dem du einen Befehl im Netzwerk-Namensraum eines anderen Prozesses ausführen kannst, lautet -n oder --net. Mit diesem Flag gibst du den Netzwerk-Namensraum für den nginx-Container an, wie in Beispiel 4-1 gezeigt.

Beispiel 4-1. Ausführen von ip link innerhalb eines Namensraumes mit nsenter
ubuntu2004:~$ sudo nsenter --net=/run/docker/netns/b87b15b217e8 ip link list
1: lo: LOOPBACK,UP,LOWER_UP mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT
 group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
4: eth0@if5: BROADCAST,MULTICAST,UP,LOWER_UP mtu 1500 qdisc noqueue state UP  1
 mode DEFAULT group default
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
1

Der Schnittstellenname lautet eth0@if5. Der Schnittstellenindex - die Zahl am Anfang der Zeile vor dem Schnittstellennamen - lautet 4.

Vergleiche das mit der Ausgabe von ip link list im Netzwerk-Namensraum host (default) aus Beispiel 4-2.

Die Verbindung zwischen den beiden Ausgängen ist das Suffix @ifX. Bei der Schnittstelle mit dem Index 5 (das ist die Schnittstelle veth68bba38 im Host-Namensraum) zeigt das Suffix auf die Schnittstelle, deren Index 4 ist. Die Schnittstelle mit dem Index 4 ist eth0 im Netzwerk-Namensraum des nginx-Containers (siehe Beispiel 4-1), und ihr Suffix zeigt auf den Schnittstellenindex 5 (siehe Beispiel 4-2).

So kannst du Veth-Paare bestimmen. In diesem Fall siehst du, dass Docker ein Veth-Paar für diesen Container erstellt hat. Ein Teil des veth-Paares befindet sich im Netzwerk-Namensraum des nginx-Containers, der andere Teil des veth-Paares bleibt im Host-Namensraum. In dieser Konfiguration wird der Datenverkehr, der in eth0@if5 im Netzwerk-Namensraum des nginx-Containers eingeht, veth68bba38@if4 im Host-Namensraum verlassen und somit von einem Namensraum in einen anderen Namensraum wechseln.

Tipp

Du kannst auch ip -d link list verwenden, um mehr Details in der Ausgabe anzuzeigen. Die zusätzliche Ausgabe enthält eine Zeile mit dem Wort veth, um ein Mitglied eines Veth-Paares zu kennzeichnen. Dieser Befehl zeigt auch deutlich an, wenn eine Schnittstelle eine Bridge-Schnittstelle ist (die zusätzliche Ausgabe zeigt bridge an) und wenn eine Schnittstelle Teil einer Bridge ist (gekennzeichnet durch bridge_slave). Falls das Suffix @ifX nicht vorhanden ist, kannst du auch den Befehl ethtool -S veth-interfaceverwenden, wodurch die Zeile peer_ifindex: in die Ausgabe aufgenommen wird. Die Zahl dort bezieht sich auf den Schnittstellenindex des anderen Mitglieds des veth-Paares.

Veth-Schnittstellen ermöglichen es dem Container-Verkehr, aus seinem eigenen Netzwerk-Namensraum in den Netzwerk-Namensraum des Hosts zu gelangen (vorausgesetzt, der Container wurde nicht mit dem --network=host Flag gestartet). Aber wie kommt der Verkehr von der Veth-Schnittstelle im Host-Namensraum in das physische Netzwerk? Wenn du auf eine Bridge tippst, hast du richtig geraten! (Um fair zu sein, wurden Bridges am Anfang dieses Abschnitts als Bestandteil des Container-Netzwerks aufgeführt).

Der Schlüssel in Beispiel 4-2, wie in "Bridging (Switching)" beschrieben , ist das Auftauchen von master docker0 in Verbindung mit veth68bba38@if4. Das Vorhandensein dieses Textes zeigt an, dass die angegebene Schnittstelle Mitglied der docker0 Linux-Bridge ist. Wenn du dir die übrigen Schnittstellen ansiehst, wirst du jedoch feststellen, dass keine von ihnen Mitglied der docker0 Bridge ist. Wie kommt der Datenverkehr dann tatsächlich in das physische Netzwerk? An dieser Stelle kommt das IP-Masquerading ins Spiel, das letzte Teil des Puzzles, wie sich Docker-Container auf einem Linux-Host mit einem Netzwerk verbinden.

Hinweis

Veth-Schnittstellen haben noch weitere Einsatzmöglichkeiten, die meisten betreffen jedoch die Verbindung von Namensräumen im Netzwerk. In den zusätzlichen Online-Inhalten zu diesem Buch findest du weitere Beispiele für die Verwendung von Veth-Schnittstellen sowie die Befehle zum Erstellen, Ändern und Entfernen von Veth-Schnittstellen.

IP-Masquerading

Bei der Erkundung von Container-Netzwerken hast du bisher gesehen, wie Container-Laufzeitsysteme wie Docker Netzwerk-Namensräume nutzen, um eine Isolierung zu gewährleisten - um zu verhindern, dass ein Prozess in einem Container Zugriff auf die Netzwerkinformationen eines anderen Containers oder des Hosts hat. Du hast auch gesehen, wie Veth-Schnittstellen (auch als Veth-Paare bezeichnet) verwendet werden, um den Netzwerk-Namensraum eines Containers mit dem Netzwerk-Namensraum des Hosts zu verbinden. Aufbauend auf die ausführliche Behandlung von Schnittstellen, Bridging und Routing in Kapitel 3 hast du auch gesehen, wie eine Linux-Bridge in Verbindung mit den veth-Schnittstellen im Host-Namensraum verwendet wird. Jetzt fehlt nur noch ein Teil: Der Containerverkehr muss aus dem Host heraus und in das Netzwerk gelangen. Hier kommt das IP-Masquerading ins Spiel.

MitIP-Masquerading kann Linux so konfiguriert werden, dass es NAT ausführt. Wie du wahrscheinlich weißt, ermöglicht NAT einem oder mehreren Computern, sich unter einer anderen Adresse mit einem Netzwerk zu verbinden. Beim IP-Masquerading verbinden sich ein oder mehrere Computer mit einem Netzwerk, indem sie die IP-Adresse des Linux-Servers verwenden. RFC 2663 bezeichnet diese Form von NAT als Network Address Port Translation(NAPT); sie wird auch als One-to-Many NAT, Many-to-One NAT, Port Address Translation(PAT) und NAT Overload bezeichnet.

Bevor wir weitermachen, sollten wir erwähnen, dass Docker verschiedene Arten von Netzwerken unterstützt:

Brücke

Dies ist die Standard-Vernetzung. Sie nutzt Veth-Schnittstellen, eine Linux-Bridge und IP-Masquerading, um Container mit physischen Netzwerken zu verbinden.

Gastgeber

Bei der Verwendung von Netzwerken im Host-Modus wird der Docker-Container nicht in seinem eigenen Netzwerk-Namensraum isoliert, sondern verwendet den Netzwerk-Namensraum des Hosts (Standard). Die offenen Ports des Containers sind über die IP-Adresse des Hosts verfügbar.

Overlay

Overlay-Netzwerke schaffen ein verteiltes Netzwerk, das mehrere Docker-Hosts umfasst. Die Overlay-Netzwerke werden mit VXLAN aufgebaut, das in RFC 7348 definiert ist. Diese Art der Vernetzung ist direkt mit dem Container-Orchestrierungsmechanismus von Docker, Swarm, verbunden.

IPvlan und macvlan

Bei IPvlan-Netzwerken können sich mehrere IP-Adressen dieselbe MAC-Adresse einer Schnittstelle teilen, während bei macvlan-Netzwerken einer Schnittstelle mehrere MAC-Adressen zugewiesen werden. Vor allem letzteres funktioniert in der Regel nicht in Cloud-Provider-Netzwerken. Sowohl IPvlan- als auch macvlan-Netzwerke sind weniger verbreitet als andere Netzwerktypen.

Tipp

Die macvlan-Vernetzung mit macvlan-Schnittstellen kann neben der Container-Vernetzung auch für andere Zwecke genutzt werden. Weitere Informationen zu macvlan-Schnittstellen und macvlan-Netzwerken findest du in den zusätzlichen Online-Inhalten zu diesem Buch.

In diesem Abschnitt über die Vernetzung von Containern ging es vor allem um die Vernetzung im Bridge-Modus mit Docker.

Bei der Verwendung von Netzwerken im Bridge-Modus mit Docker verwendet die Container-Laufzeit einen Netzwerk-Namensraum, um den Container zu isolieren, und ein Veth-Paar, um den Netzwerk-Namensraum des Containers mit dem Host-Namensraum zu verbinden. Im Host-Namensraum ist eine der Veth-Schnittstellen mit einer Linux-Bridge verbunden. Wenn du mehrere Container im gleichen Docker-Netzwerk startest, werden sie mit der gleichen Linux-Bridge verbunden und du hast sofortige Container-zu-Container-Verbindungen auf dem gleichen Docker-Host. So weit, so gut.

Wenn ein Container versucht, sich mit etwas zu verbinden, das sich nicht im selben Docker-Netzwerk befindet, hat Docker bereits eine IP-Masquerading-Regel konfiguriert, die den Linux-Kernel anweist, für alle Container in diesem Docker-Netzwerk Many-to-One-NAT durchzuführen. Diese Regel wird erstellt, wenn das Docker-Netzwerk erstellt wird. Docker erstellt bei der Installation ein Standardnetzwerk und du kannst mit dem Befehl docker network create weitere Netzwerke erstellen.

Werfen wir zunächst einen Blick auf das Standard-Docker-Netzwerk. Wenn du docker network ls aufrufst, wird eine Liste der erstellten Docker-Netzwerke angezeigt. Auf einem frisch installierten System werden nur drei Netzwerke aufgelistet (die aufgeführten Netzwerk-IDs variieren):

ubuntu2004:~$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
b4e8ea1af51b   bridge    bridge    local
add089049cda   host      host      local
fbe6029ce9f7   none      null      local

Mit dem Befehl docker network inspect werden alle Details über das angegebene Netzwerk angezeigt. Hier ist die Ausgabe von docker network inspect b4e8ea1af51b, die mehr Informationen über das Standard-Bridge-Netzwerk anzeigt (einige Felder wurden der Kürze halber ausgelassen oder entfernt):

[
    {
        "Name": "bridge",
        "Id": "b4e8ea1af51ba023a8be9a09f633b316f901f1865d37f69855be847124cb3f7c",
        "Created": "2023-04-16T18:27:14.865595005Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16"                         1
                }
            ]
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true", 2
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",              3
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
        # output omitted for brevity
    }
]
1

Das von Containern verwendete Subnetz ist 172.17.0.0/16.

2

Docker erstellt automatisch die notwendigen IP-Masquerading-Regeln für dieses Netzwerk.

3

Der Name der Bridge für dieses Netzwerk ist docker0 (zu finden unter Options ).

Du kannst diese Informationen mit den Linux-Netzwerkbefehlen überprüfen, die in Kapitel 3 und in diesem Abschnitt behandelt werden. Um z.B. die IP-Adresse des nginx-Docker-Containers zu sehen, den du zuvor gestartet hast, verwendest du eine Kombination aus nsenter und ip addr list, etwa so:

ubuntu2004:~$ sudo nsenter --net=/run/docker/netns/b87b15b217e8 ip addr list eth0
4: eth0@if5: BROADCAST,MULTICAST,UP,LOWER_UP mtu 1500 qdisc noqueue state UP
 group default
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 1
       valid_lft forever preferred_lft forever
1

Überprüft, ob der Container eine Adresse aus dem 172.17.0.0/16 Netzwerk verwendet

Ebenso zeigt die Ausführung von ip -br link list (das -br Flag steht für brief und zeigt eine verkürzte Ausgabe an), dass die von Docker erstellte Bridge tatsächlich docker0 heißt:

ubuntu2004:~$ ip -br link list
lo               UNKNOWN  00:00:00:00:00:00 LOOPBACK,UP,LOWER_UP
ens5             UP       02:95:46:0b:0f:ff BROADCAST,MULTICAST,UP,LOWER_UP
docker0          UP       02:42:70:88:b6:77 BROADCAST,MULTICAST,UP,LOWER_UP
veth68bba38@if4  UP       a6:e9:87:46:e7:45 BROADCAST,MULTICAST,UP,LOWER_UP

Der letzte Teil ist die IP-Masquerading-Regel, die du mit dem Befehl iptables überprüfen kannst. Die Verwendung von iptables ist ein Thema, für das es ein eigenes Buch geben könnte, und das es auch gibt - siehe Linux iptables Pocket Reference von Gregor N. Purdy (O'Reilly). Wir können das Thema nicht bis ins kleinste Detail behandeln, aber wir können einen Überblick geben und erklären, wie die IP-Masquerading-Regel von Docker dazu passt.

Hinweis

Technisch gesehen ist iptables durch nftables ersetzt worden. Die Linux-Gemeinschaft bezieht sich jedoch immer noch weitgehend auf iptables, selbst wenn sie sich auf nftables bezieht. Außerdem funktioniert der Befehl iptables (und seine Syntax) weiterhin, auch wenn der zugrunde liegende Mechanismus jetzt nftables ist.

Iptables bietet mehrere Tabellen, wie die Filtertabelle, die NAT-Tabelle und die Mangle-Tabelle. Außerdem gibt es mehrere Ketten, wie PREROUTING, INPUT, OUTPUT, FORWARD und POSTROUTING. Jede Kette ist eine Liste von Regeln, und nicht jede Tabelle hat jede Kette. Die Regeln in einer Kette passen den Datenverkehr anhand von Eigenschaften wie Quelladresse, Zieladresse, Protokoll, Quellport oder Zielport an. Jede Regel gibt ein Ziel an, wie ACCEPT, DROP oder REJECT.

Mit diesen zusätzlichen Informationen in der Hand, schauen wir uns den Befehl iptables an. In Beispiel 4-3 verwenden wir diesen Befehl, um dir alle Ketten und Regeln in der NAT-Tabelle zu zeigen.

Beispiel 4-3. Auflistung aller Ketten und Regeln in der NAT-Tabelle mit iptables
ubuntu2004:~$ iptables -t nat -L -n                  1
...output omitted...
Chain POSTROUTING (policy ACCEPT)
target      prot  opt source               destination
MASQUERADE  all   --  172.17.0.0/16        0.0.0.0/0 2 3 4
...output omitted...
1

Der Parameter -t nat weist iptables an, die NAT-Tabelle anzuzeigen. -L listet die Regeln auf, und -n weist iptables einfach an, Zahlen anzuzeigen, anstatt zu versuchen, sie in Namen aufzulösen.

2

Die Regel gibt die Quelle als 172.17.0.0/16 an. Wir wissen, dass dies das Subnetz ist, das dem Standard-Docker-Bridge-Netzwerk zugewiesen ist.

3

Die Regel gibt das Ziel 0.0.0.0/0 (irgendwo) an.

4

Das Ziel für den Verkehr, der der Regel entspricht, ist MASQUERADE. Dieses Ziel ist nur in der NAT-Tabelle und in der Kette POSTROUTING verfügbar.

Wenn der Verkehr auf die Kette POSTROUTING in der NAT-Tabelle trifft, wurde das Routing bereits festgelegt (weitere Informationen zur Funktionsweise von Linux-Routing findest du unter "Routing als Endhost" und "Routing als Router") und die ausgehende Schnittstelle wurde bereits anhand der Routing-Konfiguration des Hosts ausgewählt. Das Ziel MASQUERADE weist iptables an, die IP-Adresse der Ausgangsschnittstelle als Adresse für den Datenverkehr zu verwenden, der der Regel entspricht - in diesem Fall der Datenverkehr aus dem Subnetz des Docker-Netzwerks, der an einen beliebigen Ort gerichtet ist. Auf diese Weise gelangt der Verkehr bei der Verwendung eines Docker-Bridge-Netzwerks tatsächlich in das Netzwerk: Der Linux-Host bestimmt anhand seiner Routing-Tabelle eine Route und eine ausgehende Schnittstelle und sendet den Docker-Container-Verkehr an diese Schnittstelle. Die Regel in der POSTROUTING Kette der NAT-Tabelle fängt diesen Datenverkehr ab und führt ein Mann-zu-Mann-NAT unter Verwendung der IP-Adresse der ausgewählten Schnittstelle durch.

Viele Linux-Distributionen bieten auch den Befehl iptables-save an, der die iptables-Regeln in einem etwas anderen Format anzeigt. So zeigt iptables-save die von Docker erstellte Regel für das Masquerading eines Brückennetzwerks an:

ubuntu2004:~$ sudo iptables-save -t nat
...output omitted...
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
...output omitted...

In diesem Format siehst du deutlich die angegebene Quelle (-s 172.17.0.0/16), und ! -o docker0 ist eine Abkürzung, die anzeigt, dass die ausgehende Schnittstelle nicht die docker0 Bridge ist. Mit anderen Worten: Der Verkehr, der von 172.17.0.0/16 kommt, ist nicht an ein Ziel auf der docker0 Bridge gebunden. Das Ziel wird durch -j MASQUERADE angegeben.

Bevor wir zum nächsten Abschnitt übergehen, zeigen wir dir in Abbildung 4-3 eine visuelle Zusammenfassung der vorherigen Beispiele. Du kannst sehen, dass der Container in einem Netzwerk-Namensraum läuft (andere Namensräume können im selben Host existieren), der über eine Veth-Schnittstelle mit dem Standard-Namensraum (Host) verbunden ist. Die Veth-Schnittstelle ist mit der docker0 Bridge verbunden, die wiederum mit der physischen Schnittstelle des Hosts und schließlich über IP-Masquerading mit dem Rest des Netzwerks verbunden ist.

npa2 0403
Abbildung 4-3. Zusammenfassung der Container-Netzwerke

Jetzt ist es an der Zeit, zu Kubernetes überzugehen, einem beliebten Tool zur Container-Orchestrierung. Obwohl viele der in diesem Abschnitt vorgestellten Konzepte immer noch gelten - schließlich arbeitet Kubernetes mit Containern - gibt es doch einige bemerkenswerte Unterschiede. Der nächste Abschnitt befasst sich mit diesen Unterschieden und baut auf dem auf, was du bereits über Container-Netzwerke weißt.

Kubernetes

Obwohl Kubernetes nicht unbedingt mit Cloud Computing in Verbindung gebracht wird - man könnte argumentieren, dass Kubernetes eine Form von PaaS ist - hat die Verwendung des Begriffs "Cloud Native" zur Beschreibung von Kubernetes dieses erfolgreiche Open-Source-Projekt unweigerlich mit Cloud Computing in Verbindung gebracht. Du hast bereits gesehen, wie Container selbst mit Cloud Computing in Verbindung gebracht werden; es ist nur natürlich, dass die Position von Kubernetes im Bereich des Cloud Computing durch die Tatsache weiter gefestigt wird, dass es ein Container-Orchestrator ist und für die Orchestrierung des Lebenszyklus von Containern über eine Flotte von Compute-Instanzen verantwortlich ist.

Die Vorläufer von Kubernetes waren Google-interne Systeme wie Borg und später Omega, die für die Verwaltung der Rechenkapazität von Tausenden von Knotenpunkten zuständig waren. Kubernetes wurde wirklich "aus der Cloud geboren".

Hinweis

Kubernetes ist nicht der einzige Container-Orchestrator, aber der beliebteste. Andere Container-Orchestratoren sind Nomad und Docker Swarm. Viele andere Container-Orchestrierungsplattformen bauen auf Kubernetes auf, z. B. OpenShift und Rancher von SUSE.

Bevor wir auf die Vernetzung in Kubernetes eingehen, ist es wichtig, einige der wichtigsten Konzepte von Kubernetes sowie wichtige Begriffe zu erklären.

Wichtige Kubernetes-Konzepte

Kubernetes ist im Kern ein verteiltes System, das für die Verwaltung von Rechenlasten in verschiedenen Einheiten von Rechenkapazität verantwortlich ist. Bei den "Rechenlasten" handelt es sich um Container, weshalb Kubernetes oft auch als Container-Orchestrierungsplattform bezeichnet wird. Die "Rechenkapazitätseinheiten" werden als Knoten bezeichnet und können Bare-Metal-Instanzen, Cloud-Instanzen oder VMs sein, die auf einem lokalen Hypervisor wie VMware vSphere laufen.

Kubernetes-Knoten sind in Clustern organisiert. Jeder Cluster verfügt über eine Kontrollebene, die auf einem oder mehreren Knoten läuft und die Verwaltungsfunktionen für den Cluster bereitstellt. Knoten, auf denen die Komponenten der Kontrollebene des Clusters laufen, sind Control-Plane-Knoten. Knoten, die nicht Teil der Control Plane sind, sind Worker-Knoten.

Die Steuerungsebene besteht aus drei Teilen: dem API-Server, dem Controller Manager und dem Zeitplannungsprogramm. Jeder von ihnen erfüllt eine andere, aber wichtige Funktion für den Gesamtbetrieb des Kubernetes-Clusters. Das Zeitplannungsprogramm ist für die Verteilung der Arbeitslasten auf die Worker Nodes zuständig, während der API-Server die Control Plane für die Nutzer (und andere Systeme) zugänglich macht. Auf den Controller Manager gehen wir gleich ein.

Die Kontrollebene wird von einem verteilten Key-Value-Store namens etcd unterstützt, der normalerweise auf den Knoten der Kontrollebene läuft, aber auf Wunsch auch außerhalb des Clusters ausgeführt werden kann. Der etcd, der auch Cluster bildet, benötigt eine ungerade Anzahl von Instanzen, um ein Quorum zu bilden und aufrechtzuerhalten. Normalerweise gibt es in einem hochverfügbaren Kubernetes-Cluster drei etcd-Instanzen und drei Control-Plane-Instanzen. Es ist jedoch auch möglich, einen Kubernetes-Cluster mit einer einzigen etcd-Instanz und einem einzigen Control-Plane-Knoten zu betreiben.

Hinweis

Obwohl etcd Anforderungen an die Anzahl der Instanzen stellt, um ein Quorum zu bilden und aufrechtzuerhalten, gibt es für die Kubernetes Control Plane-Komponenten selbst keine solchen Anforderungen. Wenn du willst, kannst du zwei Control-Plane-Knoten betreiben, um die Verfügbarkeit zu gewährleisten. Oft läuft etcd jedoch auf den Control-Plane-Knoten selbst, und in diesem Fall findest du drei Instanzen der Kubernetes Control-Plane-Komponenten.

Der Kubernetes-API-Server implementiert eine deklarative RESTful-API: Nutzer (und andere Systeme) verwenden die Kubernetes-API nicht, um dem Cluster mitzuteilen, was er tun soll (ein imperativer Ansatz), sondern um dem Cluster mitzuteilen , was das gewünschte Ergebnis ist (ein deklarativer Ansatz). Mit der API würdest du einen Kubernetes-Cluster zum Beispiel nicht anweisen, drei Workloads zu erstellen. Stattdessen würdest du dem Kubernetes-Cluster sagen, dass du drei Workloads haben möchtest, und das "Wie" überlässt du dem Cluster selbst.

Dieses Verhalten wird als Abgleich des gewünschten Zustands (das gewünschte Ergebnis, das du dem Cluster gegeben hast) mit dem tatsächlichen Zustand (was tatsächlich im Cluster läuft oder nicht läuft) bezeichnet. Dieser Vorgang findet in Kubernetes ständig statt und wird oft als Abgleichsschleife bezeichnet. Der Abgleich zwischen dem gewünschten und dem tatsächlichen Zustand ist das Herzstück von Kubernetes und der Funktionsweise von Kubernetes.

Jede Aktion, die in einem Kubernetes-Cluster durchgeführt wird, geschieht über die Versöhnungsschleife. Für jede Art von API-Objekt, das Kubernetes kennt, muss etwas die Abstimmungsschleife implementieren. Der Controller Manager - neben dem Zeitplannungsprogramm und dem API-Server der dritte Teil der Kubernetes-Kontrollebene - ist für die Verwaltung der Controller für die eingebauten Objekte zuständig, die Kubernetes kennt. Neue Objekttypen können über eine benutzerdefinierte Ressourcendefinition (CRD) erstellt werden, aber die neuen Objekttypen erfordern auch einen Controller, der den Lebenszyklus dieser Objekte versteht: wie sie erstellt, aktualisiert und gelöscht werden.

Tipp

O'Reilly hat mehrere Bücher veröffentlicht, die sich ausschließlich mit Kubernetes beschäftigen und hilfreich sein können. Zu den empfehlenswerten Titeln gehören Kubernetes: Up and Running, 3rd Edition, von Brendan Burns et al.; Kubernetes Cookbook von Sébastien Goasguen und Michael Hausenblas; Production Kubernetes von Josh Rosso et al.; und Kubernetes Patterns von Bilgin Ibryam und Roland Huß.

Es könnte noch viel mehr über Kubernetes gesagt werden, aber diese grundlegende Einführung gibt dir genug Informationen, um den Rest dieses Abschnitts zu verstehen. Als Nächstes besprechen wir einige der grundlegenden Bausteine des Kubernetes-Netzwerks.

Bausteine des Netzwerks in Kubernetes

Kubernetes führt ein paar neue Konstrukte ein, aber es ist wichtig, sich daran zu erinnern, dass Kubernetes auf bestehenden Technologien aufbaut. In den folgenden Abschnitten besprechen wir die grundlegenden Bausteine des Kubernetes-Netzwerks und wie sie sich zu den bereits bekannten Netzwerktechnologien verhalten, wie z. B. Container-Netzwerke, IP-Routing und Lastverteilung, um nur einige zu nennen.

Pods

Ein Pod ist eine Sammlung von Containern, die sich den Netzwerkzugang und die Speicherung teilen. Ein Pod kann aus einem oder mehreren Containern bestehen. Unabhängig von der Anzahl der Container innerhalb eines Pods bleiben jedoch bestimmte Dinge gleich:

  • Alle Container innerhalb eines Pods werden gemeinsam geplant, erstellt und zerstört. Pods sind die atomare Einheit der Zeitplanung.

  • Alle Container innerhalb eines Pods teilen sich die gleiche Netzwerkidentität (IP-Adresse und Hostname). Kubernetes erreicht dies, indem sich mehrere Container denselben Netzwerk-Namensraum teilen - das heißt, sie haben dieselbe Netzwerkkonfiguration und dieselbe Netzwerkidentität. Das bringt einige Einschränkungen mit sich: Zwei Container in einem Pod können zum Beispiel nicht denselben Port verwenden. Die Grundlagen, wie sich diese Container mit der Außenwelt verbinden, bleiben wie in "Container" beschrieben , mit ein paar kleinen Änderungen.

  • Die Netzwerkidentität eines Pods (IP-Adresse und Hostname) ist flüchtig; sie wird dynamisch zugewiesen, wenn der Pod erstellt wird, und wieder freigegeben, wenn der Pod zerstört wird oder stirbt. Daher solltest du niemals Systeme oder Architekturen aufbauen, die sich auf die direkte Adressierung von Pods verlassen; denn was ist, wenn du mehrere Pods betreiben musst? Oder was passiert, wenn ein Pod stirbt und mit einer anderen Netzwerkidentität neu erstellt wird?

  • Alle Container innerhalb eines Pods teilen sich dieselben Speicher-Volumes und Mounts. Dies wird dadurch erreicht, dass die Container sich den Namensraum teilen, der für die Verwaltung der Volumes und Mounts zuständig ist.

  • Kubernetes bietet übergeordnete Mechanismen zur Verwaltung des Lebenszyklus von Pods. Mit Deployments verwaltet Kubernetes zum Beispiel ReplicaSets, die wiederum Gruppen von Pods verwalten, um sicherzustellen, dass immer die angegebene Anzahl von Pods im Cluster läuft.

Um einen Pod zu erstellen, verwendet ein Clusternutzer oder -betreiber normalerweise eine YAML-Datei, die den Pod für den Kubernetes-API-Server beschreibt. Diese YAML-Dateien werden als Manifeste bezeichnet und an den API-Server übermittelt (in der Regel über das Kubernetes-Befehlszeilentool kubectl oder ein entsprechendes Tool). Beispiel 4-4 zeigt ein Manifest für einen einfachen Pod.

Beispiel 4-4. Kubernetes Pod Manifest
apiVersion: v1     1
kind: Pod          2
metadata:          3
  name: assets
  labels:
    app.kubernetes.io/name: zephyr
spec:
  containers:      4
    - name: assets
      image: nginx 5
      ports:       6
        - name: http-alt
          containerPort: 8080
          protocol: TCP
1

Die Kubernetes-API unterstützt die Versionierung. Wenn die API-Version nur v1 lautet, wie hier, bedeutet das, dass dieses Objekt Teil der Kubernetes-Kern-API ist.

2

kind bezieht sich auf Kubernetes-Konstrukttypen wie Pod, Deployment, Service, Ingress, NetworkPolicy, etc.

3

Jedes API-Objekt hat auch Metadaten, die mit ihm verknüpft sind. Manchmal sind die Metadaten nur ein Name, aber oft - wie in diesem Beispiel - enthalten sie auch Labels. Labels werden in Kubernetes häufig verwendet.

4

Ein Pod kann einen oder mehrere Container enthalten. In diesem Beispiel befindet sich nur ein einziger Container im Pod, aber es könnten auch mehr Elemente in der Liste sein.

5

Die hier angegebene image ist ein Container-Image. Dieses Container-Image wird verwendet, um einen neuen Container im Pod zu erstellen. In diesem Beispiel befindet sich nur ein einziger Container im Pod.

6

Pods können einen oder mehrere Ports für den Rest des Clusters freigeben.

Bei der Erstellung von Pods erstellt Kubernetes die Container nicht selbst, sondern überlässt diese Aufgabe der Container-Runtime. Kubernetes interagiert mit der Container-Laufzeitumgebung über eine Standardschnittstelle, das Container Runtime Interface (CRI). Ähnliche Standardschnittstellen gibt es für die Speicherung (Container Storage Interface, CSI) und die Vernetzung (Container Network Interface, CNI). CNI ist ein wichtiger Teil des Kubernetes-Netzwerks, aber bevor wir auf CNI eingehen, werfen wir einen Blick auf die Kubernetes-Dienste.

Dienstleistungen

Wenn du dich nicht direkt mit einem Pod verbinden kannst, wie können dann Anwendungen, die in Pods laufen, über das Netzwerk kommunizieren? Dies ist eine der wichtigsten Neuerungen von Kubernetes gegenüber früheren Netzwerkmodellen. Wenn du mit VMs arbeitest, ist die Netzwerkidentität der VM (IP-Adresse und/oder Hostname) in der Regel langlebig, und du kannst dich darauf verlassen, dass du dich mit der VM verbinden kannst, um auf die Anwendungen zuzugreifen, die auf dieser VM laufen. Wenn du zu Containern und Kubernetes übergehst, gilt das nicht mehr. Die Netzwerkidentität eines Pods ist flüchtig. Und was ist, wenn es mehrere Replikate eines Pods gibt? Mit welchem Pod soll sich eine andere Anwendung oder ein anderes System verbinden?

Anstatt sich mit einem Pod (oder einer Gruppe von Pods) zu verbinden, verwendet Kubernetes einen Service(Beispiel 4-5 zeigt ein Manifest für einen einfachen Service). Ein Service erfüllt zwei wichtige Aufgaben:

  • Zuweisung einer stabilen Netzwerkidentität an eine bestimmte Untergruppe von Pods. Wenn ein Dienst erstellt wird, wird ihm eine Netzwerkidentität (IP-Adresse und Hostname) zugewiesen. Diese Netzwerkidentität bleibt für die gesamte Lebensdauer des Dienstes stabil.

  • Dient als Load Balancer für eine bestimmte Untergruppe von Pods. Der an den Dienst gesendete Verkehr wird auf die Untergruppe der Pods verteilt, die zum Dienst gehören.

Beispiel 4-5. Kubernetes Service Manifest
apiVersion: v1
kind: Service
metadata:
  name: zephyr-assets
spec:
  selector:            1
    app.kubernetes.io/name: zephyr
  ports:
    - protocol: TCP
      port: 80         2
      targetPort: 8080 3
1

Der Selektor gibt ein Label an, und die Aufnahme dieses Labels in den Selektor hat zur Folge, dass Pods mit diesem Label "Teil" des Dienstes sind, d.h. Pods mit diesem Label erhalten Verkehr, der an den Dienst gesendet wird.

2

Der Port, über den der Dienst für den Rest des Clusters zugänglich ist.

3

Der Port, den die Pods des Dienstes zur Verfügung stellen und an den der Datenverkehr gesendet wird.

Dieses Beispiel zeigt, wie die Beziehung zwischen einem Pod und einem Dienst verwaltet wird. Mithilfe von Label-Selektoren wählt der Dienst dynamisch passende Pods aus. Wenn ein Pod mit den Labels übereinstimmt, ist er "Teil" des Dienstes und erhält den an den Dienst gesendeten Verkehr. Pods, die nicht mit der Definition des Dienstes übereinstimmen, werden nicht in den Dienst aufgenommen.

Labels, wie das im vorangegangenen Beispiel, sind einfach Schlüssel-Wert-Paare, wie app.kubernetes.io/name: zephyr. Labels können willkürlich sein, aber es sollten aufkommende Standards für Labels verwendet werden, und Kubernetes hat Regeln für die Länge der Schlüssel und Werte in einem Label.

Kubernetes unterstützt verschiedene Arten von Diensten:

ClusterIP-Dienst

Bei diesem Standarddienst wird dem Dienst eine virtuelle IP-Adresse zugewiesen, die nur innerhalb des Clusters gültig ist. Es wird auch ein entsprechender DNS-Name erstellt, und sowohl die IP-Adresse als auch der DNS-Name bleiben konstant, bis der Dienst gelöscht wird. ClusterIP-Dienste sind von außerhalb des Clusters nicht zu erreichen.

NodePort Dienst

Er stellt den Dienst an einem bestimmten Port zur Verfügung. Der an die IP-Adresse eines Knotens an diesem Port gesendete Verkehr wird dann an die ClusterIP des Dienstes weitergeleitet, die wiederum den Verkehr an die Pods verteilt, die Teil des Dienstes sind. NodePort-Dienste ermöglichen es dir, den Dienst über einen externen oder manuellen Prozess bereitzustellen, z. B. indem du eine externe HAProxy-Instanz auf den definierten NodePort des Dienstes verweist.

LoadBalancer Dienst

Dazu ist eine Art Controller erforderlich, der weiß, wie man einen externen Load Balancer bereitstellt. Dieser Controller kann Teil des Cloud-Providers von Kubernetes sein, der die Integration mit den zugrundeliegenden Cloud-Plattformen unterstützt, oder er kann eine separate Software sein, die du auf dem Cluster installierst. In jedem Fall richtet Kubernetes einen NodePort Service ein und konfiguriert dann den externen LoadBalancer so, dass er den Datenverkehr an den zugewiesenen NodePort weiterleitet. LoadBalancer-Dienste sind die wichtigste Methode, um Dienste außerhalb eines Kubernetes-Clusters bereitzustellen.

Dienste arbeiten auf Schicht 4 des OSI-Modells (auf der TCP/UDP-Schicht). Daher kann ein Dienst nicht zwischen den Hostnamen unterscheiden, die z. B. in einer HTTP-Anfrage verwendet werden. Da so viele Unternehmen Kubernetes nutzen, um webbasierte Anwendungen über HTTP/HTTPS auszuführen, wurde dies zu einer Einschränkung der Services. Als Reaktion darauf hat die Kubernetes-Gemeinschaft Ingresses entwickelt.

Ingresses

Ein Ingress wird für das Layer 7 (HTTP)-Routing verwendet, das auf verschiedenen Eigenschaften einer HTTP-Anfrage basiert. Ein Ingress wird von einem Ingress-Controller verwaltet. Dabei handelt es sich um eine spezielle Software, die in deinem Kubernetes-Cluster eingesetzt wird, um die erforderlichen Funktionen auszuführen. Es gibt zahlreiche Ingress-Controller, unter anderem den ginx Ingress-Controller, den HAProxy Ingress-Controller, den Traefik Ingress-Controller und verschiedene Envoy Proxy-basierte Ingress-Controller (wie Ambassador, Contour und Gloo). Der genaue Funktionsumfang dieser verschiedenen Ingress-Controller variiert, aber alle erfüllen dieselbe Grundfunktion: Sie empfangen Ingress-Definitionen von der Kubernetes-API und führen auf Basis dieser Definitionen HTTP-Routing durch.

Hinweis

Die Kubernetes-Gemeinschaft ist gerade dabei, einen Ersatz für Ingress zu definieren. Dieses Projekt ist als Gateway API Projekt bekannt. Weitere Informationen findest du auf der Kubernetes Gateway API Website. An diesem Projekt sind auch viele Produkte und Projekte beteiligt, die als API-Gateways eingestuft werden, da ihre Kernfunktionen denen der Gateway API ähneln.

Eine Ingress-Definition (oder ein Ingress-Objekt) enthält eine Reihe von Regeln, die festlegen, wie das HTTP-Routing vom Ingress-Controller durchgeführt werden soll. Nehmen wir zum Beispiel an, dass Dienst A API-Anfragen an die /users API bearbeitet, während Dienst B API-Anfragen an die /devices API bearbeitet. api.company.com Du könntest ein Ingress-Objekt mit mehreren Regeln haben, das den an /users gesendeten Datenverkehr an Dienst A (in diesem Fall an einen Kubernetes-Dienst) und den an /devices gesendeten Datenverkehr an Dienst B weiterleitet.

Alternativ könntest du auch ein Ingress-Objekt haben, das Regeln auf der Grundlage des FQDN in der Anfrage definiert und den Datenverkehr auf der Grundlage dieses FQDN an verschiedene Dienste weiterleitet. Beispiel 4-6 zeigt ein Ingress-Manifest.

Beispiel 4-6. Kubernetes Ingress-Manifest
apiVersion: networking.k8s.io/v1 1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx-example
  rules:                         2
  - http:
      paths:
      - path: /testpath          3
        pathType: Prefix
        backend:
          service:               4
            name: test
            port:
              number: 80
1

Die API-Version für ein Ingress-Objekt gibt auch eine API-Gruppe an (networking.k8s.io). Mit dem Wachstum und der Reife von Kubernetes wurden API-Gruppen verwendet, um die wachsende API-Oberfläche zu organisieren.

2

Diese Regeln legen fest, wie der Ingress-Kontrolleur den Verkehr unter verschiedenen Bedingungen umleiten soll.

3

In diesem Fall gibt es nur eine einzige Bedingung (die path muss mit /testpath beginnen). Wenn wir jedoch neue Einträge in die Liste paths aufnehmen, können wir zusätzliche Regeln definieren, um den Datenverkehr zu anderen Diensten zu leiten.

4

In allen Fällen ist das Ziel des von einem Ingress-Controller geleiteten Datenverkehrs ein Kubernetes-Dienst.

Tipp

Ein Ingress-Controller leitet den Datenverkehr an Kubernetes-Dienste weiter, die normalerweise als ClusterIP-Dienste definiert sind. Der Ingress-Controller muss jedoch auch über einen Dienst, in der Regel einen LoadBalancer-Dienst, zugänglich sein. So kann der Datenverkehr über den Service des Ingress-Controllers in den Kubernetes-Cluster gelangen und wird dann vom Ingress-Controller an den entsprechenden Service weitergeleitet. Auf diese Weise ist nur der Ingress-Controller von außerhalb des Clusters erreichbar und alle anderen Services werden hinter dem Ingress-Controller "abgeschottet".

Andere Netzwerkkonstrukte

Pods, Services und Ingresses bilden den Großteil dessen, was regelmäßig verwendet wird, um zu definieren, wie Kubernetes Workloads offenlegen und verbinden soll. Ein paar andere Konstrukte sind es jedoch wert, kurz erwähnt zu werden:

NetworkPolicies

Mit NetworkPolicies können Nutzer/innen kontrollieren, welcher Datenverkehr in Pods hinein oder aus Pods heraus erlaubt ist. Sie sind im Grunde genommen Pod-Firewalls. Der Vergleich mit einer Firewall ist zwar praktisch, aber NetworkPolicies verhalten sich anders als die meisten Firewalls: Es gibt z. B. keine Verweigerung und keinen Sinn für die Priorität oder Reihenfolge von Regeln.

Namensräume

Nicht zu verwechseln mit den Namensräumen auf der Linux-Kernel-Ebene, bieten Kubernetes Namensräume eine logische Trennung von API-Objekten wie Pods und Services. Namensräume wirken sich auch auf die automatische DNS-basierte Service-Erkennung aus, die Kubernetes für alle Services durchführt.

Bisher hast du einige der übergeordneten Kubernetes-API-Objekte kennengelernt, die die Bausteine des Kubernetes-Netzwerks bilden. Es gibt jedoch eine Schlüsselkomponente, die eine Ebene tiefer sitzt und dafür verantwortlich ist, dass die übergeordneten Konstrukte funktionieren. Es handelt sich dabei um die CNI, die bereits in diesem Kapitel vorgestellt wurde und die einen Standardmechanismus für die Einbindung verschiedener Netzwerkmodelle in Kubernetes bietet.

Container Netzwerk Schnittstelle

Die CNI ist eine generische Spezifikation, die festlegt, wie ein Container-Netzwerk-Plug-in implementiert werden sollte. Sie legt eine Reihe von APIs und das Format der erwarteten Konfiguration fest. Kubernetes als Container-Orchestrator-Lösung implementiert die CNI-Spezifikation, sodass jedes CNI-konforme Plug-in mit jedem Kubernetes-Cluster verwendet werden kann.

Ein CNI-Plug-in kann eine oder viele Netzwerkfunktionen implementieren, z. B. die Zuweisung von IP-Adressen an Pods, Pod-zu-Pod-Kapselung, externe Konnektivität, Netzwerkrichtlinien und mehr. Einige grundlegende Plug-ins werden von der CNI-Community gepflegt, aber viele Plug-ins von Drittanbietern implementieren erweiterte Funktionen. Einige davon sind Calico, Cilium und Amazon ECS CNI Plug-ins.

Die CNI-Spezifikation schreibt nicht vor, wie das Plug-in funktionieren soll; sie schreibt nur vor, wie man mit diesem Plug-in über eine bestimmte Schnittstelle interagiert. Das CNI-Plug-in kann in jeder beliebigen Sprache geschrieben werden, es wird jedoch erwartet, dass es als Binärdatei bereitgestellt und ausgeführt wird.

Tipp

Weitere Informationen über CNI, die CNI-Spezifikation und verfügbare CNI-Plug-ins findest du im CNI-GitHub-Repository.

Mithilfe der CNI-Spezifikation kann Kubernetes eine Vielzahl von Netzwerkmodellen implementieren. In Abbildung 4-4 zeigen wir eine der vielen Kubernetes-Netzwerklösungen mit dem Kube-Router-Ansatz, um zu veranschaulichen, wie beide Welten zusammenarbeiten können. In diesem Fall verwendet jeder Kubernetes-Host einen BGP-Daemon (der im Standard-Kubernetes-Namensraum läuft), um die CIDRs des Hosts über das BGP-Protokoll bekannt zu machen. Der externe BGP-Routenreflektor vereinfacht die BGP-Architektur und macht ein ganzes Netz von BGP-Peers überflüssig. Alle Kubernetes-Hosts erfahren von den Präfixen der anderen sowie von externen Präfixen (z. B. aus dem Internet).

npa2 0404
Abbildung 4-4. Kubernetes-Netzwerk mit BGP

Um diese Kubernetes-Erkundung auf hohem Niveau abzuschließen, werfen wir einen Blick auf ein neues Netzwerkmuster, das immer beliebter wird: das Service-Mesh.

Service Mesh

Moderne Softwareanwendungen haben in letzter Zeit zur Einführung der Microservices-Architektur geführt. Diese Architektur zeichnet sich durch viele kleine und unabhängige Dienste aus, die zusammenarbeiten, um die Funktionen der Anwendung bereitzustellen. Dies ermöglicht eine flexiblere, wiederverwendbare und skalierbare Architektur. Allerdings bringt diese Architektur auch Herausforderungen mit sich, von denen einige mit der Vernetzung zusammenhängen: Wie stellen wir sicher, dass alle Dienste miteinander kommunizieren können? Und wie stellen wir sicher, dass die Kommunikation sicher, zuverlässig und nachvollziehbar ist?

Eine Antwort auf diese Fragen ist das Service-Mesh-Pattern. Ein Service Mesh ist eine spezielle Infrastrukturebene, die die Kommunikation zwischen den Diensten übernimmt. Es wird in der Regel als eine Reihe von leichtgewichtigen Proxys implementiert, die neben den Anwendungsdiensten eingesetzt werden. Diese Proxys fangen den gesamten Datenverkehr zu und von den Anwendungen ab und bieten Folgendes:

Verkehrsmanagement

Diensterkennung, Lastausgleich und Fehlertoleranz

Sicherheit

Verschlüsselung, Authentifizierung und Autorisierung sowie Zugriffskontrolle

Beobachtbarkeit

Anwendungsmetriken, verteiltes Tracing und Protokolle

Das Ziel ist klar: die allgemeinen Netzwerkaufgaben von der Anwendung auf das Service Mesh zu verlagern. So können sich die Anwendungen auf ihre eigentliche Geschäftslogik konzentrieren, ohne sich um konnektivitätsbezogene Details kümmern zu müssen.

Service Mesh ist nicht auf Kubernetes beschränkt. Netflix hatte 2017 bereits einen Tech-Stack zur Implementierung von Service-Mesh-Funktionen. Heutzutage ist Kubernetes jedoch die beliebteste Plattform für den Einsatz von Service Mesh, und beide werden oft zusammen verwendet (die meisten der Beispiele, die wir im Folgenden nennen, laufen nur in Kubernetes).

Um dies zu erreichen, benötigt die Service-Mesh-Architektur zwei Hauptkomponenten:

Datenebene

Diese Kommunikationsschicht übernimmt die eigentliche Kommunikation zwischen den Diensten. Sie wird als eine Reihe von leichtgewichtigen Proxys (Sidecar Proxys) implementiert, die neben den Anwendungsdiensten eingesetzt werden. Diese Proxys fangen den gesamten Datenverkehr zu und von den Diensten ab und implementieren die Funktionen des Service Mesh. Beispiele für Data Plane Proxies sind Envoy, Cilium, NGINX und HAProxy.

Steuerungsebene

Diese Verwaltungsschicht stellt die Konfiguration für die Datenebene bereit. Sie übernimmt deine Service-Mesh-Konfiguration und übersetzt sie in die Konfiguration für die Proxys der Datenebene. Außerdem verteilt sie Aktualisierungen der Service Discovery und sammelt Observability-Daten von der Datenebene. Zu den beliebtesten Control Plans gehören Istio, Consul und Linkerd (das auch einen Data Plane Proxy bereitstellt) oder cloud-spezifische Lösungen wie AWS App Mesh.

Hinweis

Mastering Service Mesh von Anjali Khatri und Vikram Khatri (Packt) ist eine gute Quelle, um mehr darüber zu erfahren.

In Kubernetes wird die Datenebene als eine Reihe von Sidecar-Containern implementiert, die neben den Anwendungscontainern im selben Pod eingesetzt werden. Der Datenverkehr von den Anwendungscontainern wird über die Sidecar-Container umgeleitet, wobei der dafür verwendete Mechanismus je nach Service-Mesh-Implementierung variiert. Die Steuerebene programmiert diese Sidecar-Container mit der notwendigen Konfiguration, um die Funktionen des Service Mesh zentral zu implementieren.

Man könnte das Service Mesh als eine weitere Overlay-Abstraktion verstehen, die jedoch der Anwendung am nächsten ist. Aber wie jedes andere Overlay ist auch das Service Mesh auf die zugrunde liegende Netzwerkinfrastruktur angewiesen, um Konnektivität zu gewährleisten. Deine Aufgabe als Netzwerkingenieur ist es, entweder das Service Mesh zu implementieren oder die zugrunde liegende Netzwerkinfrastruktur bereitzustellen, um es zu unterstützen und mit dem Rest des Netzwerks zu verbinden. In beiden Fällen ist es wichtig zu verstehen, wie das Service Mesh funktioniert, um es zu beherrschen.

Zusammenfassung

Cloud Computing bedeutet nicht, dass du keine Netzwerkkenntnisse brauchst. Wie du in diesem Kapitel gesehen hast, sind Netzwerke und Netzwerkautomatisierung in Cloud-basierten Umgebungen genauso wichtig wie in deinen lokalen Umgebungen.

Cloud-Netzwerkdienste, Container- und Kubernetes-Netzwerke und Service Mesh sind allesamt Netzwerktechnologien, die bestimmte Probleme lösen. Sie schließen sich nicht gegenseitig aus. Du kannst sie kombinieren, um komplette Netzwerklösungen für deine Cloud-basierten Anwendungen bereitzustellen und sie gleichzeitig auf deine Netzwerke vor Ort auszuweiten.

Im nächsten Kapitel schalten wir einen Gang höher, um deine Reise in die Welt der Netzwerkautomatisierung zu beginnen, und wir zeigen dir, wie du mit Containern Laborumgebungen aufbauen kannst. Beginnen wir mit deiner lokalen Netzwerkentwicklungsumgebung.

Get Netzwerk-Programmierbarkeit und Automatisierung, 2. 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.