Pod là thứ tạm thời; dữ liệu thì thường không. Bài này đo bốn cách gắn lưu trữ vào pod và cái nào sống sót qua việc pod bị xoá.

Thông lượng bốn loại volume, và cách PVC được cấp phát

Bốn loại, cùng phép ghi 300 MB

Hệ tệp Thời gian Thông lượng
emptyDir: {} ext4 562 ms 533 MB/giây
emptyDir: {medium: Memory} tmpfs 140 ms 2.142 MB/giây
hostPath tmpfs 119 ms 2.521 MB/giây
lớp ghi của container overlay 362 ms 828 MB/giây
PVC local-path ext4 ~230 ms ~900 MB/giây

emptyDir trong RAM nhanh gấp bốn lần emptyDir trên đĩa.

Nhưng có một cái bẫy: medium: Memory tính vào giới hạn bộ nhớ của pod. Ghi 2 GB vào đó với limits.memory: 1GiOOMKilled (phần 54) — và nguyên nhân sẽ rất khó lần ra, vì ứng dụng không hề cấp phát bộ nhớ nào.

Chú ý dòng cuối cùng của bảng trên: lớp ghi của container (overlay) nhanh hơn emptyDir trên đĩa. Đó là kết quả tôi không chờ đợi, và nó nhắc rằng "ghi vào container thì chậm" không phải luôn đúng.

Nhưng lớp ghi container vẫn là chỗ tệ nhất để ghi dữ liệu, vì lý do khác: nó không giới hạn được, không theo dõi được, và đầy đĩa node thì kubelet đuổi pod (phần 54).

Dữ liệu sống hay chết

Ghi một tệp, xoá pod, tạo lại pod:

emptyDir   ->  số tệp còn lại: 0            MẤT
PVC        ->  "du lieu quan trong"          CÒN

emptyDir gắn với vòng đời pod, không phải vòng đời container. Phân biệt này quan trọng:

  • Container khởi động lại (RESTARTS tăng) → dữ liệu còn.
  • Pod bị xoá và tạo lại → dữ liệu mất.

Nghĩa là emptyDir sống qua một lần OOMKilled nhưng không sống qua một lần triển khai.

Điều đó khiến nó hợp lý cho: thư mục tạm, bộ nhớ đệm dựng lại được, và chia sẻ tệp giữa các container trong cùng pod (phần 44).

PVC được cấp phát muộn

StorageClass: standard  rancher.io/local-path  WaitForFirstConsumer

ngay sau khi tạo PVC:   Pending
sau khi pod chạy:       Bound   pvc-f41aa46e-...   1Gi   RWO

PVC nằm ở Pending cho tới khi có pod dùng tới nó. Đây không phải lỗi — đó là volumeBindingMode: WaitForFirstConsumer.

Lý do: nếu cấp phát ngay, hệ thống lưu trữ phải chọn vùng hoặc node trước khi biết pod sẽ chạy ở đâu — và có thể chọn sai chỗ, khiến pod không bao giờ xếp được.

Chờ tới khi bộ lập lịch quyết định xong rồi mới cấp là đúng thứ tự.

Với volumeBindingMode: Immediate thì ngược lại: PVC Bound ngay, và bộ lập lịch buộc phải đặt pod ở nơi volume đã nằm.

PVC bị ghim vào node

nodeAffinity của PV:
  {"key":"kubernetes.io/hostname","operator":"In","values":["sc-worker"]}

Với local-path, PV là một thư mục trên đĩa của đúng một node. PV mang nodeAffinity trỏ vào node đó, và bộ lập lịch buộc phải đặt pod ở đấy.

Hệ quả: node chết là pod không chạy được ở đâu cả. Nó nằm Pending với node(s) had volume node affinity conflict cho tới khi node quay lại.

Đây chính là điều phần 42 đã nêu về kind. Lưu trữ mạng — NFS, đĩa đám mây, Ceph — không có ràng buộc này, và pod chuyển node tự do.

Ba chế độ truy cập

ReadWriteOnce (RWO)    một NODE gắn được, đọc và ghi
ReadOnlyMany  (ROX)    nhiều node, chỉ đọc
ReadWriteMany (RWX)    nhiều node, đọc và ghi

RWO là chế độ hay bị hiểu sai nhất. Nó nghĩa là một node, không phải một pod — nhiều pod trên cùng node gắn được cùng một PVC RWO.

Và phần lớn hệ thống lưu trữ khối (đĩa đám mây, iSCSI) chỉ hỗ trợ RWO. RWX cần hệ tệp mạng: NFS, CephFS, hoặc dịch vụ tệp của nhà cung cấp đám mây.

Đây là lý do Deployment nhiều bản dùng chung một PVC RWO sẽ kẹt: bản thứ hai xếp lên node khác không gắn được volume. Với ứng dụng có trạng thái, StatefulSet cho mỗi bản một PVC riêng — đó mới là mẫu đúng.

reclaimPolicy: điều gì xảy ra khi xoá PVC

Delete   xoá PVC là xoá luôn PV và dữ liệu       <- mặc định của phần lớn StorageClass
Retain   giữ PV lại, phải dọn tay

Delete là mặc định, và nó nghĩa là kubectl delete pvc xoá dữ liệu vĩnh viễn. Không có thùng rác, không có xác nhận.

Với dữ liệu quan trọng, tạo một StorageClass riêng với reclaimPolicy: Retain. Cái giá là PV mồ côi phải dọn tay — và đó là cái giá rẻ so với việc mất dữ liệu vì một lệnh gõ nhầm.

Bốn loại volume khác đáng biết

configMapsecret — phần 61 đã đo, gắn dưới dạng liên kết mềm và cập nhật nguyên tử.

downwardAPI — đưa thông tin về chính pod (tên, namespace, nhãn, giới hạn tài nguyên) vào tệp hoặc biến môi trường.

projected — gộp nhiều nguồn (ConfigMap, Secret, downwardAPI, token) vào một thư mục.

csi — mọi hệ thống lưu trữ hiện đại. Kubernetes không còn nhúng driver cho từng nhà cung cấp; tất cả đều qua giao diện CSI.

Thử ba mươi giây

Xem PVC nào của bạn bị ghim vào node:

kubectl get pv -o custom-columns=\
TEN:.metadata.name,PVC:.spec.claimRef.name,\
NODE:.spec.nodeAffinity.required.nodeSelectorTerms[0].matchExpressions[0].values[0],\
CHINH_SACH:.spec.persistentVolumeReclaimPolicy

Cột NODE có giá trị nghĩa là pod dùng PVC đó chỉ chạy được trên node ấy. Cột CHINH_SACHDelete nghĩa là xoá PVC sẽ xoá luôn dữ liệu.

Phần sau đo StatefulSet: vì sao cơ sở dữ liệu cần nó thay vì Deployment.