Sau bài này bạn làm được: mount volume đúng loại cho từng use case; hiểu PVC/PV dynamic provisioning; biết khi nào dùng StatefulSet vs Deployment.


Volume types — từ tạm thời đến bền vững

emptyDir — scratch space

volumes:
- name: scratch
  emptyDir: {}                   # Mặc định: disk node
  # emptyDir:
  #   medium: Memory             # tmpfs — nhanh nhưng tính vào memory limit
  #   sizeLimit: 100Mi
  • Tạo khi pod start, xoá khi pod bị xoá (không phải khi container restart).
  • Dùng cho: cache tạm, shared data giữa containers trong pod, sort/transform.
  • Không bền vững — pod mất = data mất.

hostPath — mount từ node

volumes:
- name: docker-sock
  hostPath:
    path: /var/run/docker.sock
    type: Socket

Cảnh báo lớn:

  • Pod trên node khác → path khác → data khác.
  • Rủi ro bảo mật: container access filesystem host.
  • Chỉ dùng cho: DaemonSet đọc log host, mount Docker socket (CI runner).
  • Không dùng cho persistent data.

PersistentVolume (PV) và PersistentVolumeClaim (PVC)

[StorageClass] ← Admin cấu hình (loại storage + provisioner)
       ▼ dynamic provisioning
[PersistentVolume] ← Đại diện cho disk thật (EBS, GCE PD, NFS, Ceph...)
       ▼ bound
[PersistentVolumeClaim] ← Pod yêu cầu storage
       ▼ mount
[Pod]

PVC — cách pod yêu cầu storage

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-pvc
spec:
  accessModes:
  - ReadWriteOnce                # RWO: 1 node mount (phổ biến nhất)
  resources:
    requests:
      storage: 10Gi
  storageClassName: gp3          # StorageClass (cloud provisioner)

Access modes

ModeViết tắtÝ nghĩa
ReadWriteOnceRWO1 node mount read-write (block storage)
ReadOnlyManyROXNhiều node mount read-only
ReadWriteManyRWXNhiều node mount read-write (NFS, CephFS)
ReadWriteOncePodRWOP1 pod duy nhất mount (K8s 1.29+ GA)

Phổ biến nhất: RWO cho database, app state. RWX khi nhiều pod cần write cùng volume (hiếm, cần NFS hoặc CephFS).

Mount PVC vào Pod

spec:
  containers:
  - name: app
    volumeMounts:
    - name: data
      mountPath: /data
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: data-pvc

StorageClass — dynamic provisioning

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp3
provisioner: ebs.csi.aws.com      # CSI driver
parameters:
  type: gp3
  fsType: ext4
reclaimPolicy: Delete              # Hoặc Retain
volumeBindingMode: WaitForFirstConsumer   # Chờ pod schedule rồi mới provision
allowVolumeExpansion: true         # Cho phép resize PVC

reclaimPolicy

PolicyKhi PVC bị xoá
DeletePV (và disk thật) bị xoá → mất data
RetainPV giữ lại, admin phải xoá/reuse thủ công

Production database: dùng Retain + backup strategy.

volumeBindingMode

ModeHành vi
ImmediateProvision PV ngay khi PVC tạo
WaitForFirstConsumerChờ pod schedule → provision PV ở AZ đúng

WaitForFirstConsumer quan trọng trên multi-AZ cloud: tránh provision disk ở AZ A mà pod schedule ở AZ B.


Resize PVC

# Nếu StorageClass cho phép (allowVolumeExpansion: true)
kubectl edit pvc data-pvc
# Sửa spec.resources.requests.storage: 20Gi

# Kiểm tra status
kubectl describe pvc data-pvc
# Conditions: FileSystemResizePending → resize khi pod restart

Chỉ tăng, không giảm được. Filesystem resize thường cần pod restart (unmount/remount).


StatefulSet — workload có identity

Khác Deployment:

DeploymentStatefulSet
Pod nameRandom (my-app-7d8f9-x2k)Ordinal (my-db-0, my-db-1)
PVCShared hoặc khôngMỗi pod có PVC riêng (data-my-db-0)
Startup/shutdownParallelOrdered (0 → 1 → 2)
DNSQua ServiceMỗi pod có DNS riêng (headless)

StatefulSet spec

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: my-db
spec:
  serviceName: my-db             # Headless Service (bắt buộc)
  replicas: 3
  selector:
    matchLabels:
      app: my-db
  template:
    metadata:
      labels:
        app: my-db
    spec:
      containers:
      - name: postgres
        image: postgres:16
        ports:
        - containerPort: 5432
        volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:           # Tự tạo PVC per pod
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: gp3
      resources:
        requests:
          storage: 20Gi

DNS cho StatefulSet

Cần headless Service:

apiVersion: v1
kind: Service
metadata:
  name: my-db
spec:
  clusterIP: None
  selector:
    app: my-db
  ports:
  - port: 5432

DNS: my-db-0.my-db.default.svc.cluster.local, my-db-1.my-db.default.svc.cluster.local

Khi nào dùng StatefulSet

  • Database (PostgreSQL, MySQL, MongoDB).
  • Message queue (Kafka, RabbitMQ).
  • Distributed system cần stable network identity (etcd, ZooKeeper).

Khi nào KHÔNG dùng StatefulSet

  • App stateless (API server, web) → Deployment.
  • State lưu ở managed service (RDS, Cloud SQL) → Deployment gọi external DB.
  • Bạn chỉ cần shared storage → Deployment + PVC (RWX).

CSI (Container Storage Interface)

K8s không trực tiếp quản lý storage — giao cho CSI driver:

  • AWS EBS CSI: ebs.csi.aws.com
  • GCE PD CSI: pd.csi.storage.gke.io
  • Azure Disk CSI: disk.csi.azure.com
  • NFS CSI: NFS provisioner
  • Local path: rancher.io/local-path (dev/test)
# Xem CSI drivers
kubectl get csidrivers

Tóm tắt

  • emptyDir: tạm, mất khi pod xoá. hostPath: mount host, nguy hiểm.
  • PVC/PV: storage bền vững. StorageClass + CSI driver tự provision.
  • reclaimPolicy: Delete mất data, Retain giữ lại.
  • StatefulSet: ordered identity + PVC per pod. Dùng cho database, message queue.
  • Nguyên tắc: tránh state trên K8s nếu có managed alternative (RDS, Cloud SQL).

Câu hỏi hay gặp

PVC Pending mãi — nguyên nhân?

Trả lời: (1) StorageClass không tồn tại; (2) WaitForFirstConsumer nhưng chưa có pod yêu cầu; (3) CSI driver chưa cài; (4) Quota hết (cloud disk limit). Kiểm tra Events: kubectl describe pvc.

Xoá StatefulSet có xoá PVC không?

Trả lời: Không. PVC tạo bởi volumeClaimTemplates không bị xoá khi StatefulSet bị xoá. Phải xoá PVC thủ công. Đây là by design — tránh mất data.

Database trên K8s vs managed service — chọn thế nào?

Trả lời: Managed service (RDS, Cloud SQL) nếu: team nhỏ, không muốn quản lý backup/HA/upgrade. K8s StatefulSet nếu: cần portability, on-prem, hoặc dùng operator (CloudNativePG, Zalando PostgreSQL Operator) quản lý lifecycle. Rule of thumb: nếu có thể dùng managed, dùng managed.


Bài tiếp theo (Giai đoạn V): Requests, limits, QoS và OOM — tài nguyên và ổn định cluster.