Skip to content

Volume Types & Storage Taxonomy — Bản Đồ Toàn Cảnh

Vì sao cần một bản đồ trước khi chạm vào cấu hình

Storage trên GKE là một không gian lựa chọn rộng đến mức gây tê liệt: emptyDir, configMap, secret, projected, hostPath, PVC ở tầng Kubernetes; Persistent Disk, Hyperdisk, Local SSD, Filestore, Parallelstore, Managed Lustre, Cloud Storage FUSE ở tầng GCP. Một kỹ sư mới thường chọn theo "cái nào đang chạy được" thay vì "cái nào đúng" — và hệ quả của một lựa chọn sai chỉ lộ ra sau nhiều tháng, khi node bị recreate làm mất dữ liệu, khi Pod thứ hai cần ghi mà block device không cho, hoặc khi hóa đơn Filestore Enterprise gấp mười lần mức cần.

File này dựng một bản đồ phân loại để mọi quyết định storage về sau đều bắt đầu từ ba câu hỏi gốc: (1) dữ liệu này ephemeral hay persistent — mất khi Pod/node chết có chấp nhận được không? (2) cần bao nhiêu writer/reader đồng thời và ở mức node hay pod? (3) bản chất I/O là block, file hay object? Trả lời được ba câu này thì không gian lựa chọn thu hẹp từ hơn mười xuống thường chỉ còn một hoặc hai. Theo tài liệu Google Cloud, GKE phân loại storage thành các nhóm ephemeral, block, file và object — và mỗi nhóm phục vụ một access pattern khác nhau (GKE storage overview).

Trục phân loại thứ nhất: ephemeral vs persistent

Đây là trục quan trọng nhất vì nó quyết định dữ liệu có sống sót qua sự cố hay không — và sai ở đây là nguyên nhân của mọi vụ mất dữ liệu im lặng.

Volume ephemeral: sống và chết cùng Pod

Trong Kubernetes, một nhóm volume có vòng đời gắn chặt với Pod: khi Pod bị xóa, volume biến mất cùng. Chúng không phải PV/PVC, không qua CSI, và không tồn tại độc lập.

  • emptyDir: một thư mục rỗng được tạo khi Pod được gán vào node, tồn tại suốt đời Pod trên node đó. Mặc định nằm trên disk của node (boot disk hoặc ephemeral storage); có thể đặt emptyDir.medium: Memory để dùng tmpfs (RAM), nhanh hơn nhưng tính vào memory limit của Pod. Dùng cho scratch space, cache tạm, vùng chia sẻ giữa các container trong cùng Pod. Mất khi Pod bị xóa hoặc node recreate — không bao giờ dùng cho dữ liệu cần bền.

  • configMapsecret: inject dữ liệu cấu hình và bí mật vào Pod dưới dạng file (hoặc env var). Nội dung do API server cấp từ object ConfigMap/Secret, mount read-only vào container. Đây là volume "ảo" — không có disk thật bên dưới; chúng là cách Kubernetes đưa cấu hình vào filesystem của container một cách khai báo. secret mặc định lưu trong tmpfs trên node để không chạm disk.

  • projected: gộp nhiều nguồn (configMap, secret, downwardAPI, và quan trọng nhất là serviceAccountToken) vào một thư mục duy nhất. Trên GKE, projected serviceAccountToken là cơ chế cốt lõi của Workload Identity — token OIDC ngắn hạn được project vào Pod qua volume này (xem Chương 13). Token có expirationSecondsaudience, được kubelet tự refresh.

  • downwardAPI: expose metadata của Pod (tên, namespace, labels, annotations, resource limits) vào filesystem dưới dạng file. Dùng khi ứng dụng cần tự biết thông tin về chính nó mà không gọi API server.

  • hostPath: mount một đường dẫn từ filesystem của node vào Pod. Đây là một anti-pattern bảo mật nghiêm trọng trong hầu hết trường hợp: nó phá vỡ ranh giới isolation giữa Pod và node, cho phép Pod đọc/ghi filesystem host. GKE Autopilot chặn hostPath cho workload thường; PodSecurity profile restricted cũng cấm. Chỉ dùng cho system DaemonSet đặc thù (ví dụ agent thu thập log/metric cần đọc /var/log), và phải hiểu rõ rủi ro.

Có một họ riêng gọi là generic ephemeral volumes: dùng cú pháp ephemeral.volumeClaimTemplate ngay trong Pod spec để tạo một PVC có vòng đời gắn với Pod — kết hợp được sự tiện lợi của ephemeral với việc dùng CSI driver thật (ví dụ tạo một PD tạm rồi tự xóa khi Pod chết). Hữu ích cho scratch space lớn cần một block device thật thay vì emptyDir trên boot disk.

Volume persistent: tồn tại độc lập với Pod

Ngược lại, PersistentVolume (PV) là tài nguyên tồn tại độc lập với bất kỳ Pod nào. Pod yêu cầu nó qua PersistentVolumeClaim (PVC). Khi Pod chết, PV vẫn còn; Pod mới có thể bind lại PVC cũ và thấy nguyên dữ liệu. Đây là cơ chế duy nhất đảm bảo độ bền dữ liệu qua sự cố Pod/node. Toàn bộ block storage (PD, Hyperdisk), file storage (Filestore, Parallelstore) và object (GCS FUSE) đều được tiêu thụ qua PV/PVC. File 2 đào sâu vòng đời này.

Quy luật vận hành rút ra: nếu mất dữ liệu là không chấp nhận được, dữ liệu đó phải nằm trên PV/PVC, không phải emptyDir/Local SSD/hostPath. Đây là lằn ranh sống còn.

Trục phân loại thứ hai: Block vs File vs Object

Đây là trục về bản chất vật lý của I/O, và nó quyết định access mode khả thi, hiệu năng, và ngữ nghĩa nhất quán.

Block storage — Persistent Disk & Hyperdisk

Block storage trình bày một thiết bị khối thô (raw block device) cho node, như thể một ổ đĩa được cắm vào máy. Node format nó bằng một filesystem (mặc định ext4, hoặc xfs/NTFS) rồi mount. Vì filesystem nằm trên node và không phải cluster-aware, một block device chỉ có thể được mount read-write bởi một node duy nhất tại một thời điểm — đây là gốc rễ của giới hạn ReadWriteOnce.

GKE cung cấp block storage qua hai họ: Persistent Disk (pd-standard, pd-balanced, pd-ssd, pd-extreme) và Hyperdisk (thế hệ mới, tách hiệu năng khỏi dung lượng). Cả hai dùng CSI driver pd.csi.storage.gke.io. Block storage là lựa chọn cho database, message queue, bất kỳ workload single-writer cần IOPS ổn định và latency thấp. File 3 và 4 đào sâu.

File storage — Filestore, Parallelstore, Managed Lustre

File storage trình bày một filesystem chia sẻ qua mạng (network filesystem), điển hình là NFS. Khác biệt cốt lõi với block: nhiều node có thể mount cùng lúc và ghi đồng thời, vì một server filesystem tập trung (NFS server của Filestore) làm trọng tài cho các thao tác — nó enforce consistency, locking, và metadata. Đây là lý do file storage hỗ trợ ReadWriteMany thật sự.

  • Filestore: NFS được quản lý, cho RWX truyền thống — nhiều Pod đọc/ghi shared filesystem (file 6).
  • Parallelstore: filesystem song song nền DAOS cho AI/ML throughput cực cao (file 8).
  • Managed Lustre: Lustre được quản lý cho HPC/AI (file 8).

Giá phải trả: latency cao hơn block (qua network stack), và đặc tính hiệu năng phụ thuộc nặng vào tier và pattern truy cập (nhiều file nhỏ vs ít file lớn).

Object storage — Cloud Storage FUSE

Object storage (GCS) về bản chất không phải filesystem — nó là kho key-value của các object bất biến, truy cập qua HTTP API. Cloud Storage FUSE dùng cơ chế FUSE (Filesystem in Userspace) để giả lập một filesystem POSIX trên GCS, cho phép ứng dụng dùng open/read/write/close thay vì gọi API (Cloud Storage FUSE CSI driver).

Đây là một abstraction "rò rỉ" (leaky): nó trông như filesystem nhưng hành xử như object store. Các thao tác filesystem rẻ (rename thư mục, append, random write) trở nên cực đắt vì phải rewrite cả object. Ngược lại, nó cho dung lượng gần như vô hạn, chi phí thấp, và truy cập được từ bất kỳ đâu. Use case đúng: dữ liệu training read-heavy, model weight bất biến, dữ liệu unstructured cần portability (file 7).

Trục phân loại thứ ba: Access Modes — RWO, ROX, RWX, RWOP

Access mode khai báo bao nhiêu node/pod có thể mount một volume và ở chế độ nào. Đây là nơi nhầm lẫn gây hậu quả nghiêm trọng nhất, vì cùng một nhãn lại có cơ chế enforce khác nhau giữa các loại storage.

Access modeViết tắtNgữ nghĩaĐiển hình cho
ReadWriteOnceRWOMount read-write bởi một node (nhiều Pod trên cùng node đó vẫn dùng được)Block storage (PD, Hyperdisk)
ReadOnlyManyROXMount read-only bởi nhiều nodePD ở chế độ read, Hyperdisk ML
ReadWriteManyRWXMount read-write bởi nhiều node đồng thờiFile storage (Filestore, Parallelstore, GCS FUSE)
ReadWriteOncePodRWOPMount read-write bởi đúng một Pod trên toàn clusterWorkload cần độc quyền tuyệt đối

Theo tài liệu Kubernetes, điểm tinh tế chí mạng là ngữ nghĩa "node" vs "pod": ReadWriteOnce nói về node, không phải Pod. Một PVC RWO có thể được mount bởi nhiều Pod cùng lúc nếu chúng nằm trên cùng một node — đây là hành vi mặc định và đôi khi gây bất ngờ. Nếu bạn cần đảm bảo chỉ một Pod duy nhất ghi (ví dụ để tránh hai instance database cùng mở một data dir), phải dùng ReadWriteOncePod (RWOP), access mode được thêm ổn định từ Kubernetes 1.27 (Kubernetes Access Modes).

Vì sao "RWX trên block device" là một cái bẫy chết người

Một sai lầm phổ biến: muốn nhiều Pod ghi vào một PD, kỹ sư thử đặt access mode thành ReadWriteMany. Persistent Disk có một chế độ "multi-writer" ở tầng Compute Engine, nhưng nó không làm cho filesystem an toàn. Lý do: ext4/xfs là filesystem không cluster-aware — chúng giả định độc quyền truy cập block device. Khi hai node cùng ghi qua một block device, mỗi node có cache filesystem riêng không đồng bộ với node kia, dẫn đến corrupt metadata filesystem không thể phục hồi. Theo tài liệu GKE, ReadWriteMany không được hỗ trợ cho Persistent Disk trong dùng thông thường (GKE Persistent Volumes).

Quy luật: RWX thật sự (nhiều writer an toàn) chỉ đến từ file storage — Filestore, Parallelstore, Managed Lustre, hoặc GCS FUSE — nơi có một server tập trung làm trọng tài. Block storage cho ROX (nhiều reader, không writer) là an toàn, nhưng RWX thì không. Nhầm hai điều này là con đường ngắn nhất tới corrupt dữ liệu.

ReadWriteOncePod và bài toán split-brain

ReadWriteOncePod sinh ra để giải một vấn đề thực: trong sự cố như network partition, một Pod cũ có thể chưa thực sự chết trong khi controller đã tạo Pod mới mount cùng PVC — hai Pod cùng ghi, split-brain. RWO không ngăn được vì nó chỉ giới hạn ở mức node; nếu hai Pod ở hai node thì RWO mới chặn, nhưng nếu cùng node thì không. RWOP enforce ở mức Pod, đảm bảo toàn cluster chỉ đúng một Pod mount được — đây là access mode đúng cho các database single-writer nhạy cảm với split-brain.

Khung quyết định: chọn storage theo bản chất nhu cầu

Tổng hợp ba trục trên thành một quy trình quyết định thực dụng. Đi tuần tự từ trên xuống, dừng ở dòng đầu tiên khớp:

  1. Đây là cấu hình/secret inject vào Pod?configMap / secret / projected. Không cần PV.
  2. Đây là scratch space tạm, mất khi Pod chết là OK, cần tốc độ cao nhất?emptyDir (medium Memory nếu cần RAM-speed), hoặc Local SSD nếu cần dung lượng lớn và NVMe (file 5).
  3. Dữ liệu cần bền qua sự cố Pod/node? → bắt buộc PV/PVC. Đi tiếp.
  4. Cần nhiều Pod ghi đồng thời (RWX)?
    • Throughput cực cao, AI/ML, metadata IOPS lớn → Parallelstore / Managed Lustre (file 8).
    • Shared filesystem truyền thống, nhiều reader/writer → Filestore (file 6).
    • Dữ liệu bất biến / read-heavy từ object store → Cloud Storage FUSE (file 7).
  5. Single-writer (RWO), cần block device, IOPS/latency tốt?
    • Cần tách IOPS/throughput khỏi dung lượng, hoặc IOPS rất cao → Hyperdisk (file 4).
    • Workload phổ thông → Persistent Disk pd-balanced/pd-ssd (file 3).
    • Cần survive zonal failure → Regional PD / Hyperdisk Balanced HA (file 3).
  6. Đọc song song dataset từ rất nhiều node (ROX)? → Hyperdisk ML, hoặc PD read-only multi-attach, hoặc GCS FUSE (file 4, 7).

Real-world scenario: kiến trúc storage của một nền tảng AI/ML

Xét một nền tảng training mô hình điển hình để thấy nhiều loại storage cùng tồn tại đúng vai trò:

  • Dataset gốc (hàng TB, bất biến): nằm trên Cloud Storage. Đây là nguồn chân lý, bền nhất, rẻ nhất.
  • Mount dataset vào training Pod: Cloud Storage FUSE với file cache trên Local SSD — đọc lặp lại nhanh mà không tải lại từ GCS (file 7).
  • Scratch space trong lúc training (checkpoint tạm, shuffle buffer): Local SSD emptyDir — nhanh nhất, chấp nhận mất (file 5).
  • Shared filesystem cho nhiều worker đọc/ghi cùng lúc (distributed training): Parallelstore — throughput cao, metadata IOPS lớn (file 8).
  • Model weight cuối cùng cần nạp song song lên hàng trăm inference node: Hyperdisk ML ở ROX (file 4).
  • Checkpoint quan trọng cần bền: ghi định kỳ ngược về Cloud Storage.

Mỗi loại storage giải đúng một bài toán; không có "một storage cho tất cả". Đây là tư duy mà file này muốn cài đặt: storage là một danh mục công cụ, không phải một lựa chọn duy nhất.

Common mistakes / anti-patterns

  • Dùng emptyDir cho dữ liệu cần bền vì "đang chạy ổn". Nó chạy ổn cho đến lần node recreate đầu tiên. Hệ quả ở scale: mất dữ liệu hàng loạt khi một node pool upgrade. Phòng tránh: áp quy luật "bền thì phải PV/PVC" như một policy bất di bất dịch, enforce qua admission (Chương 10).

  • Đặt ReadWriteMany cho PD và tưởng đã có shared storage. Filesystem corrupt khi hai node cùng ghi. Hệ quả: hỏng dữ liệu im lặng, phát hiện muộn. Phòng tránh: RWX = file storage, không bao giờ block storage.

  • Lạm dụng hostPath. Phá isolation, mở đường escalation từ Pod ra node. Hệ quả ở scale: một Pod bị compromise có thể chiếm node. Phòng tránh: cấm qua PodSecurity restricted / Autopilot; chỉ dùng cho system DaemonSet đã review kỹ.

  • Nhầm ReadWriteOnce là "một Pod". Hai Pod cùng node vẫn mount được PVC RWO, gây ghi đè ngoài ý muốn. Phòng tránh: dùng ReadWriteOncePod khi cần độc quyền tuyệt đối ở mức Pod.

  • Chọn Filestore Enterprise cho mọi RWX vì "nó là tier cao nhất". Chi phí gấp nhiều lần mà workload không cần HA regional. Phòng tránh: chọn tier theo nhu cầu thật (file 6).

Official references