Kubernetes Administration — Level by Level›29 · Multi-tenancy models

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.

Architect
Key wordstenancynamespace-as-a-serviceNetworkPolicyPod Security AdmissionResourceQuotavirtual clusterscluster per tenant

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

  1. Create namespaces team-a and team-b, each with the RoleBinding, ResourceQuota, LimitRange and restricted Pod Security label.
  2. Verify RBAC: kubectl auth can-i create deployments -n team-b --as=dev --as-group=team-a → no.
  3. Try to run a privileged pod in team-a and read the rejection.
  4. If your cluster's CNI enforces NetworkPolicy (for kind, create the cluster with networking.disableDefaultCNI: true and install Calico), apply the two policies to team-b, then test with wget from a team-a pod to a team-b Service: 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.