Testes de brokers

Monitorar MQTT com Prometheus e Grafana

Monitore um broker MQTT com Prometheus e Grafana: mosquitto-exporter, métricas do EMQX, um exporter de sondagem em Python, Docker Compose, dashboards e alertas.

Atualizado · 9 min de leitura

Monitorar MQTT com Prometheus e Grafana

Prometheus e Grafana são a dupla mais comum para monitorar um broker MQTT. O Prometheus coleta métricas via HTTP e avalia regras de alerta. O Grafana desenha os dashboards. Os brokers expõem métricas de duas formas: alguns têm um endpoint Prometheus nativo (EMQX, VerneMQ, HiveMQ por meio de uma extensão), enquanto outros, como o Mosquitto, só publicam tópicos $SYS e precisam de um exporter para traduzi-los.

Este guia conecta as três fontes de dados de que você provavelmente vai precisar: um exporter para o Mosquitto, o endpoint nativo do EMQX e uma sonda do lado do cliente que funciona com qualquer broker, inclusive os gerenciados. Depois, adiciona dashboards e alertas.

De onde vêm as métricas

BrokerComo o Prometheus obtém as métricas
Mosquittomosquitto-exporter assina $SYS/# e serve /metrics (porta 9234 por padrão)
EMQX 5Endpoint embutido /api/v5/prometheus/stats na porta do dashboard/API (18083), ou envio para um Pushgateway
HiveMQHiveMQ Prometheus Monitoring Extension, serve /metrics (porta 9399 por padrão)
VerneMQ/metrics embutido no listener HTTP (porta 8888 por padrão)
Broker gerenciadoMétricas do provedor, se houver, mais a sua própria sonda do lado do cliente

Métricas do broker mostram o que o broker está fazendo: conexões, throughput, filas e descartes. Uma sonda do lado do cliente mostra o que os seus dispositivos experimentam: eles conseguem conectar, e quanto tempo leva uma mensagem? Você precisa das duas. Se só puder ter uma, escolha a sonda, porque é ela que pega falhas de DNS, TLS, load balancer e autenticação.

Se $SYS é novidade para você, leia primeiro monitorar um broker com tópicos $SYS. O exporter abaixo é uma camada fina sobre isso.

Um stack local com Docker Compose

Este stack roda Mosquitto, o exporter, Prometheus e Grafana juntos. É um bom laboratório para montar dashboards antes de apontá-los para produção.

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 (somente laboratório local)
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 só é aceitável no seu notebook. Em um broker real, crie um usuário de monitoramento que possa ler $SYS/# e nada mais, e passe-o ao exporter com --user e --pass. O gerador de mosquitto.conf pode montar os blocos de listener e autenticação para você. Para um laboratório mais completo, com mais serviços, veja o stack MQTT com Docker Compose.

O exporter transforma cada tópico $SYS numérico em uma métrica cujo nome segue o caminho do tópico; por exemplo, $SYS/broker/clients/connected vira broker_clients_connected. Abra http://localhost:9234/metrics para ver os nomes exatos que a sua versão gera antes de escrever consultas.

Um exporter de sondagem para qualquer broker

Este exporter em Python (paho-mqtt 2.x e prometheus_client) conecta via TLS a cada 15 segundos, assina um tópico único, publica uma mensagem e espera que ela volte. Ele exporta se deu certo e quanto tempo cada etapa levou:

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)          # métricas em http://localhost:9105/metrics
    while True:
        success.set(1 if probe() else 0)
        time.sleep(INTERVAL)

Instale com pip install paho-mqtt prometheus-client. Abrir uma conexão nova a cada ciclo é proposital: isso exercita DNS, TLS e autenticação todas as vezes, como um dispositivo que reinicia. Use um client ID único em cada execução. O gerador de client ID explica por que IDs duplicados derrubam um ao outro.

Configuração de scrape

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"]   # onde a sonda roda

  - job_name: emqx
    metrics_path: /api/v5/prometheus/stats
    static_configs:
      - targets: ["emqx:18083"]

Remova os jobs que você não usa. Nas versões recentes do EMQX, o endpoint de estatísticas pode ser configurado para exigir autenticação. Nesse caso, adicione basic_auth com uma API key e o secret. Para testar o EMQX localmente, veja EMQX no Docker. host.docker.internal funciona no Docker Desktop. No Linux, rode a sonda como outro serviço do Compose.

Dashboards que respondem perguntas reais

Um dashboard MQTT útil tem poucos painéis, e cada um responde a uma pergunta que alguém vai fazer durante um incidente:

PainelPromQL (exemplo)
Há clientes conectados?broker_clients_connected ou sum(emqx_connections_count)
Mensagens de entrada / saída por segundorate(broker_messages_received[5m]), rate(emqx_messages_sent[5m])
Estamos descartando mensagens?rate(broker_publish_messages_dropped[5m]) ou rate(emqx_messages_dropped[5m])
Um cliente consegue conectar agora?mqtt_probe_success
Quão lenta está a entrega?histogram_quantile(0.99, sum by (le) (rate(mqtt_probe_roundtrip_seconds_bucket[10m])))
Tempo de conexãomqtt_probe_connect_seconds

Use rate() nos contadores e nunca plote o valor bruto do contador. Coloque os painéis da sonda no topo, porque eles mostram o que os usuários veem. No grafana.com há dashboards da comunidade para o exporter do Mosquitto e para o EMQX. São um bom ponto de partida, mas confira se os nomes de métricas batem com a versão do seu exporter.

Regras de alerta

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 detecta o padrão clássico de incidente: uma grande parte dos dispositivos desconecta de uma vez, depois de um certificado expirado, uma mudança de rede ou um reinício do broker. Encaminhe os alertas page pelo Alertmanager para o canal de plantão e os ticket para um lugar mais tranquilo. Para verificações externas e a matemática de SLO, veja monitoramento de uptime MQTT.

Teste o próprio monitoramento

Antes de confiar nos alertas, faça cada um disparar de propósito:

  1. Falha da sonda: troque MQTT_PASS por um valor errado e reinicie a sonda. mqtt_probe_success deve cair para 0 e MqttProbeFailing deve disparar em cerca de três minutos.
  2. Queda de clientes: conecte um lote de clientes de teste (um simulador de dispositivos ou várias abas do cliente online), espere dez minutos e feche-os. O painel de clientes conectados deve cair de acordo.
  3. Exporter fora do ar: docker compose stop mosquitto-exporter deve disparar MqttExporterDown.
  4. Latência: adicione atraso com um network shaper ou compare os números da sonda com um teste de latência dedicado.

Um alerta que nunca disparou em um teste pode não disparar também durante uma queda real.

Prometheus e Grafana trazem histórico, dashboards e alertas que o $SYS sozinho não oferece. Combine métricas do broker com uma sonda do lado do cliente, mantenha o dashboard enxuto e teste cada alerta uma vez antes de depender dele.

Perguntas frequentes

O Mosquitto tem um endpoint Prometheus embutido?

Não. O Mosquitto publica suas estatísticas em tópicos $SYS. Um exporter como o mosquitto-exporter assina $SYS/# e expõe os valores em um endpoint HTTP /metrics que o Prometheus pode coletar.

Posso monitorar um broker MQTT gerenciado com Prometheus?

Sim, pelo lado do cliente. Mesmo quando você não pode instalar um exporter ao lado do broker, uma pequena sonda que conecta, publica e recebe uma mensagem pode exportar métricas de sucesso e latência. Muitas vezes esse é o sinal mais útil.

Sobre o que devo alertar primeiro?

Comece com três alertas: a sonda falhando por alguns minutos, uma queda brusca de clientes conectados e mensagens descartadas acima de zero. Adicione alertas de latência quando souber qual é o seu p99 normal.