
StorageClass คืออะไร? ทำไมต้องรู้?
ใน Kubernetes เมื่อ Application ต้องการเก็บข้อมูลถาวร (Persistent Storage) จะใช้ PersistentVolume (PV) และ PersistentVolumeClaim (PVC) แต่ถ้าต้องสร้าง PV ด้วยมือทุกครั้ง จะเสียเวลาและ Error-prone มาก
StorageClass คือ Object ใน Kubernetes ที่กำหนด “ประเภท” ของ Storage และช่วยให้ Dynamic Provisioning สร้าง PV อัตโนมัติเมื่อมี PVC ร้องขอ โดยไม่ต้องให้ Admin สร้าง PV ล่วงหน้า
ปัญหาของ Static Provisioning
# Static Provisioning — ต้องสร้าง PV ก่อน
# Step 1: Admin สร้าง PV (ด้วยมือ)
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv-001
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /data/pv001
# Step 2: Developer สร้าง PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
# ปัญหา:
# - Admin ต้องสร้าง PV ล่วงหน้า (ขนาด, จำนวน ไม่รู้ล่วงหน้า)
# - PV ที่สร้างอาจไม่ตรงกับ PVC (ขนาดเกิน/ขาด)
# - Scale ไม่ได้ ถ้ามี 100 PVC ต้องสร้าง 100 PV ด้วยมือ
Dynamic Provisioning — แก้ปัญหาด้วย StorageClass
# Dynamic Provisioning — StorageClass สร้าง PV อัตโนมัติ
# Step 1: Admin สร้าง StorageClass (ครั้งเดียว)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
iopsPerGB: "3000"
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
# Step 2: Developer สร้าง PVC → PV ถูกสร้างอัตโนมัติ!
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-app-data
spec:
storageClassName: fast-ssd
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
# ✓ PV ถูกสร้างอัตโนมัติ ขนาดตรงตาม PVC
# ✓ ไม่ต้อง Admin มาสร้างให้
# ✓ Scale ได้ไม่จำกัด
โครงสร้าง StorageClass YAML
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard # ชื่อ StorageClass
annotations:
storageclass.kubernetes.io/is-default-class: "true" # ตั้งเป็น Default
provisioner: kubernetes.io/aws-ebs # Provisioner (ขึ้นอยู่กับ Cloud/Storage)
parameters: # Parameters เฉพาะของ Provisioner
type: gp3
fsType: ext4
reclaimPolicy: Delete # Delete หรือ Retain
volumeBindingMode: WaitForFirstConsumer # Immediate หรือ WaitForFirstConsumer
allowVolumeExpansion: true # อนุญาตให้ขยาย Volume ได้
mountOptions: # Mount options เพิ่มเติม
- discard
- noatime
reclaimPolicy — Delete vs Retain
เมื่อ PVC ถูกลบ PV ที่สร้างมาจาก Dynamic Provisioning จะทำอะไร?
| reclaimPolicy | พฤติกรรม | ใช้เมื่อ |
|---|---|---|
| Delete | ลบ PV และ Storage จริงทิ้ง (เช่น ลบ EBS Volume) | Dev/Test, ข้อมูลไม่สำคัญ, ต้องการประหยัดค่าใช้จ่าย |
| Retain | เก็บ PV ไว้ (Status = Released) ต้องมาลบเอง | Production, Database, ข้อมูลสำคัญที่ต้อง Backup ก่อนลบ |
# StorageClass แบบ Retain สำหรับ Database
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: db-storage
provisioner: kubernetes.io/aws-ebs
parameters:
type: io2
iopsPerGB: "50"
reclaimPolicy: Retain # ⚠ ข้อมูลจะไม่ถูกลบเมื่อ PVC ถูกลบ
allowVolumeExpansion: true
# เมื่อ PVC ถูกลบ:
# Delete → PV ถูกลบ + EBS Volume ถูกลบ → ข้อมูลหายหมด!
# Retain → PV ยังอยู่ (Released) + EBS Volume ยังอยู่ → Recover ได้
# ✅ Best Practice:
# - Production Database → Retain
# - Dev/Test → Delete
# - Cache/Temp → Delete
volumeBindingMode — Immediate vs WaitForFirstConsumer
| Mode | พฤติกรรม | ข้อดี | ข้อเสีย |
|---|---|---|---|
| Immediate | สร้าง PV ทันทีเมื่อ PVC ถูกสร้าง | PV พร้อมใช้ทันที | อาจสร้างใน AZ ที่ Pod ไม่ได้ Schedule → Pod ค้าง |
| WaitForFirstConsumer | รอจนมี Pod มา Mount แล้วค่อยสร้าง PV | PV สร้างใน AZ เดียวกับ Pod (แก้ปัญหา Topology) | Pod ต้องรอ PV สร้างเสร็จ |
# ปัญหาของ Immediate mode ใน Multi-AZ Cluster
# Cluster มี 3 AZ: us-east-1a, us-east-1b, us-east-1c
# Immediate mode:
# 1. PVC ถูกสร้าง → PV สร้างใน us-east-1a (สุ่ม)
# 2. Pod ถูก Schedule ไปที่ us-east-1c
# 3. Pod mount PV ไม่ได้! (EBS อยู่คนละ AZ)
# 4. Pod ค้าง Pending ตลอดไป
# WaitForFirstConsumer mode (แนะนำ!):
# 1. PVC ถูกสร้าง → ยังไม่สร้าง PV
# 2. Pod ถูก Schedule ไปที่ us-east-1c
# 3. PV สร้างใน us-east-1c (AZ เดียวกับ Pod)
# 4. Pod mount PV ได้ทันที!
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ebs-sc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer # ✅ แนะนำสำหรับ Cloud
parameters:
type: gp3
allowVolumeExpansion — ขยาย Volume ได้
# เปิดให้ขยาย Volume ได้
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: expandable-storage
provisioner: ebs.csi.aws.com
parameters:
type: gp3
allowVolumeExpansion: true # ✅ อนุญาตให้ขยาย
# วิธีขยาย PVC (ไม่ต้อง Downtime!)
# แก้ไข PVC เพิ่ม size:
kubectl patch pvc my-data -p '{"spec":{"resources":{"requests":{"storage":"50Gi"}}}}'
# ตรวจสอบสถานะ:
kubectl get pvc my-data
# STATUS: Bound
# CAPACITY: 50Gi (อาจต้องรอ Filesystem resize)
# ⚠ ข้อจำกัด:
# - ขยายได้อย่างเดียว ย่อไม่ได้!
# - บาง Provisioner ต้อง Restart Pod เพื่อ resize filesystem
# - CSI driver ต้อง support EXPAND_VOLUME capability
Default StorageClass
# ตั้ง StorageClass เป็น Default
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
annotations:
storageclass.kubernetes.io/is-default-class: "true" # ตั้งเป็น Default
provisioner: ebs.csi.aws.com
parameters:
type: gp3
# เมื่อ PVC ไม่ระบุ storageClassName:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: no-sc-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
# ไม่ระบุ storageClassName → ใช้ Default StorageClass อัตโนมัติ
# ตรวจสอบ Default StorageClass:
kubectl get sc
# NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE AGE
# standard (default) ebs.csi.aws.com Delete WaitForFirstConsumer 30d
# fast-ssd ebs.csi.aws.com Delete WaitForFirstConsumer 15d
# db-storage ebs.csi.aws.com Retain WaitForFirstConsumer 15d
# เปลี่ยน Default StorageClass:
# 1. ปิด Default เดิม
kubectl patch sc standard -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
# 2. ตั้ง Default ใหม่
kubectl patch sc fast-ssd -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
# ⚠ ระวัง: ห้ามมี Default StorageClass มากกว่า 1!
Parameters ของ Provisioner แต่ละเจ้า
AWS EBS (ebs.csi.aws.com)
# gp3 — General Purpose SSD (แนะนำ)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: aws-gp3
provisioner: ebs.csi.aws.com
parameters:
type: gp3
fsType: ext4
iops: "3000" # Baseline IOPS (ฟรีถึง 3,000)
throughput: "125" # Baseline throughput (ฟรีถึง 125 MB/s)
encrypted: "true"
kmsKeyId: "arn:aws:kms:ap-southeast-1:123456:key/xxx"
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
allowVolumeExpansion: true
# io2 — High Performance SSD (Database)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: aws-io2
provisioner: ebs.csi.aws.com
parameters:
type: io2
fsType: ext4
iopsPerGB: "50" # สูงสุด 64,000 IOPS
encrypted: "true"
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
allowVolumeExpansion: true
# st1 — Throughput Optimized HDD (Log storage)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: aws-st1
provisioner: ebs.csi.aws.com
parameters:
type: st1
fsType: ext4
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
GCE Persistent Disk (pd.csi.storage.gke.io)
# pd-standard — Standard Persistent Disk
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gce-standard
provisioner: pd.csi.storage.gke.io
parameters:
type: pd-standard
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
allowVolumeExpansion: true
# pd-ssd — SSD Persistent Disk
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gce-ssd
provisioner: pd.csi.storage.gke.io
parameters:
type: pd-ssd
replication-type: regional-pd # Regional replication สำหรับ HA
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
# pd-balanced — Balanced Persistent Disk (แนะนำ)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gce-balanced
provisioner: pd.csi.storage.gke.io
parameters:
type: pd-balanced
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
allowVolumeExpansion: true
Azure Disk (disk.csi.azure.com)
# Premium SSD (Premium_LRS)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azure-premium
provisioner: disk.csi.azure.com
parameters:
skuName: Premium_LRS
kind: Managed
fsType: ext4
cachingMode: ReadOnly
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
allowVolumeExpansion: true
# Standard SSD (StandardSSD_LRS)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azure-standard-ssd
provisioner: disk.csi.azure.com
parameters:
skuName: StandardSSD_LRS
kind: Managed
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
# Ultra Disk (UltraSSD_LRS) — สำหรับ Database
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azure-ultra
provisioner: disk.csi.azure.com
parameters:
skuName: UltraSSD_LRS
kind: Managed
diskIOPSReadWrite: "10000"
diskMBpsReadWrite: "200"
reclaimPolicy: Retain
NFS (nfs-subdir-external-provisioner)
# NFS StorageClass — สำหรับ On-premise / Shared storage
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
server: 10.0.0.100 # NFS Server IP
share: /exports/k8s-data # NFS Share path
pathPattern: "${.PVC.namespace}/${.PVC.name}" # Directory structure
onDelete: retain # retain / delete / archive
reclaimPolicy: Delete
volumeBindingMode: Immediate # NFS ไม่มี Topology constraint
allowVolumeExpansion: true
mountOptions:
- nfsvers=4.1
- hard
- rsize=1048576
- wsize=1048576
StorageClass สำหรับ Workload แต่ละประเภท
Database (MySQL, PostgreSQL, MongoDB)
# Database StorageClass — Performance + Data Safety
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: database-storage
provisioner: ebs.csi.aws.com
parameters:
type: io2
iopsPerGB: "50"
fsType: ext4
encrypted: "true"
reclaimPolicy: Retain # ⚠ ห้ามลบข้อมูล Database!
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
# ใช้กับ StatefulSet:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
template:
spec:
containers:
- name: postgres
image: postgres:16
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
storageClassName: database-storage
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 100Gi
Cache (Redis, Memcached)
# Cache StorageClass — ราคาถูก ลบได้
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: cache-storage
provisioner: ebs.csi.aws.com
parameters:
type: gp3
fsType: ext4
reclaimPolicy: Delete # Cache ลบได้ไม่เสียหาย
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
# หรือถ้า Cache ไม่จำเป็นต้อง Persist → ใช้ emptyDir
# volumes:
# - name: cache
# emptyDir:
# sizeLimit: 5Gi
Logs (ELK, Loki, Fluentd)
# Log StorageClass — Throughput สูง ราคาถูก
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: log-storage
provisioner: ebs.csi.aws.com
parameters:
type: st1 # HDD — ถูก แต่ Throughput ดี
fsType: ext4
reclaimPolicy: Delete # Log เก่าลบได้ (เก็บไว้ใน S3/GCS แทน)
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
# หรือใช้ EFS สำหรับ Shared log storage
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: efs-logs
provisioner: efs.csi.aws.com
parameters:
provisioningMode: efs-ap
fileSystemId: fs-0123456789
directoryPerms: "755"
basePath: "/logs"
reclaimPolicy: Delete
volumeBindingMode: Immediate
Monitoring Storage Provisioning
# ตรวจสอบ StorageClass ทั้งหมด
kubectl get storageclass
kubectl get sc
# ตรวจสอบ PV ที่ถูก Dynamic Provision
kubectl get pv
kubectl get pv -o wide
# ตรวจสอบ PVC Status
kubectl get pvc -A
kubectl describe pvc my-data -n production
# ดู Events ที่เกี่ยวกับ Storage
kubectl get events --field-selector reason=ProvisioningSucceeded
kubectl get events --field-selector reason=ProvisioningFailed
# ตรวจสอบ CSI Driver
kubectl get csidrivers
kubectl get csinodes
# Troubleshooting PVC Pending:
# 1. ตรวจ PVC events
kubectl describe pvc stuck-pvc
# 2. ตรวจ CSI controller logs
kubectl logs -n kube-system -l app=ebs-csi-controller
# 3. Common issues:
# - StorageClass ไม่มี → สร้าง StorageClass
# - CSI driver ไม่ได้ติดตั้ง → helm install aws-ebs-csi-driver
# - IAM permission ไม่พอ → เพิ่ม EBS create/attach policy
# - Quota เต็ม → เพิ่ม EBS volume quota ใน AWS
Prometheus Metrics สำหรับ Storage
# Prometheus metrics ที่ควร Monitor:
# kubelet_volume_stats_available_bytes — พื้นที่ว่าง
# kubelet_volume_stats_capacity_bytes — ขนาดทั้งหมด
# kubelet_volume_stats_used_bytes — พื้นที่ที่ใช้
# kubelet_volume_stats_inodes — จำนวน inodes
# kubelet_volume_stats_inodes_used — inodes ที่ใช้
# Alert เมื่อพื้นที่ใกล้เต็ม:
# Prometheus AlertRule:
groups:
- name: storage-alerts
rules:
- alert: PVCAlmostFull
expr: |
(kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes) > 0.85
for: 5m
labels:
severity: warning
annotations:
summary: "PVC {{ $labels.persistentvolumeclaim }} is 85%+ full"
- alert: PVCCriticallyFull
expr: |
(kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes) > 0.95
for: 2m
labels:
severity: critical
annotations:
summary: "PVC {{ $labels.persistentvolumeclaim }} is 95%+ full - expand NOW!"
StorageClass Best Practices
| ข้อ | Best Practice | เหตุผล |
|---|---|---|
| 1 | ใช้ WaitForFirstConsumer เสมอ (Cloud) | ป้องกัน PV สร้างผิด AZ |
| 2 | ตั้ง reclaimPolicy: Retain สำหรับ Database | ป้องกันข้อมูลสูญหาย |
| 3 | เปิด allowVolumeExpansion | ขยาย Volume ได้โดยไม่ต้อง recreate |
| 4 | เปิด Encryption สำหรับ Production | ข้อมูลถูกเข้ารหัส at rest |
| 5 | ตั้ง Default StorageClass เดียว | PVC ที่ไม่ระบุ SC จะใช้ Default |
| 6 | สร้าง StorageClass แยกตาม Workload | Database, Cache, Log ต้องการ Storage ต่างกัน |
| 7 | Monitor PVC usage ด้วย Prometheus | Alert ก่อนพื้นที่เต็ม |
| 8 | ตั้งชื่อ StorageClass ให้สื่อความหมาย | fast-ssd, db-storage, log-hdd ดีกว่า sc-1, sc-2 |
| 9 | ใช้ CSI Driver แทน in-tree provisioner | in-tree กำลังถูก deprecate ใน K8s 1.31+ |
| 10 | Backup PV ก่อน Upgrade/Migration | VolumeSnapshot หรือ Velero |
Volume Snapshot — Backup PV อัตโนมัติ
# สร้าง VolumeSnapshotClass
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: ebs-snapshot
driver: ebs.csi.aws.com
deletionPolicy: Retain
parameters:
tagSpecification_1: "Backup=true"
# สร้าง Snapshot จาก PVC
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: db-snapshot-20260416
spec:
volumeSnapshotClassName: ebs-snapshot
source:
persistentVolumeClaimName: postgres-data-0
# Restore จาก Snapshot
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data-restored
spec:
storageClassName: database-storage
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 100Gi
dataSource:
name: db-snapshot-20260416
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
สรุป — Kubernetes Storage Classes 2026
StorageClass เป็นหัวใจของ Dynamic Provisioning ใน Kubernetes ที่ทำให้ Developer ไม่ต้องรอ Admin สร้าง PV ด้วยมือ เพียงแค่สร้าง PVC ระบุ storageClassName ก็ได้ PV ตามที่ต้องการทันที
สิ่งที่ต้องจำ: ใช้ WaitForFirstConsumer สำหรับ Cloud, Retain สำหรับ Database, เปิด allowVolumeExpansion เสมอ, Monitor ด้วย Prometheus alerts ก่อนพื้นที่เต็ม และสร้าง StorageClass แยกตาม Workload type (database, cache, logs) เพื่อ Performance และ Cost ที่เหมาะสม