Vijf clusters, twee harde grenzen
Een cluster dat vandaag goed draait, zegt weinig over de toestand over een half jaar. Handmatige wijzigingen stapelen zich op, versies lopen uiteen en de reden achter een instelling verdwijnt. Met vijf clusters wordt die afwijking snel structureel. Daarom is de volledige platformlaag declaratief vastgelegd: wat in Git staat, is wat er hoort te draaien.
Het landschap bestaat uit twee clusterfamilies. infra levert managementfuncties zoals identity, centrale observability en CI-runners. worker draait workloads en bijbehorende platformdiensten. Naast nonprod en prod is er een vnext-cluster waarop nieuwe componenten en versies als eerste landen.
Nonprod en productie krijgen ieder een eigen Argo CD-installatie. Die keuze kost extra beheer, maar maakt de grens concreet: een fout, upgrade of te ruim recht in de nonprod-control plane werkt niet rechtstreeks door naar productie. Voor nonprod geldt als uitgangspunt dat data kan uitlekken. Daarom is daar uitsluitend geanonimiseerde data toegestaan. In productie wordt echte data verwerkt; daar geldt dat data niet mag uitlekken.
Gescheiden control planes
De repository is het deploymentmodel
De repository bevat ruim 3.700 bestanden. Bijna 3.000 daarvan zijn gevendorde Helm-charts; de eigenlijke platformlogica beslaat ongeveer 770 bestanden. De omvang is minder belangrijk dan de vaste structuur:
applications/
infrastructure/<component>/<cluster>/
platform/<component>/<cluster>/
applications/<workload>/<cluster>/
applicationsets/
base/
overlays/nonprod/
overlays/prod/
argocd/
bootstrap/<stage>/
projects/
clusters/ Iedere combinatie van component en cluster heeft een eigen build-root. Daarin staan kustomization.yaml, helm-values.yaml en argocd-app-config.yaml. Het laatste bestand is deploymentmetadata voor de
ApplicationSet: naam, namespace, sync-wave, doelcluster, stage en Git-revisie.
Gemeenschappelijke waarden worden soms herhaald. Dat is bewust. De build blijft binnen zijn eigen directory en één map laat volledig zien wat op dat cluster draait. Er is geen verborgen overerving door meerdere lagen nodig om een wijziging te beoordelen.
Charts worden gevendord
Upstream Helm-charts staan met hun exacte versie in de repository. Kustomize combineert de chart, aanvullende manifests en patches in één build. Daardoor heeft een render geen netwerktoegang nodig en blijft de uitkomst reproduceerbaar. Oude chartversies blijven beschikbaar zolang een cluster ze nog gebruikt.
Onboarding zonder centrale inventarislijst
Terraform maakt per omgeving één root-Application aan. Vanaf dat punt beheert Argo CD zichzelf en de rest van het platform. De bootstrap past AppProjects en de stage-specifieke ApplicationSets toe. Dertien ApplicationSets gebruiken ieder een Git-generator om configuratiebestanden te ontdekken.
Voor ieder gevonden argocd-app-config.yaml-bestand ontstaat een Application
met een voorspelbare naam, bijvoorbeeld infra-prod-cert-manager. Argo CD
voert vervolgens kustomize build --enable-helm uit, vergelijkt het resultaat
met het doelcluster en synchroniseert alleen de afwijkingen.
Van directory naar cluster
- 01Commitreviewed Git state
- 02OntdekkenApplicationSet
- 03RenderenKustomize + Helm
- 04Validerenpolicy + diff
- 05Reconciliërenprune + self-heal
Drie soorten ApplicationSets
- Lokale componenten: infrastructuur, platformdiensten en workloads uit deze repository.
- Clusterconfiguratie-gedreven: configuratie uit infra bepaalt welke instance uit een andere repository wordt aangesloten.
- Externe workloadrepositories: teams beheren hun deploymentconfiguratie zelf; de platformrepository bepaalt via welk AppProject en naar welke bestemming dat mag.
Operators en instances blijven zo gescheiden. De Keycloak- en RabbitMQ-operators horen bij het platform; hun instances staan bij de dienst die er verantwoordelijk voor is.
Volgorde en omgevingsscheiding staan in code
Platformcomponenten hebben harde afhankelijkheden. Gateway API-CRD’s moeten bestaan
voordat Istio en gateways kunnen starten. De gewenste volgorde staat daarom als syncWave bij de component. De ApplicationSet zet deze waarde als annotation op
de gegenereerde Application.
Afhankelijkheden als sync-waves
De ApplicationSets zijn één keer gedefinieerd in een gedeelde base. De stage-selector
heeft daar expres de ongeldige waarde REPLACE_ME. Alleen een overlay voor
nonprod of prod vult een geldige stage in. Een per ongeluk gerenderde base selecteert
dus niets. Ook toekomstige clusters blijven als .template staan totdat ze bij
Argo CD zijn geregistreerd.
Gegenereerde Applications gebruiken automated sync met prune en selfHeal. Handmatige clusterwijzigingen worden teruggedraaid en verwijderde
Git-resources verdwijnen uit het cluster. Afwijkingen voor controllers of webhooks
worden gericht opgenomen in ignoreDifferences, zodat noodzakelijke mutaties
geen permanente sync-lus veroorzaken.
Grenzen vóórdat een manifest het cluster bereikt
Vier AppProjects scheiden bootstrap, infrastructuur, platformdiensten en applicaties.
Per project ligt vast welke repositories, clusters, namespaces en clusterbrede
resourcetypes zijn toegestaan. Een workload kan daardoor geen ClusterRole aanmaken als het project dat niet toestaat, ook niet wanneer het manifest in de eigen repository
staat.
Argo CD gebruikt deny-all-by-default RBAC. Aanmelding verloopt via Entra ID; groepslidmaatschap bepaalt de beschikbare projecten en verhoogde rechten worden via PIM geactiveerd. Secrets volgen één route: Azure Key Vault, via Workload Identity en External Secrets Operator, naar een Kubernetes Secret. In Git staat alleen de verwijzing.
De pull request is de eerste beleidsgrens
Elke gewijzigde cluster-directory wordt gerenderd. YAML- en schemacontroles, padvalidatie, Gitleaks, RBAC-validatie, Trivy en Kyverno CLI controleren daarna de werkelijke output. De manifest-diff maakt zichtbaar wat een chart-update echt verandert; één gewijzigde versieregel kan immers honderden regels aan manifests opleveren.
- BuildKustomize per cluster-directory
- SchemaYAML en Kubernetes-resources
- SecretsGitleaks vóór merge
- PolicyKyverno CLI en Trivy
- RBACAppProjects en rechten
- ReviewDiff van gerenderde manifests
Een platformlaag van CNCF-bouwstenen
De infrastructuurlaag telt 24 componenten. Niet ieder cluster krijgt dezelfde set: de functie van het cluster bepaalt welke operators en platformdiensten nodig zijn.
| Bereik | Componenten | Functie |
|---|---|---|
| Alle clusters | Istio, Gateway API, External Secrets, ExternalDNS, Kyverno, Alloy, kube-state-metrics | Netwerk, secrets, policy en telemetry |
| infra | cert-manager, CloudNativePG, Keycloak Operator, Grafana, Mimir, Loki, Tempo, Actions Runner Controller | Centrale platform- en managementdiensten |
| worker | KEDA, RabbitMQ Operator, Redis Operator | Workloadgerichte platformdiensten |
| Selectief | Homepage, Reloader, Hubble UI, Kiali | Bediening en inzicht waar nodig |
Observability is centraal ingericht. Alloy verstuurt metrics, logs en traces vanaf elk cluster naar de LGTM-backend op infra. Dat houdt lokale agents klein en maakt analyse over clustergrenzen heen mogelijk.
Updates schuiven gecontroleerd richting productie
Renovate houdt chart- en imageversies bij, maar verandert nooit alle clusters tegelijk. Per component en per omgeving ontstaat een afzonderlijke pull request. Versies landen eerst op vnext en nonprod. Productie volgt pas wanneer een release minimaal zeven dagen beschikbaar is. De exacte versie blijft per cluster vastgelegd.
Eén versie, drie release-lanes
De verhouding tot Haven+
Haven+ is een referentiearchitectuur en implementatie voor aanvullende platformdiensten op een Haven Kubernetes-omgeving. De richting sluit aan: GitOps, open-source componenten en herhaalbare inrichting van monitoring, identity, certificaten, databases, policy en secrets. Deze vergelijking is een architectuurtoets en geen formele conformiteitsclaim.
De overlap is groot: Alloy, cert-manager, CloudNativePG, ExternalDNS, External Secrets, Grafana, Loki, Mimir, Tempo, Istio en Keycloak komen in beide landschappen terug. De invulling verschilt waar het doelplatform daarom vraagt. Logs en traces gebruiken Loki en Tempo in plaats van ECK. Secrets blijven in Key Vault in plaats van versleuteld in Git. Kyverno, KEDA, RabbitMQ, Redis en Actions Runner Controller vormen aanvullende keuzes.
De Kubernetes-patronen zijn overdraagbaar. De Azure-integraties zijn dat niet vanzelf.
De implementatie is nadrukkelijk Azure-specifiek door AKS, Azure DNS, Key Vault, Workload Identity en Azure Container Registry. Een exitstrategie moet dat onderscheid expliciet maken: GitOps- en Kubernetes-patronen kunnen mee, cloudkoppelingen vragen vervanging.
Wat deze inrichting praktisch oplevert
- Maak de cluster-directory de eenheid. Chart, values en deploymentmetadata blijven bij elkaar.
- Laat ApplicationSets ontdekken. Een generator hoeft niet te veranderen wanneer een cluster of component wordt toegevoegd.
- Scheid de control planes. Productie krijgt een grens die niet alleen van RBAC afhankelijk is.
- Maak builds reproduceerbaar. Vendor charts en pin iedere versie per cluster.
- Leg de baseline vast in code. Policy en CI toetsen afspraken bij iedere wijziging opnieuw.
- Houd secrets buiten Git. Gebruik een externe secret store met workload-identiteit.
De opzet is niet af. Componenten en referentiearchitecturen blijven ontwikkelen. De blijvende waarde zit in de werkwijze: iedere wijziging is reviewbaar, herleidbaar en terug te draaien. Voor software waar inwoners dagelijks op rekenen, is dat een basisvoorwaarde.
Bronnen en context
De beschreven implementatie komt voort uit praktijkervaring bij WIGO4IT. Dit artikel wordt niet gepresenteerd als klantcase van Horst IT Consultancy. Genoemde aantallen en organisatorische keuzes zijn gebaseerd op de aangeleverde platformbeschrijving; de documentatielinks onderbouwen de gebruikte GitOps-mechanismen.
