Zum Inhalt

Volumes

Ziel

In diesem Projekt geht es um Volumes und Mount-Punkten. Sie werden:

  • Volumes erstellen und inspizieren
  • Dateien aus ConfigMaps und Secrets mounten

Hilfsmittel

  • Versuchen Sie, die unten stehenden Aufgaben mit Hilfe der Folien und des Cheatsheets eigenständig zu lösen.
  • Sollten Sie dabei Probleme haben, finden Sie bei jeder Aufgabe einen ausklappbaren Block, in dem der Lösungsweg beschrieben wird.

Aufgabe 1: emptyDir Volumes

  • Hier finden Sie die offizielle Dokumentation für emptyDir-Volumes
  • Erstellen Sie eine Datei emptydir.yaml, die einen Pod mit dem Namen volume-emptydir beschreibt.
  • Die Pod-Spezifikation sollte ein Volume des Typs emptyDir mit dem Namen cache enthalten.
  • Mounten Sie das Volume in der Container-Spezifikation unter dem Pfad /cache.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: v1
kind: Pod
metadata:
  name: volume-emptydir
spec:
  volumes:
  - name: cache
    emptyDir: {}
  containers:
  - name: web
    image: nginx
    volumeMounts:
    - mountPath: /cache
      name: cache
  • Wenden Sie den Pod mit kubectl apply an.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl apply -f emptydir.yaml
  • Schauen Sie sich den Pod mit kubectl describe an.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl describe pod volume-emptydir
Name:             volume-emptydir
(...)
Containers:
  web:
    (...)
    Mounts:
      /cache from cache (rw)
(...)
Volumes:
  cache:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:
    SizeLimit:  <unset>
  • Überprüfen Sie die Mounts innerhalb des Pod-Containers indem Sie df im Pod ausführen.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl exec -it volume-emptydir -- df --exclude-type=tmpfs
Filesystem     1K-blocks    Used Available Use% Mounted on
overlay        619139212 8908236 584839800   2% /
/dev/sda1      619139212 8908236 584839800   2% /cache

Aufgabe 2: hostpath Volumes

  • Hier finden Sie die offizielle Dokumentation für hostPath-Volumes
  • Erstellen Sie eine Datei hostpath.yaml, die einen Pod mit dem Namen volume-hostpath beschreibt.

  • Die Pod-Spezifikation sollte ein Volume des Typs hostPath mit dem Namen data enthalten.

  • Mounten Sie das Volume in der Container-Spezifikation unter dem Pfad /data.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: v1
kind: Pod
metadata:
  name: volume-hostpath
  labels:
    exercise: volumes
spec:
  volumes:
  - name: data
    hostPath:
      path: /data
      type: DirectoryOrCreate
  containers:
  - name: web
    image: nginx
    volumeMounts:
    - mountPath: /data
      name: data
  • Wenden Sie den Pod mit kubectl apply an.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl apply -f hostpath.yaml
  • Schauen Sie sich den Pod mit kubectl describe an.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl describe pod volume-hostpath
Name:             volume-hostpath
(...)
Containers:
  web:
    (...)
    Mounts:
      /data from data (rw)
(...)
Volumes:
  data:
    Type:          HostPath (bare host directory volume)
    Path:          /data
    HostPathType:  DirectoryOrCreate
  • Überprüfen Sie die Mounts innerhalb des Pod-Containers.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl exec -it volume-hostpath -- df --exclude-type=tmpfs
Filesystem     1K-blocks    Used Available Use% Mounted on
overlay        619139212 9021932 584726104   2% /
/dev/sda1      619139212 9021928 584726108   2% /data
  • Verwenden Sie kubectl exec, um eine Datei innerhalb von /data des volume-hostpath Pods zu erstellen
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl exec -it volume-hostpath -- sh -c "echo '# here we go' > /data/foo"
  • Sehen Sie sich den volume-hostpath-Pod an, um herauszufinden, auf welchem Node er ausgerollt wurde
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl get pod volume-hostpath -o wide
  • Löschen Sie den Pod volume-hostpath mit kubectl delete.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl delete -f hostpath.yaml
  • Starten Sie den Pod erneut an mit kubectl apply.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl apply -f hostpath.yaml
  • Prüfen Sie, ob der Pod auf demselben Node erstellt wurde.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl get pod volume-hostpath -o wide
  • Prüfen Sie, ob die Datei im Pod noch existiert.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
  • Wenn der Node derselbe ist wie zuvor, sollte die Testdatei in Ihrem neuen Pod zu finden sein
kubectl exec -it volume-hostpath -- cat /data/foo
# here we go
  • Wenn der Node NICHT derselbe ist wie zuvor, sollte die Testdatei in Ihrem neuen Pod nicht zu finden sein
kubectl exec -it volume-hostpath -- cat /data/foo

cat: /data/foo: No such file or directory
command terminated with exit code 1

Hinweis

  • Daten in hostPath-Mounts werden auf einem bestimmten Knoten gespeichert
  • Pods können auf jedem verfügbaren Clusterknoten erstellt werden.
  • Wenn Sie mehr als einen Knoten haben, wird die Verwendung von hostPath für die Persistenz von Dateien während des Pod-Lebenszyklus unzuverlässig.
Hinweis: Pod auf einem Node erzwingen
---
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  nodeName: <node-name>
  # Beispiel:
  # nodeName: code-0-worker-cf4jc-6txcb
  # Verfügbare Node-Namen erhalten Sie mit "kubectl get nodes"
  volumes:
    [...]
  containers:
    [...]

Pods lassen sich explizit auf Nodes verteilen. Dies kann zu Testzwecken nützlich sein, sorgt aber auch dafür, dass der Node nicht mehr so einfach austauschbar ist und Pods nicht gestartet werden können, wenn dieser Node nicht verfügbar ist.

Aufgabe 3: ConfigMap-Mount

  • Erstellen Sie eine Datei configmap-mount.yaml, die eine ConfigMap namens my-mount enthält.
  • Die ConfigMap-Daten sollten einen Schlüssel index.html mit dem folgenden Wert enthalten:
    <html><body>Hooray!</body></html>
    
  • Alternativ können Sie mit einer KI Ihrer Wahl einen anderen HTML-Inhalt erstellen
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-mount
  labels:
    exercise: mount
data:
  index.html: |
    <html><body>Hooray!</body></html>
  • Wenden Sie die ConfigMap mit kubectl apply an
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl apply -f configmap-mount.yaml
  • Lassen Sie sich die Configmap mit kubectl get als YAML ausgeben
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl get configmap my-mount -o yaml

apiVersion: v1
data:
  index.html: |
    <html><body>Hooray!</body></html>
kind: ConfigMap
(...)
  • Schauen Sie sich das Kapitel "Using ConfigMaps as files from a pod" an
  • Erstellen Sie einen Pod namens web in pod-web-mount.yaml unter Verwendung des Images nginx (ähnlich wie in den Übungen zuvor)
  • Fügen Sie dem Pod ein Volume hinzu, das auf die ConfigMap my-mount zeigt.
  • Mappen Sie das Volume auf den Mountpfad /usr/share/nginx/html.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: v1
kind: Pod
metadata:
  name: web-mount
  labels:
    exercise: mount
spec:
  volumes:
  - name: html-files
    configMap:
      name: my-mount
  containers:
  - name: web
    image: nginx:stable-alpine
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: html-files
  • Wenden Sie es mit kubectl apply an.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl apply -f pod-web-mount.yaml
  • Schauen Sie sich den Pod mit kubectl describe an.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl describe pod web-mount
(...)
Containers:
  env:
    (...)
    Mounts:
      /usr/share/nginx/html from html-files (rw)
(...)
Volumes:
  html-files:
    Type:      ConfigMap (a volume populated by a ConfigMap)
    Name:      my-mount
    Optional:  false
(...)
  • Verwenden Sie kubectl exec, um sich mit dem Pod web-mount zu verbinden
  • Führen Sie df -a im Pod aus, um den Einhängepunkt /usr/share/nginx/html zu sehen
  • Führen Sie ls -l /usr/share/nginx/html in der Gruppe aus, um die gemounteten Dateien zu sehen
  • Führen Sie curl -s localhost in der Gruppe aus, um die Antwort des Webservers zu prüfen
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl exec -it web-mount -- df -a
/dev/sda1            619139212   8467828 585280208   1% /usr/share/nginx/html
  • Dateien auflisten:
kubectl exec -it web-mount -- ls -l /usr/share/nginx/html
total 0
lrwxrwxrwx    1 root     root            17 Feb 28 10:14 index.html -> ..data/index.html
  • Mit curl die Antwort des Webservers prüfen:
kubectl exec -it web-mount -- curl -s localhost
<html><body>Hooray!</body></html>

Aufgabe 4: Mount Secret (Optional)

Wenn Sie Dateien mounten wollen, die sicherheitskritische Daten enthalten, verwenden Sie besser ein Secret statt einer ConfigMap. Das Einbinden eines Secrets in einen Pod funktioniert im Grunde genauso wie das Einbinden einer ConfigMap.

  • Erstellen Sie eine Datei secret-mount.yaml, die die Beschreibung für ein Secret mit dem Namen my-secret-mount enthält.
  • Die Secret-Daten sollten zwei Schlüssel enthalten:
    • secret.txt mit dem Wert "abracadabra"
    • passphrase.txt mit dem Wert "open sesame".
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: v1
kind: Secret
metadata:
  name: my-secret-mount
  labels:
    exercise: mount
type: Opaque
stringData:
  secrets.txt: "abracadabra"
  passphrase.txt: "open sesame"
  • Deployen Sie das Secret mit kubectl apply.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl apply -f secret-mount.yaml
  • Lassen Sie sich das Secret als YAML ausgeben
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl get secret my-secret-mount -o yaml
apiVersion: v1
data:
  passphrase.txt: b3BlbiBzZXNhbWU=
  secrets.txt: YWJyYWNhZGFicmE=
kind: Secret
(...)
  • Erstellen Sie einen neuen Pod mit einem Volume, das auf das Secret my-secret-mount zeigt und alle Daten aus dem Secret in den Mountpfad /secrets mounted.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: v1
kind: Pod
metadata:
  name: secret-mount
  labels:
    exercise: mount
spec:
  volumes:
  - name: secret-files
    secret:
      secretName: my-secret-mount
  containers:
  - name: web
    image: nginx:stable-alpine
    volumeMounts:
    - mountPath: /secrets
      name: secret-files
  • Deployen:
kubectl apply -f pod-secret-mount.yaml
  • Schauen Sie sich den Pod mit kubectl describe an.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl describe pod secret-mount
(...)
Containers:
  web:
(...)
    Mounts:
      /secrets from secret-files (rw)
Volumes:
  secret-files:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  my-secret-mount
    Optional:    false
(...)
  • Verbinden Sie sich mit dem Pod secret-mount und überprüfen Sie die gemounteten Dateien.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
  • Mount-Files anzeigen:
kubectl exec -it secret-mount -- ls -l /secrets
total 0
lrwxrwxrwx    1 root     root            21 Feb 28 11:18 passphrase.txt -> ..data/passphrase.txt
lrwxrwxrwx    1 root     root            18 Feb 28 11:18 secrets.txt -> ..data/secrets.txt
  • Inhalt der Dateien anzeigen:
kubectl exec -it secret-mount -- cat /secrets/passphrase.txt
open sesame
kubectl exec -it secret-mount -- cat /secrets/secrets.txt
abracadabra

Aufgabe 5: PersistentVolume und PersistentVolumeClaim

Aufgabe 5.1: PersistentVolume erstellen

  • Erstellen Sie eine PersistentVolume YAML-Datei pv.yaml und definieren Sie ein PersistentVolume, das einen hostPath-Volumen-Typ verwendet und eine Kapazität von 5Gi angibt.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
apiVersion: v1
kind: PersistentVolume
metadata:
  name: dummy-pv
  labels:
    storage: dummy
spec:
  capacity:
    storage: 5Gi
  storageClassName: ""
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  hostPath:
    path: /data/pv/dummy
    type: DirectoryOrCreate
  • Deployen Sie das PersistentVolume mit kubectl apply.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl apply -f pv.yaml
  • Überprüfen Sie, ob das PersistentVolume erfolgreich erstellt wurde
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)

kubectl get pv
kubectl describe pv dummy-pv

Aufgabe 5.2: PersistentVolumeClaim definieren

  • Erstellen Sie eine PersistentVolumeClaim YAML-Datei pvc.yaml und definieren Sie einen PersistentVolumeClaim mit einer Kapazität von 5Gi.
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: dummy-pvc
spec:
  volumeName: dummy-pv
  storageClassName: ""
  accessModes:
  - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 5Gi
  • Deployen Sie den PersistentVolumeClaim
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl apply -f pvc.yaml
  • Überprüfen Sie, ob der PersistentVolumeClaim erfolgreich erstellt wurde
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)

kubectl get pvc
kubectl describe pvc dummy-pvc

Aufgabe 5.3: Pod mit PersistentVolumeClaim erstellen

  • Erstellen Sie eine Pod YAML-Datei storage-demo.yaml und definieren Sie einen Pod:

    • der den dummy-pvc PersistentVolumeClaim verwendet und
    • ein Volume "data" an das Verzeichnis /usr/share/nginx/html mountet.
    • Verwenden Sie dieses Beispiel als Ausgangspunkt:
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: storage-demo
      labels:
        exercise: storage
    spec:
      containers:
      - name: nginx
        image: nginx
    
  • Wenden Sie das Pod-Manifest an

Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
apiVersion: v1
kind: Pod
metadata:
  name: storage-demo
  labels:
    exercise: storage
spec:
  volumes:
    - name: pvc-volume
      persistentVolumeClaim:
        claimName: dummy-pvc
  containers:
    - name: nginx
      image: nginx
      volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: pvc-volume
kubectl apply -f storage-demo.yaml
  • Führen Sie kubectl describe aus, um die Ereignisse des Pods zu überprüfen und zu sehen, ob das Volume erfolgreich gemountet wurde
  • Führen Sie kubectl exec aus, um eine Datei in das gemountete Volume zu schreiben
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl exec -it storage-demo -- /bin/sh -c 'echo "Hello from Kubernetes" > /usr/share/nginx/html/index.html'
  • Löschen Sie den Pod und erstellen Sie einen neuen Pod mit demselben PersistentVolumeClaim
  • Führen Sie kubectl exec aus, um den Inhalt des gemounteten Volumes zu überprüfen
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl exec -it storage-demo -- /bin/sh -c 'cat /usr/share/nginx/html/index.html'
  • Löschen Sie den Pod und den PersistentVolumeClaim

Aufgabe 6: PersistentVolumeClaim mit CSI Plugin

In der vorherigen Aufgabe haben wir ein PV und PVC händisch angelegt. Über CSI-Plugins und StorageClasses können PVs auch automatisch erzeugt werden.

Aufgabe 6.1: PersistentVolumeClaim erstellen

  • Lassen Sie sich alle PersistentVolumes und PersistentVolumeClaims anzeigen
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl get pv,pvc
  • Lassen Sie sich alle verfügbaren StorageClasses anzeigen
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl get stor<TAB>
kubectl get storageclasses.storage.k8s.io
  • Erstellen Sie ein PVC, das die StorageClass hcloud-volumes verwendet. Die StorageClass sollte der Default sein und muss deshalb nicht explizit angegeben werden
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: hetzner-pvc
spec:
  accessModes:
  - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 5Gi
kubectl apply -f <pvc.yml>
  • Lassen Sie sich Details zum erstellten PVC anzeigen
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl describe persistentvolumeclaims hetzner-pvc
Name:          hetzner-pvc
Namespace:     default
StorageClass:  hcloud-volumes
Status:        Pending
Volume:
Labels:        <none>
Annotations:   <none>
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:
Access Modes:
VolumeMode:    Filesystem
Used By:       <none>
Events:
  Type    Reason                Age               From                         Message
  ----    ------                ----              ----                         -------
  Normal  WaitForFirstConsumer  3s (x4 over 46s)  persistentvolume-controller  waiting for first consumer to be created before binding

Der PVC ist erstellt. Das dazugehörige PersistentVolume wird aber erst erstellt, sobald das PVC verwendet wird.

Aufgabe 6.2: PersistentVolumeClaim verwenden

  • Starten Sie einen Pod, der das PVC verwendet
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
apiVersion: v1
kind: Pod
metadata:
  name: hetzner-storage-demo
  labels:
    exercise: storage
spec:
  volumes:
    - name: pvc-volume
      persistentVolumeClaim:
        claimName: hetzner-pvc
  containers:
    - name: nginx
      image: nginx
      volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: pvc-volume
  • Lassen Sie sich alle PersistentVolumes und PersistentVolumeClaims anzeigen
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
kubectl get pv,pvc
  • Sie sollten das erstellte PersistentVolume sehen.

Cleanup

  • Löschen Sie alle Objekte, die Sie erstellt haben.
  • Beobachten Sie, wie sie mit watch kubectl get all verschwinden.