Inleiding
Kubernetes multi-tenancy houdt in dat meerdere teams of klanten hun applicaties delen op één cluster. Dit kan kosten besparen en het beheer vereenvoudigen, maar brengt ook nieuwe uitdagingen mee op het gebied van security, eerlijk middelengebruik en het indammen van de zogeheten “noisy neighbors”. Kubernetes heeft van zichzelf geen ingebouwd tenant-concept; toch kunnen we met namespaces, policies en andere mechanismen verschillende tenants (bijv. teams of klanten) isoleren binnen één cluster. In dit artikel duiken we diep in Kubernetes multi-tenant governance – wat het echt betekent, welke risico’s en faalmodi er zijn, en welke lagen van governance nodig zijn om een gedeeld platform veilig en beheersbaar te houden.
We behandelen onder meer soft vs. hard multi-tenancy, noisy neighbors, privilege escalation, resource quotas, policies (Kyverno/OPA), netwerkisolatie, kostenallocatie (showback/chargeback), minimale governance voor kleine teams, en wanneer een per-tenant cluster beter is dan multi-tenancy.
Wat is Kubernetes multi-tenancy (en waarom zijn namespaces niet genoeg)?
Multi-tenancy betekent dat meerdere tenants – dit kunnen interne teams of externe klanten zijn – hun workloads op een gedeelde Kubernetes cluster draaien. Een typisch voorbeeld is een organisatie waarin meerdere teams één cluster delen (multi-team tenancy), of een SaaS-leverancier die voor elke klant een aparte instantie van de applicatie runt op één cluster (multi-customer tenancy). Het idee is dat resources efficiënter worden gebruikt en er minder clusters beheerd hoeven te worden.
Er zijn twee gradaties: soft multi-tenancy en hard multi-tenancy. Bij soft multi-tenancy is er een basisvertrouwen tussen tenants (bijvoorbeeld verschillende afdelingen binnen hetzelfde bedrijf); isolatie is vooral bedoeld om ongelukken te voorkomen en eerlijk gebruik af te dwingen, niet zozeer om kwaadwillende aanvallen tegen te houden. Denk aan logische scheiding via namespaces, RBAC en network policies, waarbij tenants elkaar in principe niet proberen te benadelen. Hard multi-tenancy daarentegen gaat uit van zero trust tussen tenants – dit is relevant als je externe klanten of potentieel malafide gebruikers hebt. Hard multi-tenancy impliceert sterke isolatie op alle niveaus, zowel control plane als data plane, om bijvoorbeeld datalekken of DoS-aanvallen tussen tenants te voorkomen. Deze twee zijn geen strikte categorieën maar liggen op een spectrum van isolatieniveaus, afhankelijk van je security-eisen.
In Kubernetes wordt isolatie meestal gestart met namespaces. Een namespace groepeert resources zodat objectnamen uniek zijn per tenant en bepaalde policies (zoals RBAC-rollen, network policies) per namespace afgedwongen kunnen worden. Het is gebruikelijk om voor elke tenant (of zelfs per applicatie van een tenant) een aparte namespace te maken als basisisolatie.
Namespaces alleen zijn echter niet voldoende voor multi-tenancy. Standaard deelt een cluster nog steeds veel componenten: nodes, netwerk, de API-server en cluster-brede resources zijn gemeenschappelijk en zonder extra maatregelen kunnen tenants elkaar daarmee beïnvloeden. Het namespace-isolatiemodel vereist dus aanvullende configuraties en strengere best practices om workloads echt gescheiden te houden. Met alleen namespaces kunnen pods van verschillende tenants nog vrij met elkaar communiceren, kunnen misconfiguraties in één namespace impact hebben op de hele cluster, en kunnen gebruikers potentieel nog teveel rechten krijgen buiten hun eigen gebied.
Kortom: namespaces bieden logische scheiding, maar extra governance-lagen zijn nodig om een multi-tenant cluster veilig en beheersbaar te maken.
Realistische faalmodi in gedeelde clusters
Wanneer meerdere tenants dezelfde cluster delen, kun je tegen diverse problemen aanlopen. We beschrijven hier een aantal realistische faalmodi – ongewenste scenario’s – in multi-tenant Kubernetes clusters en waarom governance hiervoor nodig is:
- Noisy neighbors: Dit is het fenomeen waarbij één tenant onevenredig veel resources opslokt en daarmee de prestaties van andere tenants schaadt. Bijvoorbeeld, een applicatie van Tenant A zonder limits kan alle CPU of geheugen van een node verbruiken, waardoor apps van Tenant B traag worden of geen resources meer krijgen. Ook kan overmatig gebruik van het Kubernetes control plane (bijv. floods van API-calls, logs, of events) door één partij de hele cluster beïnvloeden. Zonder beperkingen kan één “luidruchtige buur” dus een denial-of-service veroorzaken voor anderen.
- Privilege escalation: In een gedeeld cluster is toegangscontrole cruciaal
– als een niet-geautoriseerde gebruiker of workload meer rechten verwerft dan
bedoeld, kan dat rampzalig zijn. Bijvoorbeeld, als Tenant A per ongeluk
cluster-brede rechten krijgt, kan deze de workloads of gegevens van Tenant B
inzien of aanpassen. Ook kunnen pods met te hoge privileges (bijv.
privileged: trueofhostPathvolumes) uit hun container “breken” en toegang krijgen tot de node of API-server. Zo’n container escape kan een aanvaller in staat stellen om alle namespaces te compromitteren. Zonder strikte RBAC en pod-security maatregelen is privilege escalation een reëel risico in multi-tenant omgevingen. - Configuratiedrift: Met meerdere teams die op één cluster werken, bestaat
het risico op inconsistente configuraties of policy-afwijkingen over tijd –
zogeheten configuration drift. Bijvoorbeeld, elke tenant kan eigen Kubernetes
objecten of annotaties instellen; zonder centrale standaarden kan de cluster
langzaam een onsamenhangende verzameling config worden. Ook kunnen manual tweaks
via
kubectlbuiten Git om ervoor zorgen dat de daadwerkelijke clusterconfiguratie afwijkt van de beoogde instellingen. Deze drift kan leiden tot onvoorspelbaar gedrag en maakt troubleshooten lastiger. Governance (bijv. met policies en GitOps) is nodig om ervoor te zorgen dat alle teams dezelfde best practices volgen en de omgeving consistent blijft. - Cross-tenant issues: Naast bovengenoemde punten zijn er nog andere gevaren van onvoldoende isolatie. Denk aan cross-tenant interference (de workload van tenant X beïnvloedt tenant Y door resource contentie of fouten), cross-tenant aanvallen (een kwaadwillige tenant die kwetsbaarheden of misconfiguraties misbruikt om bij een ander in te breken), of data leakage (data van de ene tenant wordt per ongeluk zichtbaar voor een andere). Dit zijn scenario’s die voortkomen uit gebrekkige afscheiding tussen tenants.
Waarom governance? Bovenstaande faalmodi illustreren dat een multi-tenant cluster zonder extra maatregelen al snel onstabiel of onveilig kan worden. Om deze risico’s te beheersen, moeten we meerdere lagen van controle inbouwen: van identity & access management tot resource quotas en network policies. In de volgende secties bespreken we de belangrijkste governance-lagen voor Kubernetes multi-tenancy – en hoe ze helpen om deze faalmodi te voorkomen.
Governance-lagen voor een veilig multi-tenant platform
Een multi-tenant Kubernetes platform vereist een combinatie van verschillende governance-lagen. Deze lagen zorgen samen voor een veilige, eerlijke en beheerbare cluster waar elke tenant binnen afgesproken kaders blijft. De vijf belangrijkste lagen zijn:
- Identity & Access Management (RBAC): Beheer van gebruikers- en serviceaccountrechten zodat elke tenant alleen bij zijn eigen resources kan (least privilege).
- Resource Governance: Beperken en eerlijk verdelen van compute resources via
requests/limits en
ResourceQuotas, om noisy neighbor-effecten te voorkomen. - Netwerkisolatie: Scheiden van netwerkverkeer tussen tenants met
NetworkPolicy(en eventueel extra maatregelen) om de blast radius van incidenten te beperken. - Policy Enforcement (Policy-as-Code): Automatisch afdwingen van clusterbrede regels met tools als OPA Gatekeeper of Kyverno – bijvoorbeeld voor beveiligingspolicies en configuratiestandaarden.
- Kostenbeheer (FinOps): Inzichtelijk maken en toewijzen van kosten per tenant door kostenallocatie, met showback/chargeback rapportages, zodat gebruik transparant en beheersbaar blijft.
Hieronder bespreken we deze lagen één voor één in detail, inclusief praktische voorbeelden en configuraties.
Identity & Access Management (RBAC en gebruikersisolatie)
De eerste verdedigingslaag is identity & access management – zorgen dat gebruikers en workloads alleen kunnen waar ze mogen. Kubernetes biedt hiervoor Role-Based Access Control (RBAC). In een multi-tenant cluster moet RBAC zo ingesteld zijn dat elke team of klant slechts toegang heeft tot zijn eigen namespace(s) en geen cluster-brede rechten krijgt. Dit implementeert het principle of least privilege: geef elke gebruiker of serviceaccount het minimum aan permissies dat nodig is, niet meer.
Concreet betekent dit dat we Roles (namespace-scoped) definiëren voor acties
binnen de tenant’s namespace, en RoleBindings om die rollen aan gebruikers of
service accounts te koppelen. Bijvoorbeeld: Team Alpha krijgt een developer
Role in namespace alpha die hen toestaat Deployments, Services en Pods te
beheren in die namespace – maar die Role heeft geen toegang tot andere
namespaces of cluster-level objects. Cluster-brede rollen (ClusterRoles)
worden uitsluitend gegeven aan cluster-admins of platformbeheerders, niet aan
tenants. Zo voorkom je dat een compromise bij één tenant meteen heel de cluster
overneemt.
Daarnaast is het verstandig om via RBAC te verhinderen dat tenants zelf
cluster-level objecten aanmaken. Ze zouden bijvoorbeeld geen rechten moeten
hebben om Nodes, ClusterRoles of storageclasses te wijzigen. Ook moeten tenants
geen wijzigingen kunnen doen in gedeelde systeemnamespaces (zoals kube-system).
Zorg dat normale devops-accounts van teams niet de rechten hebben die van een
cluster-admin, hoe verleidelijk dat soms ook is voor troubleshooting – dit
voorkomt dat per ongeluk policies of settings van andere teams aangepast worden.
Service Accounts: Workloads (Pods) gebruiken service accounts om te
communiceren met de API. Ook hier geldt: geef elke applicatie een eigen service
account met alleen toegang tot de benodigde API-objects in de eigen namespace.
Bijvoorbeeld, een CI/CD pipeline pod krijgt een serviceaccount die alleen
deployments mag aanmaken in de target namespace, en niets anders. Combineer dit
met Kubernetes RoleBindings naar namespace-rollen. Zo voorkom je dat een
gecompromitteerde applicatie binnen Tenant A stiekem in de namespaces van Tenant
B API-calls kan doen.
Tenants scheiden over namespaces: Kortom, identiteiten (mensen én applicaties) moeten goed gescheiden zijn per tenant. Vaak komt dit neer op het aanmaken van één namespace per team/tenant en het koppelen van dat team aan die namespace via RBAC. Tools als Kubernetes zelf of externe identity providers (bijv. koppeling met LDAP/OIDC voor gebruikersgroepen) kunnen hierbij helpen. Het resultaat is dat Tenant A zelfs niet kan kijken of per ongeluk ingrijpen in de resources van Tenant B – de API weigert het gewoon. Dit legt een noodzakelijke basis waarboven de andere governance-lagen voortbouwen.
NB: Zelfs met strikte RBAC blijft het belangrijk om ook de inhoud van workloads te controleren. RBAC verhindert toegang tot de API, maar een malafide pod binnen geautoriseerde grenzen kan nog steeds narigheid uithalen (zoals netwerkverkeer genereren of proberen te escaleren op de host). Daarvoor komen de andere lagen (policies, network isolation) in beeld.
Resource Governance: Requests, Limits en Quota’s instellen
Om “fair share” gebruik van clusterresources te garanderen en elkaar niet in de
weg te zitten, is resource governance onmisbaar. Kubernetes biedt hiervoor
mechanieken als resource requests & limits op pods en ResourceQuotas op
namespaces. Deze zorgen ervoor dat geen enkele tenant meer CPU, geheugen of
andere resources verbruikt dan toegestaan, wat de impact van noisy neighbors
minimaliseert.
Requests & Limits: Binnen een Pod (of container) specificeren we doorgaans per container een request (het gegarandeerde minimum aan CPU/Memory dat de scheduler voor die container reserveert) en een limit (het maximale dat de container mag verbruiken). De scheduler gebruikt requests om pods te plaatsen: de som van requests op een node mag niet boven de node-capaciteit komen. Limits worden afgedwongen runtime: als een container boven zijn CPU limit gaat, wordt hij ge-throttled; als hij boven zijn memory limit gaat, kan de kernel hem OOM killen. Door requests en limits in te stellen voor elke container, zorgen we dat elke workload netjes zijn gereserveerde aandeel krijgt en niet onbeperkt kan groeien ten koste van anderen. Let op: als je een request lager zet dan de limit (toegestane burst), dan kan een container tijdelijk boven zijn request uitgaan zolang er vrije capaciteit is – dit verbetert resource-utilisatie, maar betekent ook dat er nog steeds enige invloed mogelijk is tussen workloads als meerdere containers tegelijk hun limiet opzoeken. Kies requests dus zorgvuldig (bijvoorbeeld gebaseerd op historische usage) en zet limits om een cap te stellen op abnormaal hoog verbruik.
ResourceQuota: Op het niveau van de namespace kun je een ResourceQuota
object instellen dat plafonds zet op het totale verbruik en aantal objecten in
die namespace. Hiermee voorkom je dat een tenant oneindig veel pods of resources
blijft aanmaken. Een ResourceQuota kan bijv. limiteren: max 100 pods, 200
CPU-eenheden en 500Gi memory voor namespace X, of maximaal 10 LoadBalancer
Services, etc. Zodra de tenant die grens bereikt, weigert Kubernetes verdere
objectcreatie in die namespace. Hiermee kunnen we harde garanties stellen zodat
één tenant niet de hele cluster opvult. Quota’s werken samen met requests/limits:
Kubernetes vereist dat je voor een namespace met Quota ook voor alle containers
daarin requests/limits definieert, anders kun je geen pods maken. Dit is logisch:
zonder die waarden kan Kubernetes niet bepalen of je binnen Quota blijft.
Een typische strategie is om per tenant één namespace en één ResourceQuota aan
te maken, gebaseerd op wat die tenant aan capaciteit “mag” gebruiken (bijvoorbeeld
volgens interne afspraken of een “plan”). In onderstaand YAML-fragment zien we
een voorbeeld van een ResourceQuota voor tenant team-alpha:
apiVersion: v1
kind: ResourceQuota
metadata:
name: quota-team-alpha
namespace: team-alpha
spec:
hard:
pods: "100" # max 100 pods in deze namespace
requests.cpu: "50" # max 50 vCPU aan requested CPU
limits.cpu: "100" # max 100 vCPU aan CPU limieten
requests.memory: "200Gi" # max 200Gi aan requested memory
limits.memory: "400Gi" # max 400Gi aan memory limieten
persistentvolumeclaims: "50" # max 50 PVCs
services.loadbalancers: "5" # max 5 LoadBalancer type Services
In dit voorbeeld kan team-alpha maximaal 100 pods draaien, met in totaal
bijvoorbeeld 50 vCPU aan requests (gegarandeerd) en tot 100 vCPU aan limits
(burst). Probeer de Quota per team zo in te stellen dat het ruim genoeg is voor
hun normale gebruik, maar klein genoeg om de impact op anderen te beperken.
LimitRange: Naast ResourceQuota kun je ook een LimitRange instellen per
namespace. Hiermee dwing je per Pod of Container minimale en maximale
requests/limits af. Bijvoorbeeld: min. 100m en max. 2 CPU per container, en max.
1Gi memory per container. Dit voorkomt dat een tenant hele grote of juist
onbegrensde pods lanceert die de cluster uit evenwicht brengen. Een LimitRange
zorgt er ook voor dat als iemand vergeet een limit in te stellen, Kubernetes er
één invult of de pod tegenhoudt – erg nuttig om te voorkomen dat “unlimited”
containers de hele node opslokken.
Realistische strategieën & valkuilen:
- Quotas toewijzen: Verdeel de clusterbronnen op papier over de tenants. Je kunt ervoor kiezen om de som van alle Quota’s iets hoger te laten zijn dan de daadwerkelijke cluster capaciteit (overcommit) als niet alle teams hun piek tegelijk bereiken. Maar doe dit voorzichtig. Houd ook rekening met toekomstige groei van teams.
- Default requests/limits: Gebruik eventueel een default in
LimitRangezodat nieuwe deployments automatisch een baseline request/limit krijgen als de ontwikkelaar het niet invult. - Pitfalls: Te strakke Quota kan innovatie remmen (teams kunnen geen extra pod starten bij nood, ondanks dat de cluster idle is). Te losse Quota of geen Quota kan leiden tot resource contention. Ook dekt Quota niet alle resource-aspecten: bijvoorbeeld netwerkverkeer en disk I/O hebben geen standaard Quota. Een tenant kan dus nog steeds het netwerk verstopt raken of de API-server belasten, ondanks CPU/memory quota. Voor zulke dingen kun je aanvullende maatregelen nemen (zoals node-isolatie of throttle-ingress, zie netwerkisolatie sectie).
Samengevat zorgt Resource Governance ervoor dat elke tenant zich aan zijn eerlijk
deel houdt en dat één tenant niet ongemerkt de cluster kapot belast. Het instellen
van requests/limits en ResourceQuotas is een basisvereiste in elke multi-tenant
Kubernetes omgeving om performance- en stabiliteitsproblemen te voorkomen.
Policy-as-Code: Kubernetes policies afdwingen met Kyverno en OPA Gatekeeper
Zelfs met goede RBAC en resource quotas kunnen misconfiguraties of onveilige instellingen nog problemen veroorzaken. Hier komt policy-as-code in beeld: we gebruiken Kubernetes Admission Controllers zoals OPA Gatekeeper (gebaseerd op Open Policy Agent) of Kyverno om regels centraal af te dwingen op alle resources in de cluster. Deze tools draaien als webhook en checken elke nieuwe of gewijzigde resource tegen gedefinieerde policies, zodat we onwenselijke configuraties kunnen blokkeren of aanpassen.
Validating vs. Mutating policies:
- Validating policies controleren een resource op bepaalde voorwaarden. Als er iets niet voldoet, kan de policy het object weigeren (of in audit-modus een waarschuwing loggen). Voorbeelden: “Elke pod moet een resource limit hebben” of “Containers mogen niet draaien als root user”. Zowel Gatekeeper als Kyverno ondersteunen dit validatieconcept.
- Mutating policies gaan een stap verder door automatisch velden aan te
passen of toe te voegen aan een resource. Bijvoorbeeld: “Als een Deployment
geen label ‘team’ heeft, voeg het toe” of “Zet altijd
runAsNonRoot: truebij containers zonder die setting”. Kyverno ondersteunt dit soort mutating policies out-of-the-box (en kan zelfs resources genereren), terwijl OPA Gatekeeper historisch alleen valideert (mutatie is beperkt/experimenteel). Met Kyverno kun je dus ook defaults en best practices automatisch injecteren.
Audit vs. Enforce modus: Het is verstandig om nieuwe policies eerst in audit
(of dry-run) modus te draaien. In die stand blokkeren ze geen resources, maar
loggen ze wel een melding als iets niet voldoet. Zo kun je zien hoeveel huidige
workloads in overtreding zijn en of de policy gevolgen zou hebben. Zodra je
zeker weet dat de cluster compliant is, zet je de policy in enforce modus om
daadwerkelijk changes te blokkeren die niet passen. In Kyverno geef je dit
bijvoorbeeld aan met validationFailureAction: enforce (of audit voor
audit-modus). Gatekeeper heeft een apart audit proces dat periodiek checkt op
overtredingen naast het realtime afdwingen.
Voorbeeldpolicy: Stel, we willen afdwingen dat elke container CPU en geheugen limieten heeft, om te voorkomen dat iemand onbeperkte pods draait. Hieronder een vereenvoudigde Kyverno policy als voorbeeld:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resource-limits
spec:
validationFailureAction: Audit # eerst in audit-modus testen
rules:
- name: check-limits
match:
any:
- resources:
kinds: ["Pod"]
validate:
message: "Elke container moet CPU en memory limits hebben."
pattern:
spec:
containers:
- resources:
limits:
cpu: "?*"
memory: "?*"
Deze Kyverno policy zoekt in elke nieuwe Pod of alle containers een CPU- en
memory-limit hebben; zo niet, dan faalt de validatie. In Audit-modus zal hij
alleen een waarschuwing geven, in Enforce-modus zou hij het aanmaken van de Pod
blokkeren. Met dergelijke policies kun je allerlei regels afdwingen, zoals: geen
gebruik van :latest image tags, verplichte labels op elke resource (voor o.a.
cost tracking), bepaalde opslagklassen wel/niet gebruiken, of het verbieden van
onveilige settings (bijv. privileged containers). In Gatekeeper zou je
vergelijkbare regels schrijven in Rego-taal via ConstraintTemplates en
Constraints.
Policy lifecycle en beheer: Policy-as-code betekent dat je deze regels versiebeheert (bijv. in Git) net als applicatiecode. Je ontwikkelt een policy, test hem (Kyverno heeft een CLI voor policy testen; OPA heeft conftest/unit tests voor Rego), en rolt hem gefaseerd uit. Begin eventueel in een ontwikkelcluster, of gebruik audit-modus in productie voor nieuwe policies. Monitor de audit-rapporten of er overtredingen zijn. Communiceer met teams wat er gaat veranderen (“vanaf volgende maand blokkeren we images zonder vertrouwde registry-label bijvoorbeeld”) zodat ze zich kunnen voorbereiden. Vervolgens schakel je enforce in. Dit proces herhaal je voor nieuwe policies. Tevens is het belangrijk policies periodiek te evalueren en bij te werken – Kubernetes evolueert en je security-eisen ook.
Kyverno vs. OPA Gatekeeper kort: Beide zijn populair en open-source. Gatekeeper gebruikt het krachtige Rego-query language van OPA, maar dat heeft een wat hogere leercurve. Kyverno sluit dichter aan bij Kubernetes YAML en kan daardoor eenvoudiger zijn voor clusterbeheerders (je schrijft declaratief de gewenste staat/patronen). Kyverno kan bovendien mutate- en generate-rules, wat Gatekeeper niet (volledig) kan. Echter, Gatekeeper heeft een grote library aan kant-en-klare “Constraint” templates beschikbaar (bijv. voor veel CIS Benchmarks en best practices). In dit artikel blijven we vendor-neutraal – beide tools kunnen je beleid codificeren en handhaven. Welke je kiest hangt af van je team’s voorkeur en de specifieke requirements.
Samengevat: met policy-as-code zorg je dat er een automatische poortwachter staat bij je Kubernetes API. Deze voorkomt dat individuele teams per ongeluk (of expres) instellingen gebruiken die de omgeving onveilig of onstabiel maken. Een sterke multi-tenant governance omvat altijd zo’n mechanisme, zodat je vertrouwen hebt dat alle namespace-isolatie, resource quotas en security-regels ook daadwerkelijk consistent worden toegepast op alle workloads.
Netwerkisolatie en blast radius beperken
In een standaard Kubernetes cluster is het netwerk volledig open: “default allow” – alle pods kunnen onderling communiceren, zelfs over namespace-grenzen. In een multi-tenant scenario is dit meestal ongewenst. Je wilt niet dat een compromis in tenant A direct toegang geeft tot services van tenant B. Bovendien is al dat verkeer onbeveiligd (zonder ingreep); men zou potentieel verkeer kunnen sniffen op gedeelde nodes of verkeerde endpoints aanroepen. Netwerkisolatie is dus een essentiële laag om de blast radius van een incident te beperken.
Network Policies: Kubernetes biedt NetworkPolicy-objecten om verkeer op
laag 3/4 te filteren. Met network policies kun je specificeren welk verkeer
(pod-selector + poort) van/naar een groep pods is toegestaan. Belangrijk om te
weten: zonder enige NetworkPolicy is alles toegestaan (zowel ingress als egress
verkeer). Zodra je één NetworkPolicy in een namespace definieert die op een
bepaalde pod van toepassing is, geldt voor die pod: wat niet expliciet door een
policy wordt toegelaten, is verboden (principle of least privilege voor netwerk).
Een best practice is om per namespace een “default deny” policy in te stellen voor inkomend verkeer, en vervolgens gewenste uitzonderingen toe te staan. Je kunt bijvoorbeeld alle ingress naar pods in namespace X blokkeren, behalve verkeer dat afkomstig is van pods in dezelfde namespace (zodat microservices van dezelfde tenant elkaar nog kunnen bereiken). Hiermee is inter-namespace verkeer standaard dicht. Daarnaast kun je expliciete regels toevoegen als er toch communicatie tussen specifieke namespaces nodig is (bijv. een gemeenschappelijke ingress-controller of monitoring agent die overal bij moet).
Voorbeeld van een eenvoudige NetworkPolicy die alle ingress buiten de eigen
namespace blokkeert:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-other-namespaces
namespace: team-alpha
spec:
podSelector: {} # alle pods in team-alpha
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {} # alleen verkeer van pods binnen dezelfde namespace
Bovenstaande policy zorgt dat team-alpha pods alleen verkeer accepteren van
elkaar; niets van buiten team-alpha komt erin. Omgekeerd is verkeer uitgaand
(egress) standaard nog open tenzij je ook egress policies definieert. In strengere
omgevingen wil je ook egress beperken – bijvoorbeeld voorkomen dat een
gecompromitteerde pod willekeurig data naar internet verstuurt. Je kunt egress
beperken tot bijvoorbeeld alleen interne API’s of een vaste lijst van externe
endpoints. Een veelgebruikte aanpak is om voor egress ook een default deny te
hanteren, met uitzonderingen voor noodzakelijke diensten (denk aan DNS-resolutie
naar kube-dns, of uitgaand verkeer naar bekende externe services). Kubernetes
NetworkPolicy ondersteunt egress-regels op vergelijkbare wijze als ingress.
Blast radius controle: Door network policies te combineren met de
namespace-per-tenant indeling, bereiken we dat een incident binnen één tenant zo
veel mogelijk beperkt blijft tot die tenant. Als een applicatie van Tenant A
wordt overgenomen door een aanvaller, kan die aanvaller niet zomaar via het
cluster-netwerk bij Tenant B komen, dankzij de firewalling op pod-niveau. Ook kan
malware zich niet vrij verspreiden of scannen naar kwetsbare services van andere
teams. Let wel: network policies dekken niet alles – ze opereren op IP/poort
niveau. Als twee kwaadaardige pods op dezelfde node draaien en de kernel is
gecompromitteerd, zouden ze misschien via het hostnetwerk alsnog kunnen
communiceren (dit is erg hypothetisch, maar daarom bestaan ook container sandbox
technieken als gVisor). Verder geldt dat NetworkPolicy geen encryptie
toevoegen; binnen een cluster is verkeer normaal gesproken in plaintext.
Overweeg voor zeer gevoelige data om extra maatregelen te nemen zoals mTLS tussen
services.
Implementatie details: Om NetworkPolicy te laten werken, moet je CNI-plugin
ze ondersteunen (de meeste moderne CNIs zoals Calico, Cilium doen dat). Begin
met een organisatie-breed baseline netwerkbeleid. Vaak wordt een template
toegepast dat voor elke nieuwe namespace meteen een default deny policy creëert.
Sommige platformteams automatiseren dat via een operator of via een admission
webhook (bijv. een Kyverno policy die bij namespace-creatie een default
NetworkPolicy genereert). Het belangrijkste is dat je consistente isolatie
toepast: geen enkele tenant-namespace vergeten, want een enkele namespace zonder
policies vormt meteen een lek (daar is immers alles open).
Egress controleren: Voor egress kan het zinvol zijn om een centraal egress
gateway of proxy te gebruiken, zodat alle uitgaand verkeer van pods via één
gecontroleerd punt gaat. Dit is echter meer een advanced pattern (bijvoorbeeld
met service mesh or network firewall appliances). Simpelere aanpak is specifieke
egress NetworkPolicy maken om bijv. alleen outbound DNS en web naar bekende
domeinen toe te staan. Combineer dit met cloud side oplossingen (VPC-level ACLs
of beveiligingsgroepen) voor een defense-in-depth.
Kortom, netwerkisolatie via NetworkPolicy is een must voor multi-tenancy. Het
voorkomt dat tenants op netwerkvlak interfereren en maakt de impact van een
mogelijk incident aanzienlijk kleiner. Combineer het altijd met de eerder
genoemde RBAC-beperkingen: een pod mag misschien de API-server nog pingen, maar
zonder de juiste serviceaccount rechten komt hij alsnog niet ver. Deze lagen
samen versterken elkaar.
Kostenallocatie: showback & chargeback voor multi-tenant clusters
Naast technische isolatie is kostenbeheer een belangrijk aspect van multi-tenant governance. Wanneer teams of klanten een cluster delen, wil je inzichtelijk maken wie welke resources verbruikt en ervoor zorgen dat de cloudrekening eerlijk wordt toegewezen. Kubernetes cost allocation draait om het meten van resourcegebruik per tenant en dit koppelen aan daadwerkelijke kosten in euro’s. Hierbij spreken we vaak van showback en chargeback als twee modellen.
Labels voor kostenallocatie: Om kosten per team te berekenen, moet je eerst
het gebruik per team kunnen meten. Dit doe je door consistente metadata
(labels/annotaties) te gebruiken. Bijvoorbeeld: label alle resources
(Deployments, Pods, Services, PersistentVolumes, etc.) met een label team of
cost-center. Idealiter krijgt elke namespace al een label mee die de eigenaar
of afdeling aangeeft. Bijvoorbeeld: een namespace team-alpha labelen met
team: alpha en environment: production. Door deze metadata consequent toe te
passen, kun je metrieken en kosten per label aggregeren. Je kunt dit proces
ondersteunen met policies: bijvoorbeeld een Gatekeeper of Kyverno policy die
afdwingt dat elke Deployment een team label heeft. Deze labels stem je ook af
op cloudprovider-tags (bijv. AWS tags of GCP labels) zodat kostenrapportages van
de cloud ook matchen met je Kubernetes-tenant indeling – dit vergroot de
traceerbaarheid van kosten van clusterniveau tot businessniveau.
Hoe kosten te meten: Kubernetes rekent zelf geen euro’s uit, maar je kunt gebruik maken van metrics. Enkele opties:
- Resource usage meten: Via
Prometheusof CloudWatch Container Insights kun je per pod de daadwerkelijke CPU- en geheugengebruik meten over tijd. - Requests toewijzen: Een eenvoudigere aanpak is kosten toe te wijzen op basis van geconfigureerde requests (bijv. als een pod 2 vCPU requested op een node van 0,10 €/uur per vCPU, reken je 0,20 €/uur toe aan die pod). Dit reflecteert gereserveerde capaciteit in plaats van echte gebruik.
- Hybride of geavanceerd: Er bestaan open-source tools (zoals
Kubecost) die nodekosten verdelen over pods op basis van een mix van gebruik en reservatie. Deze integreren ook met cloud billing data voor nauwkeurigheid. Zonder specifieke tool kun je ook zelf de cloudrekening (bijv. via AWS CUR of GCP billing export) combineren met Kubernetes usage data, maar dat is behoorlijk complex.
Showback vs Chargeback:
- Showback betekent dat je de kosten per team inzichtelijk maakt, zonder dat er direct interne verrekening plaatsvindt. Het is een vorm van rapportage: “Team Beta heeft deze maand €500 aan clusterresources verbruikt”. Er is geen afdwinging; het is bedoeld om bewustzijn te creëren.
- Chargeback gaat een stap verder: hierbij wijs je de kosten echt toe aan het budget van dat team of reken je ze misschien door. Met chargeback worden afdelingen “afgerekend” op hun gebruik. Het verschil is dus vooral administratief: showback is zichtbaar maken, chargeback is doorbelasten. In praktijk beginnen veel organisaties met showback (als leermiddel) en gaan later richting (functie-)chargeback als ze willen dat teams verantwoordelijkheid nemen voor hun uitgaven.
Om dit te illustreren, hieronder een fictief voorbeeld van labels op een namespace en deployment die helpen bij cost allocation:
# Namespace met kostenlabels
apiVersion: v1
kind: Namespace
metadata:
name: team-alpha
labels:
team: alpha
environment: production
cost-center: "CC-1001"
---
# Deployment gelabeld voor kosten
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: team-alpha
labels:
app: my-app
team: alpha
environment: production
spec:
# ...
Met zulke labels kun je rapportages maken per team of per omgeving. Stel bijvoorbeeld grafieken op die laten zien hoeveel CPU-uren en memory-GB-uren team alpha verbruikt heeft per maand (vervolgens te vermenigvuldigen met kostentarieven). Veel FinOps-tools en cloud dashboards ondersteunen filteren op labels of namespaces, zodat je gespecificeerde kosten per tenant kunt uitdraaien.
Showback/Chargeback proces: Bouw periodieke rapportages (bijv. maandelijkse kosten per team). Deel showback-rapporten met engineering leads om bewustzijn te vergroten – wellicht via Slack of een dashboard. Als je chargeback hanteert, zorg dan dat finance diezelfde data krijgt zodat zij het kunnen verrekenen in budgetten. Let erop om ook gedeelde kosten (zoals controleplane, shared add-ons) eerlijk te verdelen of als apart post te tonen, anders missen teams de prikkel om die te optimaliseren.
Transparantie in kosten is onderdeel van governance: het voorkomt verrassingen (“Waarom is de cloudrekening deze maand zo hoog?”) en motiveert teams zuiniger met resources om te gaan als ze zien wat het kost. In multi-tenant Kubernetes is dit extra relevant omdat overcommitment en resource sharing anders makkelijk leiden tot inefficiëntie (men ziet de echte kosten niet direct). Showback geeft inzicht; chargeback koppelt er verantwoordelijkheid aan.
Minimale governance voor kleine teams (1–5 engineers)
Niet elke organisatie heeft tientallen teams op één cluster. Wat nu als je een klein team hebt (bijv. een startup van 3 developers) of slechts een paar engineers die de cluster beheren? Moet je dan ál deze governance-maatregelen implementeren, of is dat overkill?
Voor kleine, hechte teams – zeker als ze binnen één organisatie werken en elkaar vertrouwen – kun je kiezen voor een lichte vorm van multi-tenancy. Hier zijn de tenants niet per se wantrouwend naar elkaar, maar je wilt vooral ongelukken voorkomen en een basisstructuur houden. Enkele richtlijnen voor minimale governance in zo’n situatie:
- Houd het eenvoudig: Gebruik namespaces om verschillende projecten of omgevingen te scheiden, maar ga niet onnodig complexe hiërarchie maken. Eén namespace per project of per omgeving (dev/prod) is vaak genoeg. In een heel klein team kan iedereen misschien wel overal bij, maar toch is namespace-segmentatie handig om per omgeving settings en quotas te kunnen toepassen.
- Basis RBAC: Je hoeft geen ingewikkelde rollenstructuur op te tuigen als dezelfde 5 engineers toch alle deployments doen. Maar stel in elk geval zo in dat accidentele fouten beperkt blijven. Bijvoorbeeld: geef ontwikkelaars schrijfrechten in de dev-namespaces, maar alleen leesrechten (of geautomatiseerde CD pipelines schrijfrecht) in productie-namespaces. Zo voorkomt je dat iemand per ongeluk een command in de verkeerde context uitvoert en prod raakt. Dit is “least privilege” toepassen op een pragmatische manier.
- Resource limits instellen: Ook in kleine teams gebeuren incidenten – een
onbeperkte pod kan een hele node plat trekken, ook al kennen alle engineers
elkaar. Spreek daarom af om voor elke container requests/limits te zetten en
ondersteun dat eventueel met een
LimitRangeof een lichte policy. Dit kost weinig moeite en voorkomt 90% van de noisy-neighbor problemen, zelfs binnen één team. - Eenvoudige quotas: In een team van 5 zullen quotas niet snel bereikt
worden, maar een
ResourceQuotakan toch nuttig zijn als vangnet. Bijvoorbeeld: max 200 pods in de cluster, zodat als een pipeline malfunctions en 1000 pods probeert te starten, Kubernetes ze tegenhoudt. Zie het als veiligheidspal: je verwacht het niet te raken, maar het beschermt tegen run-away scenarios. - Netwerkpolicy (optioneel): Als alle diensten van het kleine team toch met elkaar mogen praten, kun je network policies minimaal houden. Toch is het verstandig om ook hier een default-deny en dan “allow binnen namespace” policy te hanteren, zodat je alvast de basis legt voor isolatie. Het kan namelijk ook een externe actor zijn (malware) die anders lateraal beweegt. Met een paar standaard policies is de overhead laag en de winst hoog. In ontwikkelclusters kun je het eventueel wat losser laten als dat handiger is, maar voor productie is enige isolatie verstandig, klein team of niet.
- Geen overbodige tooling: Een klein team kan vaak via code reviews en goede
communicatie al veel governance managen. Als jullie discipline hebben om geen
gevaarlijke dingen te deployen, is bijvoorbeeld een volledige OPA Gatekeeper met
honderden regels misschien te zwaar. Kies je gevechten: wellicht volstaat het
om Kubernetes zelf z’n nieuwe Pod Security Standards te laten handhaven via
labels (bijv.
enforce=baselineop namespaces, zodat echt gekke dingen als privileged pods standaard verboden zijn zonder eigen policy-engine). Dat geeft best veel veiligheid met minimale inspanning. Complexe multi-tenancy operators (Capsule, Hierarchical Namespaces, etc.) zijn voor een klein team meestal overkill – beheers eerst de basis. - Kostenbewustzijn: In een kleine organisatie is showback/chargeback vaak niet nodig, omdat alles onder één budget valt. Toch wil je niet dat onzichtbare kosten zich opstapelen. Gebruik simpele tools of scripts om maandelijks te kijken welke apps veel resources vragen. Of zet alerts in als de cluster bijna volloopt. Direct financieel doorbelasten aan sub-teams heeft hier geen nut, maar inzichten wel (bijv. “onze CI-runner namespace is goed voor 70% van de CPU-uren, moeten we dat optimaliseren?”).
Al met al is de minimale governance voor kleine teams een subset van de volledige governance: je implementeert de eenvoudige dingen die grote problemen voorkomen, maar laat de zware processen achterwege. Het doel is niet bureaucratie, maar gemak en veiligheid. Zorg dat de maatregelen die je neemt aansluiten bij de grootte en skillset van je team. Te veel governance bij een miniteam kan contraproductief zijn – je wilt niet dat 2 van de 5 engineers fulltime met policy management bezig moeten zijn. Dus begin klein: basic RBAC, resource limits, wellicht een paar policies voor echt cruciale zaken (zoals verplichte imagePullPolicy of geen privileged containers). De rest kun je toevoegen naarmate het team en het gebruik groeit. Keep it simple, but safe.
Wanneer is multi-tenancy geen goed idee? (Dedicated clusters per tenant)
Multi-tenancy klinkt aantrekkelijk, maar er zijn situaties waarin het beter is om er van af te zien en elke tenant zijn eigen cluster te geven. Het opzetten van een heel per-tenant cluster (of zelfs per-tenant fysieke nodes) verhoogt de isolatie naar het hoogste niveau – ten koste van extra beheerlast en mogelijk hogere kosten. Wanneer kies je voor deze route?
- Strenge compliance of veiligheidsvereisten: Als je in een gereguleerde sector zit (financiële diensten, overheid, zorg) of te maken hebt met zeer gevoelige data, dan kan het zijn dat gedeelde infrastructuur niet is toegestaan. Sommige standaarden eisen complete scheiding van omgevingen. Een dedicated cluster per tenant kan dan nodig zijn om aan de compliance te voldoen, omdat je daarmee de “hardste” isolatie krijgt (losse API-servers, etcd, nodes). Ook intern kan je security team zich simpelweg comfortabeler voelen als kritieke workloads op een eigen cluster draaien zonder andere partijen erbij.
- Wantrouwen tussen tenants: Wanneer tenants totaal onbekenden van elkaar zijn of potentieel kwaadwillend (denk aan een public cloud scenario met externe klanten), is soft multi-tenancy soms niet voldoende. Je kunt wel alles dicht timmeren met policies, maar 0% risico bestaat niet – een nieuw Kubernetes-lek of misconfiguratie kan isolatie doorbreken. In zo’n zero trust situatie neig je naar hard multi-tenancy of zelfs dedicated clusters. Als je klanten betaalt voor een dienst en zij verwachten een eigen omgeving “voor hen alleen”, is het vaak rationeel om daadwerkelijk een eigen cluster te geven per klant. Dit voorkomt zorgen over noisy neighbors of data privacy in de perceptie van de klant.
- Complexe of conflicterende eisen per tenant: Soms hebben verschillende tenants dermate uiteenlopende requirements dat ze niet lekker samen in één cluster passen. Bijvoorbeeld: Tenant A wil Kubernetes versie 1.28 draaien vanwege nieuwe features, Tenant B heeft nog apps die alleen op 1.25 werken. Of elk tenant-team wil volledige beheercontrole, inclusief het kunnen installeren van custom CRDs of operators die cluster-scoped zijn. In een gedeelde cluster zou dat botsen (je kunt niet twee verschillende versies van een CRD schema hebben in één cluster, etc.). Een eigen cluster per tenant geeft elke partij de vrijheid om eigen upgrades, add-ons en configuraties te draaien, afgestemd op hun behoeften. Ook zaken als overlappende IP-subnets of naamgevingen zijn geen probleem meer zodra iedere tenant in zijn eigen wereld zit.
- Behoefte aan volledige blast radius isolatie: Heb je tenants met zeer zware workloads die elkaar nog steeds zouden kunnen storen (bijv. beide gebruiken alle CPU van de regio tijdens piek)? Of vrees je dat zelfs met quotas een DOS op cluster-niveau mogelijk is (bijvoorbeeld als 100 tenants tegelijk iets doen, crasht de API-server)? In zulke gevallen kun je ervoor kiezen de blast radius te beperken door meerdere kleinere clusters te gebruiken. Eén cluster per tenant betekent dat een crash of lek in cluster A géén effect heeft op cluster B. Je omzeilt zo de single point of failure van één control plane. Dit is vooral interessant als de cluster zelf complex is of vaak verandert – je voorkomt dat een foutconfiguratie direct alles raakt.
- Organisatorische redenen (Eigenaarschap & autonomie): Soms is het splitsen van clusters ook logisch omdat verschillende teams volledig autonoom willen opereren. Als elke tenant zijn eigen platformteam heeft of de autonomie wil om zelf upgrades door te voeren, past een multi-tenant gedeeld cluster niet goed bij de organisatiecultuur. Je kunt dan beter per team een cluster hebben, zodat ze eigen baas zijn en niet hoeven alignen met anderen over bijvoorbeeld onderhoudsvensters. Dit is geen technische noodzaak, maar wel een pragmatische afweging.
Dankzij managed Kubernetes diensten (zoals cloud K8s services) is de overhead van meerdere clusters tegenwoordig minder een struikelblok – het opzetten en draaiende houden van 5 clusters is makkelijker geworden, omdat de control plane vaak door de provider gerund wordt. Let wel op de andere kant: meerdere clusters betekent gescheiden netwerk, gescheiden authenticatie, meer monitoringpunten, etc. Je hebt extra tooling nodig voor multi-cluster beheer (of gewoon een goed georganiseerd team per cluster). Het is dus een trade-off tussen sterkere isolatie vs. hogere operationele complexiteit en kosten.
Wanneer kies je ervoor? Als vuistregel: kies voor aparte clusters als de risico’s of eisen dat écht vereisen. Bijvoorbeeld: een klant contractueel toegezegd een dedicated omgeving te krijgen; of interne beleidsregels die dat voorschrijven voor bepaalde dataklassen. Ook als je merkt dat je in je shared cluster heel veel kunstgrepen moet uithalen om iedereen tevreden te houden (constant custom tweaks per tenant, of policies die zo gedifferentieerd zijn dat het beheer bijna per team handwerk wordt), kan dat een teken zijn dat de tenants té verschillend zijn en beter gescheiden kunnen worden.
Soms is een hybride aanpak mogelijk: bijvoorbeeld 80% van je klanten zitten multi-tenant op een groot shared cluster, maar 20% premium klanten krijgen een eigen cluster wegens extra eisen. Kubernetes’ flexibiliteit en de cloud maken dit haalbaar. Evalueer periodiek of je multi-tenant strategie nog opweegt tegen de complexiteit. Het is geen schande om te zeggen: “voor dit geval isoleren we volledig met een eigen cluster,” zelfs al promoot Kubernetes het delen van clusters. Uiteindelijk draait het om de juiste balans tussen efficiency en risico.
Conclusie
Kubernetes multi-tenant governance is een cruciale discipline voor iedereen die clusters deelt tussen teams of klanten. Het gaat om het vinden van de juiste balans: multi-tenancy biedt efficiëntie, maar zonder goede governance ondergraaft het die voordelen door veiligheids- en stabiliteitsproblemen. In dit artikel hebben we gezien dat namespaces alléén niet volstaan – je moet denken in lagen: streng toegangsbeheer (RBAC), eerlijk delen van resources (requests/limits en quotas), netwerksegmentatie, centrale policies als code, en transparante kostenallocatie. Deze lagen vullen elkaar aan en creëren samen een platform waarop meerdere tenants veilig naast elkaar kunnen draaien zonder elkaar te storen.
Voor een technisch publiek zoals DevOps-engineers en platformbeheerders is het duidelijk dat de uitdagingen van multi-tenancy niet onoverkomelijk zijn, mits je de juiste tools en best practices inzet. Soft vs. hard multi-tenancy bepaalt hoeveel extra isolatie je inbouwt; baseer die keuze op de mate van wederzijds vertrouwen tussen tenants. Wees je bewust van realistische faalmodi (noisy neighbors, privilege escalation, configuratiedrift) en ontwerp je controls om die te mitigeren. Resource governance via quotas en limits is feitelijk een must in elke gedeelde cluster – het voorkomt oneerlijk of onbedoeld overgebruik. Policy-as-code zorgt ervoor dat alle teams zich houden aan beveiligings- en configuratieregels, zonder dat je handmatig elk manifest hoeft na te pluizen. Netwerkisolatie begrenst de impact van eventuele incidenten en voorkomt dat een fout in de ene app direct alle andere raakt. En vergeet niet de kosten: laat zien welke team wat verbruikt, zo stimuleer je verantwoordelijkheid en kun je intern doorbelasten indien gewenst.
Tegelijkertijd geldt: pas de mate van governance aan op je situatie. Een klein, intern team kan met een lichter regime toe, terwijl een SaaS-platform voor externe klanten vrijwel zeker strikte maatregelen (tot en met hard multi-tenancy of dedicated clusters) vereist. Over-engineer niet voor een scenario dat je niet hebt, maar onderschat ook nooit de risico’s van een gedeelde omgeving.
Concluderend – Kubernetes multi-tenant governance draait om vertrouwen en controle. Door duidelijke grenzen te trekken (zowel technisch als organisatorisch) kunnen we de voordelen van multi-tenancy benutten – kostenvoordeel, efficiency, centrale management – zonder aan veiligheid of stabiliteit in te boeten. Een goed beheerde multi-tenant cluster geeft elke tenant het gevoel van een eigen omgeving, terwijl ze in werkelijkheid van één krachtig, gedeeld platform gebruikmaken. Dat is de belofte, mits we het governance-stuur strak in handen houden. Met de richtlijnen in dit artikel kun je die koers uitzetten: van soft naar hard isolatie, van kleine teamcluster tot enterprise-platform – zorg voor de juiste maatregelen op het juiste moment. Kubernetes biedt de bouwstenen, maar het is aan ons om er een veilig multi-tenant “huis” van te bouwen.