De verborgen koppeling in uw infrastructuur

Ah, software-infrastructuur en servicesbeheer… een onuitputtelijke bron van spirituele wijsheid en verrijking. Het kan je zóveel leren over het leven, over liefde… over de mensheid. Eén van de vele parallellen met infrastructuurwerk is dat het in het begin allemaal eenvoudig lijkt; de monsters en uitdagingen die onder de oppervlakte sluimeren laten zich pas zien wanneer je oud en vermoeid bent geworden (wat in de softwarewereld gelukkig vrij snel gaat: werk je meer dan een jaar aan hetzelfde, dan ben je al bijna een fossiel).

Neem eens een moment om een veelvoorkomende reis in dit vakgebied te overwegen. Je komt bij een startup binnen als “DevOps-medewerker”, begint te knutselen met een homelab, of zet een website op voor de lokale schaakclub. Alles lijkt aanvankelijk heerlijk eenvoudig: je zet een oude laptop naast een vergeten ethernetpoort of huurt een VPS voor een paar cent per dag, en flasht het meest veelbelovende Google-resultaat voor “goed gratis serverbesturingssysteem alsjeblieft help geen Chinese scams geen Microsoft”. Je installeert een paar pakketten, legt je collega’s uit hoe ze het systeem kunnen bereiken, en wacht af.

Uiteindelijk gaat er iets mis: de server raakt door zijn geheugen heen, een knaagdier knaagt door het moederbord, of je server heeft het ongeluk een “gewone gebruiker” te ontmoeten (zie Figuur 1). Op dat moment besef je dat je server en zijn gebruikers misschien toch monitoring nodig hebben. Je zit een nacht lang te surfen over het internet en hebt de volgende dag een paar componenten samengevoegd tot een prachtige observability-stack. Misschien is het een SystemD drop-in, misschien ElasticSearch, Grafana en Metricbeat. Misschien heb je Prometheus en een paar exporters geïnstalleerd. Hoe dan ook, alles lijkt prima… tot op een dag…

Figuur 1: Een doorsnee gebruiker die je zorgvuldig geconfigureerde VPS volledig negeert en onderuit haalt.

Een nieuwe afdeling op het werk. Je dochter sluit zich aan bij een voetbalclub waarvan de website al vijf jaar niet meer bereikbaar is. De hardware van je thuisserver moet worden geüpgraded. Als je geluk hebt, realiseer je je dat je, zelfs als je de meest logische stappen hebt genomen en die goed hebt uitgevoerd, eigenlijk al een ernstige fout hebt gemaakt. Je moet een nieuwe server opzetten, en met de manier waarop je het hebt aangepakt betekent dat: al het werk opnieuw doen.

Je huilt, je smeekt, je onderhandelt met je god of de leegte, maar er is geen ontkomen aan. Of je voert je werk een tweede keer volledig opnieuw uit, of je maakt alles ongedaan en zoekt een manier om het proces herhaalbaar te maken.

Maar vanuit een fundamenteel perspectief: wat is hier eigenlijk de fout? Is het het niet documenteren van de genomen stappen? Is het het niet gebruiken van een automatiseringsframework zoals Ansible of SaltStack? Hoewel dat in zekere zin praktische oplossingen zijn voor het probleem, loop je door je alleen op de marketing te richten en simpelweg “gebruiker” te worden het risico het echte, diepere probleem te missen: tijdens het opzetten van deze infrastructuur zijn de functionele configuratie en het uitrolproces onlosmakelijk met elkaar verweven geraakt (Figuur 2).

De configuratie van de server is de runtime-omgeving, en de runtime-omgeving is de configuratie. Een framework zoals Ansible maakt het mogelijk om die koppeling gedeeltelijk te doorbreken, waardoor het niet alleen het probleem van herhaalbaarheid oplost, maar je ook ineens de mogelijkheid geeft om je diensten uit te rollen op verschillende doelplatformen, zoals Debian, Arch Linux, enzovoort.

Figuur 2: Scheiding van configuratie- en runtime-omgevingen door het gebruik van tools zoals Ansible.

Een van de belangrijkste voordelen van een dergelijke opzet, zeker op grotere schaal of bij complexere vereisten, is juist die mogelijkheid om doelonafhankelijk te werken. Dit stelt een organisatie in staat om de goedkoopste opties voor hardware te selecteren, zonder grote overhead te schalen over verschillende datacenters, of te profiteren van extra compliance- en betrouwbaarheidsvoordelen door het faaloppervlak te spreiden over meerdere doelomgevingen.

Voor velen lijkt de stap van een runtime-omgeving als configuratie naar een gescheiden configuratie- en runtime-omgeving voldoende.

Bij SUE en andere toonaangevende bedrijven komen we echter problemen en infrastructuurimplementaties tegen op werkelijk enorme schaal. Denk bijvoorbeeld aan een overheid of onderneming die haar eigen (semi-)publieke cloud bouwt. In dergelijke situaties kunnen de vereisten van uitgerolde diensten op elk moment veranderen, terwijl het bredere landschap eveneens complex en voortdurend in beweging is. Dit vraagt om denken buiten de gebaande paden en het loslaten van een paradigma dat uitsluitend gebaseerd is op standaardtools en basis-configuratiebeheer.

In plaats daarvan laten we onze keuzes sturen door gezonde theoretische principes en een hoger niveau van abstract denken. In dit kader wordt de behoefte aan een extra laag van stratificatie zelden direct zichtbaar, maar kan deze in de praktijk bijzonder waardevol blijken.

Figuur 3: Gescheiden configuratie-, deployment- en runtime-omgevingen

Je loskomen van een specifiek onderliggend configuratiebeheersysteem betekent dat je plots een compleet nieuw niveau van automatisering kunt introduceren. Waarom nog tijd besteden aan het zorgvuldig schrijven van Salt-states, Terraform-code of Ansible-code, wanneer dit automatisch kan worden gegenereerd voor jouw platform op basis van een eenvoudigere projectie van de invoer?

Versiebeheer

Wat betreft versiebeheer krijg je, wanneer je je infrastructuur door deze lens bekijkt, een aantal nieuwe keuzes. In combinatie met Ansible is het vrij gebruikelijk om je configuratie te versioneren via Git en GitOps (bijvoorbeeld met GitLab-runners) te gebruiken om je systemen te deployen op basis van die configuratie.

Doordat je de configuratie hebt gescheiden van de onderliggende engine, kun je uitsluitend de onderliggende systeemcode die daadwerkelijk door engineers beheerd moet worden in Git versioneren, terwijl de systeemstatus zelf op een andere plek wordt opgeslagen.

Voor sommigen lijkt dit zinloos of onnodig eigenzinnig. En toch gebruikt zelfs Kubernetes een etcd-database voor het opslaan en beheren van de configuratie van de services waarvoor het verantwoordelijk is. De systeemstatus en objecten worden in een database opgeslagen, zodat het systeem gebruik kan maken van replicatie en kan schalen tot ver buiten wat een set GitLab-runners kan bieden, terwijl tegelijkertijd de daadwerkelijke broncode van Kubernetes afzonderlijk kan worden aangepast en geversioneerd.

Als je doel is om een paar services op te zetten, is de “gebruikelijke manier” prima. Maar als je werkt aan het mogelijk maken dat je bedrijf of klanten zelfstandig systemen kunnen opzetten, vergelijkbaar met Kubernetes of Azure, dan kan de extra flexibiliteit van het opslaan van configuratie “op je eigen manier” de extra moeite ruimschoots waard worden.

Pre-flight configuratievalidatie

Wat betreft pre-flight validatie van configuratie: wanneer je infrastructuur vanuit dit perspectief bekijkt, ontstaat opnieuw een belangrijk ontwerpkeuzegebied.

In klassieke Ansible- of Terraform-workflows wordt validatie vaak impliciet uitgevoerd tijdens de uitvoering zelf: je draait een plan, voert een playbook uit, en ontdekt tijdens de deploy-fase of iets wel of niet klopt. Dit werkt prima op kleinere schaal, maar wordt fragiel zodra de omgeving complexer wordt en veranderingen sneller en door meer teams worden doorgevoerd.

Door configuratie, deployment en runtime expliciet van elkaar te scheiden, kun je validatie naar voren trekken in de keten. In plaats van te wachten tot een systeem daadwerkelijk wordt uitgerold, kun je een “pre-flight check” uitvoeren op de geprojecteerde configuratie: een gecontroleerde evaluatie waarin wordt bepaald of de gewenste toestand überhaupt consistent, compleet en uitvoerbaar is.

Dit kan variëren van eenvoudige schema-validatie tot semantische checks die controleren of resources niet conflicteren, afhankelijkheden kloppen, of policies worden nageleefd voordat er ook maar één wijziging richting productie gaat. Op grotere schaal wordt dit essentieel: fouten die pas tijdens deployment zichtbaar worden, zijn niet alleen duur, maar kunnen ook zich horizontaal verspreiden door gekoppelde systemen.

In moderne platforms zie je dit principe terug in verschillende vormen. Kubernetes bijvoorbeeld voert een declaratieve reconciliatie uit waarbij gewenste toestand eerst wordt gevalideerd en daarna continu wordt vergeleken met de werkelijke toestand in etcd. Andere systemen bouwen hier extra lagen bovenop, zoals policy engines of admission controllers, die configuraties blokkeren of aanpassen vóórdat ze überhaupt worden toegepast.

Het resultaat van zo’n pre-flight laag is niet alleen betrouwbaarheid, maar ook een verschuiving in verantwoordelijkheid: van “we hopen dat de deploy werkt” naar “we weten vooraf dat deze wijziging coherent is binnen het systeemmodel”.

Wanneer je je infrastructuur koppelt aan een publieke API, een intern developerportaal of een team dat verantwoordelijk is voor service delivery, neemt de kans aanzienlijk toe dat fouten hun weg naar je backend vinden. Zodra je vastzit aan één specifieke manier van het opslaan, beheren en representeren van je configuratie, wordt het bovendien moeilijk om die configuratie te valideren op compliance, veiligheid en correctheid.

Sta je jezelf echter toe om zelf te bepalen hoe configuratie er voor jouw platform uitziet, dan kun je gebruikmaken van een breed scala aan tools — zoals OpenAPI-specificaties — of zelfs eigen, op maat gemaakte code om configuraties te valideren, aan te passen of te weigeren op basis van wat je überhaupt wilt ondersteunen binnen je platform.

Organisatorische wendbaarheid

Een van de mooie aspecten van denken in termen van deze abstracte bouwblokken is dat wanneer de technologie die je hebt gekozen voor deployment ooit uit de mode raakt (en als de geschiedenis ons iets leert, is die “als” eigenlijk een “wanneer”), je die in feite kunt vervangen zonder informatie te verliezen die nodig is om je machines opnieuw op te bouwen of te wijzigen.

Gebruik je bijvoorbeeld Terraform en wil je overstappen naar iets dat HCL niet begrijpt… dan wordt dat lastig. Maar wanneer je infrastructuur is gedefinieerd in een meer vendor-neutraal formaat, en jij de transformatie van die basisinput naar een volledig werkend systeem zelf bepaalt, heb je veel meer flexibiliteit en keuzevrijheid.

Zoals bij elk probleem geldt ook hier: de tools die je kiest moeten passen bij het probleem dat je probeert op te lossen. GitOps en “plain” Ansible zijn leuk en werken prima voor bepaalde setups, maar wanneer schaal of toegankelijkheid een knelpunt worden, kan een andere aanpak veel logischer zijn.

Een breder, systematisch perspectief op interne processen en configuratiebeheer vraagt wat initiële inspanning. Maar in de juiste context, en met ervaren en kritisch denkvermogen als leidraad, kunnen lastige ontwerpkeuzes op de lange termijn juist aanzienlijke voordelen opleveren.

Blijf op de hoogte
Door je in te schrijven voor onze nieuwsbrief verklaar je dat je akkoord bent met onze privacyverklaring.

* required

Enter the LEGO® giveaway
Get your 3-month FREE Multistax trial
By sending this form you indicate that you have taken note of our privacy Statement.