PersistentVolume, Claim และ StorageClass
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”PersistentVolumeClaim คือ request ขอ storage ที่ Pod เอาไป mount ใช้ และจะ bind กับ PersistentVolume ที่เป็น storage ระดับ cluster ที่อยู่รอดข้าม Pod และตาม Pod ไปได้ข้าม node
ช่องว่างที่ emptyDir กับ hostPath ปิดไม่ได้
หัวข้อที่มีชื่อว่า “ช่องว่างที่ emptyDir กับ hostPath ปิดไม่ได้”emptyDir ตายไปพร้อม Pod ส่วน hostPath ผูกข้อมูลไว้กับ node เฉพาะตัว ทั้งสองแบบอยู่ไม่รอดถ้า Pod ถูก scheduler ย้ายไปรันบน hardware คนละตัว ซึ่ง scheduler มีสิทธิ์ทำแบบนั้นได้ตลอดเวลา workload แบบ stateful จริง ๆ (database, message queue หรืออะไรก็ตามที่ข้อมูลสำคัญ) ต้องการ storage ที่อยู่แยกอิสระจาก Pod หรือ node ตัวใดตัวหนึ่ง และให้ Pod ที่ถูกย้ายไปที่ใหม่ต่อกลับเข้าไปใช้ได้อีก
PersistentVolume กับ PersistentVolumeClaim
หัวข้อที่มีชื่อว่า “PersistentVolume กับ PersistentVolumeClaim”PersistentVolume (PV) คือ storage resource ระดับ cluster แนวคิดคล้าย Node ที่แทน compute capacity แต่ PV แทน storage capacity แทน admin ของ cluster provision PV แบบ static ล่วงหน้าได้ หรือจะให้ถูกสร้างแบบ dynamic ก็ได้ (อธิบายด้านล่าง)
PersistentVolumeClaim (PVC) คือ request ที่ user หรือ application ยื่นขอ “ขอ storage ขนาดเท่านี้ ด้วย access mode แบบนี้” Kubernetes จะ bind PVC เข้ากับ PV ที่ตรงกับ request นั้น ที่สำคัญคือ Pod reference แค่ PVC ไม่เคย reference PV ตรง ๆ Pod ไม่ต้องรู้หรือสนใจว่าไป bind กับ PV ตัวไหนจริง ๆ
apiVersion: v1kind: PersistentVolumemetadata: name: pv-manual-10gispec: capacity: storage: 10Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: manual hostPath: path: /mnt/dataapiVersion: v1kind: PersistentVolumeClaimmetadata: name: data-claimspec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi storageClassName: manualapiVersion: v1kind: Podmetadata: name: dbspec: containers: - name: postgres image: postgres:16 volumeMounts: - name: data mountPath: /var/lib/postgresql/data volumes: - name: data persistentVolumeClaim: claimName: data-claimAccess mode
หัวข้อที่มีชื่อว่า “Access mode”PV ประกาศว่ารองรับ access mode ไหนบ้าง ส่วน PVC ก็ขอ mode ที่ต้องการ
ReadWriteOnce(RWO) — mount แบบ read-write ได้ แต่แค่ Pod บน node เดียวในเวลาเดียวกันเท่านั้นReadOnlyMany(ROX) — mount แบบ read-only ได้จากหลาย node พร้อมกันReadWriteMany(RWX) — mount แบบ read-write ได้จากหลาย node พร้อมกัน (มีแค่บาง storage backend ที่รองรับ)ReadWriteOncePod(RWOP) — mode ใหม่กว่าและเข้มกว่า volume ถูกจำกัดให้ใช้ได้แค่ Pod เดียว ทั้ง cluster ไม่ใช่แค่จำกัดที่ node เดียว ส่วนReadWriteOnceธรรมดายังเปิดให้หลาย Pod share volume กันได้ถ้าอยู่ node เดียวกันReadWriteOncePodมาปิดช่องว่างนี้เวลาต้องการ single-writer guarantee จริง ๆ
Dynamic provisioning ด้วย StorageClass
หัวข้อที่มีชื่อว่า “Dynamic provisioning ด้วย StorageClass”การ provision PV มือทีละตัวไม่ scale StorageClass อธิบาย storage class หนึ่งประเภท พร้อม provisioner ที่สร้าง storage นั้นได้ตามต้องการ พอ PVC reference StorageClass แทนที่จะ reference PV ที่มีอยู่แล้ว driver แบบ CSI (Container Storage Interface) จะ provision PV ที่ตรงกันให้อัตโนมัติ admin ไม่ต้องมานั่งสร้างล่วงหน้าเลย
apiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: fast-ssdprovisioner: ebs.csi.aws.comparameters: type: gp3reclaimPolicy: DeletevolumeBindingMode: WaitForFirstConsumerapiVersion: v1kind: PersistentVolumeClaimmetadata: name: dynamic-claimspec: accessModes: - ReadWriteOnce storageClassName: fast-ssd resources: requests: storage: 20Gi# Watch a PVC bind to a dynamically provisioned PVkubectl get pvc dynamic-claim -wReclaim policy ตัดสินชะตากรรมหลัง PVC หายไป
หัวข้อที่มีชื่อว่า “Reclaim policy ตัดสินชะตากรรมหลัง PVC หายไป”reclaim policy กำหนดว่า storage เบื้องหลังจะเป็นยังไงหลัง PVC ของตัวเองถูกลบ
Delete— PV และ storage เบื้องหลัง (disk จริง) ถูกลบไปพร้อม PVC เป็นค่า default ของ StorageClass แบบ dynamic provisioning ส่วนใหญ่Retain— PV และข้อมูลยังอยู่ต่อหลัง PVC ถูกลบ admin เลยกู้คืนหรือเคลียร์ข้อมูลเองได้ก่อนที่ storage เบื้องหลังจะถูกปล่อยจริง ๆ
flowchart LR
pod["Pod"] -->|mounts| pvc["PVC: dynamic-claim"]
pvc -->|references| sc["StorageClass: fast-ssd"]
sc -->|CSI driver provisions| pv["New PersistentVolume"]
pv --> disk[("Underlying disk, e.g. an EBS volume")]