Broker-Tests

MQTT mit Prometheus und Grafana überwachen

MQTT-Broker mit Prometheus und Grafana überwachen: mosquitto-exporter, EMQX-Metriken, ein Python-Probe-Exporter, Docker-Compose-Stack, Dashboards und Alerts.

Aktualisiert · 9 Min. Lesezeit

MQTT mit Prometheus und Grafana überwachen

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

BrokerWie Prometheus an die Metriken kommt
Mosquittomosquitto-exporter abonniert $SYS/# und liefert /metrics (standardmäßig Port 9234)
EMQX 5Eingebauter Endpoint /api/v5/prometheus/stats am Dashboard-/API-Port (18083) oder Push an ein Pushgateway
HiveMQHiveMQ Prometheus Monitoring Extension, liefert /metrics (standardmäßig Port 9399)
VerneMQEingebautes /metrics am HTTP-Listener (standardmäßig Port 8888)
Managed BrokerMetriken 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.

docker-compose.yml
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:ro
mosquitto.conf (nur lokales Labor)
listener 1883
allow_anonymous true
sys_interval 10
grafana-datasource.yml
apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    url: http://prometheus:9090
    isDefault: true

allow_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:

mqtt_probe_exporter.py
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

prometheus.yml
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:

PanelPromQL (Beispiel)
Sind Clients verbunden?broker_clients_connected oder sum(emqx_connections_count)
Nachrichten ein / aus pro Sekunderate(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])))
Verbindungszeitmqtt_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

alerts.yml
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:

  1. Probe-Fehler: Setze MQTT_PASS auf einen falschen Wert und starte die Probe neu. mqtt_probe_success sollte auf 0 fallen und MqttProbeFailing nach etwa drei Minuten auslösen.
  2. 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.
  3. Exporter down: docker compose stop mosquitto-exporter sollte MqttExporterDown auslösen.
  4. 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.