Multi-Tenancy Models — Soft vs Hard Tenancy
Tại sao mô hình tenancy là quyết định kiến trúc gốc
Trước khi cấu hình bất kỳ NetworkPolicy hay ResourceQuota nào, bạn phải trả lời một câu hỏi định hình toàn bộ phần còn lại: các tenant của tôi có tin tưởng lẫn nhau không? Câu trả lời quyết định bạn cần soft hay hard multi-tenancy, và sai lầm ở đây không thể sửa bằng cách thêm policy về sau — nó đòi hỏi tái kiến trúc.
Lý do là vì mỗi mô hình tenancy đặt ranh giới bảo mật ở một lớp khác nhau của stack, và lớp đó quyết định blast radius khi có sự cố. Một lỗ hổng container escape trong soft multi-tenancy có thể lan ra toàn bộ node và mọi tenant trên node đó. Cùng lỗ hổng đó trong hard multi-tenancy (cluster riêng) bị giới hạn trong một cluster duy nhất. Bạn không thể "thêm isolation" sau khi đã bị xâm phạm.
Định nghĩa chính xác: soft vs hard
Hai thuật ngữ này thường bị dùng mơ hồ. Đây là định nghĩa vận hành:
Soft multi-tenancy là khi các tenant chia sẻ cùng một control plane và (thường) cùng các node, được phân tách bằng các cơ chế logic của Kubernetes: namespace, RBAC, NetworkPolicy, ResourceQuota. Giả định nền tảng là các tenant không độc hại — họ có thể vô tình gây hại (bug, cấu hình sai, noisy neighbor) nhưng không chủ động cố gắng xâm phạm tenant khác.
Hard multi-tenancy là khi các tenant được phân tách bằng ranh giới mạnh hơn lớp logic: kernel riêng (GKE Sandbox/gVisor), node vật lý riêng (sole-tenant), hoặc cluster riêng (control plane riêng). Giả định là các tenant có thể độc hại — họ chạy code không đáng tin và có thể chủ động tìm cách thoát khỏi ranh giới isolation.
Điểm mấu chốt: ranh giới giữa soft và hard không phải là một dòng đơn lẻ mà là lớp mà ranh giới bảo mật được đặt:
| Lớp ranh giới | Loại | Cơ chế | Chống được container escape? |
|---|---|---|---|
| Namespace logic | Soft | RBAC, NetworkPolicy, Quota | Không |
| Kernel riêng | Hard (nhẹ) | GKE Sandbox (gVisor) | Có (giảm đáng kể) |
| Node vật lý riêng | Hard | Sole-tenant nodes | Có (giới hạn trong node) |
| Control plane riêng | Hard (mạnh nhất) | Cluster riêng | Có (giới hạn trong cluster) |
Internal model: tại sao namespace không phải ranh giới bảo mật
Đây là điểm gây hiểu lầm nhiều nhất. Trong Kubernetes, namespace là một construct tổ chức, không phải một sandbox bảo mật. Để hiểu tại sao, hãy xem những gì được chia sẻ bất kể namespace:
Control plane: Tất cả namespace dùng chung một API server, một etcd, một scheduler, một bộ controller. Một tenant làm quá tải API server (ví dụ list tất cả Pod mỗi giây) ảnh hưởng đến mọi tenant. Một CVE trong API server phơi bày dữ liệu của mọi tenant.
Kernel node: Mọi Pod trên cùng một node chia sẻ kernel Linux của host. Container isolation dựa trên namespaces (Linux), cgroups, và seccomp/AppArmor — nhưng tất cả đều chạy trên cùng một kernel. Một container escape (CVE trong runc, kernel, hoặc container runtime) phá vỡ ranh giới này.
Tài nguyên cluster-scoped: Theo tài liệu GKE, các tenant chia sẻ "extensions, controllers, add-ons, và custom resource definitions". CRD được định nghĩa ở cấp cluster — một tenant tạo CRD ảnh hưởng đến toàn cluster. CoreDNS, kube-proxy, CNI đều dùng chung.
Node metadata server: Mọi Pod (trừ khi bị chặn) có thể truy cập metadata server
169.254.169.254. Nếu không có Workload Identity và network policy chặn, một Pod có thể đánh cắp token của node service account.
Vì vậy khi bạn nói "tenant A trong namespace A bị cô lập khỏi tenant B trong namespace B", điều bạn thực sự muốn nói là: các đối tượng API của họ tách biệt, RBAC ngăn họ đọc tài nguyên của nhau, và NetworkPolicy ngăn lưu lượng giữa họ. Nhưng họ vẫn chia sẻ kernel và control plane. Đó là lý do namespace isolation đủ cho tenant tin cậy nhưng không đủ cho code độc hại.
Mô hình tin cậy: framework quyết định
Hãy phân loại tenant theo trục tin cậy, vì đây là yếu tố quyết định:
Tenant tin cậy (trusted) — internal platform
Đây là các đội kỹ sư trong cùng một tổ chức. Họ deploy code đã qua review, không cố ý tấn công nhau, và vận hành dưới cùng một chính sách bảo mật. Rủi ro chính là vô tình: noisy neighbor (một đội tiêu hết CPU), lỗi cấu hình, hoặc bug làm sập service chung.
→ Soft multi-tenancy là phù hợp. Namespace + RBAC + ResourceQuota + NetworkPolicy giải quyết các rủi ro vô tình. Mục tiêu chính là fairness và tổ chức, không phải phòng thủ chống tấn công chủ động.
Tenant bán tin cậy (semi-trusted)
Đây là các đội/đối tác có mức tin cậy thấp hơn, hoặc workload xử lý dữ liệu nhạy cảm (PCI scope, PII). Họ không chạy code tùy ý của người ngoài, nhưng hậu quả của việc rò rỉ giữa các tenant nghiêm trọng (compliance, dữ liệu khách hàng).
→ Soft multi-tenancy được củng cố thêm. Thêm restricted Pod Security Standards, default-deny NetworkPolicy nghiêm ngặt, và cân nhắc node pool riêng (taint/toleration) hoặc GKE Sandbox cho workload nhạy cảm.
Tenant không tin cậy (untrusted) — SaaS, code khách hàng
Đây là kịch bản khó nhất: bạn chạy code do khách hàng/người dùng cuối submit. CI/CD runners thực thi pipeline tùy ý, nền tảng serverless chạy function người dùng, môi trường code playground. Giả định bắt buộc: code này sẽ cố gắng thoát ra.
→ Hard multi-tenancy là bắt buộc. Tối thiểu là GKE Sandbox (gVisor) để cô lập kernel. Lý tưởng nhất, và theo nguyên tắc của tài liệu GKE, với code thực sự không tin cậy nên dùng cluster riêng hoặc ít nhất node pool riêng kết hợp sandbox.
Production architecture patterns
Pattern 1: Single cluster, namespace-per-team (internal platform)
GKE Cluster (1 control plane)
├── namespace: team-payments (RBAC, Quota, NetPol)
├── namespace: team-search (RBAC, Quota, NetPol)
├── namespace: team-recommendations
└── namespace: platform-shared (logging, monitoring, ingress)Đây là pattern phổ biến nhất cho internal platform. Một cluster phục vụ hàng chục đội. Chi phí control plane được khấu hao trên toàn bộ. Platform team quản lý cluster, mỗi đội quản lý namespace của mình qua RBAC. Hiệu quả chi phí cao, nhưng blast radius là toàn cluster nếu có CVE control plane.
Pattern 2: Cluster-per-environment, namespace-per-tenant
Cluster: prod-us-central1 → namespace per tenant
Cluster: staging-us-central1 → namespace per tenant
Cluster: dev-us-central1 → namespace per tenantTách environment thành cluster riêng (ranh giới mạnh giữa prod/staging/dev), nhưng tenant trong cùng environment dùng chung cluster. Cân bằng giữa isolation và chi phí. Một sự cố ở dev không thể lan sang prod.
Pattern 3: Cluster-per-tenant (hard isolation cho SaaS lớn)
Cluster: tenant-acme-corp (control plane riêng)
Cluster: tenant-globex (control plane riêng)
Cluster: tenant-initech (control plane riêng)Mỗi khách hàng lớn có cluster riêng. Đây là hard multi-tenancy mạnh nhất ở lớp cluster. Dùng cho khách hàng enterprise yêu cầu cách ly tuyệt đối, hoặc khi compliance đòi hỏi. Chi phí cao (control plane × số tenant) nhưng blast radius tối thiểu. GKE Fleet (Chương 18) quản lý hàng loạt cluster này.
Pattern 4: Hybrid — shared cluster + dedicated cho tenant lớn
Mô hình thực tế nhất cho SaaS đang scale: tenant nhỏ chia sẻ cluster (cost-efficient), tenant lớn/enterprise được nâng cấp lên cluster riêng (isolation + SLA). Đây là chiến lược "pod-based" hoặc "cell-based" — mỗi cell là một cluster phục vụ N tenant, thêm cell khi cần.
Real-world scenarios
Fintech — internal platform với PCI scope
Một công ty fintech có 30 đội kỹ sư nội bộ (tin cậy) nhưng một số workload nằm trong PCI-DSS scope. Giải pháp: single cluster soft multi-tenancy cho phần lớn đội, nhưng workload PCI được tách ra node pool riêng với taint và sole-tenant nodes để đáp ứng yêu cầu cách ly vật lý của PCI. Namespace pci-cardholder có restricted PSA, default-deny NetworkPolicy, và audit logging riêng. Đây là minh họa cho việc một cluster có thể chứa nhiều mức tenancy.
SaaS code-execution platform — untrusted
Một nền tảng cho phép người dùng chạy notebook Python tùy ý. Đây là untrusted tuyệt đối. Kiến trúc: mỗi phiên người dùng chạy trong Pod với GKE Sandbox (gVisor) bắt buộc, trên node pool dành riêng cho sandbox, với NetworkPolicy egress chặn metadata server và internal services, ResourceQuota chặt để chống fork bomb / crypto mining. Không có RBAC nào được cấp cho Pod người dùng. Đây là hard multi-tenancy ở lớp kernel.
Common mistakes / anti-patterns
Anti-pattern 1: Dùng namespace cho untrusted code
Vì sao xảy ra: Đội nghĩ "tôi đã có namespace riêng và NetworkPolicy, vậy đã cô lập". Họ nhầm ranh giới tổ chức với ranh giới bảo mật.
Hệ quả ở scale: Một container escape (ví dụ CVE-2019-5736 trong runc, hoặc lỗ hổng kernel) cho phép code untrusted thoát ra node và truy cập mọi Pod trên node đó, bao gồm secrets được mount. Trong một SaaS, điều này có nghĩa rò rỉ dữ liệu chéo giữa khách hàng — một sự cố compliance/PR thảm họa.
Cách phòng tránh: Với code untrusted, luôn dùng kernel isolation (GKE Sandbox) tối thiểu, và cluster riêng cho mức tin cậy thấp nhất.
Anti-pattern 2: Dựa vào taint/toleration như security control
Vì sao xảy ra: Đội dùng taint để "ghim" workload nhạy cảm lên node riêng và nghĩ rằng tenant khác không thể vào đó.
Hệ quả ở scale: Tài liệu GKE nêu rõ taint và anti-affinity "có thể bị bỏ qua bởi tenant độc hại". Một tenant có quyền tạo Pod chỉ cần thêm toleration tương ứng vào Pod spec của mình để được schedule lên node "riêng" đó. Taint là scheduling hint, không phải access control.
Cách phòng tránh: Để buộc workload chỉ chạy trên node riêng và ngăn tenant khác vào, kết hợp taint (cho workload của bạn) với một admission policy (Gatekeeper/Policy Controller) chặn các Pod của tenant khác đặt toleration đó. Hoặc dùng node pool trong cluster riêng.
Anti-pattern 3: One giant cluster cho mọi thứ
Vì sao xảy ra: Tối ưu hóa chi phí cực đoan — gộp prod, staging, mọi đội vào một cluster để tiết kiệm control plane.
Hệ quả ở scale: Blast radius khổng lồ. Một upgrade control plane lỗi, một CRD conflict, một quota exhaustion làm ảnh hưởng toàn bộ tổ chức cùng lúc. Không có ranh giới nào giữa prod và experiment. Bạn không thể test upgrade trên một cluster nhỏ trước.
Cách phòng tránh: Tách tối thiểu theo environment (prod/non-prod thành cluster riêng). Dùng Fleet để quản lý nhiều cluster mà không tăng toil tuyến tính.
Anti-pattern 4: Hard isolation khi không cần (over-engineering)
Vì sao xảy ra: Sợ hãi quá mức, tạo cluster riêng cho mỗi đội nội bộ tin cậy.
Hệ quả ở scale: Chi phí control plane nhân lên, operational toil tăng vọt (mỗi cluster cần upgrade, monitoring, networking riêng), tài nguyên bị phân mảnh (mỗi cluster cần buffer riêng, không thể bin-pack chéo). Một tổ chức 50 đội với 50 cluster đốt tiền và nhân lực vô ích nếu các đội đều tin cậy.
Cách phòng tránh: Khớp mức isolation với mức tin cậy thực tế. Internal trusted teams → soft multi-tenancy. Đừng trả phí hard isolation cho rủi ro không tồn tại.
Framework quyết định nhanh
Tenant có chạy code untrusted/tùy ý không?
├── CÓ → Hard multi-tenancy bắt buộc
│ ├── Cần cách ly tối đa / enterprise SLA → Cluster riêng
│ └── Cost-sensitive → GKE Sandbox + node pool riêng + cluster riêng cho tier cao nhất
│
└── KHÔNG (trusted internal teams)
├── Có yêu cầu compliance (PCI/HIPAA)?
│ ├── CÓ → Soft tenancy + sole-tenant nodes/node pool riêng cho workload in-scope
│ └── KHÔNG → Soft multi-tenancy (namespace + RBAC + Quota + NetPol)
└── Tách environment: prod/non-prod thành cluster riêng