Multi-Tenancy w Kubernetes: przestrzeń nazw, przydział zasobów i HNC
Zarządzanie pojedynczym klastrem Kubernetes dla jednego zespołu jest stosunkowo proste. Prawdziwe wyzwanie zaczyna się, gdy musisz dzielić ten sam klaster między 10, 20, 50 zespołów różne, każdy z własnymi wymaganiami dotyczącymi zasobów, bezpieczeństwa i izolacji. Lub gdy oferujesz swoim klientom usługę Kubernetes jako usługę i musisz mieć pewność, że platforma A nie może widzieć ani ingerować w obciążenia zespołu B, nie może już więcej zużywać zasobów swojego budżetu i nie może ominąć polityki firmy.
Kubernetes oferuje przestrzeń nazw jako podstawowy element izolacyjny, ale samo to nie wystarczy do rzeczywistej obsługi wielu najemców. W tym artykule zobaczymy Jak zbudować kompletny system dla wielu najemców: Przydział zasobów aby ograniczyć zasoby na przestrzeń nazw, LimitZakresu ustawić wartości domyślne i maksymalne na kontener, Polityka sieciowa izolować ruch, tj Hierarchiczny kontroler przestrzeni nazw (HNC) zarządzać obiektami złożone systemy organizacyjne z dziedziczeniem polityk.
Czego się nauczysz
- Modele z wieloma dzierżawcami: przestrzeń nazw na dzierżawcę vs klaster na dzierżawcę
- ResourceQuota: Limity procesora, pamięci, magazynu i liczby obiektów na przestrzeń nazw
- LimitRange: wartości domyślne i maksymalne dla kontenerów, zapobieganie Podom bez limitów
- NetworkPolicy do izolacji między przestrzeniami nazw
- Kontrola RBAC dla dzierżaw: każdy zespół widzi tylko własną przestrzeń nazw
- HNC (Hierarchical Namespace Controller): dziedziczenie zasad, kwot i RBAC
- Vcluster: kompletne klastry wirtualne zapewniające silną izolację
- Wzorzec automatycznego dołączania najemców
Modele z wieloma najemcami
Przed wdrożeniem musisz wybrać odpowiedni model w zależności od swojego poziomu wymagana izolacja:
| Model | Izolacja | Koszt operacyjny | Koszt | Przypadek użycia |
|---|---|---|---|---|
| Przestrzeń nazw dla zespołu | Logiczne (RBAC, udział) | Bas | Minimum | Zespoły wewnętrzne charakteryzujące się wzajemnym zaufaniem |
| Przestrzeń nazw na klienta (miękka obsługa wielu dzierżawców) | Logiczne + sieciowe | Średni | Bas | SaaS z podstawową izolacją |
| Klaster wirtualny (vcluster) | Silny (oddzielny interfejs API serwera) | Wysoki | Średni | Klienci korporacyjni, izolacja CI/CD |
| Oddzielny klaster dla każdej dzierżawy | Kompletny | Bardzo wysoki | Wysoki | Rozporządzenie, dane wrażliwe |
ResourceQuota: limity przestrzeni nazw
ResourceQuota definiuje zagregowane limity dla wszystkich zasobów w przestrzeni nazw. Gdy przestrzeń nazw ma limit, pojawia się każde żądanie utworzenia zasobu zweryfikowane w oparciu o dostępny limit.
Pełny limit zasobów dla zespołu
# 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 na klasę usług
# 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: wartości domyślne i maksymalne dla kontenerów
ResourceQuota działa na poziomie przestrzeni nazw, ale jeśli Pod nie określa zasobów.requests, przydziału nie można obliczyć wykorzystania. LimitRange rozwiązuje ten problem: ustawia żądanie i limit domyślny dla każdego kontenera i definiuje dozwolone wartości minimalne i maksymalne.
# 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 dla wielu najemców
Każdy dzierżawca musi tylko zobaczyć i zmienić swoją własną przestrzeń nazw. Stwórzmy wzór powtarzalne RBAC, które można zautomatyzować dla każdego nowego zespołu:
# 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
Zasady sieciowe dotyczące izolacji między przestrzeniami nazw
# 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
Hierarchiczny kontroler przestrzeni nazw (HNC)
HNC pozwala tworzyć hierarchie przestrzeni nazw z dziedzictwem zasoby. Idealny dla złożonych struktur organizacyjnych: przestrzeń nazw „zespołowa alfa”. z podprzestrzeniami nazw „team-alpha-dev”, „team-alpha-staging”, „team-alpha-prod”, które automatycznie dziedziczą RBAC, NetworkPolicy, ResourceQuota i LimitRange od elementu nadrzędnego.
Instalacja 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
Hierarchia przestrzeni nazw dla zespołu
# 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)
Automatyczne propagowanie zasobów
# 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
Wirtualny klaster z vclusterem
Aby uzyskać silniejszą izolację, vcluster tworzy pełne klastry Kubernetes (z własnymi klastrami serwer API, harmonogram i menedżer kontrolerów), które działają w przestrzeni nazw klastra hosta. Dzierżawca ma pełny dostęp do swojego klastra wirtualnego, ale go nie widzi klaster hostów.
# 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
Automatyzacja wdrażania najemców
W klastrach z wieloma zespołami ręczne wdrażanie nie jest skalowane. Typowy wzór i zastosowanie
dedykowany Operator (lub prosty skrypt/GitOps), który automatycznie tworzy wszystkie pliki
zasoby potrzebne nowemu najemcy począwszy od 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
Monitorowanie kursów
# 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"
Najlepsze praktyki dotyczące wielu najemców
Lista kontrolna dotycząca wielu najemców produkcyjnych
- Jedna przestrzeń nazw na środowisko zespołu: zespół-alfa-dev, zespół-alfa-staging, zespół-alfa-prod; unikaj mieszania środowisk w tej samej przestrzeni nazw
- ResourceQuota zawsze zdefiniowana: bez przydziału zespół może zużywać wszystkie zasoby klastra i wpływać na innych dzierżawców
- LimitRange dla wartości domyślnych: zapobiega podom bez żądań zasobów, czyniąc przydziały zasobów i planowanie klastrów bezużytecznymi
- Domyślna odmowa NetworkPolicy: każda przestrzeń nazw musi mieć politykę blokującą cały nieautoryzowany ruch między przestrzeniami nazw
- Ograniczone standardy bezpieczeństwa kapsuły: stosuje poziom ograniczony do wszystkich przestrzeni nazw dzierżawców (patrz artykuł 6)
- Najmniejsze uprawnienia RBAC: programiści używają ClusterRole
edit, Nieadminocluster-admin - Audyt dostępu: włącz dziennik audytu, aby śledzić, kto uzyskuje dostęp do czego w każdej przestrzeni nazw
- Zautomatyzuj wdrażanie: użyj Capsule, niestandardowego operatora lub manifestu GitOps, aby konsekwentnie tworzyć wszystkie obiekty dzierżawy
Pułapki Kubernetes z wieloma dzierżawami
- Przestrzeń nazw nie jest całkowitą izolacją: niektóre obiekty o zasięgu klastra (ClusterRole, PersistentVolume, Node) są widoczne dla wszystkich dzierżawców; użyj vcluster, jeśli potrzebujesz całkowitej izolacji
- Udostępnione luki w jądrze: wszyscy najemcy korzystają z tego samego jądra Linuksa; kontener wykorzystujący lukę w jądrze może mieć wpływ na innych dzierżawców; Oceń dedykowane węzły pod kątem dzierżawców z wrażliwymi danymi
- Wyciek DNS: Domyślnie Pody mogą tłumaczyć nazwy usług na inne przestrzenie nazw (
service.namespace.svc.cluster.local); użyj NetworkPolicy, aby w razie potrzeby zablokować ruch DNS między przestrzeniami nazw - ResourceQuota bez LimitRange: jeśli nie ma LimitRange, Pody bez żądań zasobów ignorują ResourceQuota, a limit nie jest poprawnie skalowany
Wnioski i dalsze kroki
Wielodostępność w Kubernetes to kontinuum pomiędzy prostotą operacyjną a izolacją silny. W większości przypadków biznesowych z wewnętrznymi zespołami, przestrzeniami nazw z ResourceQuota, Odpowiednie LimitRange, NetworkPolicy i RBAC zapewniają wystarczającą izolację minimalne koszty operacyjne. Dla klientów korporacyjnych lub scenariuszy z wymaganiami dotyczącymi zgodności rygorystyczne, vcluster oferuje znacznie wyższy poziom izolacji przy jednoczesnym zachowaniu korzyści wspólnego klastra pod względem zasobooszczędności.
HNC zmienia zarządzanie hierarchiami przestrzeni nazw w coś skalowalnego: zamiast konieczność ręcznej replikacji RBAC i NetworkPolicy w każdej podprzestrzeni nazw policy raz w nadrzędnej przestrzeni nazw i propagują się automatycznie. Dla platform przy dziesiątkach zespołów stanowi to różnicę między systemem łatwym w utrzymaniu a systemem wymagającym dedykowany zespół zajmujący się wyłącznie konfiguracją przestrzeni nazw.
Nadchodzące artykuły z serii Kubernetes at Scale
Poprzednie artykuły
Powiązane serie
- Bezpieczeństwo Kubernetesa — Standardy RBAC i Pod Security, wymagania wstępne dla wielu dzierżawców
- Inżynieria Platformy — wielodostępność jako podstawa wewnętrznych platform programistycznych
- FinOps dla Kubernetesa — zarządzanie kosztami przestrzeni nazw i najemców







