StorageClass, dynamic provisioning và EBS CSI
Bài 42 để lộ một điểm yếu: admin phải tạo PV bằng tay trước, đoán sẵn user cần bao nhiêu ổ, dung lượng nào. Ở quy mô thật điều đó bất khả thi. Dynamic provisioning lật ngược: user chỉ khai PVC, và hệ thống tự tạo PV — thậm chí tự gọi nhà cung cấp cloud tạo hẳn một ổ đĩa mới. Linh hồn của nó là StorageClass + một CSI driver. Bài này cài EBS CSI driver thật trên cụm AWS của ta và dò từng mắt xích: cái gì watch cái gì, cái gì gọi cái gì để một ổ EBS ra đời từ một dòng YAML.
StorageClass: "lớp" lưu trữ và provisioner
Tài liệu: "A StorageClass provides a way for administrators to describe the classes of storage they offer." Trường quan trọng nhất là provisioner — "determines what volume plugin is used for provisioning PVs. This field must be specified." Đây là cái trỏ tới ai sẽ tạo PV. Với EBS, provisioner là ebs.csi.aws.com (tên CSI driver). CSI (Container Storage Interface) là chuẩn cắm-ngoài: thay vì nhồi mọi loại storage vào code Kubernetes, mỗi nhà cung cấp viết một driver riêng theo chuẩn CSI. EBS CSI driver là một chương trình chạy trong cụm (pod), không phải code lõi.
Cài EBS CSI driver (và IAM cho node)
Driver cần quyền AWS để tạo/gắn EBS. Các node EC2 của ta chưa có IAM — nên trước hết gắn một IAM role:
# tạo role tin cậy ec2, gắn managed policy AmazonEBSCSIDriverPolicy,
# tạo instance profile, associate vào 2 worker
aws iam create-role --role-name k8s-scratch-ebs-csi --assume-role-policy-document file://ec2-trust.json
aws iam attach-role-policy --role-name k8s-scratch-ebs-csi \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy
aws iam create-instance-profile --instance-profile-name k8s-scratch-ebs-csi
aws iam add-role-to-instance-profile --instance-profile-name k8s-scratch-ebs-csi --role-name k8s-scratch-ebs-csi
aws ec2 associate-iam-instance-profile --instance-id <worker-0> --iam-instance-profile Name=k8s-scratch-ebs-csi
aws ec2 associate-iam-instance-profile --instance-id <worker-1> --iam-instance-profile Name=k8s-scratch-ebs-csi
CSI driver pod sẽ lấy credentials từ IAM role của node qua IMDS. Rồi cài driver (kustomize overlay, ghim v1.50.0):
kubectl apply -k "github.com/kubernetes-sigs/aws-ebs-csi-driver/deploy/kubernetes/overlays/stable/?ref=v1.50.0"
kubectl get pods -n kube-system -l app.kubernetes.io/name=aws-ebs-csi-driver
ebs-csi-controller-...-lh5sc 6/6 Running worker-0
ebs-csi-controller-...-qd476 6/6 Running worker-1
ebs-csi-node-h7ps5 3/3 Running worker-1
ebs-csi-node-kwkfc 3/3 Running worker-0
Hai phần đáng để ý. ebs-csi-controller (Deployment) chứa 6/6 container — đáng kể nhất là sidecar external-provisioner (watch PVC, gọi driver tạo volume), external-attacher (gắn volume vào node), resizer, snapshotter, plus container driver chính. ebs-csi-node (DaemonSet, một pod/node — đúng mô hình Bài 26) lo việc mount volume trên từng node. Việc cài driver cũng tạo một object CSIDriver ebs.csi.aws.com đăng ký driver với cụm.
StorageClass + PVC + chuỗi provisioning
Tạo StorageClass trỏ provisioner đó, rồi một PVC tham chiếu nó:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata: {name: ebs-sc}
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer # chờ pod rồi mới tạo volume
reclaimPolicy: Delete
parameters: {type: gp3}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata: {name: pvc-dyn}
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: ebs-sc # <-- trỏ StorageClass, KHÔNG trỏ PV
resources: {requests: {storage: 2Gi}}
kubectl get pvc pvc-dyn ; kubectl get pv
NAME STATUS VOLUME STORAGECLASS
pvc-dyn Pending "" ebs-sc
No resources found # <-- CHƯA có PV nào
PVC Pending, không có PV. Khác Bài 42 ở hai điểm: PVC khai storageClassName (không khai dung tích cố định để khớp PV có sẵn), và chưa có PV nào tồn tại. PVC còn Pending vì volumeBindingMode: WaitForFirstConsumer, tài liệu: "delay the binding and provisioning of a PersistentVolume until a Pod using the PersistentVolumeClaim is created." Điều này quan trọng với EBS: ổ EBS gắn được vào một AZ, nên phải chờ biết pod chạy ở node/AZ nào rồi mới tạo ổ đúng chỗ. Tạo pod dùng PVC:
volumes:
- {name: v, persistentVolumeClaim: {claimName: pvc-dyn}}
Giờ chuỗi nhân-quả diễn ra, đây là phần cốt lõi cần dò kỹ:
1. user ──tạo──▶ PVC pvc-dyn (storageClassName: ebs-sc) [Pending]
2. user ──tạo──▶ Pod dùng pvc-dyn
3. scheduler ── chọn node cho Pod (vd worker-1) → "first consumer" xuất hiện
4. external-provisioner (sidecar trong ebs-csi-controller)
watch PVC + thấy đã có consumer → gọi CSI CreateVolume
5. EBS CSI driver ──gọi AWS API──▶ AWS TẠO EBS volume (gp3, 2Gi, đúng AZ)
6. external-provisioner ──tạo──▶ PV (volumeHandle = vol-id) → bind với PVC
7. external-attacher ──gọi AWS──▶ gắn EBS vào instance worker-1
8. ebs-csi-node (DaemonSet@worker-1) ── mount ổ vào pod
9. kubelet ── pod Running, thấy /data
Kiểm chứng từng mắt:
kubectl get pv # PV giờ TỰ SINH
kubectl get pv <name> -o jsonpath='{.spec.csi.volumeHandle}'
aws ec2 describe-volumes --volume-ids <vol-id>
kubectl exec dyn-pod -- cat /data/f.txt
NAME CAPACITY RECLAIM STATUS CLAIM STORAGECLASS
pvc-143e1484-... 2Gi Delete Bound default/pvc-dyn ebs-sc
volumeHandle = vol-07fb135ac8ea0717a
vol-07fb135ac8ea0717a 2 gp3 in-use ap-southeast-1a i-0a33782c408f5bf09
dynamic-ebs-data
Mọi thứ khớp: PV tự sinh tên pvc-<uid> (không ai gõ tay), volumeHandle trỏ một EBS volume thật vol-07fb... (2GB gp3, in-use, gắn vào instance i-0a33... = worker-1 nơi pod chạy). Một dòng PVC đã khiến AWS đẻ ra một ổ đĩa và gắn vào đúng máy. Đây là khác biệt nền tảng với static (Bài 42): không admin nào tạo PV — external-provisioner làm hộ, và external-attacher + ebs-csi-node lo gắn/mount.
reclaimPolicy Delete: xóa PVC là xóa cả ổ đĩa
StorageClass đặt reclaimPolicy: Delete (mặc định của dynamic provisioning). Xóa PVC:
kubectl delete pod dyn-pod ; kubectl delete pvc pvc-dyn
kubectl get pv # No resources found
aws ec2 describe-volumes --volume-ids vol-07fb135ac8ea0717a
InvalidVolume.NotFound: The volume 'vol-07fb135ac8ea0717a' does not exist.
PVC mất → PV bị xóa → và CSI driver gọi AWS xóa luôn ổ EBS (Delete). Tiện cho ephemeral, nhưng nguy cho data quý — đặt Retain (Bài 42) nếu cần giữ. Đây cũng là lý do StatefulSet (Bài 25) không xóa PVC khi scale down: chống mất data ngoài ý muốn.
🧹 Dọn dẹp
kubectl delete pod dyn-pod --now ; kubectl delete pvc pvc-dyn # -> EBS volume tự xóa
Đã xóa workload thử (ổ EBS auto-deleted). Giữ lại EBS CSI driver + StorageClass ebs-sc + IAM role (dùng tiếp cho Bài 44 snapshot và StatefulSet với storage thật). Cụm còn CoreDNS + metrics-server + ebs-csi. Manifest ở github.com/nghiadaulau/kubernetes-from-scratch, thư mục 43-storageclass-csi.
Tổng kết
Dynamic provisioning bỏ bước admin-tạo-PV-tay của Bài 42: user chỉ tạo PVC trỏ StorageClass, hệ thống tự đẻ PV. StorageClass khai provisioner (ai tạo) + parameters (loại ổ) + volumeBindingMode (Immediate hay WaitForFirstConsumer — chờ pod để chọn đúng AZ) + reclaimPolicy. CSI driver (ta cài EBS CSI v1.50.0 thật + IAM role cho node) là chương trình chạy-trong-cụm: sidecar external-provisioner watch PVC → gọi driver → AWS tạo EBS volume → PV tự sinh (volumeHandle = vol-id) → bind; external-attacher gắn ổ vào instance; ebs-csi-node (DaemonSet) mount. Ta thấy một dòng PVC sinh ra ổ vol-07fb... thật, và reclaimPolicy: Delete khiến xóa PVC là xóa luôn ổ. Chuỗi PVC→provisioner→AWS→PV→attacher→node là bộ khung của mọi CSI driver, không riêng EBS.
Bài 44 dùng chính driver này cho VolumeSnapshot: chụp ảnh tức thời một PVC (CSI gọi AWS tạo EBS snapshot), rồi khôi phục một PVC mới từ snapshot — sao lưu/nhân bản dữ liệu ở tầng storage.