Multi-Tenancy in Kubernetes: Namespace, Resource Quota e HNC
Gestire un singolo cluster Kubernetes per un singolo team e relativamente semplice. La sfida reale comincia quando devi condividere lo stesso cluster tra 10, 20, 50 team diversi, ciascuno con i propri requisiti di risorse, sicurezza e isolamento. Oppure quando offri Kubernetes-as-a-service ai tuoi clienti e devi garantire che il team A non possa vedere o interferire con i workload del team B, non possa consumare piu risorse del suo budget, e non possa bypassare le policy aziendali.
Kubernetes offre il namespace come unita di isolamento di base, ma da solo non e sufficiente per la multi-tenancy reale. In questo articolo vedremo come costruire un sistema di multi-tenancy completo: ResourceQuota per limitare le risorse per namespace, LimitRange per impostare default e massimi per container, NetworkPolicy per isolare il traffico, e Hierarchical Namespace Controller (HNC) per gestire strutture organizzative complesse con ereditarieta delle policy.
Cosa Imparerai
- Modelli di multi-tenancy: namespace-per-tenant vs cluster-per-tenant
- ResourceQuota: limiti di CPU, memoria, storage e numero di oggetti per namespace
- LimitRange: default e massimi per container, prevenzione di Pod senza limits
- NetworkPolicy per l'isolamento cross-namespace
- RBAC per tenants: ogni team vede solo il proprio namespace
- HNC (Hierarchical Namespace Controller): ereditarieta di policy, quota e RBAC
- Vcluster: virtual cluster completi per isolamento forte
- Pattern per onboarding automatizzato dei tenant
Modelli di Multi-Tenancy
Prima di implementare, bisogna scegliere il modello giusto in base al livello di isolamento richiesto:
| Modello | Isolamento | Overhead Operativo | Costo | Use Case |
|---|---|---|---|---|
| Namespace-per-team | Logico (RBAC, Quota) | Basso | Minimo | Team interni con fiducia reciproca |
| Namespace-per-cliente (soft multi-tenancy) | Logico + Network | Medio | Basso | SaaS con isolamento base |
| Virtual Cluster (vcluster) | Forte (API server separato) | Alto | Medio | Clienti enterprise, CI/CD isolation |
| Cluster separato per tenant | Completo | Molto Alto | Alto | Regolamentazione, dati sensibili |
ResourceQuota: Limiti per Namespace
La ResourceQuota definisce i limiti aggregati per tutte le risorse in un namespace. Quando un namespace ha una quota, ogni richiesta di creazione di risorse viene verificata contro la quota disponibile.
ResourceQuota Completa per un Team
# resource-quota-team-alpha.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-alpha-quota
namespace: team-alpha
spec:
hard:
# Risorse compute
requests.cpu: "20" # max 20 vCPU richieste nel namespace
limits.cpu: "40" # max 40 vCPU limits
requests.memory: "40Gi" # max 40 GB RAM richiesta
limits.memory: "80Gi" # max 80 GB RAM limits
# Storage
requests.storage: "500Gi" # max 500 GB storage totale
persistentvolumeclaims: "20" # max 20 PVC
# Per StorageClass specifica
standard.storageclass.storage.k8s.io/requests.storage: "200Gi"
ssd.storageclass.storage.k8s.io/requests.storage: "100Gi"
# Oggetti Kubernetes
pods: "100" # max 100 Pod
services: "20"
secrets: "50"
configmaps: "50"
replicationcontrollers: "20"
services.nodeports: "0" # nessun NodePort (usiamo Ingress)
services.loadbalancers: "2" # max 2 LoadBalancer
# GPU (se applicabile)
requests.nvidia.com/gpu: "4" # max 4 GPU
# Verifica utilizzo quota
kubectl describe resourcequota team-alpha-quota -n team-alpha
# Output:
# Name: team-alpha-quota
# Resource Used Hard
# -------- --- ---
# limits.cpu 8500m 40
# limits.memory 12Gi 80Gi
# pods 35 100
# requests.cpu 4200m 20
ResourceQuota per Classe di Servizio
# resource-quota-by-priority.yaml
# Separa le quote per classe di priorita
# Usa PriorityClass per fare QoS
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000
globalDefault: false
description: "Workload critici, non soggetti a eviction"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: low-priority
value: 100
globalDefault: true
description: "Workload batch, possono essere evicted"
---
# Quota separata per workload ad alta priorita
apiVersion: v1
kind: ResourceQuota
metadata:
name: high-priority-quota
namespace: team-alpha
spec:
hard:
pods: "10"
requests.cpu: "8"
requests.memory: "16Gi"
scopeSelector:
matchExpressions:
- scopeName: PriorityClass
operator: In
values: ["high-priority"]
LimitRange: Default e Massimi per Container
La ResourceQuota opera a livello di namespace, ma se un Pod non specifica resources.requests, la quota non puo calcolare l'utilizzo. Il LimitRange risolve questo: imposta request e limit default per ogni container, e definisce valori minimi e massimi consentiti.
# limitrange-team-alpha.yaml
apiVersion: v1
kind: LimitRange
metadata:
name: team-alpha-limits
namespace: team-alpha
spec:
limits:
# Valori di default per container (applicati se non specificati)
- type: Container
default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
# Massimi e minimi consentiti
max:
cpu: "4"
memory: "8Gi"
min:
cpu: "10m"
memory: "32Mi"
# Ratio max/request per prevenire burst eccessivi
maxLimitRequestRatio:
cpu: "10" # limit max 10x il request
memory: "4" # limit max 4x il request
# Per i Pod (somma di tutti i container)
- type: Pod
max:
cpu: "8"
memory: "16Gi"
# Per i PVC
- type: PersistentVolumeClaim
max:
storage: "50Gi"
min:
storage: "1Gi"
RBAC per Multi-Tenancy
Ogni tenant deve vedere e modificare solo il proprio namespace. Creiamo un pattern riproducibile di RBAC che puo essere automatizzato per ogni nuovo team:
# onboarding-team-alpha.yaml
# Script di onboarding: crea namespace + quota + limitrange + RBAC
apiVersion: v1
kind: Namespace
metadata:
name: team-alpha
labels:
team: alpha
env: production
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
---
# Gruppo di utenti: tutti i developer del team alpha
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: team-alpha-developers
namespace: team-alpha
subjects:
- kind: Group
name: "team-alpha"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: edit # ClusterRole built-in: edit permette tutto tranne RBAC e quota
apiGroup: rbac.authorization.k8s.io
---
# Tech Lead: puo anche gestire i quota (ma non cluster-admin)
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: team-alpha-lead
namespace: team-alpha
subjects:
- kind: User
name: "alice@company.com"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: admin # ClusterRole built-in: admin = edit + gestione RBAC nel namespace
apiGroup: rbac.authorization.k8s.io
# Verifica che il team non possa accedere ad altri namespace
kubectl auth can-i get pods --as-group=team-alpha --as=developer -n team-beta
# No
kubectl auth can-i get pods --as-group=team-alpha --as=developer -n team-alpha
# Yes
NetworkPolicy per Isolamento Cross-Namespace
# networkpolicy-tenant-isolation.yaml
# Isola completamente il namespace del tenant
# Permette solo traffico interno al namespace + DNS + monitoring
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: tenant-isolation
namespace: team-alpha
spec:
podSelector: {} # tutti i Pod nel namespace
policyTypes:
- Ingress
- Egress
ingress:
# Traffico solo da Pod nello stesso namespace
- from:
- podSelector: {}
# Permetti dall'ingress controller (namespace ingress-nginx)
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
egress:
# Traffico solo verso Pod nello stesso namespace
- to:
- podSelector: {}
# DNS
- ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
# Monitoring: permetti scrape da namespace monitoring
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
# Accesso a servizi comuni (es. database condiviso, internal APIs)
- to:
- namespaceSelector:
matchLabels:
shared-service: "true"
ports:
- protocol: TCP
port: 5432 # PostgreSQL condiviso
- protocol: TCP
port: 6379 # Redis condiviso
Hierarchical Namespace Controller (HNC)
HNC permette di creare gerarchie di namespace con ereditarieta delle risorse. E ideale per strutture organizzative complesse: un namespace "team-alpha" con sotto-namespace "team-alpha-dev", "team-alpha-staging", "team-alpha-prod" che ereditano automaticamente RBAC, NetworkPolicy, ResourceQuota e LimitRange dal parent.
Installazione HNC
# Installa HNC con kubectl
kubectl apply -f https://github.com/kubernetes-sigs/hierarchical-namespaces/releases/download/v1.1.0/default.yaml
# Oppure con Helm
helm repo add hnc https://kubernetes-sigs.github.io/hierarchical-namespaces/
helm install hnc hnc/hnc \
--namespace hnc-system \
--create-namespace \
--version 1.1.0
# Installa il plugin kubectl per HNC
curl -L https://github.com/kubernetes-sigs/hierarchical-namespaces/releases/download/v1.1.0/kubectl-hns_linux_amd64 \
-o /usr/local/bin/kubectl-hns
chmod +x /usr/local/bin/kubectl-hns
# Verifica
kubectl hns version
Gerarchia di Namespace per un Team
# Crea la gerarchia: team-alpha e il namespace root
# team-alpha-dev e team-alpha-prod sono subnamespace
# Crea il namespace root
kubectl create namespace team-alpha
# Crea i subnamespace (HNC li gestisce)
kubectl hns create team-alpha-dev -n team-alpha
kubectl hns create team-alpha-staging -n team-alpha
kubectl hns create team-alpha-prod -n team-alpha
# Visualizza la gerarchia
kubectl hns tree team-alpha
# Output:
# team-alpha
# ├── team-alpha-dev
# ├── team-alpha-staging
# └── team-alpha-prod
# Configura cosa viene propagato dai parent ai children
kubectl hns config set-resource networkpolicies --mode Propagate
kubectl hns config set-resource rolebindings --mode Propagate
kubectl hns config set-resource limitranges --mode Propagate
# ResourceQuota NON viene propagata (ogni sub-namespace ha la sua)
Propagazione Automatica di Risorse
# Nel namespace parent team-alpha:
# Un RoleBinding qui viene propagato automaticamente a tutti i subnamespace
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: team-alpha-developers
namespace: team-alpha # propagato automaticamente a tutti i children
annotations:
propagate.hnc.x-k8s.io/select: "true"
subjects:
- kind: Group
name: "team-alpha-devs"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: view # view in tutti i subnamespace
apiGroup: rbac.authorization.k8s.io
# RoleBinding specifico per prod: solo il tech lead
# Non si propaga ai children perche e in team-alpha-prod
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: prod-admin
namespace: team-alpha-prod
subjects:
- kind: User
name: "alice@company.com"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: edit
apiGroup: rbac.authorization.k8s.io
# Verifica propagazione
kubectl get rolebindings -n team-alpha-dev | grep team-alpha-developers
# Il RoleBinding e apparso nel subnamespace per propagazione
Virtual Cluster con vcluster
Per isolamento piu forte, vcluster crea cluster Kubernetes completi (con il proprio API server, scheduler e controller manager) che girano all'interno di un namespace del cluster host. Il tenant ha accesso completo al proprio vcluster ma non vede il cluster host.
# Installa vcluster CLI
curl -L -o vcluster "https://github.com/loft-sh/vcluster/releases/latest/download/vcluster-linux-amd64"
chmod +x vcluster
sudo mv vcluster /usr/local/bin
# Crea un virtual cluster per il team-beta
vcluster create team-beta-cluster \
--namespace vcluster-team-beta \
--connect=false \
--helm-values vcluster-values.yaml
# vcluster-values.yaml
# vcluster:
# image: ghcr.io/loft-sh/vcluster-k8s:1.30
# sync:
# nodes:
# enabled: true
# syncAllNodes: false # sync solo i nodi dove girano i Pod del vcluster
# resources:
# limits:
# cpu: "2"
# memory: "2Gi"
# Connettiti al vcluster
vcluster connect team-beta-cluster --namespace vcluster-team-beta -- kubectl get nodes
Automazione Onboarding dei Tenant
In cluster con molti team, l'onboarding manuale non scala. Un pattern comune e usare
un Operator dedicato (o un semplice script/GitOps) che crea automaticamente tutte le
risorse necessarie per un nuovo tenant a partire da un CRD Tenant:
# tenant-crd.yaml - esempio con Capsule (operator per multi-tenancy)
# Capsule e un operator CNCF che automatizza la creazione di tenant
helm repo add clastix https://clastix.github.io/charts
helm install capsule clastix/capsule \
--namespace capsule-system \
--create-namespace
---
# tenant.yaml - definisci un tenant con Capsule
apiVersion: capsule.clastix.io/v1beta2
kind: Tenant
metadata:
name: team-alpha
spec:
owners:
- name: alice@company.com
kind: User
- name: team-alpha-admins
kind: Group
namespaceOptions:
quota: 10 # max 10 namespace per questo tenant
forbiddenLabels:
denied: ["environment=production"] # il tenant non puo creare ns con certi label
resourceQuotas:
scope: Tenant # quota aggregata su tutti i namespace del tenant
items:
- hard:
requests.cpu: "50"
requests.memory: "100Gi"
requests.storage: "1Ti"
limitRanges:
items:
- limits:
- type: Container
default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
networkPolicies:
items:
- podSelector: {}
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector: {}
nodeSelector:
kubernetes.io/os: linux
# Possibile restringere il tenant a nodi specifici:
# tenant: team-alpha
Monitoring delle Quote
# Vedi utilizzo quota di tutti i namespace
kubectl get resourcequota -A -o custom-columns=\
"NAMESPACE:.metadata.namespace,NAME:.metadata.name,\
CPU-REQ:.status.used.requests\.cpu,CPU-LIM:.status.used.limits\.cpu,\
MEM-REQ:.status.used.requests\.memory"
# Alert Prometheus per quota vicina al limite
# Aggiungi questa regola PrometheusRule:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: namespace-quota-alerts
namespace: monitoring
spec:
groups:
- name: quota
rules:
- alert: NamespaceCPUQuotaExceeding80Percent
expr: |
kube_resourcequota{resource="requests.cpu",type="used"} /
kube_resourcequota{resource="requests.cpu",type="hard"} > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "Namespace {{ $labels.namespace }} al {{ $value | humanizePercentage }} della quota CPU"
- alert: NamespaceMemoryQuotaExceeding90Percent
expr: |
kube_resourcequota{resource="requests.memory",type="used"} /
kube_resourcequota{resource="requests.memory",type="hard"} > 0.9
for: 5m
labels:
severity: critical
annotations:
summary: "Namespace {{ $labels.namespace }} al 90% della quota memoria"
Best Practices per la Multi-Tenancy
Checklist Multi-Tenancy di Produzione
- Un namespace per ambiente per team: team-alpha-dev, team-alpha-staging, team-alpha-prod; evita di mescolare ambienti nello stesso namespace
- ResourceQuota sempre definita: senza quota, un team puo consumare tutte le risorse del cluster e impattare gli altri tenant
- LimitRange per i default: previene Pod senza resource request che rendono inutili le ResourceQuota e il scheduling del cluster
- NetworkPolicy default-deny: ogni namespace deve avere una policy che blocca tutto il traffico cross-namespace non autorizzato
- Pod Security Standards restricted: applica il livello restricted a tutti i namespace dei tenant (vedi articolo 6)
- RBAC minimo privilegio: i developer usano ClusterRole
edit, nonadminocluster-admin - Audit degli accessi: abilita l'audit log per tracciare chi accede a cosa in ogni namespace
- Automatizza l'onboarding: usa Capsule, un Operator custom, o un manifest GitOps per creare tutti gli oggetti del tenant in modo consistente
Trappole della Multi-Tenancy Kubernetes
- Namespace non e isolamento completo: alcuni oggetti cluster-scoped (ClusterRole, PersistentVolume, Node) sono visibili a tutti i tenant; usa vcluster se hai bisogno di isolamento completo
- Kernel vulnerabilities condivise: tutti i tenant condividono lo stesso kernel Linux; un container che sfrutta una vulnerabilita kernel puo impattare altri tenant; valuta nodi dedicati per tenant con dati sensibili
- DNS leakage: per default, i Pod possono risolvere i nomi di Service in altri namespace (
service.namespace.svc.cluster.local); usa NetworkPolicy per bloccare anche il traffico DNS cross-namespace se necessario - ResourceQuota senza LimitRange: se non c'e LimitRange, i Pod senza resource requests ignorano la ResourceQuota e la quota non viene scalata correttamente
Conclusioni e Prossimi Passi
La multi-tenancy in Kubernetes e un continuum tra semplicita operativa e isolamento forte. Per la maggior parte dei casi aziendali con team interni, namespace con ResourceQuota, LimitRange, NetworkPolicy e RBAC appropriati forniscono un isolamento sufficiente con overhead operativo minimo. Per clienti enterprise o scenari con requisiti di compliance stringenti, vcluster offre un livello di isolamento molto piu forte mantenendo i vantaggi di un cluster condiviso in termini di efficienza delle risorse.
HNC trasforma la gestione di gerarchie di namespace in qualcosa di scalabile: invece di dover replicare manualmente RBAC e NetworkPolicy in ogni sotto-namespace, si definiscono le policy una volta nel namespace parent e si propagano automaticamente. Per piattaforme con decine di team, questo fa la differenza tra un sistema manutenibile e uno che richiede un team dedicato solo per gestire la configurazione dei namespace.
Prossimi Articoli della Serie Kubernetes at Scale
Articoli Precedenti
Serie Correlate
- Sicurezza Kubernetes - RBAC e Pod Security Standards, prerequisiti della multi-tenancy
- Platform Engineering - multi-tenancy come fondamento degli Internal Developer Platform
- FinOps per Kubernetes - gestione dei costi per namespace e tenant

Commenti
Caricamento commenti...
Accedi per commentare