Warum wir keinen eigenen eBPF-Sensor geschrieben haben
Beyla löst bereits den schwierigen, mühsamen und ewigen Teil des Tracings auf Kernel-Ebene. Ihn neu zu bauen hätte uns das Einzige gekostet, das wir nicht zurückbekommen können.
23. Juni 2026 · 6 Min. Lesezeit
Der Runtime-Agent muss wissen, wie schnell ein Service antwortet und wie oft er fehlschlägt, ohne die Anwendung zu berühren. Das ist ein leicht geschriebener Satz in einem Pitch Deck. Es aus einem laufenden Prozess ohne SDK, Sidecar oder Neustart herauszubekommen ist ein anderes Problem, und in den ersten Wochen des Agent-Baus gingen wir davon aus, dass wir es selbst lösen würden.
Der Plan war auf dem Papier unkompliziert: Uprobes auf SSL_write/SSL_read für TLS-Traffic, Kprobes auf tcp_sendmsg für den Rest, HTTP- und gRPC-Framing in einem kleinen eBPF-Programm parsen, RED-Metriken (rate, errors, duration) liefern, ohne dass der Zielprozess je merkt, dass er beobachtet wird. Innerhalb einer Woche hatten wir einen Proof of Concept gegen einen einfachen Go-HTTP-Server am Laufen. Dann richteten wir ihn auf einen echten Spring-Boot-Service — und er brach zusammen.
Wo es tatsächlich bricht
Nicht beim eBPF-Verifier — dieser Teil ist wirklich gut durchdacht, und der Kernel lehnt ein Programm, das loopen oder abstürzen könnte, lange bevor es läuft, ab. Es bricht an der Userspace-Grenze, in dem Teil, den niemand im Konferenzvortrag erwähnt: Uprobe-Offsets sind eine Funktion des exakten Bibliotheks-Builds. Der Offset in SSL_write für OpenSSL 3.0.2 unter Ubuntu 22.04 ist nicht derselbe wie für OpenSSL 3.0.13 auf derselben Distribution sechs Monate später, und es ist wieder eine andere Zahl im musl-gelinkten Build von Alpine, und wieder eine andere, sobald man sich innerhalb einer JVM befindet, die ihr eigenes TLS über einen gebündelten Provider statt den des Systems abwickelt. Multiplizieren Sie das mit jeder Sprach-Runtime, jedem Container-Basisimage, jeder Nebenversion, die ein Kunde zufällig fährt, und Sie erhalten eine Offset-Matrix, die ständige Neuherleitung und einen Testaufbau für Kernel erfordert, die man nicht selbst kontrolliert.
Nichts davon ist ein Forschungsproblem. Es ist für immer ein Wartungsproblem, und es hört nicht auf, sobald man ausliefert — jedes OpenSSL-Point-Release ist eine potenzielle Regression, von der man aus einer Slack-Nachricht eines Kunden erfährt statt aus der eigenen CI.
Warum wir uns stattdessen für Beyla entschieden haben
Beyla (das eBPF-Instrumentierungsprojekt von Grafana Labs) trägt diese Wartungslast bereits, öffentlich, mit einer Community von Leuten, deren Vollzeitjob es ist, Kernel- und Bibliotheks-Drift genau in den Umgebungen zu jagen, die wir sonst Kunde für Kunde entdecken müssten. Es liefert uns RED-Metriken pro Service — Request-Rate, Fehlerrate, p50/p99-Dauer — mit null Code in der beobachteten Anwendung. Uns bleibt Spielraum, unser eigenes eBPF-Budget für den Teil auszugeben, der wirklich unserer ist: procnet, unser eigener Collector, der /proc/<pid>/net/tcp liest, um Live-TCP-Verbindungen auf Container abzubilden — eine engere, stabilere Oberfläche als das Parsen von L7-Protokollen im Kernel.
Die ehrlichen Kosten dieser Entscheidung sind, dass der Agent heute als zwei Container statt einem ausgeliefert wird — unserer und der von Beyla, beide mit erhöhten Rechten auf dem Host. Das ist Reibung, an der wir aktiv arbeiten (Beyla als überwachten Kindprozess des Agent-Binaries zu bündeln ist der nächste Schritt), aber es ist ein UX-Problem zu lösen, keine Ingenieurswette, die wir gegen ein bewegliches Ziel gewinnen müssen. Wir zeigen diese Reibung lieber ehrlich, als vorzugeben, etwas gebaut zu haben, das wir nicht gebaut haben.
Die Regel, bei der wir gelandet sind
Bauen Sie das, was tatsächlich Ihr Produkt ist. Für uns ist das der Graph: die Entitätsauflösung, die "dem Orders-Service" dieselbe Identität gibt, egal ob er aus einem Dockerfile, einem laufenden Container oder einer TCP-Verbindung gesehen wird, und die Korrelationslogik, die entscheidet, ob eine Abhängigkeit real ist. eBPF an der Kernel-ABI-Grenze, über jede Sprache und jedes Container-Basisimage hinweg, das ein Kunde jemals ausführen könnte, ist bereits der Vollzeitjob anderer Leute — und sie sind gut darin.
Möchten Sie das an Ihrem eigenen System sehen?
Für den statischen Graphen genügt eine Repository-URL. Der Runtime-Agent braucht nur eine docker-compose-Datei.
