Lesson 29 of 32 · Level 5 — Architect
Multi-tenancy models
Share clusters safely between teams or customers. Compare namespace-based tenancy, virtual clusters and dedicated clusters, and build a namespace 'tenant kit' from RBAC, quotas, network policy and Pod Security.
What "tenant" means here
A tenant is anyone who should be isolated from others: a team, a product, an environment, or an external customer. The questions are always the same: can they see each other's data? Break each other's workloads? Steal each other's capacity?
Sharing a cluster is like sharing a house. Namespaces are bedrooms: your own space, but the walls are thin and you share the kitchen. Virtual clusters are flats in one building: your own front door and rules, but one roof and one boiler. Separate clusters are separate houses: the most privacy, and the most bills.
Three models
| Namespace per tenant | Virtual cluster per tenant | Cluster per tenant | |
|---|---|---|---|
| Isolation | Soft (API + policy) | Medium (own control plane) | Strong |
| Tenant can install CRDs / cluster-wide things | ❌ | ✅ (inside the vcluster) | ✅ |
| Cost per tenant | Lowest | Low | Highest |
| Ops overhead | Low | Medium | High (fleet tooling needed) |
| Typical use | Internal teams you trust | Dev environments, CI, teams needing autonomy | Customers, regulated workloads |
Most organisations mix them: namespaces for internal teams, dedicated clusters for regulated or external tenants.
The namespace "tenant kit"
A namespace on its own isolates almost nothing. Tenancy comes from the kit you add to every tenant namespace.
1. Access: RBAC (lesson 15). The team gets edit in its namespace, and nothing elsewhere.
2. Fair share: ResourceQuota + LimitRange (lesson 23).
3. Network isolation: NetworkPolicy. Deny all incoming traffic by default, then allow traffic within the namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: team-a
spec:
podSelector: {} # all pods in the namespace
policyTypes: ["Ingress"]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace
namespace: team-a
spec:
podSelector: {}
ingress:
- from:
- podSelector: {} # any pod in THIS namespace
NetworkPolicies only work if your CNI enforces them (Calico and Cilium do; check yours).
4. Workload safety: Pod Security Admission. One label rejects privileged or root pods:
$ kubectl label namespace team-a pod-security.kubernetes.io/enforce=restricted
$ kubectl -n team-a run bad --image=nginx:1.27 --privileged
Error from server (Forbidden): pods "bad" is forbidden: violates PodSecurity "restricted:latest": privileged ...
5. Optional: dedicated nodes. Taint a node pool and give the tenant's pods tolerations plus node affinity (lesson 14), for noisy or sensitive workloads.
Package the kit as a Helm chart, a Kustomize base or a GitOps template, so every new tenant namespace is identical.
Virtual clusters
Tools such as vcluster run a lightweight Kubernetes control plane inside a namespace of the host cluster. The tenant talks to their own API server, can create namespaces and CRDs and has admin-like freedom, while their pods are actually synced into the host namespace and run on shared nodes. Good for dev environments, CI and platform self-service, where giving each team a real cluster would be too costly.
Try it: build and test a tenant kit
- Create namespaces
team-aandteam-b, each with the RoleBinding, ResourceQuota, LimitRange andrestrictedPod Security label. - Verify RBAC:
kubectl auth can-i create deployments -n team-b --as=dev --as-group=team-a→ no. - Try to run a privileged pod in
team-aand read the rejection. - If your cluster's CNI enforces NetworkPolicy (for kind, create the cluster with
networking.disableDefaultCNI: trueand install Calico), apply the two policies toteam-b, then test withwgetfrom ateam-apod to ateam-bService: it should time out.
Going deeper: tenancy trade-offs
- Shared cluster-scoped objects are the soft spot: CRDs, admission webhooks, StorageClasses, IngressClasses. Decide who owns them, and never let a tenant install cluster-wide webhooks.
- Noisy neighbours aren't only CPU: API request volume (APF, lesson 26), image pulls and log volume matter too.
- External customers running untrusted code need more than namespaces: sandboxed runtimes (gVisor, Kata Containers), dedicated nodes or dedicated clusters.
- Write the tenancy model down as a platform contract: what tenants get, what they can't do, and how to ask for exceptions.
Recap
- Tenancy spectrum: namespaces (soft) → virtual clusters (medium) → dedicated clusters (strong).
- A namespace isolates only with a kit: RBAC, quota and limits, NetworkPolicy (with an enforcing CNI), Pod Security Admission, and optionally dedicated nodes.
- Watch cluster-scoped shared objects, and use sandboxing or separate clusters for untrusted tenants.
This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.