Kapitel 4. Arbeiten mit Kubernetes-Objekten

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

Ich kann nicht verstehen, warum die Leute Angst vor neuen Ideen haben. Ich habe Angst vor den alten Ideen.

John Cage

In Kapitel 2 hast du eine Anwendung erstellt und in Kubernetes deployed. In diesem Kapitel lernst du die grundlegenden Kubernetes-Objekte kennen, die an diesem Prozess beteiligt sind: Pods, Deployments und Dienste. Außerdem erfährst du, wie du das wichtige Helm-Tool zur Verwaltung von Anwendungen in Kubernetes nutzen kannst.

Nachdem du das Beispiel in "Ausführen der Demo-App" durchgearbeitet hast , solltest du ein Container-Image im Kubernetes-Cluster laufen haben, aber wie funktioniert das eigentlich? Unter der Haube erstellt der Befehl kubectl run eine Kubernetes-Ressource namens Deployment. Und was ist das? Und wie führt ein Deployment dein Container-Image tatsächlich aus?

Einsätze

Erinnere dich daran, wie du die Demo-App mit Docker ausgeführt hast. Der Befehl docker container run hat den Container gestartet und er lief, bis du ihn mit docker stop beendet hast.

Aber nimm an, dass der Container aus einem anderen Grund beendet wird: Vielleicht ist das Programm abgestürzt, oder es gab einen Systemfehler, oder dein Rechner hatte keinen Speicherplatz mehr, oder ein kosmischer Strahl hat deine CPU im falschen Moment getroffen (unwahrscheinlich, aber es kommt vor). Angenommen, es handelt sich um eine Produktionsanwendung, bedeutet das, dass du jetzt unzufriedene Benutzer hast, bis jemand an ein Terminal geht und docker container run eingibt, um den Container wieder zu starten.

Das ist eine unbefriedigende Lösung. Was du wirklich brauchst, ist eine Art Überwachungsprogramm, das ständig überprüft, ob der Container läuft, und ihn sofort wieder startet, wenn er einmal stoppt. Auf herkömmlichen Servern kannst du dafür ein Tool wie systemd, runit oder supervisord verwenden; Docker hat etwas Ähnliches, und es wird dich nicht überraschen, dass auch Kubernetes eine Supervisor-Funktion hat: das Deployment.

Überwachung und Zeitplanungsprogramm

Für jedes Programm, das Kubernetes zu überwachen hat, erstellt es ein entsprechendes Deployment-Objekt, das einige Informationen über das Programm aufzeichnet: den Namen des Container-Images, die Anzahl der Replikate, die du ausführen willst, und was es sonst noch wissen muss, um den Container zu starten.

Zusammen mit der Bereitstellungsressource arbeitet eine Art Kubernetes-Komponente, die Controller genannt wird. Controller sind im Grunde genommen Codeteile, die kontinuierlich in einer Schleife laufen und die Ressourcen, für die sie zuständig sind, überwachen und sicherstellen, dass sie vorhanden sind und funktionieren. Wenn ein bestimmtes Deployment aus irgendeinem Grund nicht genügend Replikate hat, erstellt der Controller neue Replikate. (Wenn es aus irgendeinem Grund zu viele Replikate gäbe, würde der Controller die überzähligen abschalten. In jedem Fall stellt der Controller sicher, dass der tatsächliche Zustand mit dem gewünschten Zustand übereinstimmt).

Ein Deployment verwaltet Replikate nicht direkt, sondern erstellt automatisch ein zugehöriges Objekt namens ReplicaSet, das diese Aufgabe übernimmt. Wir werden gleich in "ReplicaSets" mehr über ReplicaSets sprechen , aber da du in der Regel nur mit Deployments interagierst, sollten wir uns zunächst mit ihnen vertraut machen.

Neustart von Containern

Auf den ersten Blick mag die Art und Weise, wie sich Deployments verhalten, etwas überraschend sein. Wenn dein Container seine Arbeit beendet und aussteigt, wird er vom Deployment neu gestartet. Wenn er abstürzt, wenn du ihn mit einem Signal beendest oder wenn du ihn mit kubectl beendest, startet das Deployment ihn neu. (So solltest du es dir vorstellen; die Realität ist ein bisschen komplizierter, wie wir noch sehen werden).

Die meisten Kubernetes-Anwendungen sind so konzipiert, dass sie lange laufen und zuverlässig sind. Daher macht dieses Verhalten Sinn: Container können aus allen möglichen Gründen ausfallen, und in den meisten Fällen würde ein menschlicher Bediener sie nur neu starten, also macht Kubernetes das standardmäßig.

Es ist möglich, diese Richtlinie für einen einzelnen Container zu ändern: zum Beispiel so, dass er nie neu gestartet wird oder dass er nur im Fehlerfall neu gestartet wird und nicht, wenn er normal beendet wurde (siehe "Richtlinien für den Neustart"). In der Regel ist das Standardverhalten (Neustart immer) jedoch das, was du willst.

Die Aufgabe eines Deployments ist es, die zugehörigen Container zu überwachen und dafür zu sorgen, dass immer die angegebene Anzahl von ihnen läuft. Wenn es weniger sind, startet es mehr. Wenn es zu viele sind, beendet es einige. Das ist viel leistungsfähiger und flexibler als ein traditionelles Supervisor-Programm.

Einsätze erstellen

Mach weiter und erstelle ein Deployment mit unserem Container-Image in deiner lokalen Kubernetes-Umgebung, damit wir uns ansehen können, wie sie funktionieren:

kubectl create deployment demo --image=cloudnatived/demo:hello
deployment.apps/demo created

Du kannst alle Einsätze sehen, die in deinem aktuellen Namensraum aktiv sind (siehe "Verwendung von Namensräumen"), indem du den folgenden Befehl ausführst:

kubectl get deployments
NAME   READY   UP-TO-DATE   AVAILABLE   AGE
demo   1/1     1            1           37s

Um detailliertere Informationen über diesen speziellen Einsatz zu erhalten, führe den folgenden Befehl aus:

kubectl describe deployments/demo
Name:                   demo
Namespace:              default
...
Labels:                 app=demo
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=demo
...

Wie du siehst, gibt es hier eine Menge Informationen, von denen die meisten im Moment nicht wichtig sind. Schauen wir uns aber den Abschnitt Pod Template genauer an:

Pod Template:
  Labels:  app=demo
  Containers:
   demo:
    Image:        cloudnatived/demo:hello
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
...

Du weißt, dass ein Deployment die Informationen enthält, die Kubernetes braucht, um den Container auszuführen, und hier ist es. Aber was ist ein Pod Template? Bevor wir diese Frage beantworten: Was ist ein Pod?

Pods

Ein Pod ist das Kubernetes-Objekt, das eine Gruppe von einem oder mehreren Containern repräsentiert(Pod ist auch der Name für eine Gruppe von Walen, was zu der vagen Seefahrer-Metaphorik von Kubernetes passt).

Warum verwaltet ein Deployment nicht einfach einen einzelnen Container direkt? Die Antwort ist, dass manchmal eine Reihe von Containern zusammen geplant werden muss, die auf demselben Knoten laufen und lokal miteinander kommunizieren, vielleicht sogar die Speicherung teilen. An dieser Stelle geht Kubernetes über das Ausführen von Containern direkt auf einem Host mit Docker hinaus. Kubernetes verwaltet ganze Kombinationen von Containern, ihre Konfiguration, Speicherung usw. in einem Cluster von Knotenpunkten.

Eine Blog-Anwendung könnte zum Beispiel einen Container haben, der die Inhalte mit einem Git-Repository synchronisiert, und einen NGINX-Webserver-Container, der die Blog-Inhalte für die Nutzer bereitstellt. Da sie sich Daten teilen, müssen die beiden Container in einem Pod zusammen geplant werden. In der Praxis haben viele Pods jedoch nur einen Container, wie in diesem Fall. (Unter "Was gehört in einen Pod?" erfährst du mehr darüber.)

Eine Pod-Spezifikation( kurz:Spec ) hat also eine Liste von containers, und in unserem Beispiel gibt es nur einen Container, demo:

demo:
 Image:        cloudnatived/demo:hello

Die Image spec ist unser Demo Docker Container-Image von Docker Hub, das alle Informationen enthält, die ein Kubernetes Deployment braucht, um den Pod zu starten und am Laufen zu halten.

Und das ist ein wichtiger Punkt. Der Befehl kubectl create deployment hat den Pod eigentlich nicht direkt erstellt. Stattdessen wurde ein Deployment erstellt, und das Deployment hat dann ein ReplicaSet erstellt, das den Pod erzeugt hat. Das Deployment ist eine Deklaration deines gewünschten Zustands: "Ein Pod soll mit dem Container demo in Betrieb sein."

ReplicaSets

Die Deployments verwalten Pods nicht direkt. Das ist die Aufgabe des ReplicaSet-Objekts.

Ein ReplicaSet ist für eine Gruppe identischer Pods bzw. Replikate zuständig. Wenn es im Vergleich zur Spezifikation zu wenige (oder zu viele) Pods gibt, startet (oder stoppt) der ReplicaSet Controller einige Pods, um die Situation zu korrigieren.

Deployments wiederum verwalten ReplicaSets und steuern, wie sich die Replikate verhalten, wenn du sie aktualisierst, z. B. indem du eine neue Version deiner Anwendung ausrollst (siehe "Deployment-Strategien"). Wenn du den Einsatz aktualisierst, wird ein neues ReplicaSet erstellt, um die neuen Pods zu verwalten. Wenn die Aktualisierung abgeschlossen ist, werden das alte ReplicaSet und seine Pods beendet.

In Abbildung 4-1 steht jedes ReplicaSet (V1, V2, V3) für eine andere Version der Anwendung mit den entsprechenden Pods.

Diagram of a Deployment managing ReplicaSets
Abbildung 4-1. Einsätze, ReplicaSets und Pods

Normalerweise wirst du nicht direkt mit ReplicaSets interagieren, da die Einsätze die Arbeit für dich erledigen, aber es ist nützlich zu wissen, was sie sind.

Gewünschten Zustand beibehalten

Die Kubernetes-Controller überprüfen ständig den von jeder Ressource angegebenen Soll-Zustand mit dem Ist-Zustand des Clusters und nehmen alle notwendigen Anpassungen vor, um sie synchron zu halten. Dieser Prozess wird Reconciliation Loop genannt, weil er in einer Endlosschleife versucht, den Ist-Zustand mit dem Soll-Zustand in Einklang zu bringen.

Wenn du zum Beispiel das demo Deployment zum ersten Mal erstellst, läuft noch kein demo Pod. Deshalb startet Kubernetes den benötigten Pod sofort. Wenn er jemals anhält, wird Kubernetes ihn erneut starten, solange das Deployment noch existiert.

Das wollen wir jetzt überprüfen, indem wir den Pod manuell entfernen. Überprüfe zunächst, ob der Pod tatsächlich läuft:

kubectl get pods --selector app=demo
NAME                    READY   STATUS    RESTARTS   AGE
demo-794889fc8d-5ddsg   1/1     Running   0          33s

Beachte, dass der Name des Pods für dich eindeutig sein wird. Du kannst auch das ReplicaSet sehen, das diesen Pod erstellt hat, indem du ausführst:

kubectl get replicaset --selector app=demo
NAME              DESIRED   CURRENT   READY   AGE
demo-794889fc8d   1         1         1       64s

Siehst du, dass das ReplicaSet eine zufällig generierte ID hat, die mit dem Anfang des Pod-Namens der Demo übereinstimmt? In diesem Beispiel hat das demo-794889fc8d ReplicaSet einen Pod namens demo-794889fc8d-5ddsg erstellt.

Führe nun den folgenden Befehl aus, um den Pod zu entfernen:

kubectl delete pods --selector app=demo
pod "demo-794889fc8d-bdbcp" deleted

Liste die Pods erneut auf:

kubectl get pods --selector app=demo
NAME                    READY     STATUS        RESTARTS   AGE
demo-794889fc8d-qbcxm   1/1       Running       0          5s
demo-794889fc8d-bdbcp   0/1       Terminating   0          1h

Du kannst sehen, wie der ursprüngliche Pod herunterfährt (sein Status ist Terminating), aber er wurde bereits durch einen neuen Pod ersetzt, der nur fünf Sekunden alt ist. Du kannst auch sehen, dass der neue Pod das gleiche ReplicaSet hat, demo-794889fc8d, aber einen neuen, eindeutigen Pod-Namen demo-794889fc8d-qbcxm. Das ist die Versöhnungsschleife bei der Arbeit.

Du hast Kubernetes über das von dir erstellte Deployment mitgeteilt, dass der demo Pod immer ein Replikat betreiben soll. Kubernetes nimmt dich beim Wort, und selbst wenn du den Pod selbst löschst, geht Kubernetes davon aus, dass du einen Fehler gemacht hast, und startet hilfsbereit einen neuen Pod, um ihn zu ersetzen.

Wenn du mit dem Deployment fertig bist, beende es und bereinige es mit dem folgenden Befehl:

kubectl delete all --selector app=demo
pod "demo-794889fc8d-qbcxm" deleted
deployment.apps "demo" deleted
replicaset.apps "demo-794889fc8d" deleted

Das Zeitplannungsprogramm von Kubernetes

Wir haben gesagt, dass das Deployment Pods erstellt und Kubernetes den benötigten Pod startet, ohne wirklich zu erklären, wie das passiert.

Das Zeitplannungsprogramm von Kubernetes ist die Komponente, die für diesen Teil des Prozesses verantwortlich ist. Wenn ein Deployment (über sein zugehöriges ReplicaSet) entscheidet, dass ein neues Replikat benötigt wird, erstellt es eine Pod-Ressource in der Kubernetes-Datenbank. Gleichzeitig wird dieser Pod zu einer Warteschlange hinzugefügt, die sozusagen der Posteingang des Zeitplannungsprogramms ist.

Die Aufgabe des Zeitplannungsprogramms ist es, die Warteschlange der ungeplanten Pods zu beobachten, den nächsten Pod aus dieser Warteschlange zu nehmen und einen Knoten zu finden, auf dem er ausgeführt werden kann. Er wählt anhand verschiedener Kriterien, einschließlich der Ressourcenanforderungen des Pods, einen geeigneten Knoten aus, vorausgesetzt, es ist einer verfügbar (wir werden in Kapitel 5 mehr über diesen Prozess erfahren).

Sobald der Pod auf einem Knoten geplant wurde, wird er vom Kubelet, das auf diesem Knoten läuft, abgeholt und seine Container gestartet (siehe "Knotenkomponenten").

Als du einen Pod in "Maintaining Desired State" gelöscht hast , hat das ReplicaSet dies erkannt und einen Ersatz gestartet. Es weiß, dass ein demo Pod auf seinem Knoten laufen sollte, und wenn es keinen findet, startet es einen. (Was würde passieren, wenn du den Knoten ganz abschalten würdest? Seine Pods würden aus dem Zeitplan fallen und zurück in die Warteschlange des Zeitplannungsprogramms wandern, um anderen Knoten zugewiesen zu werden).

Die Stripe-Ingenieurin Julia Evans hat eine herrlich klare Erklärung geschrieben, wie das Scheduling in Kubernetes funktioniert.

Ressourcenmanifeste im YAML-Format

Du weißt jetzt, wie du eine Anwendung in Kubernetes ausführen kannst, war's das? Bist du fertig? Nicht ganz. Die Verwendung des Befehls zur Erstellung eines Deployments ist zwar nützlich, aber begrenzt. Angenommen, du möchtest etwas an der kubectl create Deployment-Spezifikation ändern : den Image-Namen oder die Version zum Beispiel. Du könntest den bestehenden Einsatz löschen (mit ) und einen neuen Einsatz mit den richtigen Feldern erstellen. Aber schauen wir mal, ob wir etwas Besseres finden. kubectl delete

Da Kubernetes von Natur aus ein deklaratives System ist, das den Ist-Zustand kontinuierlich mit dem Soll-Zustand abgleicht, musst du nur den Soll-Zustand - die Deployment-Spezifikation - ändern und Kubernetes erledigt den Rest. Wie machst du das?

Ressourcen sind Daten

Alle Kubernetes-Ressourcen, wie z.B. Deployments oder Pods, werden durch Datensätze in der internen Datenbank dargestellt. Die Reconciliation-Schleife überwacht die Datenbank auf Änderungen an diesen Datensätzen und ergreift die entsprechenden Maßnahmen. Der Befehl kubectl create fügt lediglich einen neuen Datensatz in der Datenbank hinzu, der einem Deployment entspricht, und Kubernetes erledigt den Rest.

Du musst aber nicht kubectl create verwenden, um mit Kubernetes zu interagieren. Du kannst das Ressourcenmanifest (die Spezifikation für den gewünschten Zustand der Ressource) auch direkt erstellen und bearbeiten. Du kannst (und solltest) die Manifestdatei in einem Versionskontrollsystem aufbewahren. Anstatt zwingende Befehle auszuführen, um Änderungen on-the-fly vorzunehmen, kannst du deine Manifestdateien ändern und dann Kubernetes anweisen, die aktualisierten Daten zu lesen.

Manifeste für den Einsatz

Das übliche Format für Kubernetes-Manifestdateien ist YAML, obwohl es auch das JSON-Format verstehen kann. Wie sieht also das YAML-Manifest für ein Deployment aus?

Sieh dir unser Beispiel für die Demo-Anwendung an(hello-k8s/k8s/deployment.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
  labels:
    app: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
        - name: demo
          image: cloudnatived/demo:hello
          ports:
          - containerPort: 8888

Auf den ersten Blick sieht das kompliziert aus, aber es ist größtenteils ein Standardtext. Die einzigen interessanten Teile sind die gleichen Informationen, die du schon in verschiedenen Formen gesehen hast: der Name des Container-Images und der Port. Als du diese Informationen an kubectl create weitergegeben hast, hat es im Hintergrund das Äquivalent dieses YAML-Manifests erstellt und es an Kubernetes übermittelt.

Mit kubectl apply

Um die volle Leistung von Kubernetes als deklaratives Infrastruktur-als-Code-System zu nutzen, übermittelst du selbst YAML-Manifeste an den Cluster, indem du den Befehl kubectl apply verwendest.

Probiere es mit unserem Beispiel-Deployment-Manifest, hello-k8s/k8s/deployment.yaml im Demo-Repository.1

Führe die folgenden Befehle in deiner geklonten Kopie des Demo-Repos aus:

cd hello-k8s
kubectl apply -f k8s/deployment.yaml
deployment.apps "demo" created

Nach ein paar Sekunden sollte ein demo Pod in Betrieb sein:

kubectl get pods --selector app=demo
NAME                   READY   STATUS    RESTARTS   AGE
demo-c77cc8d6f-nc6fm   1/1     Running   0          13s

Wir sind aber noch nicht ganz fertig, denn um uns mit einem Webbrowser mit dem demo Pod zu verbinden, müssen wir einen Service erstellen, eine Kubernetes-Ressource, mit der du dich mit deinen bereitgestellten Pods verbinden kannst (dazu gleich mehr).

Zuerst wollen wir uns ansehen, was ein Dienst ist und warum wir einen brauchen.

Service-Ressourcen

Angenommen, du möchtest eine Netzwerkverbindung zu einem Pod (wie unserer Beispielanwendung) herstellen. Wie machst du das? Du könntest die IP-Adresse des Pods herausfinden und dich direkt mit dieser Adresse und der Portnummer der Anwendung verbinden. Aber die IP-Adresse kann sich ändern, wenn der Pod neu gestartet wird, also musst du immer wieder nachschauen, ob sie noch aktuell ist.

Schlimmer noch, es könnte mehrere Replikate des Pods geben, jedes mit unterschiedlichen Adressen. Jede andere Anwendung, die mit dem Pod Kontakt aufnehmen muss, müsste eine Liste dieser Adressen führen, was keine gute Idee ist.

Zum Glück gibt es einen besseren Weg: Eine Service-Ressource gibt dir eine einzige, unveränderliche IP-Adresse oder einen DNS-Namen, der automatisch an jeden passenden Pod weitergeleitet wird. Später in "Ingress" werden wir über die Ingress-Ressource sprechen, die ein fortgeschritteneres Routing und die Verwendung von TLS-Zertifikaten ermöglicht.

Aber schauen wir uns erst einmal genauer an, wie ein Kubernetes Service funktioniert.

Du kannst dir einen Service wie einen Web-Proxy oder einen Load Balancer vorstellen, der Anfragen an eine Reihe von Backend-Pods weiterleitet(Abbildung 4-2). Er ist jedoch nicht auf Web-Ports beschränkt: Ein Service kann Datenverkehr von jedem Port zu jedem anderen Port weiterleiten, wie im ports Teil der Spezifikation beschrieben.

Diagram showing a Service forwarding traffic to Pods
Abbildung 4-2. Ein Dienst bietet einen dauerhaften Endpunkt für eine Gruppe von Pods

Hier ist das YAML-Manifest des Dienstes für unsere Demo-App:

apiVersion: v1
kind: Service
metadata:
  name: demo
  labels:
    app: demo
spec:
  ports:
  - port: 8888
    protocol: TCP
    targetPort: 8888
  selector:
    app: demo
  type: ClusterIP

Wie du siehst, sieht sie der Bereitstellungsressource, die wir vorhin gezeigt haben, ziemlich ähnlich. Allerdings ist die kind Service , statt Deployment, und die spec enthält nur eine Liste von ports, sowie eine selector und eine type.

Wenn du ein wenig heranzoomst, kannst du sehen, dass der Dienst seinen Port 8888 an den Port 8888 des Pods weiterleitet:

...
ports:
- port: 8888
  protocol: TCP
  targetPort: 8888

Die selector ist der Teil, der dem Dienst mitteilt, wie er Anfragen an bestimmte Pods weiterleiten soll. Anfragen werden an alle Pods weitergeleitet, die mit den angegebenen Labels übereinstimmen, in diesem Fall nur an app: demo (siehe "Labels"). In unserem Beispiel gibt es nur einen Pod, der passt, aber wenn es mehrere Pods gäbe, würde der Dienst jede Anfrage an einen zufällig ausgewählten Pod senden.2

In dieser Hinsicht ist ein Kubernetes-Service ein wenig wie ein herkömmlicher Load Balancer, und tatsächlich können sowohl Services als auch Ingresses automatisch Cloud Load Balancer erstellen (siehe "Ingress").

Das Wichtigste, woran du dich jetzt erinnern solltest, ist, dass ein Deployment eine Reihe von Pods für deine Anwendung verwaltet und ein Service dir einen einzigen Einstiegspunkt für Anfragen an diese Pods bietet.

Wende das Manifest jetzt an, um den Dienst zu erstellen:

kubectl apply -f k8s/service.yaml
service "demo" created

kubectl port-forward service/demo 9999:8888
Forwarding from 127.0.0.1:9999 -> 8888
Forwarding from [::1]:9999 -> 8888

Wie zuvor wird kubectl port-forward den demo Pod mit einem Port auf deinem lokalen Rechner verbinden, so dass du dich mit deinem Webbrowser mit http://localhost:9999/ verbinden kannst.

Wenn du sicher bist, dass alles richtig funktioniert, führst du den folgenden Befehl aus, um alles zu bereinigen, bevor du mit dem nächsten Abschnitt weitermachst:

kubectl delete -f k8s/
deployment.apps "demo" deleted
service "demo" deleted
Tipp

Du kannst kubectl delete mit einem Label-Selektor verwenden, wie wir es weiter oben getan haben, um alle Ressourcen zu löschen, die dem Selektor entsprechen (siehe "Labels"). Alternativ kannst du kubectl delete -f wie hier mit einem Verzeichnis von Manifesten verwenden. Alle Ressourcen, die in den Manifestdateien beschrieben sind, werden dann gelöscht.

Abfrage des Clusters mit kubectl

Das Tool kubectl ist das Schweizer Taschenmesser von Kubernetes: Es wendet die Konfiguration an, erstellt, ändert und löscht Ressourcen und kann außerdem Informationen über die vorhandenen Ressourcen und ihren Status im Cluster abfragen.

Wir haben bereits gesehen, wie du mit kubectl get Pods und Deployments abfragen kannst. Du kannst es auch verwenden, um zu sehen, welche Knoten in deinem Cluster vorhanden sind.

Wenn du mit Minikube arbeitest, sollte es ungefähr so aussehen:

kubectl get nodes
NAME       STATUS   ROLES                  AGE   VERSION
minikube   Ready    control-plane,master   17d   v1.21.2

Wenn du alle Ressourcentypen sehen willst, verwende kubectl get all. (Tatsächlich werden hier nicht alle Ressourcen angezeigt, sondern nur die gängigsten, aber darüber wollen wir uns jetzt nicht streiten).

Um umfassende Informationen über einen einzelnen Pod (oder eine andere Ressource) zu erhalten, verwende kubectl describe:

kubectl describe pod/demo-dev-6c96484c48-69vss
Name:         demo-794889fc8d-7frgb
Namespace:    default
Priority:     0
Node:         minikube/192.168.49.2
Start Time:   Mon, 02 Aug 2021 13:21:25 -0700
...
Containers:
  demo:
    Container ID:   docker://646aaf7c4baf6d...
    Image:          cloudnatived/demo:hello
...
Conditions:
  Type           Status
  Initialized    True
  Ready          True
  PodScheduled   True
...
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  1d    default-scheduler  Successfully assigned demo-dev...
  Normal  Pulling    1d    kubelet            pulling image "cloudnatived/demo...
...

In der Beispielausgabe kannst du sehen, dass kubectl dir einige grundlegende Informationen über den Container selbst gibt, einschließlich seiner Bildkennung und seines Status, sowie eine geordnete Liste der Ereignisse, die mit dem Container passiert sind. (In Kapitel 7 werden wir mehr über die Möglichkeiten von kubectl erfahren).

Ressourcen auf die nächste Stufe bringen

Du weißt jetzt alles, was du wissen musst, um Anwendungen mithilfe von deklarativen YAML-Manifesten in Kubernetes-Clustern einzusetzen. Aber in diesen Dateien gibt es viele Wiederholungen: Du hast zum Beispiel den Namen demo, den Label-Selektor app: demo und den Port 8888 mehrmals wiederholt.

Solltest du diese Werte nicht nur einmal angeben können und sie dann überall in den Kubernetes-Manifesten referenzieren können, wo sie vorkommen?

Es wäre zum Beispiel toll, Variablen mit Namen wie container.name und container.port zu definieren und sie dann überall dort zu verwenden, wo sie in den YAML-Dateien benötigt werden. Wenn du dann den Namen der App oder die Portnummer, an der sie lauscht, ändern musst, brauchst du sie nur an einer Stelle zu ändern, und alle Manifeste würden automatisch aktualisiert werden.

Zum Glück gibt es dafür ein Werkzeug, und im letzten Abschnitt dieses Kapitels zeigen wir dir, was es alles kann.

Helm: Ein Kubernetes-Paketmanager

Ein beliebter Paketmanager für Kubernetes heißt Helm und funktioniert genau so, wie wir es im vorherigen Abschnitt beschrieben haben. Mit dem Kommandozeilen-Tool helm kannst du Anwendungen (deine eigenen oder fremde) installieren und konfigurieren. Außerdem kannst du Pakete erstellen, die Helm-Charts genannt werden und die Ressourcen, die für die Ausführung der Anwendung benötigt werden, ihre Abhängigkeiten und ihre konfigurierbaren Einstellungen vollständig angeben.

Helm ist Teil der Projektfamilie der Cloud Native Computing Foundation (siehe "Cloud Native"), was seine Stabilität und weite Verbreitung widerspiegelt.

Hinweis

Es ist wichtig zu wissen, dass ein Helm-Diagramm im Gegensatz zu den binären Softwarepaketen, die von Tools wie APT oder Yum verwendet werden, nicht das Container-Image selbst enthält. Stattdessen enthält es lediglich Metadaten darüber, wo das Image gefunden werden kann, genau wie ein Kubernetes Deployment.

Wenn du das Diagramm installierst, sucht Kubernetes selbst nach dem binären Container-Image und lädt es von dem von dir angegebenen Ort herunter. Eigentlich ist ein Helm-Chart nur ein praktischer Wrapper für Kubernetes YAML-Manifeste.

Installation von Helm

Befolge die Helm-Installationsanweisungen für dein Betriebssystem.

Um zu überprüfen, ob Helm installiert ist und funktioniert, führe aus:

helm version
version.BuildInfo{Version:"v3...GoVersion:"go1.16.5"}

Sobald dieser Befehl erfolgreich ausgeführt wurde, kannst du Helm benutzen.

Installieren einer Steuerkarte

Wie würde das Helm-Diagramm für unsere Demo-Anwendung aussehen? Im hello-helm3-Verzeichnis siehst du ein k8s-Unterverzeichnis, das im vorherigen Beispiel (hello-k8s) nur die Kubernetes-Manifestdateien für den Einsatz der Anwendung enthielt. Jetzt enthält es ein Helm-Diagramm, das sich im Verzeichnis demo befindet:

ls k8s/demo
Chart.yaml  production-values.yaml  staging-values.yaml  templates  values.yaml

Was es mit diesen Dateien auf sich hat, erfährst du im Abschnitt "Was ist in einem Helm-Diagramm?", aber jetzt wollen wir Helm nutzen, um die Demo-Anwendung zu installieren. Bereinige zunächst die Ressourcen von früheren Bereitstellungen:

kubectl delete all --selector app=demo

Dann führe den folgenden Befehl aus:

helm upgrade --install demo ./k8s/demo
NAME: demo
LAST DEPLOYED: Mon Aug  2 13:37:21 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

Wenn du die Befehle kubectl get deployment und kubectl get service verwendest, die du zuvor gelernt hast, wirst du sehen, dass Helm eine Bereitstellungsressource (die einen Pod startet) und einen Service erstellt hat, genau wie in den vorherigen Beispielen. Der Befehl helm upgrade --install erstellt außerdem ein Kubernetes Secret mit dem Typ helm.sh/release.v1, um die Veröffentlichung zu verfolgen.

Charts, Repositories und Veröffentlichungen

Dies sind die drei wichtigsten Helm-Begriffe, die du kennen musst:

  • Ein Diagramm ist ein Helm-Paket, das alle Ressourcendefinitionen enthält, die für den Betrieb einer Anwendung in Kubernetes notwendig sind.

  • Ein Repository ist ein Ort, an dem Charts gesammelt und geteilt werden können.

  • Ein Release ist eine bestimmte Instanz einer Karte, die in einem Kubernetes-Cluster läuft.

Es gibt einige Parallelen zwischen den Helm-Ressourcen und Docker-Containern:

  • Ein Helm-Repository ist ein Server, auf dem die Diagramme gespeichert und von den Clients heruntergeladen werden, ähnlich wie eine Container-Registry Container-Images speichert und bereitstellt, z.B. Docker Hub.

  • Eine Helm-Freigabe bedeutet, dass ein Diagramm in einem Cluster installiert wird, ähnlich wie ein veröffentlichtes Docker-Image als laufender Container gestartet wird.

Helm-Diagramme können von Repository-Servern heruntergeladen und installiert werden oder direkt installiert werden, indem du auf einen lokalen Pfad eines Verzeichnisses zeigst, das die Helm-YAML-Dateien im Dateisystem enthält.

Eine Karte kann viele Male im selben Cluster installiert werden. Du könntest zum Beispiel mehrere Kopien der Redis-Karte für verschiedene Anwendungen betreiben, die jeweils als Backend für verschiedene Websites dienen. Jede einzelne Instanz der Helm-Karte ist eine eigene Version.

Vielleicht möchtest du auch etwas zentral in deinem Cluster installieren, das von all deinen Anwendungen genutzt wird, wie Prometheus für die zentrale Überwachung oder den NGINX Ingress Controller für die Bearbeitung eingehender Webanfragen.

Listing Helm Releases

Um zu überprüfen, welche Versionen du gerade verwendest, rufe helm list auf:

helm list
NAME  NAMESPACE REVISION  UPDATED STATUS    CHART
demo  default   1         ...     deployed  demo-1.0.1

Um den genauen Status einer bestimmten Version zu sehen, rufe helm status gefolgt von dem Namen der Version auf. Du erhältst dieselben Informationen wie beim ersten Einsatz der Version.

Später im Buch zeigen wir dir, wie du deine eigenen Helm-Charts für deine Anwendungen erstellst (siehe "Was steckt in einem Helm-Chart?"). Für den Moment reicht es, wenn du weißt, dass Helm eine praktische Methode ist, um Anwendungen aus öffentlichen Charts zu installieren.

Viele beliebte Anwendungen werden in verschiedenen Helm-Repositories gehostet und von den Paketanbietern gepflegt. Du kannst Helm-Repositories hinzufügen und ihre Diagramme installieren. Du kannst auch deine eigenen Helm-Diagramme für deine eigenen Anwendungen hosten und veröffentlichen.

Tipp

Auf Artifact Hub, einem weiteren CNCF-Projekt, kannst du viele Beispiele für beliebte Helm-Diagramme sehen.

Zusammenfassung

In diesem Buch geht es in erster Linie um die Nutzung von Kubernetes und nicht darum, wie Kubernetes im Detail funktioniert. Unser Ziel ist es, dir zu zeigen, was Kubernetes kann, und dich schnell an den Punkt zu bringen, an dem du echte Workloads in der Produktion ausführen kannst. Es ist jedoch nützlich, zumindest einige der wichtigsten Komponenten zu kennen, mit denen du arbeiten wirst, wie Pods und Deployments. In diesem Kapitel haben wir einige der wichtigsten kurz vorgestellt. Außerdem empfehlen wir dir die Bücher Managing Kubernetes, Production Kubernetes und das Kubernetes the Hard Way Repo, wenn du dich mit dem, was unter der Haube vor sich geht, vertraut machen willst.

So faszinierend die Technologie für Geeks wie uns auch sein mag, wir sind auch daran interessiert, Dinge zu erledigen. Deshalb haben wir nicht jede Ressource, die Kubernetes bietet, ausführlich behandelt, denn es gibt eine Menge davon, und viele davon wirst du mit Sicherheit nicht brauchen (zumindest noch nicht).

Die wichtigsten Punkte, die du jetzt wissen solltest, sind:

  • Der Pod ist die grundlegende Arbeitseinheit in Kubernetes, die einen einzelnen Container oder eine Gruppe von kommunizierenden Containern bezeichnet, die gemeinsam geplant werden.

  • Ein Deployment ist eine Kubernetes-Ressource auf hoher Ebene, die Pods deklarativ verwaltet, indem sie sie einsetzt, plant, aktualisiert und bei Bedarf neu startet.

  • Ein Service ist das Kubernetes-Äquivalent zu einem Load Balancer oder Proxy, der den Datenverkehr über eine einzige, bekannte und dauerhafte IP-Adresse oder einen DNS-Namen an die entsprechenden Pods weiterleitet.

  • Das Zeitplannungsprogramm von Kubernetes sucht nach einem Pod, der noch auf keinem Node läuft, findet einen passenden Node dafür und weist das Kubelet auf diesem Node an, den Pod auszuführen.

  • Ressourcen wie Bereitstellungen werden durch Datensätze in der internen Datenbank von Kubernetes dargestellt. Extern können diese Ressourcen durch Textdateien (so genannte Manifeste) im YAML-Format dargestellt werden. Das Manifest ist eine Erklärung über den gewünschten Zustand der Ressource.

  • kubectl ist das Hauptwerkzeug für die Interaktion mit Kubernetes, mit dem du Manifeste anwenden, Ressourcen abfragen, Änderungen vornehmen, Ressourcen löschen und viele andere Aufgaben erledigen kannst.

  • Helm ist ein Kubernetes-Paketmanager. Er vereinfacht die Konfiguration und den Einsatz von Kubernetes-Anwendungen, indem er es dir ermöglicht, einen einzigen Satz von gebündelten Manifesten und Templates zu verwenden, um parametrisierte Kubernetes-YAML-Dateien zu generieren, anstatt die rohen YAML-Dateien selbst zu pflegen.

1 k8s, ausgesprochen kates, ist eine gebräuchliche Abkürzung für Kubernetes, die dem geekigen Muster folgt, Wörter als Numeronym abzukürzen: der erste und der letzte Buchstabe plus die Anzahl der Buchstaben dazwischen(k-8-s). Siehe auch i18n (Internationalisierung), a11y (Barrierefreiheit) und o11y (Beobachtbarkeit).

2 Dies ist der Standard-Algorithmus für die Lastverteilung; Kubernetes-Versionen 1.10+ unterstützen auch andere Algorithmen, wie z.B. die kleinste Verbindung. Mehr dazu findest du in der Kubernetes-Dokumentation.

Get Cloud Native DevOps mit Kubernetes, 2. Auflage now with the O’Reilly learning platform.

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