Wat Kost Digitale Autonomie Eigenlijk? Mijn FinOps-traject op een Soevereine Cloud

Een vervolg op mijn reis naar digitale autonomie — dit keer niet over bouwen, maar over weten wat je bouwsel kost.

Inleiding — Grip op kosten is grip op je infrastructuur

In een eerder artikel beschreef ik hoe ik een volledig soevereine cloud heb gebouwd: Nextcloud en Collabora als antwoord op Microsoft 365, Keycloak als eigen identity provider, alles draaiend op een Kubernetes-cluster bij Scaleway, volledig via Infrastructure as Code en GitOps beheerd. Dat fundament staat er, en het werkt.

Maar ergens onderweg stelde ik mezelf een vraag die ik niet goed kon beantwoorden: wat kost Keycloak (mijn identity-platform) mij eigenlijk, los van Nextcloud? Beide draaien op hetzelfde gedeelde cluster, in hetzelfde Scaleway-project (EU Cloud Service Provider met data centers in o.a. Parijs, Amsterdam). Ik kon een totaalfactuur zien, maar niet wat welk platform bijdroeg.

Waarom FinOps?

Hier komt FinOps (Financial Operations) om de hoek kijken. In de kern is FinOps een culturele én technische discipline die engineering, finance en de business samenbrengt. Het doel? Niet simpelweg besparen om het besparen, maar volledige transparantie en financiële controle krijgen over je (hybrid) cloudgebruik.

Mijn ervaring bij grote internationale organisaties is dat dit inzicht helaas opvallend vaak ontbreekt. Cloud-omgevingen groeien snel & organisch, rekeningen worden voldaan, maar niemand weet precies welke applicatie welke kosten drijft. En het gaat om meer dan geld alleen:

  • Risico’s & Security-voetafdruk: Zichtbaarheid in ‘onbekende’ kosten legt vaak onbeheerde of vergeten resources bloot. Minder onbekende infrastructuur betekent een kleinere attack surface en meer eigenaarschap.

  • Milieudruk & Schaarse Middelen: Cloud capacity is niet oneindig. Onnodige idle workloads verbruiken stroom en schaarse rekencapaciteit. FinOps helpt verspilling tegen te gaan en maakt je footprint duurzamer.

Voor een R&D-project is onduidelijkheid over de opbouw van de factuur misschien een curiositeit. Voor een omgeving die ik serieus wil kunnen voorleggen aan mijn klanten in bijvoorbeeld de energie- en financiële sector — organisaties die onder NIS2 en de Cyberbeveiligingswet moeten aantonen dat ze volledige controle hebben over hun kritieke infrastructuur — is het onacceptabel.

Ik wilde bewijzen dat je FinOps niet alleen op enterprise-schaal kunt toepassen, maar dat het ook uitstekend zelfstandig en pragmatisch te implementeren is in je eigen R&D-omgeving. Om te ervaren, te leren en te laten zien hoe je direct de regie pakt.

Waarom Scaleway’s eigen Cost Manager het antwoord niet gaf

Mijn eerste aanname was: Scaleway heeft vast een Cost Manager, dat lost dit toch op? Niet dus — en niet uit slordigheid van Scaleway, maar om een fundamentele reden.

De onderliggende Billing API (/billing/v2beta1/consumptions) splitst kosten uit naar organisatie, project, productcategorie en factuurperiode. Er is echter geen tag-veld op dat niveau. Een kostenregel weet niet welk “platform” de bijbehorende resource bedient; dat concept bestaat simpelweg niet in die laag.

Wilde ik kosten per platform zien, dan moest ik die koppeling zelf bouwen. Dat betekende: een consistente tagging-conventie afdwingen over al mijn infrastructuur, en een eigen rapportagelaag die tags, kosten en Kubernetes-verbruik samenvoegt.

Mijn architectuurkeuze: taxonomie als policy, niet als goede bedoeling

Een tag-conventie die niemand hoeft te volgen, wordt in de praktijk niet gevolgd — dat weet iedereen die ooit een spreadsheet met “verplichte” kolommen heeft beheerd. Dus koos ik ervoor de taxonomie niet als documentatie te behandelen, maar als afgedwongen Policy as Code (PaC).

De taxonomie zelf is compact: vier verplichte sleutels op elke resource: platform, environment, owner-repo, managed-by.

  • platform ligt vast op een gesloten lijst: iam, office, monitoring, compliance, shared-infra.

  • Elk platform heeft een eigen toegestane set environment-waarden: prod/test voor platformen die daadwerkelijk gesplitst zijn (Keycloak, Nextcloud), en shared voor platformen die dat principieel niet zijn.

Een Traefik-load balancer eerlijk verdelen over prod en test is geen zinvolle oefening — dus wordt gedeelde infrastructuur bewust nooit willekeurig gesplitst, maar als eigen, transparante overheadregel gerapporteerd.

Die regels staan niet alleen in een README. Een centraal Policy-as-Code-project bevat de canonieke taxonomie (data/taxonomy.yaml) en een Rego/OPA-policy die elk Terraform-plan ertegen toetst vóórdat er iets wordt toegepast — shift-left, zoals ik dat overal in dit project toepas. Een resource zonder de vier verplichte tags, of met een ongeldige combinatie zoals platform:monitoring + environment:prod, wordt door de pipeline geweigerd. Niet achteraf gecorrigeerd, maar geblokkeerd vóór de deployment.

Twee werelden, één taxonomie: Scaleway-resources (database, VM’s, storage, netwerk) krijgen platte “key:waarde”-string-tags. Kubernetes-resources (namespaces, Pods, Deployments) krijgen native labels met dezelfde vier sleutels. Beide voeden uiteindelijk dezelfde rapportage.

SoD in de praktijk: twee GitLab-groepen, twee Claude Code-sessies, één supervisor

Dit project is opgebouwd volgens een strikte Separation of Duties (SoD): IaC, CaC en PaC leven in gescheiden GitLab-projecten, en elke GitLab-groep heeft zijn eigen Claude Code-integratie op een eigen lokale omgeving. Voor dit FinOps-traject betekende dat:

  1. Eén sessie werkte in de groep cloud-deneve-consulting (waar Nextcloud, monitoring en de gedeelde infrastructuur leven).

  2. Een tweede, volledig losse sessie werkte in identity-1 (waar Keycloak draait).

Dat klinkt als een technisch detail, maar het is precies waar de interessantste momenten van dit traject zaten. De taxonomie moest in cloud-deneve-consulting eerst bestaan voordat identity-1 er labels tegen kon zetten — een cross-group-afhankelijkheid die ik bewust zo had ontworpen, met de instructie aan de tweede sessie om dat zelf te verifiëren in plaats van aan te nemen dat het al klaarstond.

Op een gegeven moment bleek dat inderdaad zo: de tweede sessie trof een lege taxonomie aan, meldde dat expliciet in plaats van zelf iets te verzinnen of — verleidelijker nog — het ontbrekende fundament zelf te bouwen in de andere groep. Dat laatste zou de SoD die ik juist had ingericht, direct doorbreken. De sessie pauzeerde, deed ondertussen het voorbereidende verificatiewerk dat niet van de taxonomie afhing, en wachtte op mijn signaal.

Dat is precies het besturingsmodel dat ik wilde: architectuurbeslissingen worden hier, in gesprek, gemaakt en door mij goedgekeurd; de implementatie wordt gedelegeerd aan Claude Code; niets wordt gemerged zonder dat ik het resultaat heb gezien. AI als uitvoerder binnen expliciete kaders, niet als autonome beslisser.

De SoD opsplitsing is iets dat in de praktijk ook goed werkt in organisaties in sterk gereguleerde omgevingen (financiële sector, energie sector, defensie etc). Denk aan teams die eigenaarschap hebben op Infrastructuur, Compliance/Policies, Security en bijvoorbeeld op Applicatie code/microservices. In mijn R&D omgeving houd ik me bewust aan de SoD opsplitsing om zo dicht mogelijk tegen de praktijksituatie te blijven, en het werkt voor mij heel goed.

Wat er is gebouwd

De FinOps implementatie raakte uiteindelijk zeven code repositories, verdeeld over twee GitLab-groepen:

  • cloud-deneve-pac — De canonieke taxonomie en de afdwingende Rego-policy, test-driven ontwikkeld.

  • cloud-deneve-infra — Scaleway-tags op alle bestaande resources: managed database, VM’s, load balancer, privénetwerk en object storage.

  • cloud-deneve-app — Kubernetes-labels op alle namespaces, plus de bootstrap van OpenCost als kostenmotor in het cluster.

  • cloud-deneve-cac — Een kleine aanpassing op de monitoring-VM (een node_exporter textfile-collector) zodat het dagelijkse kostenrapport landt zonder een nieuw component te introduceren.

  • cloud-deneve-finops — Een nieuw project: het rapportscript dat drie databronnen combineert, met een uitgebreide testset en een dagelijkse GitLab CI-schedule.

  • identity-1/sso-k8s — Dezelfde taxonomie toegepast op Keycloak, de Patroni-PostgreSQL-cluster en de portal/demo-apps — de “andere kant” van de infrastructuur.

  • De docs-projecten — ADR’s (Architecture Decision Records) die elke keuze en bijstelling onderweg vastleggen.

De rapportagemotor combineert drie bronnen: de tags per resource via Scaleway’s Resource API’s, de kostenregels via de Billing API, en voor het gedeelde Kubernetes-deel de kostenverdeling per namespace via OpenCost (de open-source basis waar ook Kubecost op bouwt).

Het resultaat landt bewust niet in een nieuw dashboard-systeem, maar als Prometheus-metrics in de Grafana-instance die er al stond. Eén Grafana, geen wildgroei aan tools — een principe dat ik strikt hanteer.

Wat onderweg tegenviel — en waarom dat het leerzaamste deel is

“Het compileert” is niet hetzelfde als “het werkt”. Dat bleek op een paar plekken heel concreet:

  1. Object storage tagt anders dan de rest: Vier van de vijf Scaleway-resourcetypes gebruiken een platte lijst van "key:waarde"-strings. Object storage buckets (op AWS S3-API) gebruiken een key/value-map. Twee vormen, één policy. Dit is opgelost door beide te normaliseren naar hetzelfde interne formaat en pas af te vinken nadat er specifieke testscenario’s voor de map-vorm waren toegevoegd.

  2. De policy-tool gedroeg zich anders: Conftest bleek het taxonomie-bestand te nesten op basis van de mapstructuur in plaats van de bestandsnaam — een detail dat pas zichtbaar werd bij het daadwerkelijk draaien van live tests.

  3. De Billing API koppelt op naam, niet op ID: Het risico dat twee resources met dezelfde naam kosten aan het verkeerde platform toewijzen, kwam bij een code-review naar boven. Gefixt door bij twijfel een kostenregel expliciet als “niet gekoppeld” te markeren in plaats van te gokken. Liever een zichtbaar gat dan een onzichtbare fout.

  4. Een gat in de eigen boekhouding: Het repo dat na een eerdere migratie de facto eigenaar was geworden van de Keycloak/Patroni-resources (sso-k8s) stond niet in de canonieke lijst van repo-namen. Een klein documentatiegat dat direct is hersteld.

  5. Incomplete namespace-labels: Bij de eerste live run meldde het systeem “7 van de 10 namespaces volledig gelabeld”. De cluster-brede argocd– en kube-system-namespaces waren over het hoofd gezien. Precies het soort randjes dat pas zichtbaar wordt zodra je tegen levende infrastructuur test.

Geen van deze bevindingen wijst op een verkeerde architectuurkeuze. Ze onderstrepen waarom “verify, don’t assume” de kern van dit traject is.

Labels op een draaiende Keycloak: meer dan een metadata-patch

Een detail dat gemakkelijk over het hoofd te zien is: labels toevoegen aan Kubernetes-workloads raakt niet alleen metadata. Zodra een label in de Pod-template terechtkomt, verandert de pod-template-hash. Dat triggert een rolling update.

Voor Patroni (mijn 3-node PostgreSQL-cluster) is dat onschuldig en functioneel identiek aan eerdere switchover-tests. Voor Keycloak, draaiend met één replica en zonder voorbereide sessie-clustering, betekent dat een echt, kort downtime-venster.

Ik heb er bewust voor gekozen dat venster te accepteren en te timen op een rustig moment, in plaats van snel op te schalen naar twee replica’s. Zonder voorbereide Infinispan/JGroups-sessieclustering geeft “even opschalen” namelijk geen betrouwbare zero-downtime; het introduceert juist een nieuw, ongetest risico (inconsistente sessies) om een overzichtelijk probleem op te lossen. Meer complexiteit toevoegen om risico te verlagen is niet altijd de juiste ruil.

Met ArgoCD’s selfHeal actief paste de merge zich direct toe op productie. Ik heb de wijziging als een geobserveerde actie uitgevoerd: vooraf en achteraf de clusterstatus gecontroleerd, en na afloop een echte login-flow getest in plaats van blind op readiness-probes te vertrouwen.

Het dashboard: wat werkt, en wat nog niet

Het resultaat staat inmiddels in Grafana (zie onder).

Het Kubernetes-kostendeel (gevoed door OpenCost) toont live data: totale clusterkosten, uitgesplitst per platform, environment en owner-repo, tot op namespace-niveau. identity-1 (IAM) blijkt daarin het duurste onderdeel van het gedeelde cluster, gevolgd door office (Nextcloud) en monitoring. Precies het inzicht dat ik aan het begin van dit traject miste.

Screenshot – FinOps Dashboard – Grafana

De Scaleway-facturatiedata gekoppeld aan getagde resources staat er op de kaart nog niet direct in. Het dashboard toont dat nu ook eerlijk: 0 gematchte, 16 ongematchte factuurregels. Dit is geen verborgen bug, maar een aparte metric die laat zien wat er speelt: de infrastructuur is simpelweg te recent (opnieuw) aangemaakt om al in de facturatiecyclus te verschijnen. Scaleway’s billingdata heeft verwerkingstijd nodig. Het is een openstaand punt dat vanzelf bijtrekt, en het dashboard laat dat transparant zien.

Wat bewust nog niet is gebouwd: cluster-brede afdwinging van labels via een Kubernetes admission-controller (zoals Gatekeeper). Dat zou voor dit formaat project nu premature complexiteit zijn; pas zinvol zodra labeldrift zich in de praktijk voordoet.

Nog een kostenbesparing: de GitLab-runner naar huis gehaald

Los van de FinOps-tooling heb ik in dezelfde periode mijn GitLab-runner verplaatst van een gehoste omgeving naar lokaal. Een kleine ingreep, maar het past binnen dezelfde FinOps-gedachte: elke euro of resource die niet direct bijdraagt aan de waarde van het platform, hoort m.i. niet in de maandelijkse rekening thuis.

Wat ik heb geleerd over het besturen van meerdere AI-sessies

Dit traject leerde me iets over een belangrijk organisatorisch aspect van modern software engineering: hoe je twee onafhankelijke AI-coding-sessies, elk gebonden aan hun eigen scope, laat samenwerken zonder de vooraf ingerichte Separation of Duties te ondermijnen.

De sleutel was niet de AI blind te vertrouwen, maar de sessies te instrueren om te verifiëren in plaats van aan te nemen— inclusief aannames uit mijn eigen prompts.

  • Toen de aanname klopte dat identity-1 vrijwel geen eigen Scaleway-resources meer beheerde, was dat het resultaat van een actieve controle door de AI.

  • Toen de aanname dat de taxonomie al klaarstond niet bleek te kloppen, stopte de sessie. Er werd gerapporteerd en gewacht, in plaats van dat de AI zelfstandig de grens tussen de twee GitLab-groepen overschreed.

Dat is de kern van verantwoord AI-gebruik in een omgeving met echte productie-impact: niet de AI laten beslissen wanneer iets veilig is, maar de kaders zo inrichten dat “ik kan dit niet verifiëren” een geldig antwoord is — zodat de mens aan het stuur blijft op de cruciale momenten.

Conclusie — Soevereiniteit vraagt om kostentransparantie

Een soevereine cloud bouwen is stap één. Weten wat elk onderdeel ervan daadwerkelijk kost — betrouwbaar, geautomatiseerd en zonder handmatig spreadsheetwerk — is een minstens zo belangrijk vervolg.

Voor organisaties die onder NIS2 moeten kunnen aantonen dat ze volledige controle hebben over hun kritieke infrastructuur, is dit geen luxe. Kosten die je niet kunt toewijzen, zijn risico’s en blinde vlekken die je niet kunt beoordelen. Bovendien helpt het direct om de ecologische voetafdruk en de druk op schaarse middelen te verlagen.

Dit FinOps-traject heeft aangetoond dat deze inzichten zelfstandig goed te realiseren zijn. Het levert een helder, herbruikbaar patroon op:

  1. Een canonieke taxonomie.

  2. Afgedwongen als Policy as Code in plaats van een ‘vrijblijvende afspraak’.

  3. Consistent toegepast over gescheiden platformen door gescheiden AI-sessies (virtuele teams) onder menselijke supervisie én goed gebruik van CI/CD en DevSecOps concepten.

Dit patroon is direct toepasbaar op een volgende enterprise- of klantomgeving.

Dit artikel is onderdeel van een serie over het bouwen en beheren van een soevereine cloud-omgeving. Geïnteresseerd in hoe deze principes vertaald kunnen worden naar uw organisatie? Neem gerust contact op.

Tags: #finops #kubernetes #gitops #policy-as-code #opa #rego #digital-sovereignty #eu-cloud #terraform #argocd #devsecops #open-source #scaleway #it-architecture

Disclaimer

Dit artikel is uitsluitend bedoeld voor informatieve en educatieve doeleinden. De beschreven architectuur, configuraties, tools en aanpakken weerspiegelen persoonlijke ervaringen en inzichten opgedaan tijdens een specifiek leertraject. Aan de inhoud van dit artikel kunnen geen rechten worden ontleend. De auteur aanvaardt geen aansprakelijkheid voor eventuele schade, directe of indirecte gevolgen voortvloeiend uit het gebruik van de in dit artikel beschreven informatie, configuraties of methodieken. Prijsindicaties zijn gebaseerd op tarieven ten tijde van publicatie en kunnen zijn gewijzigd. Wet- en regelgeving waarnaar verwezen wordt, is aan verandering onderhevig — raadpleeg altijd de meest recente officiële bronnen. Voor toepassing in uw specifieke situatie wordt professioneel advies aanbevolen.

Dit artikel is tot stand gekomen met assistentie van een AI-systeem. De technische implementatie, de inhoudelijke keuzes, de conclusies en de eindredactie zijn volledig van de auteur.

Cover-Photo by Jp Valery on Unsplash