OpenTelemetry hilft zu sehen, was innerhalb einer Anwendung und auf dem Server passiert. Ein Programm schickt Traces von Anfragen, Metriken und Logeinträge an eine zentrale Stelle, von dort lassen sie sich an einen beliebigen Speicher weitergeben und auf Zeitleisten und Diagrammen ansehen. Der wichtigste Vermittler in diesem Aufbau heißt OpenTelemetry Collector, und genau ihn richten wir auf dem VPS ein. In diesem Artikel starten wir Collector und Jaeger über Docker Compose, senden Testdaten, binden eine Python-Anwendung ohne Änderung ihres Codes an, öffnen einen Trace im Browser und fügen die Metriken des Servers selbst hinzu. Alle Schritte haben wir auf einem Testserver mit Debian 12 nachvollzogen, die Version von Collector war zum Zeitpunkt der Prüfung 0.163.1, die von Jaeger 2.22.0.
Wie OpenTelemetry aufgebaut ist
Solange eine Anwendung aus einem einzigen Prozess besteht, hilft meist ein Log, ihr Verhalten zu verstehen. Läuft eine Anfrage jedoch durch mehrere Dienste, zum Beispiel durch eine Webanwendung, eine Warteschlange und eine Datenbank, reicht das Log nicht mehr aus. Man möchte den Weg einer bestimmten Anfrage sehen und wissen, wie viel Zeit sie bei jedem Schritt verbracht hat, und nebenbei verstehen, ob der Server selbst an die Grenzen von Arbeitsspeicher oder Festplatte stößt. Die Daten, die Programme und Server über ihre eigene Arbeit liefern, nennt man Telemetrie. OpenTelemetry, kurz OTel, ist ein offenes Set aus Spezifikationen, Programmierschnittstellen und Werkzeugen, das beschreibt, wie Telemetrie erzeugt, übertragen und gesammelt wird. Es ist an keinen bestimmten Anbieter gebunden, deshalb lassen sich dieselben Daten sowohl an Jaeger als auch an Prometheus oder an einen kommerziellen Dienst senden. Dabei sollte man gleich die Grenze erkennen. Laut der Definition in der offiziellen Dokumentation ist OpenTelemetry kein Observability-System, es speichert also selbst keine Daten und zeichnet keine Diagramme, sondern überlässt Speicherung und Darstellung anderen Programmen.
Telemetrie gliedert sich in Signale von drei Hauptarten. Ein Trace (auf Deutsch Ablaufverfolgung, im Alltag sagt man meist einfach Trace) beschreibt den Weg einer einzelnen Anfrage durch die Anwendung. Er besteht aus Spans, und jeder Span steht für eine einzelne Arbeitseinheit innerhalb der Anfrage, zum Beispiel die Verarbeitung einer HTTP-Anfrage oder einen Zugriff auf die Datenbank. Ein Span hat einen Namen, eine Start- und Endzeit, Attribute und Kennungen. Alle Spans eines Traces tragen dieselbe Trace-ID, und bei einem verschachtelten Span ist die ID des übergeordneten Spans angegeben. Anhand dieser Verbindungen zeichnet Jaeger später den Aufrufbaum, den wir in Schritt 5 sehen werden. Eine Metrik ist eine Messung, die während des Betriebs erfasst wird, zum Beispiel der belegte Arbeitsspeicher oder die Anzahl der Anfragen. Im Gegensatz zu einem Trace beschreibt sie das Verhalten des Systems insgesamt und nicht einer einzelnen Anfrage, deshalb eignet sie sich gut für Diagramme und Benachrichtigungen. Ein Log ist ein Eintrag über ein Ereignis. Damit niemand auf gewohnte Logging-Bibliotheken verzichten muss, schließt sich OpenTelemetry an sie an, und wenn in der Anwendung das SDK oder die automatische Instrumentierung aktiviert ist, werden den Logeinträgen die Kennungen des aktiven Traces und Spans automatisch hinzugefügt. So lässt sich eine Logzeile mit der Anfrage verknüpfen, bei der sie entstanden ist.
Sehen wir uns nun an, wie die Daten die Anwendung verlassen. Im Code selbst wird die API verwendet, die festlegt, wie Telemetrie erzeugt wird, und für ihre Umsetzung in einer bestimmten Sprache ist das SDK zuständig. Das SDK sammelt die erzeugten Spans und Metriken, verarbeitet sie und exportiert sie. Tracing muss man nicht für jedes Framework von Hand schreiben, denn es gibt Instrumentierungsbibliotheken, die für Anfragen beliebter Bibliotheken selbst Spans erzeugen, und in Sprachen, die das unterstützen, werden sie ganz ohne Codeänderung eingebunden. Genau so gehen wir bei der Flask-Anwendung vor. Das SDK sendet die Daten über das Protokoll OTLP, das zwei Übertragungsvarianten hat. OTLP/gRPC verwendet standardmäßig den Port 4317 und OTLP/HTTP den Port 4318, wobei die Daten über HTTP an die Adressen /v1/traces, /v1/metrics und /v1/logs gehen. Das sind zwei verschiedene Protokolle auf zwei verschiedenen Ports, und man verwechselt sie sehr leicht. Im Abschnitt mit den häufigen Fragen haben wir nachgestellt, welche Fehler dabei auftreten.
Die Anwendung lässt sich so einstellen, dass sie die Daten direkt an den Speicher sendet, und zum Kennenlernen von OpenTelemetry oder für einen kleinen Teststand ist das zulässig. In den meisten Fällen startet man neben den Diensten jedoch einen Collector, ein eigenes Vermittlerprogramm, das die Telemetrie annimmt, verarbeitet und weiterleitet. Für die Anwendung ist das einfacher, denn sie gibt die Daten schnell ab und vergisst sie, während wiederholte Sendeversuche, die Bündelung in Batches, die Verschlüsselung und das Filtern sensibler Daten der Collector übernimmt. Intern besteht er aus einer Reihe von Pipelines, je eine für jedes Signal. Eine Pipeline beginnt mit Empfängern (receivers), die Daten entgegennehmen, zum Beispiel über OTLP oder aus den Metriken des Servers. Danach folgen Prozessoren (processors), die die Daten unterwegs verändern, zum Beispiel zu Batches bündeln. Am Ende der Pipeline stehen Exporter (exporters), die das Ergebnis an einen oder mehrere Speicher senden. Es gibt eine Feinheit, über die Einsteiger stolpern. Es genügt nicht, eine Komponente im eigenen Konfigurationsabschnitt zu beschreiben, man muss sie zusätzlich im Abschnitt service aufführen, sonst wird sie nicht aktiviert. Wir sehen das in der Konfiguration in Schritt 2. Den Collector gibt es in mehreren Builds, und wir nehmen contrib, in dem es deutlich mehr Empfänger gibt als im Basis-Build. In den Images der Version 0.163.1 sind es 108 Empfänger gegenüber 7.
Die Rolle von Jaeger im Aufbau
Jaeger ist ein offenes System zum Speichern und Ansehen von Traces. In unserem Aufbau dient es als Speicher und Weboberfläche und gehört selbst nicht zu OpenTelemetry. Jaeger 2.22.0 nimmt Daten über OTLP an, in seinem Log sind die Zeilen Starting GRPC server und Starting HTTP server zu sehen, deshalb übergibt der Collector ihm die Traces mit demselben Protokoll.
Was Sie brauchen
- Einen Server mit Debian 12 und mindestens 2 GB Arbeitsspeicher, zum Beispiel den Tarif DE-RZ9-1. Auf unserem Server belegte der Collector zwischen 45 und 70 MB Arbeitsspeicher, Jaeger zwischen 20 und 60 MB und die Demo-Anwendung mit Flask etwa 120 MB. Die Docker-Images belegten auf der Festplatte etwa 750 MB, die virtuelle Python-Umgebung weitere 67 MB. Wenn Sie noch keinen Server haben, bestellen Sie ihn nach der Anleitung.
- Eine SSH-Verbindung, zum Beispiel über Termius. Die Befehle werden als
rootausgeführt. - Docker und Docker Compose. Wie Sie sie installieren, steht in einem eigenen Artikel. Bei uns waren Docker 29.9.2 und Compose 5.6.1 installiert.
- Python 3 für die Demo-Anwendung. Unter Debian 12 ist es bereits vorhanden, unsere Version ist 3.11.2.
- Ein Terminal und einen Browser auf Ihrem Computer, um Jaeger über den SSH-Tunnel zu öffnen.
Schritt 1. Bereiten Sie den Ordner und die Compose-Datei vor
Erstellen Sie den Projektordner und wechseln Sie in ihn.
mkdir -p /opt/otel
cd /opt/otelErstellen Sie nun die Datei compose.yaml. Kopieren Sie den Block unten vollständig, mit der Zeile cat am Anfang und EOF am Ende, und fügen Sie ihn in das Terminalfenster ein.
cat > compose.yaml <<'EOF'
# Jaeger speichert und zeigt Traces an, der Collector nimmt Telemetrie entgegen
services:
jaeger:
image: cr.jaegertracing.io/jaegertracing/jaeger:2.22.0
container_name: otel-jaeger
restart: unless-stopped
ports:
- "127.0.0.1:16686:16686" # Jaeger-Weboberfläche, nur vom Server selbst aus
collector:
image: otel/opentelemetry-collector-contrib:0.163.1
container_name: otel-collector
restart: unless-stopped
volumes:
- ./otel-config.yaml:/etc/otelcol-contrib/config.yaml:ro
ports:
- "127.0.0.1:4317:4317" # OTLP gRPC
- "127.0.0.1:4318:4318" # OTLP HTTP
depends_on:
- jaeger
EOFDie Datei beschreibt zwei Container.
- jaeger speichert Traces und zeigt sie in der Weboberfläche an. Sein Port 16686 ist nur an der internen Serveradresse
127.0.0.1veröffentlicht. - collector nimmt Telemetrie entgegen. Seine Ports 4317 (OTLP/gRPC) und 4318 (OTLP/HTTP) sind ebenfalls nur auf
127.0.0.1veröffentlicht, deshalb erreichen ihn Anwendungen auf dem Server selbst, aus dem Internet ist er jedoch nicht erreichbar.
Die Einstellungsdatei des Collectors wird nur lesend in den Container eingebunden, wir erstellen sie im nächsten Schritt. Die Versionen der Images sind ausdrücklich angegeben, damit Sie nach einem Neustart dieselbe Version erhalten und nicht eine neue.
Warum die Ports nur auf 127.0.0.1 liegen
Der Collector aus diesem Artikel ist nicht durch Passwörter geschützt und nimmt Daten von jedem an, der sich mit dem Port verbinden kann. Die offizielle Dokumentation von OpenTelemetry empfiehlt, den Port des Collectors in Docker nur auf 127.0.0.1 zu veröffentlichen, wenn ihn Programme vom selben Server nutzen. Außerdem schließt die Firewall UFW die Docker-Ports nicht von selbst, darüber steht Genaueres im Artikel über Docker. Wir haben von einem Heimcomputer aus geprüft, dass Verbindungen zu den Ports 4317, 4318 und 16686 an der externen Adresse des Servers nicht zustande kommen und mit einem Timeout enden.
Schritt 2. Schreiben Sie die Collector-Konfiguration
Erstellen Sie die Datei otel-config.yaml auf dieselbe Weise und kopieren Sie den Block vollständig.
cat > otel-config.yaml <<'EOF'
receivers:
# Empfang von Telemetrie der Anwendungen über das Protokoll OTLP
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
# Fasst Daten zu Batches zusammen, so wird der Versand effizienter
batch:
exporters:
# Traces gehen an Jaeger
otlp_grpc/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
# Alles, was der Collector empfängt, wird in sein Log ausgegeben
debug:
verbosity: basic
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp_grpc/jaeger, debug]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [debug]
logs:
receivers: [otlp]
processors: [batch]
exporters: [debug]
EOFGehen wir die Datei Abschnitt für Abschnitt durch.
- receivers beschreibt die Empfänger. Der Empfänger
otlpwartet auf Daten an den Ports 4317 (gRPC) und 4318 (HTTP). Die Adresse0.0.0.0bedeutet, dass der Empfang auf allen Adressen innerhalb des Containers läuft, während der Zugriff von außen dadurch begrenzt wird, wie wir die Ports incompose.yamlveröffentlicht haben. - processors beschreibt die Prozessoren. Der Prozessor
batchfasst die Daten vor dem Senden zu Batches zusammen. - exporters beschreibt die Exporter. Der Name
otlp_grpc/jaegerbesteht aus dem Typ und einer eigenen Bezeichnung nach dem Schrägstrich, so lassen sich mehrere Exporter desselben Typs führen. Die Adressejaeger:4317ist der Name des Dienstes auscompose.yaml, Docker findet den Container anhand dieses Namens selbst. Die Zeileinsecure: trueschaltet die Verschlüsselung ab, weil der Austausch innerhalb des Docker-Netzwerks auf einem Server stattfindet. Der Exporterdebuggibt alles Empfangene im Log des Collectors aus, der Modusbasiczeigt eine Zeile pro Datenbatch. - service setzt die Pipelines zusammen. Für jedes Signal ist angegeben, woher die Daten kommen, wie sie verarbeitet werden und wohin sie gesendet werden. Traces gehen sowohl an Jaeger als auch ins Log, Metriken und Logs vorerst nur ins Log.
Entfernen Sie die Adresse 0.0.0.0 nicht
Wenn Sie beim Empfänger keinen endpoint angeben, lauscht der Collector nur an der internen Adresse des Containers 127.0.0.1, und die veröffentlichten Ports funktionieren nicht mehr. Wir haben das auf dem Testserver geprüft, Einzelheiten stehen im Abschnitt mit den häufigen Fragen.
Komponentennamen in älteren Anleitungen
In Version 0.163.1 heißt der OTLP-Exporter über gRPC otlp_grpc und der Empfänger für Servermetriken host_metrics. In älteren Anleitungen finden sich die Namen otlp und hostmetrics. Die Konfigurationsprüfung akzeptiert auch diese, wir verwenden aber die neuen Bezeichnungen.
Schritt 3. Prüfen Sie die Konfiguration und starten Sie den Collector
Bevor Sie den Collector starten, prüfen Sie die Einstellungsdatei. Der Befehl unten startet einen einmaligen Collector-Container mit Ihrer Datei und meldet einfach, ob die Konfiguration korrekt ist.
docker compose run --rm --no-deps collector validate --config=/etc/otelcol-contrib/config.yaml
echo $?Wenn alles in Ordnung ist, gibt der Befehl nichts aus, und echo $? zeigt 0. Bei einem Fehler in der Datei sehen Sie dessen Text, Beispiele stehen im Abschnitt mit den häufigen Fragen. Starten Sie jetzt die Container.
docker compose up -dBeim ersten Start lädt Docker zuerst die Images herunter. Sehen Sie sich nach einigen Sekunden die letzte Zeile des Collector-Logs an.
docker logs otel-collector 2>&1 | tail -n 1Gesucht ist die Zeile Everything is ready. Begin running and processing data., sie bedeutet, dass die Empfänger gestartet sind.

Die Konfiguration ist korrekt, der Collector läuft und ist bereit, Daten zu empfangen
Die Zeilen des Collector-Logs sind lang, weil am Ende jeder Zeile ein technischer Block mit der Beschreibung des Collectors selbst steht. Für uns sind der Anfang der Zeile und die Zahlen am Ende wichtig.
Schritt 4. Senden Sie Test-Traces
Um den Collector zu prüfen, bevor die echte Anwendung angebunden wird, nehmen wir das Werkzeug telemetrygen. Es gehört zum Projekt OpenTelemetry und kann Test-Traces, -Metriken und -Logs erzeugen. Wir starten es in einem Container im Netzwerk des Servers und senden drei Traces.
docker run --rm --network host \
ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest \
traces --otlp-insecure --traces 3 2>&1 | grep -E "generated|export:"--network hostgibt dem Container das Netzwerk des Servers, deshalb sieht das Werkzeug den Port127.0.0.1:4317.--otlp-insecureschaltet die Verschlüsselung ab. Unser Collector hat keine, und ohne diesen Parameter versucht das Werkzeug, sich per TLS zu verbinden, und erhält einen Fehler.--traces 3legt die Anzahl der Traces fest. Das Werkzeug erzeugt einen Trace pro Sekunde, deshalb dauert das Senden von drei Traces etwa 5 Sekunden.- Der Filter
greplässt nur die Abschlusszeile und Zeilen mit Sendefehlern übrig. Das Werkzeug ist sehr gesprächig.
Die Zeile traces generated bedeutet nur, dass das Werkzeug Daten erzeugt hat. Ob sie beim Collector angekommen sind, zeigt sein Log.
docker logs otel-collector 2>&1 | grep Traces | tail -n 2
Der Collector hat die Traces empfangen und ins Log geschrieben
In jeder Zeile Traces steht "spans": 2. Das Werkzeug erzeugt einen Trace aus zwei Spans, und der Collector erhält die Traces, sobald sie entstehen, und gibt für jeden Batch eine Zeile aus.
Denselben Test können Sie auch über HTTP durchführen. Fügen Sie dazu den Parameter --otlp-http hinzu und geben Sie den Port 4318 an.
docker run --rm --network host \
ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest \
traces --otlp-http --otlp-insecure --otlp-endpoint localhost:4318 --service telemetrygen-http --traces 2Der Parameter --service legt den Namen des Dienstes fest, unter dem die Traces in Jaeger erscheinen, so lassen sie sich leicht von den ersten unterscheiden.
Schritt 5. Öffnen Sie Jaeger über den SSH-Tunnel
Die Weboberfläche von Jaeger ist vor dem Internet verschlossen, deshalb müssen Sie sich über einen SSH-Tunnel mit ihr verbinden.
Öffnen Sie auf Ihrem Computer ein neues Terminalfenster und erstellen Sie den Tunnel. Ersetzen Sie
IP-des-Serversdurch die Adresse Ihres VPS.bashssh -L 16686:127.0.0.1:16686 root@IP-des-ServersÖffnen Sie, ohne dieses Fenster zu schließen, im Browser auf Ihrem Computer die Adresse
http://127.0.0.1:16686.Wählen Sie in der Liste Service den Eintrag
telemetrygenund klicken Sie auf Find Traces.
Es öffnet sich eine Seite mit den gefundenen Traces. Wir haben drei Traces gesendet, und alle drei sind in der Tabelle zu sehen. Jeder hat zwei Spans, und die Dauer beträgt etwa 123 Mikrosekunden, weil telemetrygen standardmäßig sehr kurze Spans erzeugt.

Test-Traces von telemetrygen in Jaeger
Klicken Sie auf den Namen eines beliebigen Traces, und seine Zeitleiste mit den enthaltenen Spans öffnet sich.
Jaeger speichert Traces nur im Arbeitsspeicher
Im Modus aus unserer Compose-Datei schreibt Jaeger keine Daten auf die Festplatte. Laut seiner Dokumentation ist das ein flüchtiger Speicher im Arbeitsspeicher. Wir haben das geprüft, indem wir den Container neu gestartet haben, danach waren die früheren Traces verschwunden. Eine dauerhafte Speicherung von Traces wird in diesem Artikel nicht eingerichtet, zum Kennenlernen und Debuggen reicht der Arbeitsspeicher.
Schritt 6. Binden Sie eine Python-Anwendung an
Testdaten sind gut zum Prüfen, aber der echte Nutzen beginnt, wenn Ihre Anwendung die Telemetrie sendet. Wir nehmen eine kleine Webanwendung mit Flask und binden OpenTelemetry ohne Änderungen an der Logik an. Als Grundlage dient ein Beispiel aus der offiziellen OpenTelemetry-Anleitung für Python, dem wir einen eigenen Span und eine kleine Pause hinzugefügt haben, damit der Trace anschaulich wird.
Installieren Sie das Paket für virtuelle Python-Umgebungen. Unter Debian 12 gehört es nicht zur Standardausstattung.
bashapt install -y python3-venvErstellen Sie den Anwendungsordner und die virtuelle Umgebung und aktivieren Sie sie anschließend.
bashmkdir -p /opt/otel/app cd /opt/otel/app python3 -m venv venv source venv/bin/activateAm Anfang der Terminalzeile erscheint der Vermerk
(venv).Installieren Sie Flask, OpenTelemetry und den OTLP-Exporter. Starten Sie danach
opentelemetry-bootstrap, es findet in der Umgebung bereits installierte Bibliotheken, zum Beispiel Flask, und installiert für sie Instrumentierungspakete.bashpip install flask opentelemetry-distro opentelemetry-exporter-otlp opentelemetry-bootstrap -a installAuf unserem Server wurden Flask 3.1.3, OpenTelemetry SDK 1.45.1 und opentelemetry-distro 0.66b1 installiert.
Erstellen Sie die Anwendungsdatei
app.py.bashcat > app.py <<'EOF' import logging import time from random import randint from flask import Flask, request from opentelemetry import trace app = Flask(__name__) logger = logging.getLogger(__name__) tracer = trace.get_tracer("diceroller.tracer") @app.route("/rolldice") def roll_dice(): player = request.args.get("player", default=None, type=str) result = str(roll()) if player: logger.warning("%s is rolling the dice: %s", player, result) else: logger.warning("Anonymous player is rolling the dice: %s", result) return result def roll(): # Eigener Abschnitt des Traces innerhalb der Anfrage with tracer.start_as_current_span("roll") as span: time.sleep(0.3) # Simulation von Arbeit value = randint(1, 6) span.set_attribute("roll.value", value) return value EOFDie Anwendung antwortet auf der Adresse
/rolldicemit einer Zufallszahl von 1 bis 6. Der einzige Zugriff auf OpenTelemetry in ihr ist der Spanroll, den wir selbst erstellt haben, um einen verschachtelten Abschnitt zu sehen. Alles Übrige erledigt die automatische Instrumentierung.Starten Sie die Anwendung über
opentelemetry-instrument.bashopentelemetry-instrument --service_name dice-server --logs_exporter otlp flask run -p 8000--service_name dice-serverlegt den Namen des Dienstes in Jaeger fest.--logs_exporter otlpaktiviert das Senden von Logs. Traces und Metriken gehen laut Dokumentation auch ohne diesen Parameter über OTLP/gRPC anlocalhost:4317.flask run -p 8000ist der gewöhnliche Startbefehl der Anwendung. Wenn der Port 8000 bei Ihnen belegt ist, wählen Sie einen anderen.
Das Fenster bleibt fast leer, und das ist normal. Die Logeinträge der Anwendung sammelt jetzt OpenTelemetry und sendet sie an den Collector, im Terminal werden sie nicht ausgegeben.
Öffnen Sie ein zweites SSH-Fenster und rufen Sie die Anwendung mehrmals auf.
bashfor p in Anna Boris Clara; do curl "http://127.0.0.1:8000/rolldice?player=$p"; echo; doneJede Antwort verzögert sich wegen der Pause im Code um etwa eine Drittelsekunde. Prüfen Sie nach einigen Sekunden das Collector-Log.
bashdocker logs otel-collector 2>&1 | grep -E "Traces|Logs" | tail -n 2

Drei Anfragen ergaben sechs Spans und Logeinträge der Anwendung
Drei Anfragen ergaben sechs Spans, also zwei pro Anfrage, und die Logeinträge der Anwendung. Der Collector hat alle drei Signale empfangen.
Kehren Sie nun zu Jaeger zurück, aktualisieren Sie die Seite, wählen Sie in der Liste Service den Eintrag dice-server und klicken Sie auf Find Traces. Öffnen Sie einen beliebigen Trace GET /rolldice und klicken Sie auf den verschachtelten Span roll.

Die Anfrage und der verschachtelte Span roll, unten seine Attribute
Der obere Balken ist die gesamte Anfrage GET /rolldice, sie wurde von der Instrumentierungsbibliothek für Flask erstellt. Darunter liegt der verschachtelte Span roll, den wir von Hand erstellt haben. Er nimmt fast die gesamte Zeit der Anfrage ein, etwa 300 von 301 Millisekunden, weil sich genau dort unsere Pause befindet. In den Attributen des Spans ist roll.value mit der gewürfelten Zahl zu sehen. So sieht man im Trace sofort, in welchem Teil der Anfrage die Zeit verloren geht.
Beenden Sie die Anwendung mit der Tastenkombination Ctrl + C im ersten Fenster.
Senden über HTTP statt gRPC
Die Anwendung lässt sich über eine Umgebungsvariable auf OTLP/HTTP umstellen. Fügen Sie vor dem Startbefehl OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf hinzu, und die Daten gehen an den Port 4318. Wenn Sie eine andere Adresse des Collectors brauchen, legt sie die Variable OTEL_EXPORTER_OTLP_ENDPOINT fest. Wir haben beide Wege geprüft, bei HTTP erschien der Dienst in Jaeger genauso wie bei gRPC.
Schritt 7. Fügen Sie Servermetriken hinzu
Bisher nimmt der Collector Daten von Anwendungen entgegen, misst aber selbst nichts. Wir bringen ihm mit dem Empfänger host_metrics bei, Metriken des Servers zu sammeln. Er kann Prozessorauslastung, Arbeitsspeicher, Festplatten und Netzwerk erfassen.
Wechseln Sie zurück in den Projektordner.
cd /opt/otelÖffnen Sie die Einstellungsdatei des Collectors.
bashnano otel-config.yamlSpeichern Sie die Änderungen mit der Tastenkombination Ctrl + O, danach mit Enter, und beenden Sie den Editor mit der Tastenkombination Ctrl + X.
Fügen Sie im Abschnitt
receiversdirekt nachotlpeinen neuen Empfänger hinzu, mit derselben Einrückung von zwei Leerzeichen.yaml# Metriken des Servers selbst host_metrics: root_path: /hostfs collection_interval: 10s scrapers: cpu: memory: load: filesystem: network:scraperslistet auf, was genau gemessen wird. Wir haben Prozessor, Arbeitsspeicher, durchschnittliche Last, Dateisystem und Netzwerk genommen. In der Dokumentation des Empfängers gibt es weitere, zum Beispiel Festplatten und Prozesse.collection_intervallegt die Messhäufigkeit fest. Standardmäßig ist es eine Minute, wir haben 10 Sekunden eingestellt, um das Ergebnis schneller zu sehen.root_pathwird benötigt, weil der Collector in einem Container läuft. Damit er den Server und nicht seinen eigenen Container sieht, wird das Stammverzeichnis des Servers in den Ordner/hostfseingebunden, und inroot_pathwird dieser Ordner angegeben.
Fügen Sie in derselben Datei den neuen Empfänger zur Metrik-Pipeline hinzu.
yamlmetrics: receivers: [otlp, host_metrics]Ändern Sie den Modus des Exporters
debug, damit im Log nicht nur Zähler, sondern auch die Werte selbst stehen. Ersetzen Sie im Abschnittexportersdie Zeileverbosity: basicdurchverbosity: detailedund speichern Sie die Datei.Öffnen Sie die Datei
compose.yamlund binden Sie das Stammverzeichnis des Servers lesend im Abschnittvolumesdes Dienstescollectorein.bashnano compose.yamlyamlvolumes: - ./otel-config.yaml:/etc/otelcol-contrib/config.yaml:ro - /:/hostfs:ro # Dateisystem des Servers für Host-MetrikenPrüfen Sie die Konfiguration und erstellen Sie den Collector-Container neu.
bashdocker compose run --rm --no-deps collector validate --config=/etc/otelcol-contrib/config.yaml docker compose up -d
Warten Sie etwa 20 Sekunden, bis der Collector die ersten Messungen durchgeführt hat, und sehen Sie nach, welche Metriken er gesammelt hat.
docker logs otel-collector --since 12s 2>&1 | grep "Name: system" | sort -uUm sicherzugehen, dass die Zahlen echt sind, nehmen wir den Wert des belegten Arbeitsspeichers aus dem Log und vergleichen ihn mit dem Befehl free.
docker logs otel-collector --since 12s 2>&1 | grep -A12 system.memory.usage | grep -E "state|Value" | head -2
free -b | head -2
Der Collector sammelt Servermetriken, und der belegte Arbeitsspeicher stimmt mit der Ausgabe von free überein
Der Collector lieferte 13 Metriken mit Namen, die mit system. beginnen. Zum Beispiel ist system.cpu.load_average.1m die durchschnittliche Last pro Minute, system.memory.usage der Arbeitsspeicher in Byte nach Zuständen, system.filesystem.usage der belegte Platz auf den Datenträgern und system.network.io das Volumen der übertragenen Daten. Der belegte Arbeitsspeicher aus der Metrik liegt nahe an der Spalte used in der Ausgabe von free, der kleine Unterschied entstand, weil die Werte zu verschiedenen Zeitpunkten erfasst wurden.
Stellen Sie den Modus basic wieder ein
Der ausführliche Modus schreibt jede Minute etwa 90 KB ins Log, das sind rund 130 MB pro Tag. Wenn Sie genug gesehen haben, setzen Sie in otel-config.yaml die Zeile verbosity: basic wieder ein und starten Sie den Collector mit dem Befehl docker compose restart collector neu.
Vorerst landen die Metriken nur im Log. Um sie zu speichern und Diagramme zu zeichnen, müssen Sie sie an einen Metrikspeicher senden, und im Collector gibt es dafür Exporter, zum Beispiel prometheus. Das ist ein Thema für einen eigenen Artikel.
Nützliche Befehle
docker compose ps # Zustand der Container und Ports
docker compose logs -f collector # Log des Collectors in Echtzeit
docker compose restart collector # Einstellungsdatei neu einlesen
docker compose run --rm --no-deps collector validate --config=/etc/otelcol-contrib/config.yaml # Einstellungen prüfen
docker compose down # Container stoppen und entfernenNach dem Stoppen mit dem Befehl docker compose down verschwinden die Traces in Jaeger, da sie nur im Arbeitsspeicher liegen.
Häufige Fragen
Muss ich die Ports 4317, 4318 und 16686 in UFW öffnen
Nein. Anwendungen auf demselben Server verbinden sich mit 127.0.0.1, und Jaeger öffnen wir über einen SSH-Tunnel. Die Ports sind in Compose nur auf 127.0.0.1 veröffentlicht, und wir haben von einem anderen Computer aus geprüft, dass an der externen Adresse des Servers keine Verbindung zu ihnen zustande kommt. Ersetzen Sie 127.0.0.1:4317:4317 nicht ohne Not durch 4317:4317. Die Firewall UFW schließt die Docker-Ports nicht, und der Collector ohne Passwörter wäre für das gesamte Internet offen.
Die Anwendung läuft, aber im Collector und in Jaeger ist nichts zu sehen, und im Log steht Failed to auto initialize OpenTelemetry
Der OTLP-Exporter ist nicht installiert. Wir haben die Anwendung nur mit opentelemetry-distro gestartet und folgende Zeilen erhalten.
RuntimeError: Requested component 'otlp_proto_grpc' not found in entry point 'opentelemetry_traces_exporter'
Failed to auto initialize OpenTelemetryDie Anwendung selbst antwortet dabei weiter auf Anfragen, deshalb bleibt der Fehler leicht unbemerkt. Beenden Sie die Anwendung, führen Sie pip install opentelemetry-exporter-otlp aus und starten Sie sie erneut.
In Jaeger ist nichts zu sehen, und im Terminal der Anwendung erscheint kein einziger Fehler
Sendefehler werden im Terminal nicht ausgegeben, weil Logeinträge, auch Meldungen von OpenTelemetry selbst, von der automatischen Instrumentierung gesammelt werden. Wir haben geprüft, dass mit dem Parameter --logs_exporter console dieselben Einträge zusammen mit den Exportfehlern auf dem Bildschirm erscheinen. Starten Sie die Anwendung so.
opentelemetry-instrument --service_name dice-server --logs_exporter console flask run -p 8000In der Konsole erscheinen dann sowohl die Einträge der Anwendung selbst als auch die Meldungen über Exportfehler, Beispiele dafür stehen in den nächsten beiden Fragen.
Failed to export traces to localhost:4318 … Trying to connect an http1.x server
Die Anwendung sendet Daten über gRPC, aber die Adresse zeigt auf den HTTP-Port. Wir haben den Fehler nachgestellt, indem wir die Anwendung mit der Collector-Adresse http://localhost:4318 über die Variable OTEL_EXPORTER_OTLP_ENDPOINT gestartet haben.
Failed to export traces to localhost:4318, error code: StatusCode.UNAVAILABLE, error details: failed to connect to all addresses; last error: INTERNAL: ipv4:127.0.0.1:4318: Failed parsing HTTP/2 (Expected SETTINGS frame as the first frame, got frame type 80) (Trying to connect an http1.x server (HTTP status 400))Es gibt zwei Auswege. Entweder tragen Sie wieder den gRPC-Port 4317 ein, oder Sie stellen die Anwendung mit der Variable OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf auf HTTP um. Beim Werkzeug telemetrygen sieht derselbe Fehler aus wie http2: frame too large, note that the frame header looked like an HTTP/1.1 header.
BadStatusLine … encountered while exporting spans batch, malformed HTTP response
Der umgekehrte Fall. Die Anwendung oder das Werkzeug senden Daten über HTTP, aber die Adresse zeigt auf den gRPC-Port. Wir haben die Anwendung mit den Variablen OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf und OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 gestartet.
Transient error ('Connection aborted.', BadStatusLine('\x00\x00\x06\x04\x00\x00\x00\x00\x00\x00\x05\x00\x00@\x00')) encountered while exporting spans batch, retrying in 0.82s.
Failed to export spans batch due to timeout, max retries or shutdown.Merken Sie sich die Zuordnung. Der Port 4317 nimmt nur gRPC an, der Port 4318 nur HTTP. Vergessen Sie bei HTTP auch bei telemetrygen den Parameter --otlp-http nicht.
transport: Error while dialing … connect: connection refused
Der Collector läuft nicht oder die Adresse ist falsch angegeben. Wir haben diesen Fehler erhalten, als wir Traces an den Port 4319 gesendet haben, auf dem niemand lauscht.
traces export: exporter export timeout: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:4319: connect: connection refused"Prüfen Sie mit dem Befehl docker compose ps, dass der Container läuft, und dass die Ports auf dem Server lauschen.
ss -tln | grep -E ":(4317|4318|16686) "In der Ausgabe müssen drei Zeilen mit der Adresse 127.0.0.1 stehen.
tls: first record does not look like a TLS handshake
Das Werkzeug telemetrygen verbindet sich standardmäßig über TLS, aber unser Collector nimmt Daten ohne Verschlüsselung an. Wir haben diesen Fehler erhalten, als wir das Werkzeug ohne den Parameter --otlp-insecure gestartet haben.
transport: authentication handshake failed: tls: first record does not look like a TLS handshakeFügen Sie den Parameter --otlp-insecure hinzu.
error reading server preface: connection reset by peer
Der Port ist veröffentlicht, aber der Collector im Container lauscht an der falschen Adresse. Das passiert, wenn beim Empfänger otlp kein endpoint angegeben ist. Wir haben die Konfiguration mit leeren grpc: und http: geprüft, und im Collector-Log erschienen folgende Adressen.
"endpoint": "127.0.0.1:4317"
"endpoint": "127.0.0.1:4318"Telemetrygen antwortet darauf mit einer Fehlermeldung.
traces export: exporter export timeout: rpc error: code = Unavailable desc = connection error: desc = "error reading server preface: connection reset by peer"Tragen Sie in der Konfiguration endpoint: 0.0.0.0:4317 für gRPC und endpoint: 0.0.0.0:4318 für HTTP ein, wie in Schritt 2, und starten Sie den Collector neu.
references exporter "jaeger" which is not configured
In der Pipeline ist eine Komponente angegeben, die es in ihrem eigenen Konfigurationsabschnitt nicht gibt. Wir haben diesen Fehler erhalten, als wir in der Trace-Pipeline jaeger statt otlp_grpc/jaeger geschrieben haben.
Error: service::pipelines::traces: references exporter "jaeger" which is not configuredDer Name im Abschnitt service muss mit dem Namen in exporters übereinstimmen, einschließlich des Teils nach dem Schrägstrich. Genauso meldet der Collector auch nicht beschriebene Empfänger und Prozessoren.
receivers::host_metrics: invalid root_path: stat /hostfs: no such file or directory
In compose.yaml ist das Stammverzeichnis des Servers nicht im Ordner /hostfs eingebunden, während er in den Einstellungen des Empfängers host_metrics angegeben ist.
Error: receivers::host_metrics: invalid root_path: stat /hostfs: no such file or directoryFügen Sie im Abschnitt volumes die Zeile - /:/hostfs:ro hinzu, wie in Schritt 7.
Unsere Ressourcen
Telegram-Kanal: https://t.me/f3cloud_news
Hosting-Website: https://f3cloud.com