Ich zeige Schritt für Schritt, wie ich bcc tools mit eBPF einsetze, um Engpässe auf Linux-Servern zügig einzugrenzen und zu beheben. Dabei nutze ich praktikable Workflows, messe reale Latenzen im Kernel und verknüpfe Ereignisse aus CPU, I/O und Netzwerk zu einer klaren Ursachenanalyse.
Zentrale Punkte
- eBPF liefert tiefes Tracing mit geringem Overhead.
- bcc tools decken CPU, I/O, Netzwerk und Prozesse ab.
- Produktionsnah ohne App-Änderungen einsetzbar.
- Checkliste mit zehn Werkzeugen für den Start.
- Sicherheit durch Verifier und klare Policies.
Warum eBPF für Linux Performance Engineering
Ich greife zu eBPF, weil ich Kernel-Ereignisse sicher, selektiv und mit sehr wenig Overhead messen will. Klassische Tools zeigen Summen, doch sie erklären selten, warum Threads warten, Pakete erneut gesendet werden oder I/O hängen bleibt; eBPF füllt diese Lücke mit konkreten Events. Die Programme laufen im Kernel, der Verifier prüft sie vorab, und ich kann ohne Reboot starten. So korreliere ich Userland-Calls mit Kernelpfaden und erhalte ein Bild, das unmittelbare Optimierungen erlaubt. Wer den Einstieg vertiefen möchte, findet einen Überblick in meiner kurzen Einführung zur eBPF Performance-Analyse, die das Zusammenspiel von Tracing und Observability skizziert.
Was sind bcc tools und wo finde ich sie
Die bcc tools sind fertige Diagnoseprogramme auf Basis von eBPF und liegen typischerweise unter /usr/share/bcc/tools. Ich starte sie direkt auf der Shell, erhalte klare Standardausgaben und muss meine Anwendungen nicht umbauen. Die Sammlung deckt Prozesse, Systemaufrufe, Dateisysteme, Block-I/O, Netzwerk, Scheduler und Profiling ab und eignet sich damit für produktive Analysen. Da ich gezielt Tracing aktiviere, bleibt der Einfluss gering und Messfehler durch Überwachung fallen klein aus. Für tiefergehende Fälle ergänze ich die Werkzeuge um eigenes eBPF oder setze zusätzlich Sampling-Profile ein.
Installation und Voraussetzungen
Ich installiere die bcc tools über die Paketverwaltung (bcc-tools bzw. bpfcc-tools) auf gängigen Distributionen. Nötig sind ein Kernel mit eBPF-Support (ab 4.x, besser 4.9+), aktivierte BPF-Funktionen und ausreichende Rechte zum Laden der Programme. Auf produktiven Servern prüfe ich die eBPF-Fähigkeiten von Kernel und Distribution vorab in einer Testumgebung, damit spätere Messungen zuverlässig laufen. Sicherheitsprofile, die eBPF komplett sperren, verhindere ich durch abgestimmte Policies. Einen praktischen Überblick zu Setup und Bedienung geben die kompakten Hinweise zu eBPF-Analysewerkzeugen.
Vor dem Start: System- und Sicherheits-Checks
Bevor ich in der Produktion messe, verifiziere ich die Basisfähigkeiten des Hosts. Damit vermeide ich Fehlstarts und bekomme reproduzierbare Ergebnisse.
- Kernel-Features prüfen:
uname -rund verfügbare BPF-Funktionen (z. B. per Feature-Check). Wichtig sind kprobes/tracepoints, BTF (für stabile Typinformationen) und perf-Events. - Rechte und Policies: Ich stelle sicher, dass nur berechtigte Nutzer eBPF laden dürfen (CAP_BPF/CAP_SYS_ADMIN oder entsprechende Policy) und dass LSM-Profile das Laden nicht blockieren.
- Systemparameter:
kernel.unprivileged_bpf_disabledist in produktiven Umgebungen meist aktiv. Ich arbeite daher bewusst aus gesicherten Sessions und mit klarer Auditierung. - Transparente Pfade: Ich halte Verzeichnisse wie
/sys/kernel/debug/tracingund/sys/fs/bpfim Blick, um Artefakte nach Messungen aufzuräumen.
Diese Hygiene sorgt dafür, dass ich Messungen gezielt und reproduzierbar fahre – ohne Seiteneffekte.
Praxisleitfaden: Die ersten zehn Werkzeuge
Für einen schnellen Performance-Check nutze ich eine feste Reihenfolge. Damit grenze ich CPU-, I/O- oder Netzwerkursachen sauber ein und entscheide, ob ich tiefer in Stacks oder Timings schaue. Die Tabelle zeigt die Kernaufgabe der Tools und die Frage, die ich damit kläre. Ich halte die Laufzeit zunächst kurz und wiederhole Messungen, sobald ich einen Verdacht bestätigen will. So verhindere ich blinde Flecken und verliere keine Zeit bei akuten Incidents.
| Tool | Beobachtet | Typische Frage |
|---|---|---|
| execsnoop | Neue Prozesse | Wer startet kurzlebige Jobs, die Last erzeugen? |
| opensnoop | Dateiöffnungen | Welche Pfade werden ständig geöffnet oder geloggt? |
| ext4slower (xfs*, btrfs*, zfs*) | Langsame FS-Operationen | Welche Aufrufe zeigen hohe Latenzen pro Volume? |
| biolatency | Block-I/O-Verteilung | Gibt es sporadische oder dauerhafte Latenzspitzen? |
| biosnoop | Einzelne I/O-Requests | Welcher Prozess treibt bestimmte Geräte in die Knie? |
| cachestat | Page-Cache-Verhalten | Lohnt mehr RAM oder leidet die App an Misses? |
| tcpconnect | Neue TCP-Verbindungen | Wer spricht wie oft welchen Service an? |
| tcpaccept | Angenommene Verbindungen | Welche Server-Sockets stehen unter starker Last? |
| tcpretrans | Retransmissions | Deutet Paketverlust auf instabile Pfade hin? |
| runqlat | Scheduler-Latenzen | Warten Threads zu lange auf CPU-Zeit? |
Zusätzlich setze ich profile ein, um Hotspots im User- oder Kernelspace zu erkennen und Call-Stacks zu aggregieren. Damit entdecke ich teure Regex, ineffiziente Treiber oder Spinlocks, die ich im Code oder in der Konfiguration entschärfe. Ich nutze kurze Sampling-Intervalle und vergleiche mehrere Läufe, damit Ausreißer sichtbar werden. Dieser Mix aus Überblick und Tiefgang spart mir viel Analysezeit. Anschließend teste ich die Optimierung unter derselben Last erneut.
Erweiterung: Off-CPU, Locks und Wartezeiten sichtbar machen
Nicht jede hohe Latenz ist CPU-gebunden. Oft warten Threads „Off-CPU“ auf I/O, Locks oder Wakeups. Hier helfen ergänzende bcc-Tools und -Profile:
- Off-CPU-Analyse: Ich messe, wie lange Threads nicht auf der CPU sind und welche Stacks dorthin führen. Das trennt Rechenzeit von Wartezeit und zeigt Blockierer.
- Lock-Contention: Ich schaue gezielt auf kritische Kernel- und Userspace-Locks. Lange Haltezeiten oder hohe Contentions deuten auf Serialisierungspunkte hin, die ich aufbreche (z. B. durch Sharding, feinere Granularität, andere Datenstrukturen).
- Wakeup-Pfade: Latenzen zwischen „wurde geweckt“ und „läuft wieder“ offenbaren Scheduling- und Prioritätsprobleme oder zu große Worker-Pools.
Ich korreliere diese Signale mit runqlat und biolatency, um zwischen Speicher-, I/O- und Scheduler-Ursachen zu unterscheiden.
Messhygiene: Filter, Dauer, Schwellen
Damit eBPF-Messungen reproduzierbar bleiben, arbeite ich mit drei Grundregeln:
- Kurz und gezielt: Ich lasse Tools anfänglich nur kurz laufen (z. B. 10–30 Sekunden) und fokussiere auf verdächtige PIDs, Container oder Sockets.
- Schwellen setzen: Bei „*slower“-Tools filtere ich kleine Latenzen heraus, um das Rauschen zu reduzieren und nur problematische Calls zu sehen.
- Rate begrenzen: Ich nutze selektive Filter (z. B. Prozessname, TIDs, Ports), um Eventraten klein zu halten. So bleibt der Overhead minimal und ich vermeide Dropped Events.
Erst wenn ich ein Muster sehe, verlängere ich die Laufzeit oder erweitere den Scope. Dadurch bekomme ich saubere und verlässliche Stichproben.
Praxis-Szenario 1: Unerklärlich hohe CPU-Last
Zeigt die CPU-Anzeige dauerhaft hohe Werte, starte ich mit execsnoop, um kurzlebige Prozesse zu erkennen. Danach messe ich mit runqlat, wie lange Threads auf CPU-Zeit warten, und prüfe, ob die Runqueues überfüllt sind oder Prioritäten unpassend liegen. Werden Wartezeiten auffällig, reduziere ich die Arbeiterzahl, ändere Thread-Pools oder entzerre Cronjobs, damit der Scheduler greifen kann. Mit profile sammle ich Stacks und finde die echten Hotspots in Bibliotheken und im eigenen Code. Erst wenn ich diese Hinweise zusammenführe, treffe ich Entscheidungen zu Limits, Garbage Collection, Affinitäten oder Compiler-Flags.
Praxis-Szenario 2: I/O-Latenzen und träge Anwendungen
Beschweren sich Nutzer über Hänger bei geringer CPU, prüfe ich mit ext4slower langsame Dateisystemaufrufe pro Prozess. Als Nächstes schaue ich mit biolatency auf die Verteilung der Block-I/O-Zeiten je Gerät, um sporadische Spitzen oder dauerhafte Engpässe zu erkennen. biosnoop zeigt mir, ob ein einzelner Dienst ungesund viele kleine Writes erzeugt und damit Queueing auslöst, das andere Prozesse ausbremst. Mit cachestat sehe ich, ob der Page Cache trifft oder ob Misses dominieren und mehr RAM helfen würde. Am Ende entscheide ich, ob sich Batch-Writes, größere Buffers oder ein Umzug auf schnelleren Storage auszahlen.
Praxis-Szenario 3: Netzwerkpfade und Microservices
In verteilten Umgebungen starte ich mit tcpconnect, um Verbindungsaufbau zwischen Diensten zu messen. Dann prüfe ich mit tcpaccept, welche Server-Sockets besonders viele Eingänge haben und ob Limits auf der Listener-Seite greifen. tcpretrans deckt erneute Sendungen auf und grenzt Transportprobleme gegen Applikationsfehler ab, bevor ich Timeouts und Retries anpasse. Mit diesen drei Signalen sehe ich, ob das Netz, die App oder ein Upstream-Service die Latenz treibt. Danach passe ich Backoff-Strategien, Keepalive-Werte, Load-Balancer-Settings und Puffergrößen an.
Betriebssicherheit und Verlässlichkeit von eBPF
Ich lade nur vertrauenswürdige Tools und teste eigene eBPF-Programme zuerst auf Staging. Der Kernel-Verifier blockiert fehlerhafte Programme, doch ich setze zusätzlich Limits für Maps und Buffers, damit Speicher sauber begrenzt bleibt. Logging bewahre ich auf, um Verhalten und Nebenwirkungen im Blick zu behalten und bei Bedarf rasch einzugreifen. Policies legen fest, wer eBPF laden darf, damit die Kontrolle beim Plattform-Team bleibt und Security-Anforderungen eingehalten werden. Diese Regeln sorgen dafür, dass Tracing in Produktionsumgebungen verlässlich bleibt und keine Überraschungen erzeugt.
Container- und Kubernetes-Umgebungen
In Containern trenne ich systematische Probleme von Pod-spezifischen Effekten. Dafür filtere ich Messungen nach cgroup, Namespace oder PID-Bereich. Viele bcc-Tools erlauben Filter auf Prozessnamen oder -IDs; alternativ messe ich auf dem Host und ordne Ereignisse per cgroup den Workloads zu. Wichtig:
- PID-Namespace: PIDs unterscheiden sich zwischen Host und Container. Ich mappe die IDs oder filtere über Prozessnamen/Ports.
- Ressourcen-Quoten: CPU-Throttling durch CFS-Quoten zeigt sich als lange Wartezeiten ohne volle Systemauslastung. Ich erkenne das über runqlat in Kombination mit Metriken der Quoten.
- Netzwerknamespaces: Für Socket-Analysen achte ich auf den richtigen Namespace. Ich messe am Host-Interface und korreliere mit Pod-IPs und Ports.
So bleiben die Messergebnisse aussagekräftig, auch wenn viele Workloads dicht nebeneinander laufen.
Integration in Observability-Stacks
Ich ersetze mein Monitoring nicht, ich ergänze es mit eBPF. bcc tools liefern mir die Tiefe, während Metriksysteme, Logs und APM die Breite zeigen; zusammen entsteht ein stimmiges Bild. Traces aus bcc leite ich bei Bedarf in Log-Pipelines, triggere Snapshots bei Incidents und dokumentiere die Befunde im Team. Für punktuelle Profile nutze ich Sampling zusätzlich zu Timelines aus Metriken, damit Anomalien greifbar werden. Wer ergänzend Scripting bevorzugt, findet in bpftrace im Hosting eine schlanke Möglichkeit, Ad-hoc-Fragen mit Miniskripten zu beantworten.
Häufige Stolpersteine – und wie ich sie umgehe
- Noisy Neighbor: Einzelne Jobs erzeugen kurzlebige, aber intensive Last. execsnoop plus profile decken diese Muster zuverlässig auf; ich timeboxe sie oder isoliere sie per Quoten.
- NUMA und Affinitäten: Hohe Latenzen trotz freier Kerne deuten auf Cross-NUMA-Zugriffe. Ich prüfe CPU-Affinitäten, Speicherbindung und IRQ-Verteilung.
- IRQ/SoftIRQ-Hotspots: Netzwerklast kann ksoftirqd-Kerne sättigen. Ich beobachte Retransmits, verteile IRQs über RSS/Queues und justiere RPS/XPS.
- Page-Cache-Effekte: Kaltstarts wirken langsamer. Ich berücksichtige Warmup-Phasen und vergleiche cachestat-Werte vor und nach Last.
- Kernel-Updates: Kprobes können sich bei Versionssprüngen ändern. Ich bevorzuge stabile Tracepoints, teste vorab und halte ein Minimal-Set parat.
Arbeitsabläufe, die sich bewährt haben
- Incident-Snapshot: 60–120 Sekunden kombinierter Lauf (execsnoop, runqlat, biolatency, tcpretrans, profile). Danach fokussiere ich auf das auffällige Subsystem.
- Baseline-Routine: Wöchentlich kurze Messungen auf Kernpfaden (z. B. Storage- und Netzwerkprofil). Abweichungen erkenne ich so frühzeitig.
- Change-Validierung: Vor und nach Konfigurationsänderungen vergleiche ich dieselben Messpunkte, um den Effekt quantifizierbar zu machen.
Kontinuierliches Linux Performance Engineering
Ich behandle Performance als laufenden Prozess, nicht als Einmalaktion. In CI/CD integriere ich kurze eBPF-basierte Checks, um Regressionen früh zu sehen und vor dem Rollout zu stoppen. In Wartungsfenstern messe ich typische Pfade unter Last, baue Baselines auf und dokumentiere akzeptable Latenzbereiche. So erkenne ich Abweichungen schnell und spare mir Rätselraten im Incident, weil Vergleichsdaten vorliegen. Diese Routine zahlt direkt auf Verfügbarkeit, Kostenkontrolle und Nutzererlebnis ein.
Mini-Fallstudie: Vom Symptom zur Ursache in 12 Minuten
Ein API-Cluster meldet steigende 99p-Latenzen bei unveränderter RPS. Ich starte einen Incident-Snapshot: tcpconnect zeigt keine Anomalien im Verbindungsaufbau, tcpretrans bleibt niedrig – das Netzwerk ist es wohl nicht. runqlat meldet kurze, aber häufige Wartezeiten; profile zeigt Hotspots in einer JSON-Serialisierung. Parallel beobachte ich mit cachestat einen Einbruch der Cache-Hitrates während Peaks. Die Korrelation legt nahe: Viele kleine Payloads, die synchron serialisiert und sofort geschrieben werden.
Ich verifiziere mit ext4slower, das mehrere Millisekunden-lange fsyncs auf demselben Volume für den API-Prozess zeigt; biolatency bestätigt sporadische Queuespitzen auf dem betroffenen Gerät. Gegenmaßnahme: Batching der Writes, größerer Buffer und asynchrones Flushen an weniger sensiblen Punkten. Nach dem Rollout sinken die 99p-Latenzen um 35 %, die Cache-Hitrate erholt sich, und runqlat zeigt wieder schmale Verteilungen.
Zusammenfassung für die Praxis
Mit bcc tools und eBPF verschaffe ich mir in kurzer Zeit Klarheit über CPU, I/O und Netzwerk, ohne Anwendungen zu ändern. Die Checkliste aus execsnoop, opensnoop, ext4slower, biolatency, biosnoop, cachestat, tcpconnect, tcpaccept, tcpretrans und runqlat bildet einen schlüssigen Startpunkt. Ergänzend setze ich profile ein, um Hotspots sichtbar zu machen und Codepfade zu entschärfen. Durch saubere Policies, Logging und Limits bleibt der Einsatz im Kernel sicher und nachvollziehbar. Wer diese Methode konsequent anwendet, löst Leistungsprobleme schneller, plant Kapazitäten besser und senkt Kosten pro Anfrage.


