Skip to content

StatefulSets, Volume Expansion & Backup for GKE

Vì sao đây là chương về vận hành, không phải cấu hình

Ba chủ đề của file này — StatefulSet storage, volume expansion, và backup — không phải về "chọn loại storage nào" (các file trước đã giải) mà về vận hành dữ liệu qua thời gian: làm sao mỗi replica database giữ đúng dữ liệu của nó qua reschedule, làm sao tăng dung lượng khi dữ liệu lớn lên mà không downtime, và làm sao bảo vệ dữ liệu khỏi thảm họa. Đây là những thứ quyết định một stateful workload "chạy được trong demo" có trở thành "vận hành được 3 năm" hay không.

Điểm chung của cả ba: chúng là nơi mối quan hệ giữa abstraction Kubernetes và tài nguyên GCP bên dưới phải được giữ nhất quán qua các thao tác vòng đời. StatefulSet giữ ánh xạ Pod↔PVC↔PV ổn định; expansion phải resize cả disk GCP lẫn filesystem; backup phải capture cả manifest Kubernetes lẫn dữ liệu volume. Hiểu sai bất kỳ liên kết nào dẫn tới mất dữ liệu hoặc downtime.

StatefulSet và storage: identity bền vững

Deployment coi mọi Pod như nhau (cattle) — Pod nào cũng thay thế được, không Pod nào "sở hữu" dữ liệu riêng. Điều này không dùng được cho stateful: replica 0 của một database cluster có dữ liệu khác replica 1, và khi replica 0 bị reschedule nó phải lấy lại đúng dữ liệu của nó, không phải của replica 1. StatefulSet giải bài này bằng identity bền vững.

volumeClaimTemplates: mỗi Pod một PVC riêng

StatefulSet dùng volumeClaimTemplates để tạo một PVC riêng cho mỗi Pod, đặt tên theo ordinal cố định: <template>-<statefulset>-<ordinal>. Ví dụ StatefulSet postgres với template data tạo data-postgres-0, data-postgres-1, data-postgres-2. Ánh xạ này bền vững: Pod postgres-1 luôn bind PVC data-postgres-1, luôn thấy đúng disk của nó, dù reschedule sang node nào, dù restart bao nhiêu lần (Kubernetes StatefulSets).

yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres
  replicas: 3
  selector:
    matchLabels: { app: postgres }
  template:
    metadata:
      labels: { app: postgres }
    spec:
      containers:
      - name: postgres
        image: postgres:16
        volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: pd-ssd-retain      # Retain để an toàn
      resources:
        requests:
          storage: 100Gi

PVC không bị xóa khi scale-down — đặc tính an toàn cốt lõi

Một đặc tính bảo vệ dữ liệu quan trọng: khi scale-down một StatefulSet (giảm replica), các PVC không bị xóa tự động. Scale postgres từ 3 xuống 2 xóa Pod postgres-2 nhưng giữ nguyên PVC data-postgres-2 và disk của nó. Khi scale lại lên 3, Pod postgres-2 mới bind lại đúng PVC cũ và thấy nguyên dữ liệu (Kubernetes StatefulSets).

Đây là hành vi an toàn-mặc-định và là một lý do mạnh để dùng StatefulSet cho mọi stateful workload thay vì Deployment + PVC rời. Nó cũng giải thích vì sao helm uninstall ít nguy hiểm hơn với StatefulSet: PVC do volumeClaimTemplates tạo không bị Helm xóa như PVC khai báo rời (file 2). Kubernetes 1.27+ thêm persistentVolumeClaimRetentionPolicy để tùy chỉnh hành vi này (xóa PVC khi delete StatefulSet nếu muốn) — nhưng mặc định giữ PVC là lựa chọn an toàn nên giữ cho dữ liệu quan trọng.

Ordered operations và hệ quả storage

StatefulSet triển khai và update theo thứ tự ordinal (0, 1, 2...) mặc định — Pod n+1 chỉ bắt đầu khi Pod n đã Ready. Với storage, điều này nghĩa là rolling update một database cluster diễn ra tuần tự, mỗi node tự attach/detach disk của nó theo thứ tự. Hệ quả: update chậm hơn (tuần tự) nhưng an toàn hơn (không hạ cả cluster cùng lúc). Kết hợp với PodDisruptionBudget để bảo vệ trong cả update lẫn node maintenance (Chương 6, 15) — PDB minAvailable đảm bảo đủ replica giữ quorum trong khi disk được dời.

Volume expansion: tăng dung lượng online và cold

Dữ liệu lớn lên theo thời gian; disk cấp ban đầu cuối cùng sẽ đầy. Volume expansion cho phép tăng dung lượng PVC mà không tạo lại — nhưng có những ràng buộc và cơ chế phải hiểu để tránh downtime hoặc kẹt.

Điều kiện và cơ chế

Expansion yêu cầu StorageClass có allowVolumeExpansion: true — và phải bật từ đầu, vì không sửa được StorageClass của PVC đã tồn tại (GKE PD CSI Driver). Đây là lý do best practice luôn bật allowVolumeExpansion ngay cả khi chưa cần (file 2, 3).

Tăng dung lượng bằng cách sửa spec.resources.requests.storage của PVC lên giá trị lớn hơn. Cơ chế hai bước:

  1. Resize disk ở GCP: CSI driver gọi Compute Engine API tăng dung lượng PD/Hyperdisk.
  2. Resize filesystem: filesystem (ext4/xfs) trên disk được mở rộng để dùng dung lượng mới.

Online vs cold resize

  • Online resize (không downtime): PD/Hyperdisk hỗ trợ tăng dung lượng và mở rộng filesystem trong khi Pod đang chạy cho nhiều trường hợp — đây là hành vi mong muốn cho production, không cần dừng database. ext4 và xfs hỗ trợ online grow.
  • Cold resize: một số tình huống cần Pod restart để filesystem nhận dung lượng mới (tùy phiên bản, loại filesystem, trạng thái). Khi đó expansion hoàn tất sau khi Pod được tạo lại.

Quy luật vận hành: chỉ tăng được, không giảm. Kubernetes không hỗ trợ shrink volume (rủi ro mất dữ liệu quá cao). Nếu cần giảm, phải migrate dữ liệu sang PVC nhỏ hơn thủ công. Hệ quả: provision dung lượng thận trọng từ đầu — bắt đầu nhỏ hợp lý rồi expand khi cần, vì over-provision lớn không reverse được mà vẫn tính tiền.

Với StatefulSet, expansion cần làm trên từng PVC (data-postgres-0, -1, -2...) vì mỗi Pod có PVC riêng; sửa volumeClaimTemplates không tự áp cho PVC đã tạo (template chỉ áp cho PVC mới). Đây là một thao tác vận hành cần script hóa cho cluster nhiều replica.

Backup for GKE: bảo vệ cả config lẫn dữ liệu

Snapshot PD thuần (file 3) bảo vệ dữ liệu một volume, nhưng không bảo vệ trạng thái Kubernetes — manifest, ánh xạ PVC↔PV, cấu hình workload. Khôi phục một cluster từ snapshot PD thuần đòi hỏi tái tạo thủ công toàn bộ object Kubernetes và ghép lại đúng disk — chậm, dễ sai. Backup for GKE giải bài toán này: nó backup cả hai một cách phối hợp.

Backup gồm hai phần

Theo tài liệu, Backup for GKE capture hai dạng dữ liệu (Backup for GKE):

  1. Config backup: tập manifest tài nguyên Kubernetes trích từ API server — ghi lại trạng thái workload (Deployment, StatefulSet, ConfigMap, Service, PVC...).
  2. Volume backups: snapshot tương ứng với các PVC tìm thấy trong config backup — dữ liệu thật trên PD.

Sự phối hợp này là điểm khác biệt: restore không chỉ là "có lại disk" mà là tái tạo cả workload lẫn dữ liệu nhất quán với nhau, kể cả sang cluster khác.

Giới hạn quan trọng phải biết

Backup for GKE KHÔNG backup (Backup for GKE):

  • Cấu hình cluster (node pool, kích thước, feature) — phải tái tạo riêng (IaC/Terraform).
  • Container image — nằm ở Artifact Registry, backup riêng.
  • State dịch vụ ngoài (Cloud SQL, load balancer) — quản lý riêng.
  • Volume không phải Persistent Disk — đặc biệt Filestore NFS không được backup (file 6 — phải tự thiết lập Filestore backup).

Đây là khoảng trống dễ bỏ sót: một cluster dùng cả PD lẫn Filestore mà chỉ dựa Backup for GKE sẽ không bảo vệ dữ liệu Filestore. Kế hoạch DR phải bao quát mọi loại storage, không giả định một công cụ cover tất cả.

Các resource: BackupPlan, Backup, RestorePlan, Restore

Mô hình bốn object (Backup for GKE):

  • BackupPlan: cấu hình backup — cluster nguồn, phạm vi (toàn bộ/chọn lọc namespace), region lưu, lịch tự độngretention policy.
  • Backup: một bản backup tại một thời điểm; tạo nó khởi động quá trình.
  • RestorePlan: khuôn restore tái dùng — cluster đích, cách xử lý xung đột, transformation rules (sửa resource khi restore, ví dụ đổi StorageClass khi restore sang region khác).
  • Restore: một lần khôi phục từ một Backup vào cluster đích.

Năng lực then chốt cho DR: cross-project và cross-cluster — backup ở project/region này restore sang project/region khác, dùng cho disaster recovery, migration cluster, hay nhân bản môi trường (clone prod sang staging). Transformation rules cho phép điều chỉnh resource trong lúc restore để khớp môi trường đích.

yaml
# Ví dụ BackupPlan với lịch và retention (thể hiện khái niệm)
apiVersion: gkebackup.gke.io/v1
kind: BackupPlan
metadata:
  name: prod-daily-backup
spec:
  cluster: projects/PROJECT/locations/us-central1/clusters/prod
  retentionPolicy:
    backupRetainDays: 30
  backupSchedule:
    cronSchedule: "0 2 * * *"        # 2h sáng mỗi ngày
  backupConfig:
    allNamespaces: true
    includeVolumeData: true           # backup cả dữ liệu PD
    includeSecrets: true

Snapshot lifecycle và chiến lược DR

Một chiến lược backup production trưởng thành kết hợp nhiều tầng, mỗi tầng cho một loại thảm họa:

  • Volume snapshot (PD CSI, file 3): nhanh, incremental, cho restore nhanh một volume. Lập lịch định kỳ, retention ngắn-trung.
  • Backup for GKE: phối hợp config + volume, cho DR toàn workload, cross-region. Retention dài hơn, lịch hàng ngày.
  • Application-consistent backup: với database, dùng pre-backup hook gọi cơ chế quiesce của database (CHECKPOINT, pg_backup, flush) để snapshot nhất quán application-level, không chỉ crash-consistent (file 3). Backup for GKE hỗ trợ hook để chạy lệnh trong container trước/sau backup.
  • Filestore backup riêng: vì Backup for GKE không cover Filestore (file 6).
  • Object versioning (GCS): cho dữ liệu trên Cloud Storage (file 7, 8).

Hai metric định hình mọi quyết định backup:

  • RPO (Recovery Point Objective): mất bao nhiêu dữ liệu chấp nhận được → quyết định tần suất backup. RPO 1 giờ nghĩa là backup mỗi giờ.
  • RTO (Recovery Time Objective): khôi phục trong bao lâu → quyết định cơ chế restore (snapshot nhanh vs full restore). RTO thấp ưu tiên snapshot incremental + Stateful HA (file 3).

Retention phải cân giữa chi phí (snapshot tốn dung lượng) và yêu cầu compliance/recovery (giữ bao lâu). Một policy điển hình: snapshot mỗi 6 giờ giữ 7 ngày + Backup for GKE hàng ngày giữ 30 ngày + monthly giữ 1 năm cho compliance.

Real-world scenario: vận hành PostgreSQL StatefulSet end-to-end

Khép lại chương bằng một bức tranh vận hành đầy đủ, gom mọi mảnh:

  • StatefulSet 3 replica, volumeClaimTemplates với StorageClass Regional PD Retain, allowVolumeExpansion: true (file 2, 3).
  • Stateful HA Operator force-attach khi node/zone failure (file 3).
  • PodDisruptionBudget minAvailable: 2 giữ quorum qua maintenance.
  • Expansion: khi disk đạt ~80%, expand từng PVC online (script qua 3 ordinal) — không downtime.
  • Backup nhiều tầng: VolumeSnapshot mỗi 6h (RTO thấp) + Backup for GKE hàng ngày với pre-backup hook CHECKPOINT (application-consistent, cross-region DR) + retention 30 ngày.
  • DR test định kỳ: restore Backup for GKE sang cluster staging ở region khác để verify backup thật sự khôi phục được — backup không test là backup không tồn tại.

Bức tranh này là sự khác biệt giữa "có database chạy trên Kubernetes" và "vận hành được database production trên Kubernetes": identity bền, failover tự động, mở rộng không downtime, và nhiều tầng bảo vệ dữ liệu đã được kiểm chứng khôi phục.

Common mistakes / anti-patterns

  • Dùng Deployment + PVC rời cho stateful. Mất identity Pod↔PVC, PVC dễ bị xóa nhầm. Phòng tránh: luôn StatefulSet với volumeClaimTemplates cho stateful.

  • Quên allowVolumeExpansion từ đầu. Không expand được khi disk đầy, phải migrate. Phòng tránh: bật ngay khi tạo StorageClass.

  • Over-provision dung lượng vì sợ không shrink được. Trả tiền cho dung lượng thừa vĩnh viễn. Phòng tránh: provision nhỏ hợp lý + expand online khi cần.

  • Dựa hoàn toàn Backup for GKE mà quên nó không cover Filestore. Mất dữ liệu Filestore khi DR. Phòng tránh: kế hoạch DR bao mọi loại storage; Filestore backup riêng.

  • Backup crash-consistent cho database mà tưởng application-consistent. Restore có thể không nhất quán. Phòng tránh: pre-backup hook quiesce database.

  • Không bao giờ test restore. Phát hiện backup hỏng đúng lúc thảm họa. Phòng tránh: DR drill định kỳ, restore sang cluster test.

  • Scale-down StatefulSet tưởng mất dữ liệu, hoặc ngược lại quên PVC còn tốn tiền. Hiểu sai hành vi giữ-PVC. Phòng tránh: biết rõ PVC được giữ khi scale-down — vừa là an toàn vừa là chi phí cần quản.

Official references