Menu
Szybki wybór
Hosting Domeny VPS SSL Kalkulator Porównania FAQ
Aktywne kody
Wszystkie kody rabatowe

Kubernetes StatefulSet — wdrażanie baz danych w klastrze

Opublikowano: 10 kwietnia 2026 · Kategoria: VPS / Kubernetes

Deployment to domyślny wybór dla bezstanowych serwisów w Kubernetes, ale bazy danych mają wymagania, których Deployment nie spełnia: stała tożsamość poda, dedykowany wolumen per replika i uporządkowane uruchamianie. StatefulSet powstał właśnie dla takich scenariuszy. Ten artykuł pokazuje jak wdrożyć MySQL i PostgreSQL w K8s, zarządzać PersistentVolumes i tworzyć backupy przez CronJob oraz VolumeSnapshot.

StatefulSet vs Deployment — kluczowe różnice

Cecha Deployment StatefulSet
Tożsamość poda Losowy hash (app-7d4f9c) Stabilna ordynalna (db-0, db-1)
PersistentVolume Współdzielony lub brak Dedykowany PVC per pod
Kolejność startu Równoległa (dowolna) Sekwencyjna (db-0 → db-1 → db-2)
Headless Service Opcjonalny Wymagany (stable DNS per pod)
Rolling update Równoległy Odwrócona kolejność (N → 0)
Zastosowanie Bezstanowe API, web serwerzy Bazy danych, kolejki, ZooKeeper

Headless Service i DNS per pod

StatefulSet wymaga headless Service (clusterIP: None) — bez wirtualnego IP. Kubernetes tworzy wtedy rekordy DNS dla każdego poda osobno: pod-0.service.namespace.svc.cluster.local. Replika może więc zawsze połączyć się z masterem po stałym adresie, niezależnie od restartów.

# headless-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  clusterIP: None        # headless — brak wirtualnego IP
  selector:
    app: mysql
  ports:
    - port: 3306
      name: mysql
---
# Zwykly Service do dostepu aplikacji (tylko odczyt przez repliki)
apiVersion: v1
kind: Service
metadata:
  name: mysql-read
spec:
  selector:
    app: mysql
  ports:
    - port: 3306

StatefulSet dla MySQL — manifest krok po kroku

# mysql-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  serviceName: "mysql"       # headless Service name
  replicas: 1
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:8.0
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: root-password
            - name: MYSQL_DATABASE
              value: "appdb"
          ports:
            - containerPort: 3306
              name: mysql
          volumeMounts:
            - name: data
              mountPath: /var/lib/mysql
          resources:
            requests:
              memory: "512Mi"
              cpu: "250m"
            limits:
              memory: "1Gi"
              cpu: "1"
          readinessProbe:
            exec:
              command: ["mysqladmin", "ping", "-h", "127.0.0.1"]
            initialDelaySeconds: 30
            periodSeconds: 10
  volumeClaimTemplates:       # PVC tworzony per pod automatycznie
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: "fast-ssd"
        resources:
          requests:
            storage: 20Gi
# Secret z haslem (base64)
kubectl create secret generic mysql-secret \
  --from-literal=root-password=SuperSecretPass123 \
  -n default

# Aplikacja manifestow
kubectl apply -f headless-service.yaml
kubectl apply -f mysql-statefulset.yaml

# Sprawdzenie
kubectl get statefulset mysql
kubectl get pvc          # data-mysql-0 = 20Gi Bound
kubectl get pods         # mysql-0 Running

# DNS dla poda (z dowolnego poda w klastrze)
nslookup mysql-0.mysql.default.svc.cluster.local

PostgreSQL w StatefulSet

Schemat dla PostgreSQL jest analogiczny — zmienia się obraz, zmienne środowiskowe i ścieżka montowania. Warto dodać fsGroup w securityContext, bo PostgreSQL wymaga konkretnych uprawnień na katalogu danych.

spec:
  securityContext:
    fsGroup: 999       # GID grupy postgres w kontenerze
  containers:
    - name: postgres
      image: postgres:16
      env:
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: pg-secret
              key: password
        - name: POSTGRES_DB
          value: "appdb"
        - name: PGDATA
          value: /var/lib/postgresql/data/pgdata
      volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql/data
      readinessProbe:
        exec:
          command: ["pg_isready", "-U", "postgres"]
        initialDelaySeconds: 15
        periodSeconds: 5

Backup — CronJob z mysqldump / pg_dump

# mysql-backup-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: mysql-backup
spec:
  schedule: "0 3 * * *"       # co noc o 3:00 UTC
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: backup
              image: mysql:8.0
              env:
                - name: MYSQL_ROOT_PASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: mysql-secret
                      key: root-password
              command:
                - /bin/sh
                - -c
                - |
                  DATE=$(date +%Y%m%d-%H%M)
                  mysqldump -h mysql-0.mysql.default.svc.cluster.local \
                    -u root -p$MYSQL_ROOT_PASSWORD --all-databases \
                    | gzip > /backup/full-$DATE.sql.gz
                  echo "Backup done: /backup/full-$DATE.sql.gz"
                  ls -lh /backup/ | tail -10
              volumeMounts:
                - name: backup-storage
                  mountPath: /backup
          volumes:
            - name: backup-storage
              persistentVolumeClaim:
                claimName: backup-pvc

VolumeSnapshot — błyskawiczne migawki

VolumeSnapshot to migawka na poziomie storage drivera (CSI) — działa w sekundach niezależnie od rozmiaru bazy. Wymaga VolumeSnapshotClass od twojego CSI providera (np. Longhorn, AWS EBS CSI, GCE PD CSI).

# VolumeSnapshotClass (np. dla Longhorn)
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: longhorn-snap
driver: driver.longhorn.io
deletionPolicy: Delete
---
# Snapshot konkretnego PVC
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: mysql-snap-20260410
spec:
  volumeSnapshotClassName: longhorn-snap
  source:
    persistentVolumeClaimName: data-mysql-0
---
# Odtworzenie PVC ze snapshotu
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-restore-pvc
spec:
  dataSource:
    name: mysql-snap-20260410
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: 20Gi

StorageClass — dobór dla baz danych

  • Local storage — najszybsze (brak sieciowego I/O), ale pod jest przywiązany do węzła. Brak automatycznej replikacji — jeśli węzeł padnie, baza jest niedostępna do czasu jego powrotu. Dobry dla dev lub gdy sam zarządzasz replikacją bazy.
  • NFS PV — działa z RWX, ale latency I/O może być problemem dla baz danych o wysokim obciążeniu zapisu. Przydatny jako backend backupów.
  • Ceph / Rook — distributed storage z replikacją, snapshots i resizem PVC. Wymaga minimum 3 węzłów OSD. Najlepszy wybór dla on-prem produkcji.
  • Longhorn — lżejszy od Ceph, łatwiejszy w instalacji, dobry dla małych klastrów (3-10 węzłów). Wbudowane snapshots i offsite backup do S3.
  • Chmurowe CSI — AWS EBS gp3, GCE pd-ssd, Azure Premium Disk. Najłatwiejsze w zarządzaniu, dobre IOPS, snapshots w jednej linii. Droższe niż on-prem.

Wskazówka: Dla dużych produkcyjnych baz danych (100 GB+, high-write) rozważ managed bazę (RDS, Cloud SQL, PlanetScale) lub dedykowanego operatora K8s (Percona Operator for MySQL, CloudNative PG dla PostgreSQL). Operator zarządza replikacją, failoverem i backupami automatycznie, co jest trudne do poprawnego zrobienia samodzielnie.

Najczęstsze pytania

Czym różni się StatefulSet od Deployment w Kubernetes? +
Deployment tworzy bezstanowe pody — każdy pod jest identyczny, może być zastąpiony w dowolnej kolejności i nie ma stałej tożsamości. StatefulSet gwarantuje stabilne nazwy podów (np. mysql-0, mysql-1), stałe PersistentVolumes przypisane do konkretnego poda, oraz uporządkowane uruchamianie i skalowanie (jeden pod naraz, od 0 do N). Jest to kluczowe dla baz danych, gdzie mysql-0 jest masterem a mysql-1 repliką, i zamiana kolejności spowodowałaby utratę danych.
Czy warto uruchamiać bazy danych w Kubernetes? +
To zależy od kontekstu. Dla małych aplikacji i środowisk developerskich K8s StatefulSet to dobre rozwiązanie — jeden manifest zarządza bazą i aplikacją. Dla dużej produkcji (100+ GB danych, wysokie IOPS) wiele firm wybiera managed bazy (RDS, Cloud SQL) lub bare metal, bo K8s dodaje warstwę abstrakcji, która może zwiększać latency I/O. Środek drogi to operator bazy danych (Percona Operator, CloudNative PG), który zarządza replikacją i backupami automatycznie.
Co to są volumeClaimTemplates w StatefulSet? +
volumeClaimTemplates to sekcja w specyfikacji StatefulSet, która automatycznie tworzy PersistentVolumeClaim dla każdego poda. Jeśli masz StatefulSet z 3 replikami i jednym volumeClaimTemplate (np. 10Gi), Kubernetes stworzy 3 osobne PVC: data-mysql-0, data-mysql-1, data-mysql-2. Każdy pod dostaje swój dedykowany wolumen. Po usunięciu poda wolumen NIE jest usuwany — dopiero po manualnym usunięciu PVC lub usunięciu StatefulSet z --cascade=foreground.
Jak zrobić backup bazy danych działającej w Kubernetes? +
Są dwie popularne strategie: (1) CronJob uruchamiający mysqldump/pg_dump wewnątrz poda (lekki, nie wymaga dodatkowych narzędzi, ale backup jest logiczny i powolny dla dużych baz). (2) VolumeSnapshot — migawka na poziomie storage drivera (CSI), tworzona przez Kubernetes VolumeSnapshotClass. Snapshot jest błyskawiczny, ale wymaga wspierającego CSI drivera (np. AWS EBS, GCE PD, Longhorn). Do zarządzania backupami zewnętrznie polecany jest Velero — snapshot PV + backup manifestów K8s.
Jak dobrać StorageClass dla bazy danych w K8s? +
Kluczowe parametry to: access mode (bazy potrzebują RWO — ReadWriteOnce, nie RWX), provisioner (lokal czy chmura), reclaim policy (Retain dla produkcji, Delete dla dev), oraz volumeBindingMode (WaitForFirstConsumer wymusza scheduling na węźle z dyskiem — ważne dla local storage). W chmurze wybierz StorageClass z SSD gp3 lub premium (niższe latency). Dla on-prem rozważ Longhorn lub Rook-Ceph jako backend CSI.

Sprawdź oferty pasujące do tego scenariusza

Poniżej masz szybkie przejścia do ofert i stron z kodami rabatowymi tam, gdzie są dostępne.