Reglas de alerta
mikroscope dashboards gen escribe, junto a cada dashboard, un fichero de aprovisionamiento de las
alertas unificadas de Grafana con las reglas que se desprenden de los propios contadores de fallos de
los dashboards, del registro del kernel que lee el agente y de las detecciones del colector. Todo
umbral es cero (un contador que no debería moverse), una muestra (la regla del agente en silencio),
una proporción de un techo que publicó el propio equipo o, en una regla, un múltiplo del último día
del propio equipo. Ninguno es un número compilado para un router concreto.
Ficheros
Sección titulada «Ficheros»| Fichero | Reglas | Lenguaje de consulta |
|---|---|---|
dashboards/ |
14 | SQL de InfluxDB 3 |
dashboards/ |
15 | PromQL |
dashboards/ |
10 | SQL de PostgreSQL |
Desliza en horizontal para ver todas las columnas
El fichero de InfluxDB tiene una regla menos porque “The sampler is slipping ticks” todavía no tiene
forma en SQL. El contador sí llega a InfluxDB —el colector lo lee del /sampler del agente y
escribe mikroscope_—, así que la regla se podría escribir; no se ha escrito.
El fichero de PostgreSQL se traduce del de InfluxDB, y además deja fuera las reglas que el esquema
SQL no puede responder con la misma forma. mikroscope-l2-loop y mikroscope-port-link-down suman
un recuento por muestra de registros de puerto del kernel, y el destino SQL escribe en su lugar una
fila por registro del kernel, en mikroscope_event. mikroscope-port-errors nombra una
columna por contador del MAC, y el destino SQL escribe mikroscope_api_ifcounter con una fila por
contador. mikroscope-bridge-port-dark queda fuera por la misma razón: nombra rx_packet,
tx_unicast y tx_broadcast como columnas, y lee bridge, que esa tabla tampoco tiene. La
traducción las descarta en vez de escribir una consulta que parece correcta y responde a otra cosa.
Una prueba unitaria comprueba cada columna que lee cada consulta de alerta de PostgreSQL contra las
tablas que declara el destino SQL; lee el esquema, no una base de datos
(no ejecutadas contra PostgreSQL).
Cada fichero es apiVersion: 1 con un grupo de reglas, mikroscope, en una carpeta llamada
mikroscope, organización 1, evaluado cada minuto. Las reglas se aprovisionan en vez de ir dentro de
los dashboards, así que un operador que no quiera ninguna no copia nada. forward --grafana,
dashboards publish y dashboards import no instalan ninguna, y uninstall --targets dashboard no
quita ninguna.
Instalar las reglas
Sección titulada «Instalar las reglas»-
Sustituye el marcador de la fuente de datos por el UID de tu fuente de datos (
mikroscope-<almacén>si la creó el colector odashboards publish) y escribe el fichero en el directorio de aprovisionamiento de Grafana. Los ficheros de aprovisionamiento no resuelven la entrada${DS_MIKROSCOPE}de un dashboard, así que la fuente de datos es un marcador literal,DS_UID_PLACEHOLDER:Ventana de terminal sed 's/DS_UID_PLACEHOLDER/<uid>/g' dashboards/mikroscope-alerts-influxdb.yaml \> /etc/grafana/provisioning/alerting/mikroscope-alerts-influxdb.yamlUsa el fichero que corresponda a la fuente de datos a la que pertenece el UID.
/etc/es la ruta que nombra la cabecera del propio fichero generado; el directorio es cosa de Grafana.grafana/ provisioning/ alerting/ -
Reinicia Grafana, o pide a su API de administración que recargue los ficheros aprovisionados. El formato del fichero y la recarga están en la guía de aprovisionamiento de alertas por fichero de Grafana.
Todas las reglas tienen la misma forma, la que escribe el propio editor de reglas de Grafana:
- A — la consulta, contra tu fuente de datos, con un rango de tiempo relativo de los últimos 600 s. Toda consulta SQL y toda consulta de Prometheus sobre un contador acota además su propia ventana (1, 2, 5 o 10 minutos, o 1 hora, más abajo; la regla de despertares lee además las 24 horas anteriores a sus 10 minutos, diga lo que diga el rango de 600 s de A); las dos reglas de Prometheus sobre gauges, la térmica y la de conntrack, leen el último valor, y la de la cola de salida toma el máximo de su gauge en el último minuto.
- B — reduce A a un número por serie con
last, descartando los valores no numéricos. - C — compara B con el umbral. C es la condición de la regla.
Cada regla lleva las etiquetas severity (critical o warning) y source: mikroscope, una
anotación summary y execErrState: Error. Lo que hace Grafana después con una regla cuya consulta
falla es comportamiento de Grafana, descrito en su propia documentación de los estados Error y No
Data
(no probado aquí).
| Regla (uid) | Salta cuando | Umbral (C) | Severidad | for | Sin datos significa | Almacenes |
|---|---|---|---|---|---|---|
mikroscope-agent-silent | llegó al almacén menos de 1 muestra nueva en los últimos 2 minutos | < 1 | critical | 2m | Alerting | los tres |
mikroscope-softnet-drops | softnet_stat descartó un paquete en los últimos 5 minutos | > 0 | critical | 0s | OK | los tres |
mikroscope-oom-kill | oom_kill de /proc/vmstat se movió en los últimos 5 minutos | > 0 | critical | 0s | OK | los tres |
mikroscope-detections | cualquier detección en los últimos 5 minutos salvo microburst e ipc-collapse, que se dibujan y se guardan pero no avisan | > 0 | warning | 0s | OK | los tres |
mikroscope-thermal-near-critical | una zona al 85 % o más de su propio disparo crítico | > 0 | critical | 1m | OK | los tres |
mikroscope-conntrack-near-limit | objetos activos de nf_conntrack por encima de 0,8 del límite del kernel | > 0,8 | warning | 5m | OK | los tres |
mikroscope-agent-oom | el propio cgroup del agente registró un OOM kill en los últimos 5 minutos | > 0 | critical | 0s | OK | los tres |
mikroscope-l2-loop | un registro de own-address en cualquier puerto en los últimos 5 minutos | > 0 | critical | 0s | OK | InfluxDB 3, Prometheus |
mikroscope-port-link-down | un registro de link-down en cualquier puerto en los últimos 5 minutos | > 0 | warning | 0s | OK | InfluxDB 3, Prometheus |
mikroscope-port-errors | el MAC de algún puerto contó un error tipificado —desbordamiento, FCS, colisión— durante 5 minutos seguidos | > 0 | warning | 5m | OK | InfluxDB 3, Prometheus |
mikroscope-bridge-port-dark | un puerto de bridge recibió paquetes mientras el bridge no le envió ni una trama unicast ni una broadcast, durante 10 minutos | > 0 | warning | 10m | OK | InfluxDB 3, Prometheus |
mikroscope-wakeup-storm | la tasa de cambios de contexto de los últimos 10 minutos supera 4 veces su media de las 24 horas anteriores, durante 10 minutos | > 4 | warning | 10m | OK | los tres |
mikroscope-egress-queue-drops | la cola de salida de algún puerto descartó un paquete en cada uno de los últimos 10 minutos: congestión sostenida, nunca una ráfaga suelta | > 0 | warning | 10m | OK | los tres |
mikroscope-ecc-failure | la NAND informó de un fallo ECC no corregible en la última hora | > 0 | critical | 0s | OK | los tres |
mikroscope-ticks-slipped | el muestreador perdió un tick por retraso en los últimos 5 minutos | > 0 | warning | 5m | OK | Prometheus |
Desliza en horizontal para ver todas las columnas
“Sin datos significa” es el noDataState de la regla. La regla del agente en silencio es la única en
la que el silencio es el fallo, así que la ausencia de datos la hace saltar; para todas las demás, no
tener datos es la lectura sana.
El título de cada regla, en inglés como aparece en Grafana, y debajo una traducción abreviada de su
anotación summary (el texto generado está en inglés):
- mikroscope agent stopped delivering samples. No llegaron muestras nuevas al almacén en los últimos dos minutos: se paró el agente, se paró el colector o se cortó el camino entre ellos. Todas las demás reglas están ciegas mientras esta salta.
- Packets dropped in the kernel receive path.
softnet_statdescartó un paquete: una cola por CPU estaba llena. Pérdida inequívoca dentro del router, invisible para cualquier contador SNMP o de RouterOS. Cero es la lectura esperada. - The kernel OOM-killed a process.
oom_killde/proc/vmstatse movió: el kernel mató un proceso para recuperar memoria. Qué proceso no se puede saber desde el contenedor (no hay espacio de nombres de PID). - The collector’s derive stage flagged an event. Saltó una regla de detección (counter-reset,
agent-restart, agent-oom, reboot, link-flap, conntrack-cliff, conntrack-high, thermal-high,
thermal-rising). La regla, la clave, el valor y el umbral están en la sección de detecciones y en
el dashboard como anotación.
microbursteipc-collapsese dibujan y se guardan como cualquier detección, pero no la disparan: describen cómo lleva el tráfico un router sano, y fueron la mayoría de las detecciones en un día medido (medido). - A thermal zone is within 15 % of its own critical trip. La lectura está al 85 % o más del punto
de disparo crítico declarado por la zona. El techo es el de la propia placa, leído de
/sys, no un número compilado. - The connection table is above 80 % of nf_conntrack_max. Objetos activos de
nf_conntrackpor encima del propio techo del kernel. Pasado el techo, el router descarta las conexiones nuevas. El límite es el sysctl que leyó el agente, no un número compilado. - The sampler is slipping ticks. Hubo ticks que terminaron después de que tocara el siguiente. La cadencia no se está cumpliendo: el equipo va falto de CPU, el conjunto de fuentes es demasiado caro para la cadencia, o la cuota de CPU del contenedor limitó al agente (consulta el contador de limitación del observador).
- mikroscope’s own container was OOM-killed. El kernel mató un proceso dentro del cgroup del
agente: el anillo de captura y las capturas se han perdido, y todos los números de la ventana son
sospechosos. Sube
--memory-maxo bajaRATE_HZ,BUFFER_SoCAPTURE_MB. - The bridge received its own address back: a layer-2 loop signature. El registro del kernel
informó de
received packet on <port> with own address as source address: una trama que envió el router volvió a entrar, que es el aspecto que tiene un bucle a través de un switch o un punto de acceso aguas abajo. La etiqueta del puerto dice de qué cable se trata. Se lee de/dev/kmsgcon el agente, sin API. La forma de InfluxDB necesita un almacén que haya tenido al menos un registro de puerto clasificado porkind. El caso real del bucle de capa 2 muestra la firma. - A port’s link went down. El registro del kernel informó de un link-down en un puerto: un cable
desconectado, un par que se reinició o se apagó, una renegociación. La detección de link-flap del
colector cubre el caso repetido; esto es el suceso único. Se lee de
/dev/kmsgcon el agente, sin API; el puerto, su comentario y su rol están en los sucesos de puerto de la sección del registro del kernel. - A port is counting typed MAC errors. El MAC de un puerto está contando errores tipificados: tramas que no pudo aceptar. El más común en una LAN conmutada es rx-overflow, la FIFO de recepción llenándose más rápido de lo que el chip la drena, y es una firma de microrráfaga y no de carga. Los errores de FCS y las colisiones significan otra cosa: cableado, dúplex, un puerto muriéndose. Necesita la capa de API: un contador del MAC no se ve desde dentro del contenedor. No distingue un error real de un reinicio del contador, así que un router que se reinicia dentro de la ventana la dispara una vez.
- A bridge port is receiving but the bridge sends it nothing. Un puerto miembro de un bridge ha
recibido paquetes durante diez minutos mientras el bridge no le entregó ni una trama unicast ni una
broadcast. Lo que hay detrás transmite y no recibe nada de vuelta: STP mantiene el puerto
descartando, o el bridge aprendió todos los hosts de detrás por otro camino. RouterOS muestra el
puerto en marcha y sin errores todo el tiempo. Un bucle de capa 2 se lee así en el puerto al que
el bridge ha dejado de entregar (caso real). Se espera que
salte en un diseño redundante, donde un puerto alternate de RSTP descarta a propósito, y en un
puerto con
broadcast-flood=nouhorizon. Necesita la capa de API. - The kernel is switching context four times as often as over its last day. Algo empezó a
despertarse muy a menudo: un proceso o un driver en espera activa, un cliente de monitorización,
un contenedor en un bucle cerrado. Mira qué empezó cuando saltó (
/user/active, contenedores nuevos, una integración nueva) y la sección de interrupciones y softirqs. Una integración de monitorización que consulta el router por la API es una de las causas (caso real). Un cambio deliberado, como un contenedor nuevo o un conjunto de reglas más pesado, también la dispara. - A port’s egress queue has been dropping every minute for ten minutes. La cola de salida de un puerto ha descartado paquetes en cada uno de los últimos diez minutos. A diferencia de las demás reglas de contador, el contador de esta sí debe moverse: descartar es como una cola llena le dice a quien envía que afloje. Lo que la dispara es un enlace sencillamente demasiado pequeño para lo que se le pide llevar, o un shaper puesto por debajo del tráfico. Necesita la capa de API.
- The NAND reported an uncorrectable ECC failure.
ecc_failuressubió en una partición MTD: una lectura que la corrección de errores no pudo arreglar, es decir, pérdida de datos en la flash. Cualquier incremento es un incidente.
Consultas
Sección titulada «Consultas»# mikroscope-agent-silent (< 1)sum(increase(mikroscope_samples_total[2m]))# mikroscope-softnet-drops (> 0)sum(increase(mikroscope_softnet_total{kind="dropped"}[5m]))# mikroscope-oom-kill (> 0)sum(increase(mikroscope_vm_events_total{event="oom_kill"}[5m]))# mikroscope-detections (> 0)sum(increase(mikroscope_collector_detections_total{rule!~"microburst|ipc-collapse"}[5m]))# mikroscope-thermal-near-critical (> 0)count(mikroscope_thermal_celsius >= on(zone) 0.85 * mikroscope_thermal_critical_celsius)# mikroscope-conntrack-near-limit (> 0.8)max(mikroscope_slab_active_objects{cache="nf_conntrack"} / mikroscope_slab_limit_objects{cache="nf_conntrack"})# mikroscope-ticks-slipped (> 0)sum(increase(mikroscope_slipped_total[5m]))# mikroscope-agent-oom (> 0)sum(increase(mikroscope_self_oom_kills_total[5m]))# mikroscope-l2-loop (> 0)sum(increase(mikroscope_kmsg_port_records_total{kind="own-address"}[5m]))# mikroscope-port-link-down (> 0)sum(increase(mikroscope_kmsg_port_records_total{kind="link-down"}[5m]))# mikroscope-port-errors (> 0)sum(increase(mikroscope_api_interface_counter_total{counter=~"rx-overflow|rx-fcs-error|rx-fragment|rx-too-short|rx-too-long|rx-jabber|tx-fcs-error|tx-late-collision|tx-excessive-collision"}[5m]))# mikroscope-bridge-port-dark (> 0)count((sum by (interface) (increase(mikroscope_api_interface_counter_total{counter="rx-packet"}[10m])) > 0) and on (interface) (sum by (interface) (increase(mikroscope_api_interface_counter_total{counter="tx-unicast"}[10m])) == 0) and on (interface) (sum by (interface) (increase(mikroscope_api_interface_counter_total{counter="tx-broadcast"}[10m])) == 0) and on (interface) (mikroscope_api_interface_info{bridge!=""}))# mikroscope-wakeup-storm (> 4)sum(rate(mikroscope_context_switches_total[10m])) / sum(rate(mikroscope_context_switches_total[24h] offset 10m))# mikroscope-egress-queue-drops (> 0)max(max_over_time(mikroscope_api_interface{kind="tx_queue_drops"}[1m]))# mikroscope-ecc-failure (> 0)sum(increase(mikroscope_mtd_ecc_failures_total[1h]))Las 15 leen el /metrics del colector, que es la única
exposición que hay —
Configurar en Grafana
tiene el único trabajo de scrape. mikroscope_slipped_total está entre ellas: el agente es el
dueño del ticker y lo único que puede contar un tick perdido, e informa del número por /sampler,
que el colector lee cada minuto y renderiza. Así que la regla se dispara sobre una cifra de como
mucho un minuto en vez de un scrape, que para un contador que vale 0 en un equipo sano es la misma
alerta.
-- mikroscope-agent-silent (< 1)SELECT count(1) AS value FROM mikroscope_cpu WHERE time >= now() - interval '2 minutes'-- mikroscope-softnet-drops (> 0)SELECT coalesce(sum(dropped), 0)::BIGINT AS value FROM mikroscope_softnet WHERE time >= now() - interval '5 minutes'-- mikroscope-oom-kill (> 0)SELECT coalesce(sum(oom_kill), 0)::BIGINT AS value FROM mikroscope_vm WHERE time >= now() - interval '5 minutes'-- mikroscope-detections (> 0)SELECT count(1) AS value FROM mikroscope_detection WHERE time >= now() - interval '5 minutes' AND rule NOT IN ('microburst', 'ipc-collapse')-- mikroscope-thermal-near-critical (> 0)SELECT count(1) AS value FROM (SELECT zone, max(celsius) AS c, max(critical_celsius) AS crit FROM mikroscope_thermal WHERE time >= now() - interval '2 minutes' AND critical_celsius IS NOT NULL GROUP BY zone) AS zones WHERE c >= 0.85 * crit-- mikroscope-conntrack-near-limit (> 0.8)SELECT max(active) * 1.0 / nullif(max("limit"), 0) AS value FROM mikroscope_slab WHERE time >= now() - interval '2 minutes' AND cache = 'nf_conntrack' AND "limit" IS NOT NULL-- mikroscope-agent-oom (> 0)SELECT coalesce(sum(oom_kill), 0)::BIGINT AS value FROM mikroscope_self WHERE time >= now() - interval '5 minutes' AND oom_kill IS NOT NULL-- mikroscope-l2-loop (> 0)SELECT coalesce(sum(count), 0)::BIGINT AS value FROM mikroscope_kmsg WHERE time >= now() - interval '5 minutes' AND kind = 'own-address'-- mikroscope-port-link-down (> 0)SELECT coalesce(sum(count), 0)::BIGINT AS value FROM mikroscope_kmsg WHERE time >= now() - interval '5 minutes' AND kind = 'link-down'-- mikroscope-port-errors (> 0)SELECT coalesce(sum(v), 0)::BIGINT AS value FROM (SELECT interface, greatest(max(rx_overflow) - min(rx_overflow), 0)::BIGINT + greatest(max(rx_fcs_error) - min(rx_fcs_error), 0)::BIGINT + greatest(max(rx_fragment) - min(rx_fragment), 0)::BIGINT + greatest(max(rx_too_short) - min(rx_too_short), 0)::BIGINT + greatest(max(rx_too_long) - min(rx_too_long), 0)::BIGINT + greatest(max(rx_jabber) - min(rx_jabber), 0)::BIGINT + greatest(max(tx_fcs_error) - min(tx_fcs_error), 0)::BIGINT + greatest(max(tx_late_collision) - min(tx_late_collision), 0)::BIGINT + greatest(max(tx_excessive_collision) - min(tx_excessive_collision), 0)::BIGINT AS v FROM mikroscope_api_ifcounters WHERE time >= now() - interval '5 minutes' GROUP BY interface) AS ports-- mikroscope-bridge-port-dark (> 0)SELECT count(*)::BIGINT AS value FROM (SELECT interface, max(rx_packet) - min(rx_packet) AS drx, max(tx_unicast) - min(tx_unicast) AS dtu, max(tx_broadcast) - min(tx_broadcast) AS dtb FROM mikroscope_api_ifcounters WHERE time >= now() - interval '10 minutes' AND bridge IS NOT NULL AND bridge <> '' GROUP BY interface) AS ports WHERE drx > 0 AND dtu = 0 AND dtb = 0-- mikroscope-wakeup-storm (> 4)SELECT CASE WHEN base_min >= 720 THEN (now_sum / nullif(now_min * 60.0, 0)) / nullif(base_sum / (base_min * 60.0), 0) ELSE 0.0 END AS value FROM (SELECT sum(CASE WHEN time >= now() - interval '10 minutes' THEN CAST(ctxt AS BIGINT) ELSE 0 END) AS now_sum, count(DISTINCT CASE WHEN time >= now() - interval '10 minutes' THEN date_bin(interval '1 minute', time) END) AS now_min, sum(CASE WHEN time < now() - interval '10 minutes' THEN CAST(ctxt AS BIGINT) ELSE 0 END) AS base_sum, count(DISTINCT CASE WHEN time < now() - interval '10 minutes' THEN date_bin(interval '1 minute', time) END) AS base_min FROM mikroscope_stat WHERE time >= now() - interval '1450 minutes') AS w-- mikroscope-egress-queue-drops (> 0)SELECT coalesce(max(tx_queue_drops), 0)::BIGINT AS value FROM mikroscope_api_iface WHERE time >= now() - interval '1 minute'-- mikroscope-ecc-failure (> 0)SELECT coalesce(sum(delta), 0)::BIGINT AS value FROM (SELECT max(ecc_failures) - min(ecc_failures) AS delta FROM mikroscope_mtd WHERE time >= now() - interval '1 hour' AND ecc_failures IS NOT NULL GROUP BY "partition") AS partsTodo coalesce() sobre un agregado lleva ::BIGINT. El destino de InfluxDB escribe sus contadores
sin signo, así que coalesce(sum(count), 0) sería coalesce(UInt64, Int64), un par que el plugin
de InfluxDB de Grafana no sabe mapear: responde HTTP 200 sin marcos y sin error, Grafana lo
interpreta como NoData, y una regla que declara noDataState: OK marcaría OK mientras ocurre
justo aquello que vigila (encontrado en Grafana).
TestCoalesceIsCastInAlertSQL falla si una regla se deja el cast. El SQL de los paneles fija el
mismo fallo con un cast a BIGINT de greatest() sobre un agregado
(internal/), y TestGreatestIsCastForTheInfluxPlugin falla si un
panel se lo deja; un panel al menos falla a gritos, con un 500. La regla de conntrack lee el
techo del slab como "limit", el nombre que le da el destino de InfluxDB; la traducción a
PostgreSQL lo convierte en limit_objs.
-- mikroscope-agent-silent (< 1)SELECT count(1) AS value FROM mikroscope_cpu WHERE time >= now() - interval '2 minutes'-- mikroscope-softnet-drops (> 0)SELECT coalesce(sum(dropped), 0)::BIGINT AS value FROM mikroscope_softnet WHERE time >= now() - interval '5 minutes'-- mikroscope-oom-kill (> 0)SELECT coalesce(sum(oom_kill), 0)::BIGINT AS value FROM mikroscope_vm WHERE time >= now() - interval '5 minutes'-- mikroscope-detections (> 0)SELECT count(1) AS value FROM mikroscope_detection WHERE time >= now() - interval '5 minutes' AND rule NOT IN ('microburst', 'ipc-collapse')-- mikroscope-thermal-near-critical (> 0)SELECT count(1) AS value FROM (SELECT zone, max(celsius) AS c, max(critical_celsius) AS crit FROM mikroscope_thermal WHERE time >= now() - interval '2 minutes' AND critical_celsius IS NOT NULL GROUP BY zone) AS zones WHERE c >= 0.85 * crit-- mikroscope-conntrack-near-limit (> 0.8)SELECT max(active_objs) * 1.0 / nullif(max("limit_objs"), 0) AS value FROM mikroscope_slab WHERE time >= now() - interval '2 minutes' AND cache = 'nf_conntrack' AND "limit_objs" IS NOT NULL-- mikroscope-agent-oom (> 0)SELECT coalesce(sum(oom_kill), 0)::BIGINT AS value FROM mikroscope_self WHERE time >= now() - interval '5 minutes' AND oom_kill IS NOT NULL-- mikroscope-wakeup-storm (> 4)SELECT CASE WHEN base_min >= 720 THEN (now_sum / nullif(now_min * 60.0, 0)) / nullif(base_sum / (base_min * 60.0), 0) ELSE 0.0 END AS value FROM (SELECT sum(CASE WHEN time >= now() - interval '10 minutes' THEN CAST(ctxt AS BIGINT) ELSE 0 END) AS now_sum, count(DISTINCT CASE WHEN time >= now() - interval '10 minutes' THEN date_bin(interval '1 minute', time, TIMESTAMPTZ 'epoch') END) AS now_min, sum(CASE WHEN time < now() - interval '10 minutes' THEN CAST(ctxt AS BIGINT) ELSE 0 END) AS base_sum, count(DISTINCT CASE WHEN time < now() - interval '10 minutes' THEN date_bin(interval '1 minute', time, TIMESTAMPTZ 'epoch') END) AS base_min FROM mikroscope_stat WHERE time >= now() - interval '1450 minutes') AS w-- mikroscope-egress-queue-drops (> 0)SELECT coalesce(max(tx_queue_drops), 0)::BIGINT AS value FROM mikroscope_api_iface WHERE time >= now() - interval '1 minute'-- mikroscope-ecc-failure (> 0)SELECT coalesce(sum(delta), 0)::BIGINT AS value FROM (SELECT max(ecc_failures) - min(ecc_failures) AS delta FROM mikroscope_mtd WHERE time >= now() - interval '1 hour' AND ecc_failures IS NOT NULL GROUP BY "partition") AS partsUmbrales
Sección titulada «Umbrales»- Cero, para un contador que no debería moverse. Descartes de RX del kernel, OOM kills del
kernel, detecciones, ticks perdidos por retraso, OOM kills del propio agente, fallos ECC no
corregibles, errores MAC tipificados en cualquier puerto y los registros de puerto del kernel cuyo
kindesown-addressolink-down. Un equipo sano lee cero en cada uno. - Cero puertos de bridge a oscuras, durante diez minutos.
mikroscope-bridge-port-darkcuenta los puertos de bridge que recibieron paquetes mientras el bridge no les envió ni unicast ni broadcast; en un bridge sano el recuento es 0, y el periodo de espera de 10 minutos es lo que impide que un momento de calma la dispare. - Una muestra, para el agente en silencio. Menos de una muestra en dos minutos es ninguna, a cualquier cadencia configurada.
- Una proporción del propio disparo térmico de la placa. El 85 % del punto de disparo crítico más
bajo que declara cada zona, que el agente lee de
/sys/class/thermaly envía junto a cada lectura. Una zona que no declara disparo crítico queda fuera de la consulta en vez de compararse con un techo inventado. - Una proporción del propio límite de conexiones del kernel. 0.8 del límite de
nf_conntrackque leyó el agente. La ocupación sale de/proc/slabinfo, que necesita un contenedor privilegiado. - Cero otra vez, para un contador que sí debe moverse — con el juicio en la duración. La cola
de salida es el único caso aquí en que la lectura sana no es exactamente cero en todos los
equipos: descartar es como una cola llena le dice a quien envía que afloje, así que un enlace
brevemente saturado descarta unos cuantos paquetes y está funcionando como se diseñó. Un umbral en
paquetes por segundo sería un número que el equipo no publica, así que
mikroscope-egress-queue-dropsmantiene el cero y pregunta solo por el último minuto con un periodo de espera de diez: hacen falta diez minutos seguidos descartando para que dispare, y ninguna ráfaga puede lograrlo por grande que sea (una ráfaga medida). Esta es la forma que hay que copiar para cualquier regla futura cuya lectura sana no sea cero. - Un múltiplo del último día del propio equipo. Una tasa de cambios de contexto no tiene un valor
sano que sirva para todas las placas, así que
mikroscope-wakeup-stormcompara el router consigo mismo: la tasa media de los últimos diez minutos frente a la media de las 24 horas anteriores, y salta por encima de 4 (cocientes sanos y de tormenta). Cada lado se divide entre los minutos que cubre de verdad, y la regla espera a tener 12 horas de base, de modo que un almacén con menos de un día no parece una tormenta. La regla anuncia un cambio de régimen y se apaga cuando la nueva tasa pasa a ser la base.
Las reglas no alertan por squeezes de softnet: en el router medido, alrededor del 11,2 %
de las muestras por CPU llevan un squeeze, así que alertar con “squeeze > 0” te
despertaría para siempre. Lo que llega en su lugar a la alerta de detecciones es la detección
microburst: tres muestras de ráfaga en una CPU en 60 s. Una muestra de ráfaga es aquella en la que
una cola de softnet descartó un paquete, o agotó su presupuesto más veces que el percentil 90 móvil
de esa CPU y al menos tres, mientras el recuento de paquetes de la muestra estaba en su mediana móvil
o por debajo. Consulta Reglas de detección.
Límites
Sección titulada «Límites»- Qué proceso. La regla de OOM dice que el kernel mató algo. Desde dentro del contenedor no hay espacio de nombres de PID que diga qué.
- Qué puerto. Toda regla de puerto se reduce a un número: las dos del registro del kernel y la de errores MAC suman sobre los puertos, la de la cola de salida toma el máximo y la de puerto de bridge a oscuras cuenta puertos. Así que una regla que salta dice que hubo una firma de bucle, un link-down o un puerto a oscuras, no en qué cable. El puerto está en los dos paneles de sucesos de puerto de la sección del registro del kernel, con su comentario y sus listas de interfaces, o en la sección de tráfico por interfaz.
- Los puntos ciegos de un agente sin privilegios. Las reglas de la tabla de conexiones y de ECC leen fuentes que necesitan un contenedor privilegiado. Sin él esas medidas nunca llegan al almacén, y en Prometheus una consulta sobre una métrica inexistente no devuelve datos — lo que estas reglas leen como OK.
- Un colector sin la capa de API. Las reglas de errores de puerto, de puerto de bridge a
oscuras y de cola de salida leen contadores de RouterOS que solo consulta la capa de API. Con
--api-mode offnada de eso llega al almacén: en Prometheus las tres reglas no leen datos y en PostgreSQL la de la cola de salida, la única de las tres en ese fichero, lee 0; las dos cosas son OK. En un almacén InfluxDB que nunca ha tenido esas tablas las consultas fallan y se aplicaexecErrState: Error. - Una tabla que el almacén nunca ha tenido. InfluxDB 3 rechaza al planificarla una consulta que
nombra una tabla o una columna inexistente, así que en un almacén que nunca ha tenido una
detección, o
mikroscope_mtdcon un agente sin privilegios, la consulta de esa regla debería fallar y aplicarseexecErrState: Erroren lugar de OK (no observado). - Nada mientras salta la regla del agente en silencio. Todas las reglas leen el mismo flujo — incluida la de ticks perdidos por retraso, cuya única consulta va contra el colector, no contra el agente—, así que sin muestras llegando leen cero o ningún dato, y las dos cosas son OK.
Qué formas de estas reglas se han cargado en Grafana y se han visto evaluar, y lo que no se ha probado, está en Probado en.