Salta al contenuto principale

07 - Multi-Tenancy in Kubernetes: Namespace, Resource Quota e HNC

Multi-Tenancy in Kubernetes: Namespace, Resource Quota e HNC Gestire un singolo cluster Kubernetes per un singolo team e relativamente semplice.

Cosa ne pensi di questo articolo?

07 - Multi-Tenancy in Kubernetes: Namespace, Resource Quota e HNC
Kubernetes AvanzatoFoto:Christina MorilloPexels

In Breve

Multi-Tenancy in Kubernetes: Namespace, Resource Quota e HNC Gestire un singolo cluster Kubernetes per un singolo team e relativamente semplice.

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, non admin o cluster-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.

Serie Correlate

Prova Questi Strumenti

Strumenti gratuiti correlati a questo argomento.

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti