VolumeSnapshot và CSI snapshot

K
Kai··5 min read

Bài 43 cho ta ổ đĩa bền tự sinh. Câu hỏi tiếp theo của vận hành thật: sao lưu nó thế nào? VolumeSnapshot chụp ảnh tức thời (point-in-time) nội dung một PVC — và với EBS CSI, snapshot đó là một EBS snapshot thật trên AWS, dùng để khôi phục hay nhân bản. Bài này khép Part IX, và mô hình của nó song song với PV/PVC nên ta đã có sẵn trực giác.

Ba object, song song với PV/PVC

Tài liệu cho một phép loại suy gọn:

VolumeSnapshot        : VolumeSnapshotContent  ::  PersistentVolumeClaim : PersistentVolume
(user xin chụp,          (ảnh chụp THẬT ở             (user xin lưu trữ)     (lưu trữ thật)
 namespaced)              backend, cluster-scoped)
VolumeSnapshotClass   ~  StorageClass            (driver + tham số)
  • VolumeSnapshot"a request for snapshot of a volume by a user", namespaced, như PVC.
  • VolumeSnapshotContent"a snapshot taken from a volume ... a cluster resource", như PV.
  • VolumeSnapshotClass — driver + tham số, như StorageClass.

Quan trọng: ba thứ này là CRD, không thuộc core API — phải cài riêng (như VPA ở Bài 40), cùng một snapshot-controller trong control plane. Còn cái thực thi việc chụp là sidecar csi-snapshotter — nó đã nằm sẵn trong ebs-csi-controller (6/6 container ở Bài 43).

Cài snapshot CRD + controller

# CRD (volumesnapshots, volumesnapshotcontents, volumesnapshotclasses + group...) từ external-snapshotter v8.2.0
kubectl apply -f .../client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml
# ... (3 CRD chính)
# snapshot-controller (rbac + deployment) vào control plane
kubectl apply -f .../deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml

Phân vai cần nhớ: snapshot-controller (control plane) điều phối object Snapshot↔Content; csi-snapshotter (sidecar trong ebs-csi-controller) gọi driver để thực sự tạo snapshot ở AWS. Tạo VolumeSnapshotClass trỏ driver EBS:

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata: {name: ebs-vsc}
driver: ebs.csi.aws.com
deletionPolicy: Delete

Chụp một PVC

Dựng một PVC + pod ghi data (dùng ebs-sc của Bài 43), rồi tạo VolumeSnapshot trỏ vào PVC:

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata: {name: snap1}
spec:
  volumeSnapshotClassName: ebs-vsc
  source: {persistentVolumeClaimName: src-pvc}      # chụp PVC này

Chuỗi nhân-quả (song song với provisioning Bài 43):

user ──tạo──▶ VolumeSnapshot snap1 (source: src-pvc)
snapshot-controller ── thấy snap1 → tạo VolumeSnapshotContent
csi-snapshotter (sidecar) ── gọi driver CreateSnapshot
EBS CSI driver ──gọi AWS──▶ AWS tạo EBS snapshot (snap-...)
                            ▼ khi AWS báo completed
VolumeSnapshot.readyToUse = true  ◀──bound──▶ VolumeSnapshotContent
kubectl get volumesnapshot snap1
kubectl get volumesnapshotcontent <name> -o jsonpath='{.status.snapshotHandle}'
aws ec2 describe-snapshots --snapshot-ids <snap-id>
NAME    READYTOUSE   SOURCEPVC   RESTORESIZE   SNAPSHOTCONTENT
snap1   true         src-pvc     2Gi           snapcontent-6fee...

snapshotHandle = snap-06de0c51fde8e085f
snap-06de0c51fde8e085f   2   completed   vol-01a0fa04f3b2b37a6

readyToUse=true sau ~70s (AWS hoàn tất snapshot), bound với VolumeSnapshotContent, và snapshotHandle là một EBS snapshot thật (snap-06de..., state completed, nguồn từ volume của src-pvc). Object Kubernetes ↔ snapshot AWS khớp 1-1, đúng mô hình PVC↔PV.

Khôi phục: PVC mới từ snapshot và cái bẫy page cache

Khôi phục bằng cách tạo PVC mới với dataSource trỏ VolumeSnapshot:

apiVersion: v1
kind: PersistentVolumeClaim
metadata: {name: restore-pvc}
spec:
  accessModes: ["ReadWriteOnce"]
  storageClassName: ebs-sc
  dataSource: {name: snap1, kind: VolumeSnapshot, apiGroup: snapshot.storage.k8s.io}
  resources: {requests: {storage: 2Gi}}

Pod dùng restore-pvc lên Running, ổ EBS mới được tạo từ snapshot (aws ec2 describe-volumes cho thấy SnapshotId trỏ đúng snap-06de...). Nhưng đọc file thì... rỗng:

kubectl exec restore-pod -- ls -la /data
-rw-r--r--  1 root root  0 May 23 17:47 important.txt     # 0 byte!

File có (inode được chụp) nhưng nội dung 0 byte, trong khi pod nguồn có 24 byte. Lý do nằm ở chỗ EBS snapshot là block-level, point-in-time: nó chụp đúng trạng thái block device tại thời điểm chụp. Lúc đó dữ liệu echo > file mới nằm trong page cache của OS, chưa được flush xuống đĩa, nên snapshot không có nó. Cách sửa: sync (hoặc quiesce app) trước khi chụp:

kubectl exec src-pod -- sync          # đẩy page cache xuống block device
# tạo snap2, restore từ snap2:
kubectl exec restore2-pod -- cat /data/important.txt
data-goc-truoc-snapshot

Sau sync, snapshot bắt được data, và PVC khôi phục đọc đúng data-goc-truoc-snapshot từ một ổ hoàn toàn mới. Bài học áp dụng cho mọi snapshot block-level (kể cả backup database): phải quiesce (flush/freeze) trước khi chụp, nếu không snapshot chỉ "crash-consistent" — như vừa rút điện. Database thật dùng hook (vd fsfreeze, hoặc lệnh flush của DB) để snapshot application-consistent.

🧹 Dọn dẹp

kubectl delete pod restore2-pod src-pod --now
kubectl delete pvc restore2-pvc src-pvc            # -> EBS volumes tự xóa (Delete)
kubectl delete volumesnapshot snap1 snap2          # -> EBS snapshots tự xóa (Delete)

deletionPolicy: Delete trên VolumeSnapshotClass khiến xóa VolumeSnapshot là xóa luôn EBS snapshot trên AWS (kiểm chứng: describe-snapshots trống). Giữ lại snapshot-controller + CRD + VolumeSnapshotClass + EBS CSI cho các bài sau. Cụm còn CoreDNS + metrics-server + ebs-csi + snapshot-controller. Manifest ở github.com/nghiadaulau/kubernetes-from-scratch, thư mục 44-volumesnapshot.

Tổng kết

VolumeSnapshot sao lưu PVC ở tầng storage, mô hình song song PV/PVC: VolumeSnapshot (user xin, namespaced) ↔ VolumeSnapshotContent (ảnh thật ở backend, cluster-scoped), với VolumeSnapshotClass (driver+tham số) như StorageClass — đều là CRD phải cài riêng (snapshot-controller + sidecar csi-snapshotter có sẵn trong EBS CSI). Chụp: user tạo VolumeSnapshot → snapshot-controller tạo Content → csi-snapshotter gọi driver → AWS tạo EBS snapshotreadyToUse=true. Khôi phục: PVC mới với dataSource trỏ snapshot → ổ EBS mới từ snapshot. Bài học lớn: snapshot là block-level point-in-time — phải sync/quiesce trước khi chụp, kẻo data trong page cache không vào snapshot (lần đầu ta restore ra file 0 byte; sau sync thì đủ). deletionPolicy: Delete xóa snapshot là xóa luôn ở AWS.

Hết Part IX — ta đã đi từ volume gắn-pod tới lưu trữ bền động và sao lưu. Part X nâng cấp mạng: Bài 45 mở đầu với Cilium và eBPF (lý thuyết) — vì sao thay kube-proxy + bridge (Bài 12–14) bằng một CNI dựa eBPF, eBPF là gì, và Cilium làm gì khác ở tầng datapath, trước khi Bài 46 migrate cụm sang Cilium kube-proxy-less thật.