Pruebas de brokers

Monitorización externa de disponibilidad MQTT y alertas

Monitoriza la disponibilidad de un broker MQTT desde fuera: sonda sintética publish/subscribe, caducidad de certificados, cron, heartbeats, Slack, PagerDuty y SLO.

Actualizado · 9 min de lectura

Monitorización externa de disponibilidad MQTT y alertas

La monitorización externa de disponibilidad responde a una sola pregunta: ¿podría ahora mismo un dispositivo fuera de tu red conectarse al broker e intercambiar un mensaje? Las métricas internas no pueden responderla. No ven fallos de DNS, certificados caducados, balanceadores rotos ni un cambio en el firewall. Solo lo ve una sonda que se comporta como un cliente y se ejecuta desde fuera.

Esta guía construye esa sonda, la programa, envía las alertas a Slack o PagerDuty y muestra los cálculos de SLO que necesitas para elegir el intervalo de comprobación y el umbral de alerta.

Qué significa "disponible" para un broker MQTT

"El puerto está abierto" es una definición débil. Un broker solo es útil cuando funcionan todas estas capas:

CapaComprobaciónFallo típico que detecta
DNSEl hostname resuelveDominio caducado, registro erróneo tras una migración
TCPEl puerto 8883 acepta la conexiónFirewall, security group, proceso del broker caído
TLSEl handshake funciona y el certificado es válido N días másCertificado caducado o que no coincide
MQTT CONNECTCONNACK con código de éxitoBackend de autenticación caído, credenciales rotadas, límite de conexiones alcanzado
Ida y vueltaEl mensaje publicado se recibe dentro del tiempo límiteEnrutamiento roto, clúster dividido, sobrecarga

Comprueba cada capa por separado e informa de cuál ha fallado. "Falló TLS, certificado caducado" se arregla en cinco minutos. "Falló la sonda" empieza una investigación de media hora. Para comprobaciones manuales puntuales, consulta comprobar el estado de un broker MQTT y comprobar si un puerto MQTT está abierto.

Una sonda mínima con los clientes de Mosquitto

Si ya tienes mosquitto_pub y mosquitto_sub, una sonda en shell ocupa una docena de líneas:

mqtt-probe.sh
#!/usr/bin/env bash
set -u
HOST=broker.connectmq.com; PORT=8883
TOPIC="testmqtt/uptime/$(hostname)-$$"
MSG="ping-$(date +%s)"
OUT=$(mktemp)

mosquitto_sub -h "$HOST" -p "$PORT" --capath /etc/ssl/certs \
  -u "$MQTT_USER" -P "$MQTT_PASS" -t "$TOPIC" -q 1 -C 1 -W 10 > "$OUT" &
SUB=$!
sleep 1
mosquitto_pub -h "$HOST" -p "$PORT" --capath /etc/ssl/certs \
  -u "$MQTT_USER" -P "$MQTT_PASS" -t "$TOPIC" -q 1 -m "$MSG"
wait $SUB

if grep -qx "$MSG" "$OUT"; then echo "OK"; rm -f "$OUT"; exit 0
else echo "FAIL: no round trip within 10 s"; rm -f "$OUT"; exit 1; fi

-C 1 hace que el suscriptor termine tras un mensaje, y -W 10 hace que se rinda a los diez segundos. El código de salida es todo lo que necesita un planificador o un servicio de heartbeat.

Una sonda por capas en Python

Esta versión (paho-mqtt 2.x) comprueba cada capa, la mide, informa de los días que faltan para que caduque el certificado e imprime una línea JSON fácil de enviar a los logs:

mqtt_uptime_probe.py
import json, os, socket, ssl, sys, threading, time, uuid
import paho.mqtt.client as mqtt

HOST, PORT, TIMEOUT = "broker.connectmq.com", 8883, 10
result = {"host": HOST, "ts": int(time.time())}

def fail(stage, detail):
    result.update(ok=False, stage=stage, detail=str(detail))
    print(json.dumps(result)); sys.exit(1)

t = time.perf_counter()
try:
    socket.getaddrinfo(HOST, PORT)
except OSError as e:
    fail("dns", e)
try:
    ctx = ssl.create_default_context()
    with socket.create_connection((HOST, PORT), timeout=TIMEOUT) as raw:
        with ctx.wrap_socket(raw, server_hostname=HOST) as tls:
            not_after = ssl.cert_time_to_seconds(tls.getpeercert()["notAfter"])
except ssl.SSLError as e:
    fail("tls", e)
except OSError as e:
    fail("tcp", e)
result["cert_days_left"] = int((not_after - time.time()) // 86400)
result["tls_ms"] = round((time.perf_counter() - t) * 1000)

topic = f"testmqtt/uptime/{uuid.uuid4().hex[:8]}"
connack, got = {}, threading.Event()
c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"testmqtt-uptime-{uuid.uuid4().hex[:6]}")
c.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASS"))
c.tls_set_context(ctx)
c.on_connect = lambda cl, ud, fl, rc, pr: (connack.update(rc=rc),
                                           None if rc.is_failure else cl.subscribe(topic, qos=1))
c.on_subscribe = lambda cl, ud, mid, rcs, pr: cl.publish(topic, "ping", qos=1)
c.on_message = lambda cl, ud, msg: got.set()

t = time.perf_counter()
try:
    c.connect(HOST, PORT, keepalive=15)
except OSError as e:
    fail("connect", e)
c.loop_start()
ok = got.wait(TIMEOUT)
c.disconnect(); c.loop_stop()
if "rc" not in connack:
    fail("connect", "no CONNACK")
if connack["rc"].is_failure:
    fail("connect", connack["rc"])
if not ok:
    fail("roundtrip", f"no message within {TIMEOUT} s")
result.update(ok=True, roundtrip_ms=round((time.perf_counter() - t) * 1000))
print(json.dumps(result))
sys.exit(2 if result["cert_days_left"] < 14 else 0)

El código de salida 0 significa sano, 1 significa caído (el campo stage dice dónde) y 2 significa que funciona, pero el certificado caduca en menos de dos semanas. Esto último es un aviso, no una alerta de guardia. Cuando falla la etapa connect, el reason code (por ejemplo Not authorized) te dice si es un problema de credenciales o de ACL. Búscalo con la herramienta de reason codes o en errores de conexión MQTT.

Programar la sonda

  • cron o un timer de systemd en una VM pequeña fuera de la red del broker: * * * * * /opt/probe/mqtt_uptime_probe.py >> /var/log/mqtt-probe.log 2>&1. Es sencillo y se ejecuta cada minuto.
  • Varias ubicaciones. Ejecuta la misma sonda desde dos o tres regiones o proveedores. Alerta cuando falla la mayoría. Si falla una sola ubicación, suele ser la red de esa ubicación.
  • schedule de GitHub Actions sirve para comprobaciones poco críticas, pero el intervalo mínimo es de cinco minutos y las ejecuciones pueden empezar con retraso. No sirve para avisar a la guardia.
  • Uptime Kuma (autoalojado) incluye un tipo de monitor MQTT y funciona bien si prefieres una interfaz gráfica.

Lo que ejecuta la sonda también puede fallar. Usa un heartbeat (dead man's switch): tras cada ejecución correcta, la sonda hace ping a una URL de un servicio de heartbeat como Healthchecks.io o un push monitor de Uptime Kuma. Si los pings se detienen, o ha caído el broker o ha caído la máquina de la sonda, y en ambos casos recibes una alerta:

Línea de cron con heartbeat
* * * * * /opt/probe/mqtt-probe.sh && curl -fsS -m 10 --retry 3 "$HEARTBEAT_URL" > /dev/null

Canales de alerta

Envía las alertas a donde alguien las vaya a ver de verdad, y solo cuando haya pruebas suficientes. Una regla habitual es tres fallos consecutivos desde al menos dos ubicaciones, con un mensaje de recuperación cuando se resuelve.

Incoming webhook de Slack
curl -fsS -X POST -H 'Content-Type: application/json' \
  -d '{"text":":red_circle: MQTT probe failed at stage tls (broker.connectmq.com:8883)"}' \
  "$SLACK_WEBHOOK_URL"
PagerDuty Events API v2
curl -fsS -X POST https://events.pagerduty.com/v2/enqueue \
  -H 'Content-Type: application/json' \
  -d '{"routing_key":"'"$PD_ROUTING_KEY"'","event_action":"trigger",
       "dedup_key":"mqtt-probe-broker",
       "payload":{"summary":"MQTT round trip failing","source":"mqtt-probe","severity":"critical"}}'

Envía la misma petición con "event_action":"resolve" y la misma dedup_key para cerrar el incidente automáticamente. Deja los avisos de caducidad de certificados en Slack o en el correo. Hay que actuar esta semana, no a las 3 de la madrugada. Si ya usas Prometheus, exporta la sonda como métricas y alerta desde allí. Consulta monitorizar MQTT con Prometheus y Grafana.

Cálculos de SLO

Un objetivo de disponibilidad solo tiene sentido junto con su presupuesto de caída:

DisponibilidadCaída por 30 díasCaída por año
99 %7 h 12 min3,65 días
99,5 %3 h 36 min1,83 días
99,9 %43,2 min8 h 46 min
99,95 %21,6 min4 h 23 min
99,99 %4,3 min52,6 min

Mídela como sondas correctas ÷ sondas totales. Una sonda por minuto da 43.200 muestras en 30 días, así que el 99,9 % permite 43 sondas fallidas. De ahí salen tres consecuencias:

  • El intervalo limita la precisión. Con una sonda por minuto, una caída de 20 segundos puede pasar desapercibida o contar como un minuto entero. Si quieres medir un 99,99 %, sondea cada 10–15 segundos.
  • El tiempo de detección se come el presupuesto. Tres fallos a intervalos de un minuto suponen unos tres minutos hasta que se avisa a alguien. Está bien para el 99,9 % (43 minutos) y es casi todo el presupuesto con el 99,99 %.
  • Tu SLO no es el SLA del proveedor. El SLA cubre el broker. Tu sonda mide todo el camino, incluidos tu DNS, tus certificados y tu red. Lee SLA y disponibilidad de brokers MQTT antes de comparar ambos.

Vigila el presupuesto de errores: si a mitad de mes ya has gastado 30 de tus 43 minutos, congela los cambios arriesgados en el broker hasta que se recupere.

Prueba el camino de las alertas

Una sonda que nunca ha fallado no está probada. Antes de depender de ella:

  1. Pon una contraseña incorrecta y confirma que falla la etapa connect y que la alerta llega a Slack o PagerDuty.
  2. Apunta HOST a un nombre que no resuelve y confirma stage: dns.
  3. Detén el cron y confirma que el servicio de heartbeat te avisa.
  4. Comprueba a mano que lo que ve la sonda coincide con la realidad: conéctate con el cliente MQTT online y publica en el topic de la sonda mientras se ejecuta.

La monitorización externa de disponibilidad es un pequeño script, un planificador y un heartbeat. Comprueba cada capa por separado, alerta solo con fallos repetidos desde más de una ubicación, elige el intervalo de la sonda a partir de tu SLO y rompe la configuración a propósito una vez para demostrar que las alertas llegan.

Preguntas frecuentes

¿Basta con comprobar que el puerto 1883 está abierto para monitorizar la disponibilidad MQTT?

No. Un broker puede aceptar conexiones TCP y a la vez rechazar logins, fallar en TLS o no enrutar mensajes. Una comprobación real se conecta con credenciales, se suscribe, publica un mensaje y confirma que llega dentro de un tiempo límite.

¿Cada cuánto debería ejecutarse una sonda de disponibilidad MQTT?

Cada minuto es un buen valor por defecto. El intervalo limita la precisión con la que mides la disponibilidad: con una comprobación por minuto, las caídas de menos de un minuto pueden pasar desapercibidas, y un presupuesto mensual del 99,99 % (unos cuatro minutos) se agota antes de que salte una alerta de tres fallos.

¿Cuánto tiempo de caída permite un 99,9 %?

Unos 43 minutos por mes de 30 días, o aproximadamente 8 horas y 46 minutos al año. El 99,95 % permite unos 22 minutos al mes, y el 99,99 % unos 4,3 minutos.