Prometheus und Grafana sind der übliche Stack, um einen MQTT-Broker zu überwachen. Prometheus holt Metriken per HTTP ab und wertet Alert-Regeln aus. Grafana zeichnet die Dashboards. Broker stellen ihre Metriken auf zwei Arten bereit: Manche haben einen nativen Prometheus-Endpoint (EMQX, VerneMQ, HiveMQ über eine Extension), andere wie Mosquitto veröffentlichen nur $SYS-Topics und brauchen einen Exporter, der sie übersetzt.
Dieser Guide bindet alle drei Datenquellen an, die du wahrscheinlich brauchst: einen Exporter für Mosquitto, den nativen EMQX-Endpoint und eine clientseitige Probe, die mit jedem Broker funktioniert, auch mit Managed Brokern. Danach kommen Dashboards und Alerts dazu.
Woher die Metriken kommen
| Broker | Wie Prometheus an die Metriken kommt |
|---|---|
| Mosquitto | mosquitto-exporter abonniert $SYS/# und liefert /metrics (standardmäßig Port 9234) |
| EMQX 5 | Eingebauter Endpoint /api/v5/prometheus/stats am Dashboard-/API-Port (18083) oder Push an ein Pushgateway |
| HiveMQ | HiveMQ Prometheus Monitoring Extension, liefert /metrics (standardmäßig Port 9399) |
| VerneMQ | Eingebautes /metrics am HTTP-Listener (standardmäßig Port 8888) |
| Managed Broker | Metriken des Anbieters, falls vorhanden, plus deine eigene clientseitige Probe |
Brokerseitige Metriken zeigen, was der Broker tut: Verbindungen, Durchsatz, Queues und verworfene Nachrichten. Eine clientseitige Probe zeigt, was deine Geräte erleben: Können sie sich verbinden, und wie lange braucht eine Nachricht? Du brauchst beides. Wenn du nur eins haben kannst, nimm die Probe, denn sie erkennt Fehler bei DNS, TLS, Load Balancer und Authentifizierung.
Wenn $SYS neu für dich ist, lies zuerst einen Broker mit $SYS-Topics überwachen. Der Exporter unten ist nur eine dünne Schicht darüber.
Ein lokaler Stack mit Docker Compose
Dieser Stack startet Mosquitto, den Exporter, Prometheus und Grafana zusammen. Er ist ein gutes Labor, um Dashboards zu bauen, bevor du sie auf Produktion richtest.
services:
mosquitto:
image: eclipse-mosquitto:2
ports: ["1883:1883"]
volumes:
- ./mosquitto.conf:/mosquitto/config/mosquitto.conf:ro
mosquitto-exporter:
image: sapcc/mosquitto-exporter
command: ["--endpoint", "tcp://mosquitto:1883"]
ports: ["9234:9234"]
depends_on: [mosquitto]
prometheus:
image: prom/prometheus
ports: ["9090:9090"]
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./alerts.yml:/etc/prometheus/alerts.yml:ro
grafana:
image: grafana/grafana
ports: ["3000:3000"]
volumes:
- ./grafana-datasource.yml:/etc/grafana/provisioning/datasources/prometheus.yml:rolistener 1883
allow_anonymous true
sys_interval 10apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: http://prometheus:9090
isDefault: trueallow_anonymous true ist nur auf dem Laptop vertretbar. Auf einem echten Broker legst du einen eigenen Monitoring-Benutzer an, der $SYS/# lesen darf und sonst nichts, und übergibst ihn dem Exporter mit --user und --pass. Der mosquitto.conf-Generator erzeugt dir die Listener- und Authentifizierungsblöcke. Ein umfangreicheres Labor mit mehr Diensten findest du im MQTT-Docker-Compose-Stack.
Der Exporter macht aus jedem numerischen $SYS-Topic eine Metrik, deren Name dem Topic-Pfad folgt; aus $SYS/broker/clients/connected wird zum Beispiel broker_clients_connected. Öffne http://localhost:9234/metrics, um die genauen Namen deiner Version zu sehen, bevor du Queries schreibst.
Ein Probe-Exporter für jeden Broker
Dieser Python-Exporter (paho-mqtt 2.x und prometheus_client) verbindet sich alle 15 Sekunden per TLS, abonniert ein eindeutiges Topic, publisht eine Nachricht und wartet, bis sie zurückkommt. Er exportiert, ob das geklappt hat und wie lange jeder Schritt gedauert hat:
import os, ssl, threading, time, uuid
import paho.mqtt.client as mqtt
from prometheus_client import Gauge, Histogram, start_http_server
HOST, PORT, INTERVAL = "broker.connectmq.com", 8883, 15
success = Gauge("mqtt_probe_success", "1 if the last connect/publish/receive cycle worked")
connect_s = Gauge("mqtt_probe_connect_seconds", "TCP + TLS + CONNACK time of the last probe")
roundtrip = Histogram("mqtt_probe_roundtrip_seconds", "Subscribe, publish and receive time",
buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5))
def probe() -> bool:
topic = f"testmqtt/probe/{uuid.uuid4().hex[:8]}"
connected, received = threading.Event(), threading.Event()
c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=f"testmqtt-probe-{uuid.uuid4().hex[:6]}")
c.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASS"))
c.tls_set(cert_reqs=ssl.CERT_REQUIRED)
c.on_connect = lambda cl, ud, fl, rc, pr: connected.set() if not rc.is_failure else None
c.on_subscribe = lambda cl, ud, mid, rcs, pr: cl.publish(topic, "ping", qos=1)
c.on_message = lambda cl, ud, msg: received.set()
t0 = time.perf_counter()
try:
c.connect(HOST, PORT, keepalive=10)
c.loop_start()
if not connected.wait(5):
return False
connect_s.set(time.perf_counter() - t0)
t1 = time.perf_counter()
c.subscribe(topic, qos=1)
if not received.wait(5):
return False
roundtrip.observe(time.perf_counter() - t1)
return True
except OSError:
return False
finally:
c.disconnect()
c.loop_stop()
if __name__ == "__main__":
start_http_server(9105) # Metriken unter http://localhost:9105/metrics
while True:
success.set(1 if probe() else 0)
time.sleep(INTERVAL)Installiere die Abhängigkeiten mit pip install paho-mqtt prometheus-client. Die neue Verbindung in jedem Zyklus ist Absicht: So werden DNS, TLS und Authentifizierung jedes Mal mitgetestet, genau wie bei einem Gerät, das neu startet. Verwende bei jedem Lauf eine eindeutige Client-ID. Der Client-ID-Generator erklärt, warum sich doppelte IDs gegenseitig rauswerfen.
Scrape-Konfiguration
global:
scrape_interval: 15s
rule_files: ["/etc/prometheus/alerts.yml"]
scrape_configs:
- job_name: mosquitto
static_configs:
- targets: ["mosquitto-exporter:9234"]
- job_name: mqtt-probe
static_configs:
- targets: ["host.docker.internal:9105"] # wo die Probe läuft
- job_name: emqx
metrics_path: /api/v5/prometheus/stats
static_configs:
- targets: ["emqx:18083"]Entferne die Jobs, die du nicht brauchst. Bei neueren EMQX-Versionen lässt sich der Stats-Endpoint so konfigurieren, dass er Authentifizierung verlangt. Dann ergänzt du basic_auth mit API-Key und Secret. Um EMQX lokal auszuprobieren, siehe EMQX in Docker. host.docker.internal funktioniert mit Docker Desktop. Unter Linux startest du die Probe stattdessen als weiteren Compose-Service.
Dashboards, die echte Fragen beantworten
Ein nützliches MQTT-Dashboard hat wenige Panels, und jedes beantwortet eine Frage, die bei einem Incident gestellt wird:
| Panel | PromQL (Beispiel) |
|---|---|
| Sind Clients verbunden? | broker_clients_connected oder sum(emqx_connections_count) |
| Nachrichten ein / aus pro Sekunde | rate(broker_messages_received[5m]), rate(emqx_messages_sent[5m]) |
| Verwerfen wir Nachrichten? | rate(broker_publish_messages_dropped[5m]) oder rate(emqx_messages_dropped[5m]) |
| Kann sich gerade ein Client verbinden? | mqtt_probe_success |
| Wie langsam ist die Zustellung? | histogram_quantile(0.99, sum by (le) (rate(mqtt_probe_roundtrip_seconds_bucket[10m]))) |
| Verbindungszeit | mqtt_probe_connect_seconds |
Wende rate() auf Zähler an und zeichne nie den rohen Zählerwert. Setz die Probe-Panels nach oben, denn sie zeigen, was die Nutzer sehen. Auf grafana.com gibt es Community-Dashboards für den Mosquitto-Exporter und für EMQX. Sie sind ein guter Ausgangspunkt, aber prüfe, ob die Metriknamen zu deiner Exporter-Version passen.
Alert-Regeln
groups:
- name: mqtt
rules:
- alert: MqttProbeFailing
expr: max_over_time(mqtt_probe_success[3m]) == 0
labels: { severity: page }
annotations:
summary: "No successful MQTT round trip for 3 minutes"
- alert: MqttProbeSlow
expr: histogram_quantile(0.99, sum by (le) (rate(mqtt_probe_roundtrip_seconds_bucket[10m]))) > 0.5
for: 10m
labels: { severity: ticket }
- alert: MqttClientsDropped
expr: broker_clients_connected < 0.7 * (broker_clients_connected offset 10m)
for: 2m
labels: { severity: page }
- alert: MqttExporterDown
expr: up{job="mosquitto"} == 0
for: 2m
labels: { severity: ticket }MqttClientsDropped erkennt das klassische Incident-Muster: Ein großer Teil der Geräte trennt sich gleichzeitig, etwa nach einem abgelaufenen Zertifikat, einer Netzwerkänderung oder einem Broker-Neustart. Leite page-Alerts über den Alertmanager an deinen Bereitschaftskanal und ticket-Alerts an einen ruhigeren Ort. Für externe Checks und SLO-Rechnung siehe MQTT-Uptime-Monitoring.
Das Monitoring selbst testen
Bevor du den Alerts vertraust, löse jeden einmal absichtlich aus:
- Probe-Fehler: Setze
MQTT_PASSauf einen falschen Wert und starte die Probe neu.mqtt_probe_successsollte auf 0 fallen undMqttProbeFailingnach etwa drei Minuten auslösen. - Client-Einbruch: Verbinde eine Reihe von Testclients (ein Gerätesimulator oder mehrere Tabs des Online-Clients), warte zehn Minuten und schließe sie dann. Das Panel für verbundene Clients sollte entsprechend fallen.
- Exporter down:
docker compose stop mosquitto-exportersollteMqttExporterDownauslösen. - Latenz: Füge mit einem Network Shaper Verzögerung hinzu oder vergleiche die Werte der Probe mit einem eigenen Latenztest.
Ein Alert, der im Test nie ausgelöst hat, löst womöglich auch bei einem echten Ausfall nicht aus.
Prometheus und Grafana liefern Historie, Dashboards und Alerts, die $SYS allein nicht bietet. Kombiniere Broker-Metriken mit einer clientseitigen Probe, halte das Dashboard klein und teste jeden Alert einmal, bevor du dich auf ihn verlässt.
Häufig gestellte Fragen
Hat Mosquitto einen eingebauten Prometheus-Endpoint?
Nein. Mosquitto veröffentlicht seine Statistiken über $SYS-Topics. Ein Exporter wie mosquitto-exporter abonniert $SYS/# und stellt die Werte über einen HTTP-Endpoint /metrics bereit, den Prometheus abfragen kann.
Kann ich einen Managed MQTT Broker mit Prometheus überwachen?
Ja, von der Client-Seite aus. Auch wenn du keinen Exporter neben dem Broker installieren kannst, kann eine kleine Probe, die sich verbindet, publisht und eine Nachricht empfängt, Erfolgs- und Latenzmetriken exportieren. Das ist ohnehin oft das nützlichste Signal.
Worauf sollte ich zuerst alarmieren?
Starte mit drei Alerts: die Probe schlägt einige Minuten lang fehl, die Zahl verbundener Clients bricht plötzlich ein, und verworfene Nachrichten liegen über null. Latenz-Alerts kommen dazu, sobald du dein normales p99 kennst.


