Modul 8 — Orchestrace: Kubernetes

38. Persistent Volumes a stavové aplikace

Jak Kubernetes řeší perzistentní úložiště pro Pody pomocí PersistentVolume a PersistentVolumeClaim a jak fungují stavové aplikace pomocí StatefulSet.

Odhadovaná délka studia: 55 minut · Stav: nehotovo

Technologie: Kubernetes

Úvod a kontext

Pody jsou efemérní stejně jako kontejnery — když Kubernetes Pod smaže
a vytvoří nový (například při rolling update nebo po pádu uzlu), local
disková data uvnitř Podu zmizí. Pro bezstavové webové aplikace to
nevadí, ale databáze, message queue nebo cokoliv, co si musí pamatovat
data mezi restarty, potřebuje úložiště nezávislé na životním cyklu
konkrétního Podu — obdobu Docker named volumes, jen v měřítku celého
clusteru, kde Pod může být naplánován na kterýkoliv z mnoha uzlů.

Teorie

PersistentVolume a PersistentVolumeClaim

Kubernetes odděluje jak je úložiště fyzicky poskytnuto od jak ho
aplikace požaduje, pomocí dvou objektů:

  • PersistentVolume (PV) — reprezentuje skutečný kus úložiště v
    clusteru (disk v cloudu, NFS share, lokální disk). Vytváří ho
    obvykle administrátor clusteru nebo automaticky provisioner.
  • PersistentVolumeClaim (PVC) — "požadavek" na úložiště, který
    vytváří vývojář/aplikace ("potřebuji 10 GiB úložiště, které přežije
    restart Podu"). Kubernetes najde (nebo dynamicky vytvoří) odpovídající
    PV a naváže ho na PVC.

Tento model odpovídá tomu, jak Pod žádá CPU/paměť přes resources
aplikace deklaruje potřebu, cluster ji uspokojí z dostupných zdrojů.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: db-data
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: standard

StorageClass a dynamické provisioning

V praxi se PV zřídka vytváří ručně — clustery mají definované
StorageClass objekty, které popisují, jak automaticky (dynamicky)
vytvořit nový PV při vzniku PVC (např. v cloudu automaticky zřídit
nový EBS/Persistent Disk odpovídající velikosti). storageClassName v
PVC odkazuje na tuto třídu; PV pak vznikne samo, bez ručního zásahu
administrátora.

Access Modes

  • ReadWriteOnce (RWO) — svazek může mountnout k zápisu jen jeden
    uzel najednou (typické pro blokové cloudové disky, např. databáze).
  • ReadOnlyMany (ROX) — víc uzlů může mountnout jen ke čtení.
  • ReadWriteMany (RWX) — víc uzlů může mountnout k zápisu současně
    (vyžaduje síťový souborový systém typu NFS, ne obyčejný blokový disk).

Reclaim Policy

Když je PVC smazán, co se stane s daty na PV, závisí na reclaim
policy
: Delete (výchozí u dynamicky provisionovaných svazků — disk
se fyzicky smaže), nebo Retain (disk zůstane zachován, ale
odpojen — musí ho ručně znovu přiřadit administrátor). Pro produkční
databáze se často vědomě nastavuje Retain, aby náhodné smazání PVC
nesmazalo i data.

Použití PVC v Podu

spec:
  containers:
    - name: db
      image: postgres:16
      volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql/data
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: db-data

StatefulSet: Deployment pro stavové aplikace

Obyčejný Deployment se pro stavové aplikace (databáze, distribuované
fronty) často nehodí, protože jeho Pody jsou vzájemně zaměnitelné a
mají náhodné jméno. StatefulSet poskytuje navíc:

  • stabilní, předvídatelné jméno Podu (db-0, db-1, db-2
    místo náhodného sufixu),
  • stabilní síťovou identitu přes tzv. headless Service,
  • vlastní PVC pro každou repliku, které přežije restart a zůstane
    spárováno se "svým" Podem (db-0 má vždy stejná data, i po
    restartu),
  • řízené, sekvenční spouštění a mazání replik (užitečné např. pro
    primární/replikovanou topologii, kde pořadí startu záleží).
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: db
spec:
  serviceName: db
  replicas: 3
  selector:
    matchLabels:
      app: db
  template:
    metadata:
      labels:
        app: db
    spec:
      containers:
        - name: db
          image: postgres:16
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 10Gi

volumeClaimTemplates zajistí, že se pro každou repliku (db-0,
db-1, db-2) automaticky vytvoří samostatný PVC a ten zůstává s ní
svázaný napříč restarty.

Praktický příklad

kubectl apply -f statefulset.yaml
kubectl get pods
# db-0   Running
# db-1   Running
# db-2   Running

kubectl get pvc
# data-db-0   Bound   10Gi
# data-db-1   Bound   10Gi
# data-db-2   Bound   10Gi

# smazání Podu db-0 — StatefulSet ho nahradí novým se stejným jménem
# a stejným PVC, tedy se stejnými daty
kubectl delete pod db-0
kubectl get pods
# db-0 se znovu objeví s totožnou identitou i daty

Shrnutí

PersistentVolume a PersistentVolumeClaim oddělují fyzické úložiště od
požadavku aplikace na úložiště, obvykle s dynamickým provisioningem
přes StorageClass. Access mode určuje, kolik uzlů smí svazek
současně mountovat a jak; reclaim policy určuje osud dat po smazání
PVC. Pro databáze a jiné stavové služby se místo Deploymentu používá
StatefulSet, který dává každé replice stabilní jméno a vlastní
perzistentní PVC, jenž s ní zůstává spárovaný napříč restarty.

Kontrolní otázky

  1. Jaký je rozdíl v roli mezi PersistentVolume a
    PersistentVolumeClaim?
  2. Kdy použijete access mode ReadWriteOnce a kdy ReadWriteMany?
  3. Proč se pro databázi v Kubernetes obvykle používá StatefulSet
    místo Deploymentu?
  4. Mini cvičení: napište StatefulSet se 2 replikami jednoduchého
    image (např. busybox se smyčkou zapisující čas do souboru) s
    volumeClaimTemplates o velikosti 1Gi, nasaďte ho a příkazem
    kubectl get pvc ověřte, že vznikly dva samostatné PVC, jeden pro
    každou repliku.

Lekce na sebe nejsou zamčené — libovolnou lekci můžete otevřít i označit jako hotovou v jakémkoliv pořadí.