Kapitel 4. Zeitplanungsprogramm und Werkzeuge
Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com
Der Scheduling-Teil des CKA konzentriert sich auf die Auswirkungen der Definition von Ressourcengrenzen bei der Auswertung durch das Zeitplannungsprogramm von Kubernetes. Das Standard-Laufzeitverhalten des Zeitplannungsprogramms kann auch durch die Definition von Node-Affinitätsregeln sowie von Taints und Toleranzen verändert werden. Von diesen Konzepten wird nur erwartet, dass du die Feinheiten der Ressourcengrenzen und ihre Auswirkungen auf das Zeitplannungsprogramm in verschiedenen Szenarien verstehst. Schließlich werden in diesem Bereich des Lehrplans Kenntnisse über die Verwaltung von Manifesten und Templating-Tools auf hohem Niveau vorausgesetzt.
In diesem Kapitel werden die folgenden Konzepte behandelt:
-
Ressourcengrenzen für Pods
-
Imperative und deklarative Manifestverwaltung
-
Gängige Templating-Tools wie Kustomize,
yq
, und Helm
Wie sich Ressourcenbeschränkungen auf diePod-Planung auswirken
Ein Kubernetes-Cluster kann aus mehreren Knotenpunkten bestehen. Das Zeitplannungsprogramm von Kubernetes entscheidet anhand verschiedener Regeln (z.B. Node-Selektoren, Node-Affinität, Taints und Toleranzen), welcher Knoten für die Ausführung der Arbeitslast ausgewählt wird. In der CKA-Prüfung wird nicht verlangt, dass du die oben genannten Scheduling-Konzepte verstehst, aber es wäre hilfreich, wenn du eine grobe Vorstellung davon hättest, wie sie auf hohem Niveau funktionieren.
Eine Metrik, die bei der Workload-Planung eine Rolle spielt, ist die von den Containern eines Pods definierte Ressourcenanforderung. Übliche Ressourcen, die angegeben werden können, sind CPU und Speicher. Das Zeitplannungsprogramm stellt sicher, dass die Ressourcenkapazität des Knotens die Ressourcenanforderungen des Pods erfüllen kann. Genauer gesagt,ermittelt das Zeitplannungsprogramm die Summe der Ressourcenanforderungen pro Typ für alle im Pod definierten Container und vergleicht sie mit den verfügbaren Ressourcen des Knotens. Abbildung 4-1 veranschaulicht den Zeitplanungsprozess auf der Grundlage der Ressourcenanforderungen.
Definieren von Container-Ressourcenanfragen
Jeder Container eines Pods kann seine eigenen Ressourcenanforderungen definieren. Tabelle 4-1 beschreibt die verfügbaren Optionen sowie einen Beispielwert.
YAML-Attribut | Beschreibung | Beispielwert |
---|---|---|
|
CPU-Ressourcen-Typ |
|
|
Typ der Speicherressource |
|
|
Riesige Seite Ressourcentyp |
|
|
Typ der flüchtigen Speicherung von Ressourcen |
|
Kubernetes verwendet Ressourceneinheiten für Ressourcentypen, die von den Standard-Ressourceneinheiten wie Megabyte und Gigabyte abweichen. Alle Feinheiten dieser Einheiten zu erklären, würde den Rahmen dieses Buches sprengen, aber du kannst die Details in der Dokumentation nachlesen.
Um die Verwendung dieser Ressourcenanforderungen transparent zu machen, sehen wir uns eine Beispieldefinition an. Das in Beispiel 4-1 gezeigte Pod-YAML-Manifest definiert zwei Container, die jeweils eigene Ressourcenanforderungen haben. Jeder Knoten, auf dem der Pod ausgeführt werden darf, muss mindestens eine Speicherkapazität von 320Mi und 1250m CPU unterstützen, also die Summe der Ressourcen für beide Container.
Beispiel 4-1. Einstellen von Container-Ressourcenanforderungen
apiVersion
:
v1
kind
:
Pod
metadata
:
name
:
rate-limiter
spec
:
containers
:
-
name
:
business-app
image
:
bmuschko/nodejs-business-app:1.0.0
ports
:
-
containerPort
:
8080
resources
:
requests
:
memory
:
"256Mi"
cpu
:
"1"
-
name
:
ambassador
image
:
bmuschko/nodejs-ambassador:1.0.0
ports
:
-
containerPort
:
8081
resources
:
requests
:
memory
:
"64Mi"
cpu
:
"250m"
In diesem Szenario haben wir es mit einem Minikube-Kubernetes-Cluster zu tun, der aus drei Knoten besteht, einem Control-Plane-Knoten und zwei Worker-Knoten. Der folgende Befehl listet alle Knoten auf:
$ kubectl get nodes NAME STATUS ROLES AGE VERSION minikube Ready control-plane,master 12d v1.21.2 minikube-m02 Ready <none> 42m v1.21.2 minikube-m03 Ready <none> 41m v1.21.2
Im nächsten Schritt erstellen wir den Pod aus dem YAML-Manifest. Das Zeitplannungsprogramm platziert den Pod auf dem Knoten namens minikube-m03
:
$ kubectl create -f rate-limiter-pod.yaml pod/rate-limiter created $ kubectl get pod rate-limiter -o yaml | grep nodeName: nodeName: minikube-m03
Wenn du dir den Knoten genauer ansiehst, kannst du seine maximale Kapazität, wie viel davon zuweisbar ist und die Speicheranforderungen der Pods, die auf dem Knoten geplant sind, einsehen. Der folgende Befehl listet die Informationen auf und komprimiert die Ausgabe auf die relevanten Teile:
$ kubectl describe node minikube-m03 ... Capacity: cpu: 2 ephemeral-storage: 17784752Ki hugepages-2Mi: 0 memory: 2186612Ki pods: 110 Allocatable: cpu: 2 ephemeral-storage: 17784752Ki hugepages-2Mi: 0 memory: 2186612Ki pods: 110 ... Non-terminated Pods: (3 in total) Namespace Name CPU Requests CPU Limits \ Memory Requests Memory Limits AGE --------- ---- ------------ ---------- \ --------------- ------------- --- default rate-limiter 1250m (62%) 0 (0%) \ 320Mi (14%) 0 (0%) 9m ...
Es ist durchaus möglich, dass ein Pod nicht geplant werden kann, weil auf den Knoten nicht genügend Ressourcen zur Verfügung stehen. In diesen Fällen wird das Ereignisprotokoll des Pods diese Situation mit den Gründen PodExceedsFreeCPU
oder PodExceedsFreeMemory
anzeigen. Weitere Informationen zur Fehlerbehebung und Lösung dieser Situation findest du im entsprechenden Abschnitt der Dokumentation.
Festlegen von Container-Ressourcenlimits
Eine weitere Kennzahl, die du für einen Container festlegen kannst, sind seine Ressourcenlimits. Ressourcenlimits stellen sicher, dass der Container nicht mehr als die zugewiesenen Ressourcen verbrauchen kann. Du könntest zum Beispiel festlegen, dass die Anwendung, die im Container läuft, nicht mehr als 1000m CPU und 512Mi Arbeitsspeicher verbrauchen darf.
Je nach der vom Cluster verwendeten Container-Laufzeitumgebung führt das Überschreiten einer der zulässigen Ressourcengrenzen zum Abbruch des im Container laufenden Anwendungsprozesses oder dazu, dass das System die Zuweisung von Ressourcen über die Grenzen hinaus gänzlich verhindert. Wie dieContainer-Laufzeitumgebung Docker mit Ressourcengrenzen umgeht, wird in der Dokumentation ausführlich beschrieben.
Tabelle 4-2 beschreibt die verfügbaren Optionen und gibt einen Beispielwert an.
YAML-Attribut | Beschreibung | Beispielwert |
---|---|---|
|
CPU-Ressourcen-Typ |
|
|
Typ der Speicherressource |
|
|
Riesige Seite Ressourcentyp |
|
|
Typ der flüchtigen Speicherung von Ressourcen |
|
Beispiel 4-2 zeigt die Definition von Grenzen in Aktion. Hier kann der Container business-app
nicht mehr als 512Mi Arbeitsspeicher und 2000m CPU verwenden. Der Container mit dem Namen ambassador
definiert ein Limit von 128Mi Arbeitsspeicher und 500m CPU.
Beispiel 4-2. Container-Ressourcenlimits festlegen
apiVersion
:
v1
kind
:
Pod
metadata
:
name
:
rate-limiter
spec
:
containers
:
-
name
:
business-app
image
:
bmuschko/nodejs-business-app:1.0.0
ports
:
-
containerPort
:
8080
resources
:
limits
:
memory
:
"512Mi"
cpu
:
"2"
-
name
:
ambassador
image
:
bmuschko/nodejs-ambassador:1.0.0
ports
:
-
containerPort
:
8081
resources
:
limits
:
memory
:
"128Mi"
cpu
:
"500m"
Nehmen wir an, dass der Pod auf dem Knoten minikube-m03
geplant wurde. Wenn du die Details des Knotens beschreibst, siehst du, dass die CPU- und Speicherbegrenzungen wirksam wurden. Aber das ist noch nicht alles. Kubernetes weist automatisch die gleiche Menge an Ressourcen für die Requests zu, wenn du nur die Limits definierst:
$ kubectl describe node minikube-m03 ... Non-terminated Pods: (3 in total) Namespace Name CPU Requests CPU Limits \ Memory Requests Memory Limits AGE --------- ---- ------------ ---------- \ --------------- ------------- --- default rate-limiter 1250m (62%) 1250m (62%) \ 320Mi (14%) 320Mi (14%) 11s ...
Festlegen von Requests und Limits für Container-Ressourcen
Es wird empfohlen, dass du für jeden Container Ressourcenanforderungen und Limits festlegst. Es ist nicht immer einfach, diese Ressourcenanforderungen zu bestimmen, insbesondere bei Anwendungen, die noch nicht in einer Produktionsumgebung getestet wurden. Lasttests der Anwendung zu einem frühen Zeitpunkt während des Entwicklungszyklus können bei der Analyse des Ressourcenbedarfs helfen. Weitere Anpassungen können vorgenommen werden, indem der Ressourcenverbrauch der Anwendung nach der Bereitstellung im Cluster überwacht wird. Beispiel 4-3 fasst Ressourcenanforderungen und Limits in einem einzigen YAML-Manifest zusammen.
Beispiel 4-3. Einstellen von Requests und Limits für Containerressourcen
apiVersion
:
v1
kind
:
Pod
metadata
:
name
:
rate-limiter
spec
:
containers
:
-
name
:
business-app
image
:
bmuschko/nodejs-business-app:1.0.0
ports
:
-
containerPort
:
8080
resources
:
requests
:
memory
:
"256Mi"
cpu
:
"1"
limits
:
memory
:
"512Mi"
cpu
:
"2"
-
name
:
ambassador
image
:
bmuschko/nodejs-ambassador:1.0.0
ports
:
-
containerPort
:
8081
resources
:
requests
:
memory
:
"64Mi"
cpu
:
"250m"
limits
:
memory
:
"128Mi"
cpu
:
"500m"
Als Ergebnis kannst du die verschiedenen Einstellungen für Ressourcenanforderungen und Limits sehen:
$ kubectl describe node minikube-m03 ... Non-terminated Pods: (3 in total) Namespace Name CPU Requests CPU Limits \ Memory Requests Memory Limits AGE --------- ---- ------------ ---------- \ --------------- ------------- --- default rate-limiter 1250m (62%) 2500m (125%) \ 320Mi (14%) 640Mi (29%) 3s ...
Objekte verwalten
Kubernetes-Objekte können mithilfe von imperativen Befehlen ( kubectl
) erstellt, geändert und gelöscht werden oder indem ein kubectl
Befehl gegen eine Konfigurationsdatei ausgeführt wird, die den gewünschten Zustand eines Objekts, ein sogenanntes Manifest, definiert. Die primäre Definitionssprache für ein Manifest ist YAML, du kannst dich aber auch für JSON entscheiden, das in der Kubernetes-Gemeinschaft weniger verbreitet ist. Es wird empfohlen, dass Entwicklungsteams diese Konfigurationsdateien in Versionskontroll-Repositories übertragen, um Änderungen im Laufe der Zeit zu verfolgen und zu überprüfen.
Die Modellierung einer Anwendung in Kubernetes erfordert oft eine Reihe von unterstützenden Objekten, von denen jedes sein eigenes Manifest haben kann. Du möchtest zum Beispiel ein Deployment erstellen, das die Anwendung auf fünf Pods ausführt, eine ConfigMap, um Konfigurationsdaten als Umgebungsvariablen zu injizieren, und einen Service für den Netzwerkzugang.
Dieser Abschnitt konzentriert sich in erster Linie auf die deklarative Objektverwaltung mit Hilfe von Manifesten. Eine ausführlichere Diskussion über die imperative Unterstützung findest du in den entsprechenden Abschnitten der Dokumentation. Außerdem gehen wir auf Tools wie Kustomize und Helm ein, um dir einen Eindruck von ihren Vorteilen, Fähigkeiten und Arbeitsabläufen zu vermitteln.
Deklarative Objektverwaltung mit Konfigurationsdateien
Die deklarative Objektverwaltung erfordert eine oder mehrere Konfigurationsdateien im Format YAML oder JSON, die den gewünschten Zustand eines Objekts beschreiben. Mit diesem Ansatz kannst du Objekte erstellen, aktualisieren und löschen.
Objekte erstellen
Um neue Objekte zu erstellen, führst du den Befehl apply
aus, indem du mit der Option -f
auf eine Datei, ein Dateiverzeichnis oder eine Datei zeigst, auf die eine HTTP(S)-URL verweist. Wenn eines oder mehrere der Objekte bereits existieren, synchronisiert der Befehl die an der Konfiguration vorgenommenen Änderungen mit dem Live-Objekt.
Um die Funktionalität zu demonstrieren, gehen wir von den folgenden Verzeichnissen und Konfigurationsdateien aus. Die folgenden Befehle erstellen Objekte aus einer einzelnen Datei, aus allen Dateien innerhalb eines Verzeichnisses und aus allen Dateien in einem Verzeichnis rekursiv:
. ├── app-stack │ ├── mysql-pod.yaml │ ├── mysql-service.yaml │ ├── web-app-pod.yaml │ └── web-app-service.yaml ├── nginx-deployment.yaml └── web-app ├── config │ ├── db-configmap.yaml │ └── db-secret.yaml └── web-app-pod.yaml
Erstellen eines Objekts aus einer einzelnen Datei:
$ kubectl apply -f nginx-deployment.yaml deployment.apps/nginx-deployment created
Objekte aus mehreren Dateien innerhalb eines Verzeichnisses erstellen:
$ kubectl apply -f app-stack/ pod/mysql-db created service/mysql-service created pod/web-app created service/web-app-service created
Erstellen von Objekten aus einem rekursiven Verzeichnisbaum mit Dateien:
$ kubectl apply -f web-app/ -R configmap/db-config configured secret/db-creds created pod/web-app created
Erstellen von Objekten aus einer Datei, auf die eine HTTP(S)-URL verweist:
$ kubectl apply -f https://raw.githubusercontent.com/bmuschko/cka-study-guide/ \ master/ch04/object-management/nginx-deployment.yaml deployment.apps/nginx-deployment created
Der Befehl apply
hält die Änderungen fest, indem er die Anmerkung mit dem Schlüssel kubectl.kubernetes.io/last-applied-configuration
hinzufügt oder ändert. Ein Beispiel für die Annotation in der Ausgabe des Befehls get pod
findest du hier:
$ kubectl get pod web-app -o yaml apiVersion: v1 kind: Pod metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{}, \ "labels":{"app":"web-app"},"name":"web-app","namespace":"default"}, \ "spec":{"containers":[{"envFrom":[{"configMapRef":{"name":"db-config"}}, \ {"secretRef":{"name":"db-creds"}}],"image":"bmuschko/web-app:1.0.1", \ "name":"web-app","ports":[{"containerPort":3000,"protocol":"TCP"}]}], \ "restartPolicy":"Always"}} ...
Objekte aktualisieren
Das Aktualisieren eines bestehenden Objekts erfolgt mit demselben Befehl apply
. Du musst nur die Konfigurationsdatei ändern und dann den Befehl gegen sie ausführen. Beispiel 4-4 ändert die bestehende Konfiguration eines Deployments in der Datei nginx-deployment.yaml
. Wir haben ein neues Label mit dem Schlüssel team
hinzugefügt und die Anzahl der Replikate von 3 auf 5 geändert.
Beispiel 4-4. Geänderte Konfigurationsdatei für ein Deployment
apiVersion
:
apps/v1
kind
:
Deployment
metadata
:
name
:
nginx-deployment
labels
:
app
:
nginx
team
:
red
spec
:
replicas
:
5
...
Der folgende Befehl wendet die geänderte Konfigurationsdatei an. Die Anzahl der Pods, die von dem zugrunde liegenden ReplicaSet kontrolliert werden, beträgt nun 5. Der Verwendungsnachweis kubectl.kubernetes.io/last-applied-configuration
spiegelt die letzte Änderung der Konfiguration wider:
$ kubectl apply -f nginx-deployment.yaml deployment.apps/nginx-deployment configured $ kubectl get deployments,pods NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/nginx-deployment 5/5 5 5 10m NAME READY STATUS RESTARTS AGE pod/nginx-deployment-66b6c48dd5-79j6t 1/1 Running 0 35s pod/nginx-deployment-66b6c48dd5-bkkgb 1/1 Running 0 10m pod/nginx-deployment-66b6c48dd5-d26c8 1/1 Running 0 10m pod/nginx-deployment-66b6c48dd5-fcqrs 1/1 Running 0 10m pod/nginx-deployment-66b6c48dd5-whfnn 1/1 Running 0 35s $ kubectl get deployment nginx-deployment -o yaml apiVersion: apps/v1 kind: Deployment metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{}, \ "labels":{"app":"nginx","team":"red"},"name":"nginx-deployment", \ "namespace":"default"},"spec":{"replicas":5,"selector":{"matchLabels": \ {"app":"nginx"}},"template":{"metadata":{"labels":{"app":"nginx"}}, \ "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx", \ "ports":[{"containerPort":80}]}]}}}} ...
Löschen von Objekten
Es gibt zwar eine Möglichkeit, Objekte mit dem Befehl apply
zu löschen, indem du die Optionen --prune -l <labels>
angibst, aber es wird empfohlen, ein Objekt mit dem Befehl delete
zu löschen und ihn auf die Konfigurationsdatei zu verweisen. Der folgende Befehl löscht ein Deployment und die von ihm kontrollierten Objekte (ReplicaSet und Pods):
$ kubectl delete -f nginx-deployment.yaml deployment.apps "nginx-deployment" deleted $ kubectl get deployments,replicasets,pods No resources found in default namespace.
Deklarative Objektverwaltung mit Kustomize
Kustomize ist ein Tool, das mit Kubernetes 1.14 eingeführt wurde und die Verwaltung von Manifesten vereinfachen soll. Es unterstützt drei verschiedene Anwendungsfälle:
-
Erzeugen von Manifesten aus anderen Quellen. Zum Beispiel, indem du eine ConfigMap erstellst und ihre Schlüssel-Wert-Paare aus einer Eigenschaftsdatei ausfüllst.
-
Hinzufügen einer gemeinsamen Konfiguration für mehrere Manifeste. Zum Beispiel kannst du einen Namensraum und eine Reihe von Bezeichnungen für eine Bereitstellung und einen Dienst hinzufügen.
-
Zusammenstellen und Anpassen einer Sammlung von Manifesten. Zum Beispiel das Festlegen von Ressourcengrenzen für mehrere Bereitstellungen.
Die zentrale Datei, die benötigt wird, damit Kustomize funktioniert, ist die Kustomisierungsdatei. Der standardisierte Name für die Datei ist kustomization.yaml
und kann nicht geändert werden. Eine Kustomization-Datei definiert die Verarbeitungsregeln, nach denen Kustomize arbeitet.
Kustomize ist vollständig in kubectl
integriert und kann in zwei Modi ausgeführt werden: Rendering der Verarbeitungsausgabe auf der Konsole oder Erstellen der Objekte. Beide Modi können mit einem Verzeichnis, einem Tarball, einem Git-Archiv oder einer URL arbeiten, solange sie die Kustomize-Datei und die referenzierten Ressourcendateien enthalten:
- Rendering der produzierten Ausgabe
-
Im ersten Modus wird der Unterbefehl
kustomize
verwendet, um das Ergebnis auf der Konsole darzustellen, aber die Objekte werden nicht erstellt. Dieser Befehl funktioniert ähnlich wie die Option "Trockenlauf", die du vielleicht von dem Befehlrun
kennst:$ kubectl kustomize <target>
- Erstellen der Objekte
-
Der zweite Modus verwendet den Befehl
apply
in Verbindung mit der Befehlszeilenoption-k
, um die von Kustomize verarbeiteten Ressourcen anzuwenden, wie im vorherigen Abschnitt erklärt:$ kubectl apply -k <target>
In den folgenden Abschnitten wird jeder der Anwendungsfälle anhand eines Beispiels erläutert. Eine vollständige Übersicht über alle möglichen Szenarien findest du in der Dokumentation oder im Kustomize GitHub Repository.
Manifeste zusammenstellen
Eine der Kernfunktionen von Kustomize ist es, ein zusammengesetztes Manifest aus anderen Manifesten zu erstellen. Das Kombinieren mehrerer Manifeste zu einem einzigen mag für sich genommen nicht sehr nützlich erscheinen, aber viele der anderen Funktionen, die später beschrieben werden, bauen auf dieser Fähigkeit auf. Angenommen, du möchtest ein Manifest aus einer Verteilungs- und einer Service-Ressourcendatei zusammenstellen. Alles, was du tun musst, ist, die Ressourcendateien in denselben Ordner zu legen wie die Anpassungsdatei:
. ├── kustomization.yaml ├── web-app-deployment.yaml └── web-app-service.yaml
Die kustomization-Datei listet die Ressourcen im Abschnitt resources
auf, wie in Beispiel 4-5 gezeigt.
Beispiel 4-5. Eine Anpassungsdatei, die zwei Manifeste kombiniert
resources
:
-
web-app-deployment.yaml
-
web-app-service.yaml
Der Unterbefehl kustomize
zeigt das kombinierte Manifest an, das alle Ressourcen enthält, die durch drei Bindestriche (---
) getrennt sind, um die verschiedenen Objektdefinitionen zu kennzeichnen:
$ kubectl kustomize ./ apiVersion: v1 kind: Service metadata: labels: app: web-app-service name: web-app-service spec: ports: - name: web-app-port port: 3000 protocol: TCP targetPort: 3000 selector: app: web-app type: NodePort --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: web-app-deployment name: web-app-deployment spec: replicas: 3 selector: matchLabels: app: web-app template: metadata: labels: app: web-app spec: containers: - env: - name: DB_HOST value: mysql-service - name: DB_USER value: root - name: DB_PASSWORD value: password image: bmuschko/web-app:1.0.1 name: web-app ports: - containerPort: 3000
Erzeugen von Manifesten aus anderen Quellen
Weiter oben in diesem Kapitel haben wir gelernt, dass ConfigMaps und Secrets erstellt werden können, indem sie auf eine Datei verweisen, die die eigentlichen Konfigurationsdaten für sie enthält. Kustomize kann bei diesem Prozess helfen, indem es die Beziehung zwischen dem YAML-Manifest dieser Konfigurationsobjekte und ihren Daten abbildet. Außerdem wollen wir die erstellte ConfigMap und Secret als Umgebungsvariablen in einen Pod einfügen. In diesem Abschnitt erfährst du, wie du dies mit Hilfe von Kustomize erreichen kannst.
Die folgende Datei- und Verzeichnisstruktur enthält die Manifestdatei für den Pod und die Konfigurationsdateien, die wir für die ConfigMap und Secret benötigen. Die obligatorische Anpassungsdatei befindet sich auf der Stammebene des Verzeichnisbaums:
. ├── config │ ├── db-config.properties │ └── db-secret.properties ├── kustomization.yaml └── web-app-pod.yaml
In kustomization.yaml
kannst du festlegen, dass die ConfigMap und das Secret-Objekt mit dem angegebenen Namen erstellt werden sollen. Der Name der ConfigMap soll db-config
lauten und der Name des Secret wird db-creds
sein. Die beiden Generatorattribute configMapGenerator
und secretGenerator
verweisen auf eine Eingabedatei, in die die Konfigurationsdaten eingegeben werden. Alle zusätzlichen Ressourcen können mit dem Attribut resources
angegeben werden. Beispiel 4-6 zeigt den Inhalt der Anpassungsdatei.
Beispiel 4-6. Eine Anpassungsdatei mit einer ConfigMap und einem Secret-Generator
configMapGenerator
:
-
name
:
db-config
files
:
-
config/db-config.properties
secretGenerator
:
-
name
:
db-creds
files
:
-
config/db-secret.properties
resources
:
-
web-app-pod.yaml
Kustomize erzeugt ConfigMaps und Secrets, indem es ein Suffix an den Namen anhängt. Du kannst dieses Verhalten sehen, wenn du die Objekte mit dem Befehl apply
erstellst. Die ConfigMap und das Secret können im Pod-Manifest mit ihrem Namen referenziert werden:
$ kubectl apply -k ./ configmap/db-config-t4c79h4mtt unchanged secret/db-creds-4t9dmgtf9h unchanged pod/web-app created
Diese Benennungsstrategie kann mit dem Attribut generatorOptions
in der kustomization-Datei konfiguriert werden. WeitereInformationen findest du in der Dokumentation.
Probieren wir auch den Unterbefehl kustomize
aus. Anstatt die Objekte zu erstellen, gibt der Befehl die verarbeitete Ausgabe auf der Konsole aus:
$ kubectl kustomize ./ apiVersion: v1 data: db-config.properties: |- DB_HOST: mysql-service DB_USER: root kind: ConfigMap metadata: name: db-config-t4c79h4mtt --- apiVersion: v1 data: db-secret.properties: REJfUEFTU1dPUkQ6IGNHRnpjM2R2Y21RPQ== kind: Secret metadata: name: db-creds-4t9dmgtf9h type: Opaque --- apiVersion: v1 kind: Pod metadata: labels: app: web-app name: web-app spec: containers: - envFrom: - configMapRef: name: db-config-t4c79h4mtt - secretRef: name: db-creds-4t9dmgtf9h image: bmuschko/web-app:1.0.1 name: web-app ports: - containerPort: 3000 protocol: TCP restartPolicy: Always
Hinzufügen einer gemeinsamen Konfiguration für mehrere Manifeste
Anwendungsentwickler arbeiten normalerweise an einem Anwendungsstack, der aus mehreren Manifesten besteht. Ein Anwendungsstapel kann zum Beispiel aus einem Frontend-Microservice, einem Backend-Microservice und einer Datenbank bestehen. Es ist üblich, für alle Manifeste die gleiche, übergreifende Konfiguration zu verwenden. Kustomize bietet eine Reihe von unterstützten Feldern (z. B. Namensräume, Labels oder Anmerkungen). In der Dokumentation findest du Informationen zu allen unterstützten Feldern.
Für das nächste Beispiel gehen wir davon aus, dass ein Einsatz und ein Dienst im selben Namensraum leben und einen gemeinsamen Satz von Bezeichnungen verwenden. Der Namensraum heißt persistence
und das Label ist das Schlüssel-Wert-Paar team: helix
. Beispiel 4-7 zeigt, wie man diese gemeinsamen Felder in der Anpassungsdatei einstellt.
Beispiel 4-7. Eine Anpassungsdatei mit einem gemeinsamen Feld
namespace
:
persistence
commonLabels
:
team
:
helix
resources
:
-
web-app-deployment.yaml
-
web-app-service.yaml
Um die referenzierten Objekte in der Anpassungsdatei zu erstellen, führe den Befehl apply
aus. Achte darauf, dass du vorher den Namensraum persistence
erstellst:
$ kubectl create namespace persistence namespace/persistence created $ kubectl apply -k ./ service/web-app-service created deployment.apps/web-app-deployment created
Die YAML-Darstellung der verarbeiteten Dateien sieht wie folgt aus:
$ kubectl kustomize ./ apiVersion: v1 kind: Service metadata: labels: app: web-app-service team: helix name: web-app-service namespace: persistence spec: ports: - name: web-app-port port: 3000 protocol: TCP targetPort: 3000 selector: app: web-app team: helix type: NodePort --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: web-app-deployment team: helix name: web-app-deployment namespace: persistence spec: replicas: 3 selector: matchLabels: app: web-app team: helix template: metadata: labels: app: web-app team: helix spec: containers: - env: - name: DB_HOST value: mysql-service - name: DB_USER value: root - name: DB_PASSWORD value: password image: bmuschko/web-app:1.0.1 name: web-app ports: - containerPort: 3000
Anpassen einer Sammlung von Manifesten
Kustomize kann den Inhalt eines YAML-Manifests mit einem Codeschnipsel aus einem anderen YAML-Manifest zusammenführen. Typische Anwendungsfälle sind das Hinzufügen einer Sicherheitskontextkonfiguration zu einer Pod-Definition oder das Festlegen von Ressourcengrenzen für ein Deployment. In der kustomization-Datei können verschiedene Patch-Strategien angegeben werden wiepatchesStrategicMerge
und patchesJson6902
. Eine ausführlichere Diskussion über die Unterschiede zwischen den Patch-Strategien findest du in der Dokumentation.
Beispiel 4-8 zeigt den Inhalt einer Anpassungsdatei, die eine Einsatzdefinition in der Datei nginx-deployment.yaml
mit dem Inhalt der Datei security-context.yaml
patcht.
Beispiel 4-8. Eine Anpassungsdatei, die einen Patch definiert
resources
:
-
nginx-deployment.yaml
patchesStrategicMerge
:
-
security-context.yaml
Die in Beispiel 4-9 gezeigte Patch-Datei definiert einen Sicherheitskontext auf Containerebene für die Pod-Vorlage des Deployments. Zur Laufzeit versucht die Patch-Strategie, den Container mit dem Namen nginx
zu finden und die zusätzliche Konfiguration zu erweitern.
Beispiel 4-9. Das YAML-Manifest des Patches
apiVersion
:
apps/v1
kind
:
Deployment
metadata
:
name
:
nginx-deployment
spec
:
template
:
spec
:
containers
:
-
name
:
nginx
securityContext
:
runAsUser
:
1000
runAsGroup
:
3000
fsGroup
:
2000
Das Ergebnis ist eine gepatchte Einsatzdefinition, wie sie in der Ausgabe deskustomize
Unterbefehls (siehe unten). Der Patch-Mechanismus kann auf andere Dateien angewendet werden, die eine einheitliche Sicherheitskontextdefinition erfordern:
$ kubectl kustomize ./ apiVersion: apps/v1 kind: Deployment metadata: labels: app: nginx name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - image: nginx:1.14.2 name: nginx ports: - containerPort: 80 securityContext: fsGroup: 2000 runAsGroup: 3000 runAsUser: 1000
Gemeinsame Templating Tools
Wie im vorherigen Abschnitt gezeigt, bietet Kustomize Templating-Funktionen. Das Kubernetes-Ökosystem bietet noch weitere Lösungen für dieses Problem, die wir hier besprechen werden. Wir werden auf den YAML-Prozessor yq
und die Templating-Engine Helm eingehen.
Den YAML-Prozessor verwenden yq
Das Tool yq
wird verwendet, um den Inhalt einer YAML-Datei zu lesen, zu ändern und zu erweitern. In diesem Abschnitt werden alle drei Anwendungsfälle demonstriert. Eine detaillierte Liste der Anwendungsbeispiele findest du im GitHub-Repository. In der CKA-Prüfung wirst du möglicherweise aufgefordert, diese Techniken anzuwenden, obwohl von dir nicht erwartet wird, dass du alle Feinheiten der jeweiligen Tools kennst. Die Version von yq
, die für die Beschreibung der folgenden Funktionen verwendet wird, ist 4.2.1.
Werte lesen
Das Auslesen von Werten aus einer bestehenden YAML-Datei erfordert die Verwendung eines YAML-Pfadausdrucks. Ein Pfadausdruck ermöglicht es dir, tief in der YAML-Struktur zu navigieren und den Wert eines gesuchten Attributs zu extrahieren. Beispiel 4-10 zeigt das YAML-Manifest eines Pods, der zwei Umgebungsvariablen definiert.
Beispiel 4-10. Das YAML-Manifest eines Pods
apiVersion
:
v1
kind
:
Pod
metadata
:
name
:
spring-boot-app
spec
:
containers
:
-
image
:
bmuschko/spring-boot-app:1.5.3
name
:
spring-boot-app
env
:
-
name
:
SPRING_PROFILES_ACTIVE
value
:
prod
-
name
:
VERSION
value
:
'1.5.3'
Um einen Wert zu lesen, verwendest du den Befehl eval
oder die Kurzform e
, gibst den YAML-Pfadausdruck an und verweist auf die Quelldatei. Die folgenden beiden Befehle lesen den Namen des Pods und den Wert der zweiten Umgebungsvariablen, die von einem einzelnen Container definiert wird. Beachte, dass der Pfadausdruck mit einem obligatorischen Punkt (.
) beginnen muss, um den Wurzelknoten der YAML-Struktur zu kennzeichnen:
$ yq e .metadata.name pod.yaml spring-boot-app $ yq e .spec.containers[0].env[1].value pod.yaml 1.5.3
Werte ändern
Das Ändern eines bestehenden Wertes ist so einfach wie die Verwendung desselben Befehls und das Hinzufügen des Flags -i
. Die Zuweisung des neuen Wertes zu einem Attribut erfolgt durch die Zuweisung zum Pfadausdruck. Der folgende Befehl ändert die zweite Umgebungsvariable der YAML-Datei des Pods auf den Wert 1.6.0:
$ yq e -i .spec.containers[0].env[1].value = "1.6.0" pod.yaml $ cat pod.yaml ... env: - name: SPRING_PROFILES_ACTIVE value: prod - name: VERSION value: 1.6.0
Zusammenführen von YAML-Dateien
Ähnlich wie Kustomize kann auch yq
mehrere YAML-Dateien zusammenführen. Kustomize ist auf jeden Fall leistungsfähiger und komfortabler, aber yq
kann für kleinere Projekte sehr nützlich sein. Angenommen, du möchtest die in Beispiel 4-11 gezeigte Definition des Sidecar-Containers in die YAML-Datei des Pods einbinden.
Beispiel 4-11. Das YAML-Manifest einer Containerdefinition
spec
:
containers
:
-
image
:
envoyproxy/envoy:v1.19.1
name
:
proxy-container
ports
:
-
containerPort
:
80
Der Befehl, mit dem dies erreicht wird, lautet eval-all
. Angesichts der vielen Konfigurationsoptionen für diesen Befehl werden wir nicht ins Detail gehen. Einen tieferen Einblick in die Operation "Multiplizieren (Zusammenführen)" findest du im yq
Benutzerhandbuch. Der folgende Befehl fügt den Sidecar-Container an das bestehende Container-Array des Pod-Manifests an:
$ yq eval-all 'select(fileIndex == 0) *+ select(fileIndex == 1)' pod.yaml \ sidecar.yaml apiVersion: v1 kind: Pod metadata: name: spring-boot-app spec: containers: - image: bmuschko/spring-boot-app:1.5.3 name: spring-boot-app env: - name: SPRING_PROFILES_ACTIVE value: prod - name: VERSION value: '1.5.3' - image: envoyproxy/envoy:v1.19.1 name: proxy-container ports: - containerPort: 80
Helm benutzen
Helm ist eine Templating-Engine und ein Paketmanager für eine Reihe von Kubernetes-Manifesten. Zur Laufzeit ersetzt es Platzhalter in YAML-Vorlagendateien durch tatsächliche, vom Endnutzer definierte Werte. Das Artefakt, das von Helm erzeugt wird, ist eine sogenannte Chart-Datei, die die Manifeste bündelt, die die API-Ressourcen einer Anwendung umfassen. Diese Diagrammdatei kann in einen Paketmanager hochgeladen werden, um sie während des Bereitstellungsprozesses zu verwenden. Das Helm-Ökosystem bietet eine Vielzahl von wiederverwendbaren Charts für gängige Anwendungsfälle in einem zentralen Chart-Repository (z. B. für Grafana oder PostgreSQL).
Aufgrund der Fülle an Funktionen, die Helm bietet, werden wir nur die Grundlagen besprechen. Bei der CKA-Prüfung wird nicht erwartet, dass du ein Helm-Experte bist, sondern dass du mit den Vorteilen und Konzepten vertraut bist. Ausführlichere Informationen zu Helm findest du in der Benutzerdokumentation. Die Version von Helm, die für die Beschreibung der Funktionen hier verwendet wird, ist 3.7.0.
Standard Chart Struktur
Ein Diagramm muss einer vordefinierten Verzeichnisstruktur folgen. Du kannst einen beliebigen Namen für das Stammverzeichnis wählen. Innerhalb dieses Verzeichnisses müssen zwei Dateien existieren: Chart.yaml
und values.yaml
. Die Datei Chart.yaml
beschreibt die Metainformationen des Diagramms (z. B. Name und Version). Die Datei values.yaml
enthält die Schlüssel-Wert-Paare, die zur Laufzeit verwendet werden, um die Platzhalter in den YAML-Manifesten zu ersetzen. Alle Templating-Dateien, die in die Archivdatei der Tabelle gepackt werden sollen, müssen im Verzeichnis templates
abgelegt werden. Dateien, die sich im Verzeichnis template
befinden, müssen keinen Namenskonventionen folgen.
Die folgende Verzeichnisstruktur zeigt ein Beispieldiagramm. Das Verzeichnis templates
enthält eine Datei für einen Pod und einen Service:
$ tree . ├── Chart.yaml ├── templates │ ├── web-app-pod-template.yaml │ └── web-app-service-template.yaml └── values.yaml
Die Kartendatei
Die Datei Chart.yaml
beschreibt das Diagramm auf einer hohen Ebene. Zu den obligatorischen Attributen gehören die API-Version des Diagramms, der Name und die Version. Zusätzlich können optionale Attribute angegeben werden. Eine vollständige Liste der Attribute findest du in der entsprechenden Dokumentation. Beispiel 4-12 zeigt das absolute Minimum einer Diagrammdatei.
Beispiel 4-12. Eine grundlegende Helm-Diagrammdatei
apiVersion
:
1.0.0
name
:
web-app
version
:
2.5.4
Die Wertedatei
In der Datei values.yaml
werden Schlüssel-Wert-Paare definiert, die die Platzhalter in den YAML-Vorlagendateien ersetzen sollen. Beispiel 4-13 gibt vier Schlüssel-Wert-Paare an. Beachte, dass die Datei leer sein kann, wenn du keine Werte zur Laufzeit ersetzen willst.
Beispiel 4-13. Eine Helm-Werte-Datei
db_host
:
mysql-service
db_user
:
root
db_password
:
password
service_port
:
3000
Die Templating-Dateien
Die Templating-Dateien müssen sich im Verzeichnis templates
befinden. Eine Vorlagendatei ist ein reguläres YAML-Manifest, das (optional) Platzhalter mit Hilfe von doppelten geschweiften Klammern ({{ }}
) definieren kann. Um einen Wert aus der Datei values.yaml
zu referenzieren, verwendest du den Ausdruck {{ .Values.<key> }}
. Um zum Beispiel den Wert des Schlüssels db_host
zur Laufzeit zu ersetzen, verwendest du den Ausdruck {{ .Values.db_host }}
. Beispiel 4-14 definiert einen Pod als Template und legt gleichzeitig drei Platzhalter fest, die auf Werte aus values.yaml
verweisen.
Beispiel 4-14. Das YAML-Templating-Manifest eines Pods
apiVersion
:
v1
kind
:
Pod
metadata
:
labels
:
app
:
web-app
name
:
web-app
spec
:
containers
:
-
image
:
bmuschko/web-app:1.0.1
name
:
web-app
env
:
-
name
:
DB_HOST
value
:
{{
.Values.db_host
}}
-
name
:
DB_USER
value
:
{{
.Values.db_user
}}
-
name
:
DB_PASSWORD
value
:
{{
.Values.db_password
}}
ports
:
-
containerPort
:
3000
protocol
:
TCP
restartPolicy
:
Always
Ausführen von Helm-Befehlen
Das ausführbare Programm Helm verfügt über eine Vielzahl von Befehlen. Wir wollen einige davon demonstrieren. Der Befehl template
rendert die Diagrammvorlagen lokal und zeigt die Ergebnisse auf der Konsole an. In der folgenden Ausgabe kannst du den Vorgang in Aktion sehen. Alle Platzhalter wurden durch ihre tatsächlichen Werte ersetzt, die aus der Datei values.yaml
stammen:
$ helm template . --- # Source: Web Application/templates/web-app-service-template.yaml ... --- # Source: Web Application/templates/web-app-pod-template.yaml apiVersion: v1 kind: Pod metadata: labels: app: web-app name: web-app spec: containers: - image: bmuschko/web-app:1.0.1 name: web-app env: - name: DB_HOST value: mysql-service - name: DB_USER value: root - name: DB_PASSWORD value: password ports: - containerPort: 3000 protocol: TCP restartPolicy: Always
Wenn du mit dem Ergebnis zufrieden bist, solltest du die Vorlagendateien in einer Chart-Archivdatei bündeln. Die Diagrammarchivdatei ist eine komprimierte TAR-Datei mit der Dateiendung .tgz
. Der Befehl package
wertet die Metadaten von Chart.yaml
aus, um den Dateinamen des Diagrammarchivs zu ermitteln:
$ helm package . Successfully packaged chart and saved it to: /Users/bmuschko/dev/projects/ \ cka-study-guide/ch04/templating-tools/helm/web-app-2.5.4.tgz
Eine vollständige Liste der Befehle und typischen Arbeitsabläufe findest du auf der Dokumentationsseite von Helm.
Zusammenfassung
Ressourcengrenzen sind einer der vielen Faktoren, die der Algorithmus des Zeitplannungsprogramms kube berücksichtigt, wenn er entscheidet, auf welchem Knoten ein Pod eingeplant werden kann. Ein Container kann Ressourcenanforderungen und Limits angeben. Das Zeitplannungsprogramm wählt einen Knotenpunkt auf der Grundlage seiner verfügbaren Hardwarekapazität aus.
Die deklarative Verwaltung von Manifesten ist die bevorzugte Methode zum Erstellen, Ändern und Löschen von Objekten in realen Cloud-nativen Projekten. Das zugrundeliegende YAML-Manifest soll in die Versionskontrolle eingecheckt werden und verfolgt automatisch die Änderungen an einem Objekt einschließlich seines Zeitstempels für einen entsprechenden Commit-Hash. Mit den Befehlen kubectl apply
und delete
können diese Vorgänge für ein oder mehrere YAML-Manifeste durchgeführt werden.
Es entstanden zusätzliche Werkzeuge für eine bequemere Manifestverwaltung. Kustomize ist vollständig in die Werkzeugkette von kubectl
integriert. Es hilft bei der Erstellung, Zusammenstellung und Anpassung von Manifesten. Tools mit Templating-Funktionen wie yq
und Helm können die verschiedenen Arbeitsabläufe zur Verwaltung von Anwendungsstapeln, die durch eine Reihe von Manifesten repräsentiert werden, weiter vereinfachen.
Prüfungsgrundlagen
- Verstehe die Auswirkungen von Ressourcengrenzen auf die Planung
-
Ein Container, der durch einen Pod definiert ist, kann Requests und Limits für Ressourcen festlegen. Arbeite Szenarien durch, in denen du diese Grenzen einzeln und gemeinsam für Single- und Multi-Container-Pods definierst. Nach der Erstellung des Pods solltest du die Auswirkungen auf die Planung des Objekts auf einem Node sehen können. Übe außerdem, wie du die verfügbare Ressourcenkapazität eines Knotens ermitteln kannst.
- Verwalte Objekte mit dem imperativen und deklarativen Ansatz
-
YAML-Manifeste sind wichtig, um den gewünschten Zustand eines Objekts auszudrücken. Du musst wissen, wie du mit dem Befehl
kubectl apply
Objekte erstellen, aktualisieren und löschen kannst. Der Befehl kann auf eine einzelne Manifestdatei oder auf ein Verzeichnis verweisen, das mehrere Manifestdateien enthält. - Ein umfassendes Verständnis der gängigen Templating-Tools
-
Kustomize,
yg
und Helm sind etablierte Tools für die Verwaltung von YAML-Manifesten. Ihre Templating-Funktionen unterstützen komplexe Szenarien wie das Zusammenstellen und Zusammenführen mehrerer Manifeste. Für die Prüfung solltest du einen praktischen Blick auf die Tools, ihre Funktionen und die Probleme werfen, die sie lösen.
Beispiel-Übungen
Die Lösungen zu diesen Übungen findest du im Anhang.
-
Schreibe ein Manifest für einen neuen Pod namens
ingress-controller
mit einem einzelnen Container, der das Bildbitnami/nginx-ingress-controller:1.0.0
verwendet. Setze für den Container die Ressourcenanforderung auf 256Mi für den Speicher und 1 CPU. Setze die Ressourcengrenzen auf 1024Mi Arbeitsspeicher und 2,5 CPUs. -
Verwende das Manifest, um den Pod in einem Cluster mit drei Knoten einzuplanen. Sobald er erstellt ist, identifiziere den Knoten, auf dem der Pod läuft. Schreibe den Namen des Knotens in die Datei
node.txt
. -
Erstelle das Verzeichnis mit dem Namen
manifests
. Erstelle in diesem Verzeichnis zwei Dateien:pod.yaml
undconfigmap.yaml
. Die Dateipod.yaml
sollte einen Pod namensnginx
mit dem Bildnginx:1.21.1
definieren. Die Dateiconfigmap.yaml
definiert eine ConfigMap namenslogs-config
mit dem Schlüssel-Wert-Paardir=/etc/logs/traffic.log
. Erstelle beide Objekte mit einem einzigen, deklarativen Befehl. -
Ändere das ConfigMap-Manifest, indem du den Wert des Schlüssels
dir
in/etc/logs/traffic-log.txt
änderst. Wende die Änderungen an. Lösche beide Objekte mit einem einzigen deklarativen Befehl. -
Verwende Kustomize, um einen gemeinsamen Namensraum
t012
für die Ressourcendateipod.yaml
festzulegen. Die Dateipod.yaml
definiert den Pod mit dem Namennginx
mit dem Bildnginx:1.21.1
ohne einen Namespace. Führe den Kustomize-Befehl aus, der das umgewandelte Manifest auf der Konsole ausgibt.
Get Zertifizierter Kubernetes Administrator (CKA) Studienführer 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.