Límites de resolución
Cada número de CPU que produce mikroscope se cuenta en ticks del kernel, así que el cambio más pequeño que puede mostrar lo pone el kernel, no el agente. Una fuente lee por debajo del tick, los contadores de la propia CPU, y el anillo del agente fija hasta dónde llega cualquier lectura hacia atrás.
Resolución del tick
Sección titulada «Resolución del tick»Un tick dura 10 ms. /proc/stat no cuenta tiempo, cuenta ticks de
USER_HZ, 100 por segundo. Una muestra de 100 ms puede contener por tanto 10 ticks por núcleo, así que la
fracción de ocupación de un núcleo se resuelve en escalones del 10 %,
y la media de cuatro núcleos en escalones del 2,5 %. Sobre 1 s, la resolución de un núcleo es del 1 %.
Eso es aritmética del tick, no una propiedad del agente, y ninguna cadencia ni ajuste lo cambia.
El agente envía los ticks en bruto y el intervalo real de cada muestra (dt_ns), nunca un
porcentaje, de modo que la ventana sobre la que divides la eliges tú.
La contabilidad de ticks también está cuantizada en los bordes: un intervalo de 100,3 ms puede llevar 11 ticks, lo que daría una fracción de ocupación por encima de 1. La fracción de ocupación derivada está limitada a 1; los ticks en sí siguen en bruto.
Muestrear más rápido no afina esto. A 100 Hz una muestra contiene 0 o 1 tick ocupado, así que la fracción de ocupación por muestra tiene dos valores posibles; por encima de unos 20 Hz los contadores de ticks son un indicador de ocupación más que un porcentaje. Lo que sí compra una cadencia mayor está en Techo de muestreo.
Contadores de la PMU
Sección titulada «Contadores de la PMU»Una fuente lee por debajo del tick: la unidad de monitorización del rendimiento de la CPU (PMU),
a través de perf_event_open, que el agente recoge como la fuente perf cuando el contenedor
es privilegiado. Es la única fuente de mikroscope que no viene de un fichero.
Una muestra en la que /proc/stat no marca ningún tick ocupado en ningún núcleo puede contener aun
así millones de ciclos e instrucciones ejecutadas (medido).
El tick redondea ese trabajo hasta hacerlo desaparecer; el contador no.
El número que merece la pena vigilar es el de instrucciones por ciclo (IPC), y el agente no lo calcula: envía las cuentas en bruto y la división la haces tú. El IPC distingue un núcleo que trabaja de un núcleo parado esperando a la memoria, algo que ningún contador de ticks puede expresar. Compáralo núcleo a núcleo: el IPC en reposo de dos núcleos de una misma placa puede diferir en más del doble (medido).
- El agente pide siete contadores:
cycles,instructions,cache-references,cache-misses,branch-instructions,branch-missesybus-cycles. - Cuáles se abren depende de la CPU. Un contador que no se puede abrir está ausente, nunca a
cero, así que lee la etiqueta
counterdemikroscope_en vez de dar por hecho un conjunto (CPU sin probar).perf_ events_ total{counter, cpu} - Los eventos genéricos
stalled-frontendystalled-backendno se piden. DevuelvenENOENTdonde se probaron y harían falta códigos de evento en bruto, propios de cada CPU. - Cada cuenta lleva
enabled_nsyrunning_ns. En una CPU que multiplexa sus contadores, la cuenta en bruto cubre solo la parte del intervalo en que funcionó el contador, no el intervalo. - Sin
privileged=yesfalta la familia entera: los contadores se abren a nivel de sistema, y eso un contenedor sin privilegios no puede hacerlo. Consulta Modo privilegiado.
El colector usa esos mismos dos contadores para una de sus detecciones, ipc-collapse: por
núcleo, una vez que hay al menos 20 s de historia, que las instrucciones por ciclo de un segundo caigan por debajo de la mitad de su mediana de los 60 s
anteriores mientras la tasa de ciclos está por encima de su propia mediana. Se describe junto
con las demás reglas en Reglas de detección.
PSI, schedstat y trazado
Sección titulada «PSI, schedstat y trazado»No esperes un reloj más fino de PSI, cuyos totales de espera van en microsegundos, ni
de schedstat, cuyos tiempos de ejecución y espera van en nanosegundos. El kernel de
RouterOS medido hasta ahora no tiene ni /proc/pressure ni /proc/schedstat (medido).
El agente lee ambos donde el kernel los tiene (aún no en un router).
En un kernel cuya columna irq de /proc/stat se queda en 0, como en el medido, el tiempo de IRQ
hardware se cuenta dentro de system.
Tampoco hay un camino de trazado. El kernel medido no tiene ni eBPF, ni kprobes, ni ftrace, y el router no tiene directorio de módulos del kernel, ni cabeceras, ni compilador con que añadirlos (comprobado). Una función del kernel que falta no se puede añadir desde un contenedor.
El agente detecta al arrancar lo que tiene el kernel y lo publica en /capabilities: el mapa
sources dice qué fuentes lee realmente este despliegue. Los campos de una fuente ausente faltan
en cada muestra y en cada destino, nunca valen cero.
Profundidad del anillo
Sección titulada «Profundidad del anillo»El otro límite duro es la profundidad, no la resolución. El agente guarda sus muestras en un
anillo de --buffer segundos, 60 s por defecto y entre 10 y 3600 s permitidos, que contiene
cadencia × búfer muestras. Nada más antiguo existe en ningún sitio del router.
Esa profundidad es también toda la ventana de la sección health de doctor, que lee el anillo
una vez (el anillo entero cuando no guarda más de 10 000 muestras, y si no las 10 000 más
recientes, así que lo más corto entre --buffer y 10 000 / cadencia segundos) e imprime cuántos segundos cubrió. Un doctor limpio dice que no hubo bucle, churn de
STP, flap de enlace ni descartes de softnet en el último minuto con los valores por defecto, no
en todo el día; un fallo que ocurre una vez al día es cosa de los paneles y de las reglas de alerta.
Un corte del colector o del grabador más corto que el anillo se rellena al reconectar: pide
since=<seq> y recibe todas las muestras que se perdió. Un corte más largo que el anillo se
informa como un hueco de longitud conocida, nunca se tapa. El agente responde con una línea
{"gap":{"from":…, que nombra los números de secuencia perdidos, antes de las muestras
que aún conserva; record lo escribe como un marcador que dice samples N..M lost, y forward
lo cuenta en mikroscope_ y se lo pasa a cada destino.
Un anillo más largo cuesta memoria en el agente, y el agente rechaza uno que no quepa. Al arrancar
cobra cada línea a 3 456 B: la clase de tamaño del asignador de la
que se sirve una línea con todas las fuentes activas (3 230 B), porque es
lo que paga el heap. Una placa con más núcleos o líneas de interrupción cuesta más por línea, y
una sin PMU, menos. El agente suma el presupuesto de la captura por disparo y sale con un error si
el total supera el memory.max del contenedor. Si el total es más de la mitad del límite blando
de memoria de Go, arranca pero registra un aviso, porque un heap tan justo mantiene al recolector
de basura trabajando sin parar. Cómo dimensionar ambos límites está en
Coste del agente.