Ich zeige dir, wie du mit bpftool zielgerichtet in laufende Linux-Systeme schaust, eBPF-Programme steuerst und dabei ohne Kernel-Rebuild aussagekräftige Telemetrie erhältst. Der Beitrag führt Schritt für Schritt durch Installation, Kernkonzepte, typische Einsätze und sinnvolle Routinen, damit du Kernelanalyse sicher in Betrieb und Entwicklung nutzt.
Zentrale Punkte
Zum Einstieg fasse ich die wichtigsten Aspekte zusammen, damit du die folgenden Kapitel gezielt einordnest und Prioritäten setzen kannst.
- Kernnähe: Direkter Zugriff auf eBPF-Programme, Maps und Statistiken
- Transparenz: Verifier-Logs, Bytecode und JIT-Dumps für Fehlersuche
- Produktionsreife: JSON-Ausgaben, Skriptbarkeit, reproduzierbare Abläufe
- Breite: Netzwerk, Systemaufrufe, Scheduler, cgroups, perf_events
- Ökosystem: Ergänzt High-Level-Tools wie BCC und bpftrace
Ich nutze die genannten Punkte, um praxisnahe Schritte zu zeigen und Entscheidungen zu erleichtern. So erkennst du schnell, wo bpftool direkten Nutzen bringt und wo andere Tools besser passen. Die Liste dient als Leitfaden für die Beispiele in den weiteren Kapiteln und hält den Fokus auf Messbarkeit. Halte beim Lesen dein Zielsystem im Blick, denn Konfiguration und Kernel-Version bestimmen die Optionen. Je klarer du deinen Zweck formulierst, desto schneller liefern eBPF und bpftool Signal statt Rauschen.
eBPF als sichere Laufzeit im Kernel
eBPF stellt eine sichere Laufzeit im Kernel bereit, die kleine Programme an definierte Ereignisse bindet und vor der Ausführung streng prüft. Der Verifier verhindert unzulässige Speicherzugriffe und Schleifen, dadurch bleiben Systeme kontrollierbar und betriebsfähig. Ich hänge Programme an Kprobes, Tracepoints, XDP oder cgroups an und erhalte präzise Kontextdaten. Diese Nähe liefert Messwerte ohne teure Syscall-Übergänge und ohne Module zu bauen. So entsteht eine flexible Telemetrie-Schicht, die ich mit bpftool sichtbar, testbar und steuerbar mache.
Installation und Voraussetzungen
Ich prüfe zuerst Kernel-Version und Features, denn viele Funktionen entfalten sich ab 5.x voll. Auf Distributionen installiere ich bpftool als Paket oder baue es aus den Kernel-Sourcen unter tools/bpf/bpftool, je nach Pflegezustand des Systems. Für den Build brauche ich Clang/LLVM, libelf, make sowie passende Header, damit die Toolchain zum Kernel passt. Nach der Installation kontrolliere ich mit “bpftool version” die Verfügbarkeit und gleiche sie mit meinen Anforderungen ab. Stimmen Kernel-Fähigkeiten, starte ich Tests auf einem separaten System, bevor produktive Hosts folgen.
bpffs und Pinning: Objekt-Lebenszyklus im Blick
Für reproduzierbare Abläufe mountiere ich zuerst das BPF-Dateisystem unter “/sys/fs/bpf”. Fehlt es, richte ich es mit “mount -t bpf bpf /sys/fs/bpf” ein und prüfe Namespaces, wenn Container im Spiel sind. Anschließend pinne ich geladene Objekte an stabile Pfade, zum Beispiel “bpftool prog pin id X /sys/fs/bpf/myapp/xdp_ingress” oder “bpftool map pin id M /sys/fs/bpf/myapp/counters”. So überleben Programme, Links und Maps Prozessneustarts, bleiben auffindbar und sind eindeutig adressierbar.
Ich strukturiere die Pinning-Hierarchie nach Service, Hook und Version, etwa “/sys/fs/bpf/dienst/hook/version”. Das erleichtert Rollbacks und Side-by-Side-Tests. Bei Attachments bevorzuge ich den Link-Ansatz: “bpftool link list” zeigt mir stabile Handles, “bpftool link pin id L /sys/fs/bpf/myapp/link_xdp” hält die Verknüpfung fest. Beim Aufräumen entferne ich zuerst die Pins (rm), dann sind Objekte freigegeben. So vermeide ich Waisen-Programme, die unbemerkt weiterlaufen.
Zentrale Subkommandos und Konzepte
bpftool strukturiert Befehle nach Objekttypen wie prog, map, cgroup oder feature, wodurch sich Arbeitsabläufe logisch gliedern lassen. Ich nutze “prog list” und “prog show” für den Überblick, “dump xlated/jited” für tiefe Einsichten und “map dump/lookup” für Datenfluss-Analysen. Das Subkommando “feature” zeigt die aktivierten Helper und Map-Typen, was spätere Fehler vermeidet. JSON-Ausgaben erleichtern die Automatisierung in CI/CD und Konfigurationsmanagement. Die folgende Tabelle fasst typische Aufgaben und Beispiele kompakt zusammen.
| Objekt | Aufgabe | Beispiel |
|---|---|---|
| prog | Programme auflisten und beschreiben | bpftool prog list | bpftool prog show id X |
| prog | Bytecode/JIT einsehen | bpftool prog dump xlated id X | dump jited id X |
| prog | Laden und Anhängen | bpftool prog load file.o /sys/fs/bpf/p && … attach |
| map | Inhalte und Keys prüfen | bpftool map dump id M | map lookup id M key HEX |
| feature | Kernel-Fähigkeiten anzeigen | bpftool feature probe |
BTF, CO-RE und Skeletons im Alltag
Ich achte darauf, dass BTF im Kernel verfügbar ist, weil es CO-RE (Compile Once – Run Everywhere) und komfortable Debug-Ausgaben ermöglicht. Mit “bpftool feature probe” sehe ich, ob BTF aktiv ist, und inspiziere die Typinformationen bei Bedarf mit “bpftool btf dump file /sys/kernel/btf/vmlinux”. Für die Entwicklung generiere ich aus den Kernel-Typen eine passende Header-Datei über “bpftool gen vmlinux”, wodurch ich Strukturen sicher referenzieren kann. Das reduziert Bruchstellen bei Kernel-Updates erheblich.
Beim Packaging setze ich auf Skeletons: “bpftool gen skeleton obj.o” erzeugt einen C-Wrapper, der Laden, Anhängen, Map-Zugriffe und Cleanups kapselt. Damit schrumpft mein Glue-Code, und ich halte die Interaktion zwischen User Space und eBPF-Programm robust. CO-RE hilft mir, dieselben Artefakte auf unterschiedlichen Kerneln einzusetzen, solange Helper und Hooks verfügbar sind – das prüfe ich früh mit “feature probe”.
Performance-Analyse mit bpftool
Für Leistungsfragen zähle ich mit bpftool die Aufrufe einzelner Programme, messe Laufzeiten und gleiche sie mit Workload-Spitzen ab. So erkenne ich, welche Traces stark feuern oder ob ein XDP-Filter auf heißen Pfaden zu viel CPU bindet. Ich bewerte dann, ob Sampling oder engere Filter logischer sind. Bei Anomalien schaue ich mir JIT-Dumps an, um Codepfade zu verstehen und unnötige Instruktionen zu meiden. Am Ende fließen die Zahlen in Dashboards, damit Betreiber dauerhafte Transparenz behalten.
Zählwerte, per-CPU-Maps und Statistiken
Ich werte “bpftool prog show id X” aus, um “run_time_ns” und “run_cnt” zu prüfen. Deren Verhältnis zeigt mir durchschnittliche Laufzeiten, Ausreißer deute ich mit Workload-Metriken. Bei Map-Zählern achte ich auf per-CPU-Varianten: Manche Dumps zeigen pro-CPU-Werte, andere aggregieren. Für genaue Analysen nutze ich maschinenlesbare Ausgaben und berechne Aggregationen bewusst, damit Spitzen auf einzelnen CPUs nicht untergehen.
Für schnelle Einblicke in Trace-Ausgaben rufe ich “bpftool prog tracelog” auf. Damit lese ich Print-Ausgaben aus dem Tracing-Puffer, ohne separate Tools zu bemühen. In produktiven Umgebungen begrenze ich solche Prints stark und ersetze sie durch Zähler in Maps oder Ringpuffer-Events, um Overhead und Rauschen zu vermeiden.
Netzwerk-Observability: Pakete, Flows, Fehler
Im Netzwerkumfeld prüfe ich XDP- und TC-Programme, lese Maps mit Zählerständen aus und identifiziere Hotspots entlang der Datenwege. Ich nutze bpftool, um droppende Regeln sichtbar zu machen und Flows zu charakterisieren. Treten Fehlentscheidungen bei Filtern auf, zeigen Map-Dumps die realen Schlüssel und Werte. So finde ich rasch Unterschiede zwischen erwarteter und tatsächlicher Verarbeitung. Für vertiefende Toolauswahl hilft mir dieser Überblick zu eBPF-Analysewerkzeuge, der den Einsatz im Hosting-Umfeld konkret einordnet.
XDP/TC-Varianten und Sichtbarkeit mit bpftool net
Beim Netzpfad prüfe ich mit “bpftool net” die an Interfaces angehefteten Programme. So erkenne ich, ob XDP im Generic-, Native- oder Offload-Modus läuft und welche TC-Hooks (ingress/egress) belegt sind. Stimmen die Modi nicht, korrigiere ich Attach-Optionen oder Treiber-Parameter. Ich dokumentiere die Ausgaben regelmäßig als Artefakt, damit Änderungen an Netzpfaden nachvollziehbar bleiben.
Für Hotpaths ziele ich auf kurze Pfade: XDP-Programme sollen früh entscheiden (pass/drop/redirect), TC-Programme konsolidieren Regeln und vermeiden redundante Lookups. Mit Map-Statistiken bewerte ich Trefferqualität, und JIT-Dumps geben Aufschluss, ob Sprungmuster ungünstig sind. Werden Queuing- oder Checksum-Kosten sichtbar, justiere ich Filter und überdenke die Platzierung zwischen XDP und TC.
Sicherheitsüberwachung und Compliance
Ich verwende eBPF-Programme für Prozess-Starts, Dateizugriffe und Netzereignisse, um sicherheitsrelevante Muster zu sehen. Mit bpftool prüfe ich, welche Programme aktiv sind, wo sie andocken und ob Regeln greifen. Stimmen die Anhängepunkte, kontrolliere ich Map-Inhalte, um Richtlinien belegt zu dokumentieren. Bei Verdachtsfällen greife ich auf Verifier-Logs und Bytecode zurück, um die Logik zu überprüfen. Diese Einsicht beschleunigt Audits und macht Agent-Verhalten für Teams verständlich.
Berechtigungen, Isolierung und Sicherheitsmodelle
Im Betrieb achte ich auf klare Berechtigungen. Auf vielen Systemen sind unprivilegierte eBPF-Funktionen deaktiviert; ich plane daher mit dedizierten Service-Accounts und spezifischen Capabilities. Je nach Kernel-Version kommen CAP_BPF, CAP_PERFMON und CAP_NET_ADMIN zum Einsatz, während CAP_SYS_ADMIN nur dort genutzt wird, wo es unvermeidbar ist. Ich isoliere bpffs nach Namespaces, wenn Container eigene Traces benötigen, und grenze cgroups so ab, dass Attachments gezielt wirken.
Für Compliance friere ich sensible Maps nach dem Befüllen mit “bpftool map freeze” ein. Dadurch werden Richtlinien schreibgeschützt, während Programme weiterhin lesen können. In Audits dokumentiere ich den Programm-Tag und die Attach-Punkte, sodass Entscheidungen reproduzierbar bleiben, auch wenn Artefakte neu gebaut werden.
Eigene eBPF-Programme: Laden, Anhängen, Debuggen
In der Entwicklung kompiliere ich C-Quellen mit Clang zu eBPF-Objekten, lade sie mit bpftool und verbinde sie mit Hooks. Schlägt der Verifier an, speichere ich das Log und reduziere Schritt für Schritt riskante Pfade. Ich kontrolliere übersetzten Bytecode und JIT-Ausgabe, um Instruktionsfolgen zu bewerten. Stimmen die Ergebnisse, schreibe und lese ich Testdaten über Maps und prüfe Eckfälle. Das verkürzt Feedback-Schleifen und hält meine Toolchain für Experimente wie auch Produktion einheitlich.
CO-RE-Strategie und stabile Artefakte
Damit Builds länger tragen, setze ich auf CO-RE. Ich binde BTF-Informationen ein, nutze “gen vmlinux”, und prüfe die Relocations während des Ladens. Treten Abweichungen in Kernel-Strukturen auf, deckt das Verifier-Log die Stellen auf. Ich halte die Programme so generisch wie möglich und lagere policies in Maps aus. Der Vorteil: Bei Schemaänderungen aktualisiere ich nur die Daten, nicht den Code. Mit Skeletons automatisiere ich Setup, Pinning und Cleanup, was gerade in CI/CD-Pipelines Fehlerquoten deutlich senkt.
Zusammenspiel mit High-Level-Tools
Für schnelle Erfolge setze ich zuerst auf BCC-Skripte und nutze sie als Ausgangspunkt für tiefergehende Analysen. Sobald ein Skript nützliche Signale liefert, inspiziere ich mit bpftool die zugrunde liegenden Programme und Maps. Dieser Wechsel zeigt mir, was wirklich im Kernel geladen ist und welche Datenstrukturen laufen. Dadurch trenne ich Komfort-Schicht und tatsächliche Objekte klar. Für den Überblick lohnt sich ein Blick auf diese kompakten BCC-Tools, die häufige Fragen mit wenigen Befehlen abdecken.
Best Practices für den Betrieb
Ich trenne Test- und Produktionsumgebungen strikt, sammle Verifier-Logs früh und halte Rollbacks bereit. Vor jedem Rollout prüfe ich “bpftool feature”, damit Programmtyp, Helper und Map-Varianten zum Ziel passen. Statistiken der Programme binde ich in bestehendes Monitoring ein, um Overhead sichtbar zu halten. Alle Anhängepunkte dokumentiere ich fortlaufend, denn nur so behalten Teams den Überblick. Wer tiefer einsteigen möchte, findet bei den eBPF-Analysewerkzeuge weitere Impulse für Workflows.
Ressourcenmanagement, Cleanup und Rollback
Ich nutze Pins, um definierte Zustände zu schaffen, und räume sie aktiv auf. Für Rollbacks halte ich die vorherige Version im selben Namespace bereit (z. B. “/sys/fs/bpf/myapp/v1” und “/sys/fs/bpf/myapp/v2”). Das Umschalten erfolgt durch Neu-Attach oder Link-Wechsel mit minimaler Ausfallzeit. Anschließend entferne ich alte Links und Maps, damit keine Ressourcen lecken. Vor dem Löschen prüfe ich, ob noch Referenzen existieren (“prog show”, “link list”, “map show”).
Um Konfigurationsdrift zu vermeiden, friere ich Maps, die Policies enthalten, und schreibe Änderungen ausschließlich durch definierte Deployments. Batch-Updates plane ich außerhalb von Lastspitzen, beobachte Laufzeit und Fehlerzähler, und bestätige erfolgreiche Aktualisierungen mit einem zweiten “map dump”.
Automatisierung und JSON-Ausgaben
JSON-Flag und maschinenlesbare Formate machen bpftool gut skriptbar für CI/CD, CMDBs und Audits. Ich versiegle Builds reproduzierbar, dokumentiere Hashes der Objektdateien und speichere bpffs-Pfade. So verknüpfe ich Deployments mit konkreten Programmen und Maps. Einfache Wrapper-Skripte schreiben Statusberichte nach jeder Änderung auf die Konsole und in Artefakte. Dadurch bleibt die eBPF-Landschaft dauerhaft prüfbar.
Vertrauen schaffen: Tags, Hashes und Artefakte
Ich lese nach dem Laden den Programm-Tag aus (“bpftool prog show id X”), der aus dem Bytecode abgeleitet ist. Den Tag verknüpfe ich mit Buildnummer und Commit-Hash in meiner CMDB. Bei späteren Prüfungen vergleiche ich erwarteten und aktuellen Tag – so erkenne ich Abweichungen ohne Zugriff auf die Original-Binaries. Für Maps protokolliere ich Typ, Key-/Value-Größen und Flags, damit sich Strukturänderungen bei Updates rechtzeitig zeigen.
bpftrace in der Praxis
Für Ad-hoc-Traces nutze ich bpftrace, wenn wenige Zeilen Syntax schnell Antworten liefern sollen. Den erzeugten Effekt überprüfe ich anschließend mit bpftool, um Programme, Anhängepunkte und Maps exakt zu sehen. So kombiniere ich Ausdrucksstärke mit Kernnähe und halte beide Sichten synchron. Für einen Startpunkt eignet sich dieser kurze Überblick zu bpftrace, der typische Abfragen gut rahmt. Sobald ein Pattern sitzt, migriere ich es bei Bedarf in kompakte C-Programme.
Fehleranalyse mit Verifier-Logs
Bei Ablehnungen durch den Verifier suche ich zuerst nach potenziellen Null-Dereferenzen, fehlenden Bounds-Checks oder zu langen Pfaden. Ich vereinfache die Logik, isoliere fragliche Helferaufrufe und validiere Offsets. Hilfreich ist es, große Maps zu verkleinern und Hotpaths in klar abgegrenzte Blöcke zu teilen. JIT-Dumps zeigen mir, ob Schleifen ungewollt expandieren oder Sprünge ineffizient ausfallen. Mit jedem Schritt schrumpfen Fehlermeldungen, bis das Programm verlässlich lädt.
Typische Fehlerbilder schnell einordnen
Sehe ich Meldungen wie “invalid mem access” oder “R.. unbounded loop”, prüfe ich Array-Grenzen, Pointer-Validierung und Loop-Begrenzungen. Bei CO-RE-Problemen deuten Hinweise auf fehlende oder unpassende BTF-Daten; ich verifiziere “/sys/kernel/btf/vmlinux” und passe Zielstrukturen an. Schlägt das Laden aufgrund fehlender Helper fehl, zeigt “feature probe” die verfügbaren Helfer und Map-Typen. Treten beim Dumpen JIT-Probleme auf, kontrolliere ich, ob JIT aktiviert ist und ob Hardening-Optionen die Ausgabe unterbinden.
Verhaken sich Attachments, liegt oft ein noch gepinnter Link vor. Ich liste Links auf, löse sie gezielt und entferne anschließend die Pins. Bei “EBUSY” prüfe ich, ob eine andere Instanz des Dienstes Objekte offen hält und plane eine kurze, koordinierte Umschaltung.
Ausblick: bpftool und moderne Kernelanalyse
Mit neuen Kernel-Releases wachsen Programmtypen, Helper und Statistiken, und bpftool spiegelt diese Fortschritte schnell wider. Ich plane deshalb Zeit für regelmäßige Updates ein, damit Tooling und Dokumentation auf Stand bleiben. JSON-Verbesserungen und neue Subkommandos eröffnen zusätzliche Automatisierungspfade. Gleichzeitig reift das Zusammenspiel mit High-Level-Stacks, was Onboarding vereinfacht. Wer diese Entwicklung aktiv verfolgt, gewinnt bei Diagnose, Tuning und Sicherheit Tempo.
Kurz zusammengefasst
bpftool gibt mir direkten Zugriff auf eBPF-Programme und ihre Datenstrukturen und macht Abläufe im Kernel sichtbar. Ich erkenne Engpässe, prüfe Sicherheitsregeln und entwickle eigene Traces, ohne den Kernel umzubauen. Mit sauberer Installation, klaren Tests und Skripting bleibt die Nutzung reproduzierbar. High-Level-Tools beschleunigen den Einstieg, während bpftool die tatsächlichen Objekte verlässlich dokumentiert. So bringe ich Observability und Diagnose auf ein belastbares Niveau, das im täglichen Betrieb trägt.


