Uitgangssituatie
Het platform bestond uit twee werelden: een productie- en een acceptatiecluster op managed Kubernetes, en daarnaast een flinke groep klassieke VM's met databases, webservers en message brokers. Die VM's werden gemonitord door één Prometheus-server op een losse monitoringhost, met Jaeger voor traces en Graylog voor applicatielogs. Elk onderdeel had zijn eigen interface, zijn eigen bewaartermijn en zijn eigen gaten.
Het doel: één plek waar je bij een storing metrics, logs en traces naast elkaar legt, met dezelfde labels, en waar productie en acceptatie gescheiden blijven.
Ontwerp
| Signaal | Opslag | Route |
|---|---|---|
| Metrics | Prometheus + Thanos (S3) | Prometheus per cluster met Thanos-sidecar; de VM-monitoringhost doet remote_write naar Thanos Receive |
| Logs | Loki (S3) | Alloy-DaemonSet in Kubernetes, Alloy op de monitoringhost voor VM's |
| Traces | Tempo (S3) | OpenTelemetry via Alloy, met een persistente wachtrij in elke schakel |
Een paar keuzes die het verschil maakten:
- Twee gescheiden stacks. Productie en acceptatie hebben elk hun eigen Thanos, Loki en Tempo met een eigen bucket. Grafana kan bij beide, maar een vergissing in acceptatie raakt productie niet.
- De monitoringhost zo licht mogelijk. Daar kwam alleen een
remote_writebij, geen extra componenten. Series die niemand gebruikt (per-index-statistieken van Elasticsearch, ruim de helft van alle series) worden vóór verzending gedropt. - Buffer in elke schakel. Tussen het datacenter en het cluster loopt een tunnel. Iedere hop heeft een disk-backed queue, zodat een korte onderbreking geen gat in de data geeft.
- Alles in Git. Dashboards, alertregels, datasources en Helm-values staan in repositories en worden door Flux uitgerold. Alerts hebben
promtool-unittests en elke alert linkt naar een runbook.
Traces migreren zonder big bang
Jaeger draaide al jaren en de developers waren eraan gewend. In plaats van een harde overstap bleef Jaeger de voordeur: een tweede exporter stuurt een kopie naar Alloy, die de spans via de tunnel naar Tempo brengt. Zo konden we Jaeger en Tempo dagenlang naast elkaar vergelijken, alleen op aantallen, en pas daarna omschakelen.
Een les onderweg: tail sampling leek verstandig, maar liet precies de trage traces vallen, omdat de root-span pas binnenkwam na het beslismoment. Bij een paar honderd spans per seconde is sampling niet nodig; nu gaat alles naar Tempo.
Privacy by design
Traces en logs bevatten meer dan je denkt: zoekparameters, e-mailadressen, identificerende nummers. Een transform-processor in Alloy maskeert die waarden voordat ze worden opgeslagen, terwijl queries, ID's en tijdstempels leesbaar blijven. Bij de eerste versie miste de regex query-strings die met ? beginnen; een controle met TraceQL (alleen tellingen) vond het lek binnen een uur. Sindsdien test elke maskeringsregel ook op die varianten.
Snelle dashboards met recording rules
Security-dashboards die elke alert met | json parsten, werden traag zodra er veel data was. De oplossing was hybride: de overzichtspanelen komen uit metrics die de Loki-ruler elke minuut berekent (recording rules met remote_write naar Prometheus), de detailpanelen blijven in Loki. Een dashboard dat eerst seconden nodig had, laadt nu direct.
Resultaat
- Eén Grafana met productie en acceptatie, metrics, logs en traces gekoppeld via dezelfde labels.
- Lange retentie voor metrics: 90 dagen ruw, daarna gedownsampled tot twee jaar, in goedkope objectopslag.
- Alerts die zelf getest worden, met runbooks die beschrijven hoe je een oorzaak herkent en afstreept.
- Gevoelige gegevens gemaskeerd vóór opslag, aantoonbaar gecontroleerd.
Wat ik ervan leerde
Observability is geen product dat je installeert, het zijn afspraken: welke labels, welke bewaartermijn, wie reageert op welke alert. De techniek volgt daarna. En: meet nooit op een korte steekproef. De meeste "problemen" die ik tegenkwam, bleken meetfouten in mijn eigen query.