
🌐 También en: English · Deutsch · Français
Deep Dive de la serie en torno al Ansible Tour. Nivel: administración Linux avanzada. El tour explica los roles; los deep dives explican la técnica que hay debajo – cómo funciona, por qué está aquí, qué hace exactamente esta instalación con ella, cómo operarla en el día a día y qué otras opciones habría habido.
Cada servidor tiene aquí su propio Prometheus y su propio Grafana (la parte 6 del tour muestra los roles). Este artículo es la guía de lectura: qué son las cifras, cómo es lo normal en estas máquinas, qué gráficos importan cuando algo va mal – y cómo está construida la alerta, incluido el error que la mantuvo muda durante semanas.
🧭 Cómo funcionan Prometheus y Grafana
Pull, series temporales, etiquetas
Los exportadores son pequeños programas que exponen cifras por HTTP – el node exporter para el host, mysqld_exporter para MariaDB, php-fpm_exporter para PHP, cAdvisor dentro del kubelet para los contenedores, kube-state-metrics para los objetos de Kubernetes. Prometheus los recoge («scrape») cada 15 segundos y guarda cada cifra como una serie temporal, identificada por un nombre y etiquetas como container="nextcloud-fpm". Grafana pregunta a Prometheus en su lenguaje PromQL y dibuja la respuesta.
Tres funciones que cubren la mayoría de los gráficos
rate(x[5m]): aumento por segundo de un contador, promediado en cinco minutos – peticiones por segundo, segundos de CPU por segundo (= núcleos).increase(x[1h]): cuánto creció un contador en una hora – reinicios, OOM kills, periodos limitados.max_over_time(…[7d])ysum by (…): el peor valor de un periodo, y la suma de series según etiquetas elegidas. Con la segunda ocurren la mayoría de los errores de lectura (ver abajo).
Alertas sin Alertmanager
Grafana evalúa por sí mismo las reglas de alerta: una consulta (A), un umbral (B), una duración («durante 5 minutos»). Si la condición se mantiene lo suficiente, envía un correo al punto de contacto. Para un administrador y tres servidores, eso sustituye a un Alertmanager aparte.
🛡️ Por qué está aquí
- Tendencias en lugar de momentos. 30 días de historia responden a «¿desde cuándo?» y «¿es normal?», algo que
topnunca puede. - Un solo sitio para host, base de datos, PHP, contenedores – y, desde octubre de 2026, los logs al lado (Loki).
- Abierto y estándar. Prometheus y Grafana son el idioma de la monitorización de Kubernetes; ya existen paneles para cada exportador.
🔧 Qué hace este repositorio
| Pieza | Detalle |
|---|---|
| Prometheus | common_prometheus: intervalo de 15 s, 30 días de retención, escucha en 127.0.0.1; 5 trabajos de recogida por servidor, ~7.500 series, 0,5–0,8 GB en disco |
| Grafana | common_grafana: paquete del repositorio de Grafana, detrás del ingress bajo /grafana, puerto 3000 abierto solo para el propio nodo; nivel de log warn |
| Fuentes de datos | Prometheus y Loki, creadas por la API con UID fijos |
| Paneles | 1860 Node Exporter Full, 7362 MySQL Overview, 4912 PHP-FPM, más el propio «Container-Ressourcen» |
| Alertas | 11 reglas de métricas y 4 de logs, un punto de contacto por correo |
| Endurecimiento | dominio SELinux propio para Grafana, Prometheus y los exportadores; el login de Grafana protegido por una jail de fail2ban y el bloqueo propio de Grafana |
Todo se aprovisiona mediante la API HTTP de Grafana con pequeños scripts en Python que Ansible escribe y ejecuta. Los paneles se importan con UID fijos, para que enlaces y marcadores sobrevivan a una reimportación; el panel 1860 recibe además un parche en sus variables, porque la API de importación deja sin inicializar su fuente de datos y sus variables node/job, y cada gráfico mostraba «No data» al abrirlo por primera vez.
📊 Leer los paneles
Node Exporter Full (1860) – el host
| Gráfico | Normal aquí | Mirar más de cerca cuando |
|---|---|---|
| CPU Busy | de media 5 % (blog), 9 % y 16 % (Nextcloud); percentil 95 por debajo del 30 % | por encima del 70 % de forma sostenida – «CPU Basic» muestra entonces si es user, system o iowait |
| Sys Load | por debajo del número de núcleos, picos breves algo por encima (reinicio, escaneos nocturnos) | por encima del número de núcleos durante más de unos minutos |
| RAM Used | 16–32 % de media, como mucho 39 % | sube durante días sin volver a bajar – una fuga, no carga |
| SWAP Used | casi cero | crece de forma constante |
| Root FS Used | claramente por debajo del 80 % | la alerta salta al 80 % |
| Disk IOps / Read/Write | picos breves de noche (escaneos, reinicio) | iowait alto junto con mucha E/S durante el día |
| Network Traffic | bajo, picos por subidas y copias de seguridad | tráfico saliente sostenido que nadie sabe explicar |
Un patrón se repite cada noche: un hueco de unos minutos hacia las 03:30. No es una caída sino el reinicio planificado tras las actualizaciones; un hueco a cualquier otra hora merece una mirada. Y el servidor Nextcloud mayor muestra para el propio K3s varias veces el tiempo de CPU del blog, por el mismo trabajo – una propiedad de su generación de procesador, explicada en el deep dive de K3s, no un error de configuración.
MySQL Overview (7362) – la base de datos
| Gráfico | Normal aquí | Mirar más de cerca cuando |
|---|---|---|
| MySQL Connections | un puñado, picos de 6 a 15 | cerca de max_connections (50) |
| MySQL Questions / Queries | algunas decenas por segundo, sigue la hora del día | un salto sin más usuarios |
| InnoDB Buffer Pool | gran parte sin usar, tasa de aciertos > 99 % | páginas libres cerca de cero y lecturas de disco en aumento |
| Temporary Objects | algunas tablas temporales en disco (columnas TEXT) | las tablas en disco crecen más deprisa que las consultas |
| Slow Queries | 0 | cualquier cosa por encima de cero – el slow log dice cuáles |
PHP-FPM (4912) – los workers
| Gráfico | Normal aquí | Mirar más de cerca cuando |
|---|---|---|
| Active / idle processes | unos pocos activos, la mayoría ociosos | los activos se acercan a max_children |
| Listen queue | 0 | por encima de cero durante más de un minuto |
| Max children reached | 0 | aumenta – el pool es demasiado pequeño |
| Slow requests | 0 | aumenta – el log del pod nombra el script |
Container-Ressourcen – el panel propio
Ocho gráficos por namespace: CPU y memoria como fracción del límite del contenedor, limitación de CPU, núcleos y working set en absoluto, reinicios y OOM kills por hora, el último motivo de terminación por contenedor y los límites y requests configurados. Responde a la única pregunta que los otros paneles no pueden: ¿está frenado un contenedor por su propio límite mientras el host tiene margen? Unos pocos puntos de limitación son normales para un PHP a ráfagas; un contenedor limitado por encima del 50 % es el que necesita más CPU.
El error de lectura
Al escribir el deep dive de rendimiento, una consulta para el contenedor PHP del blog mostró un pico de memoria de 940 MiB con un límite de 1 GiB. Había sumado todas las series con ese nombre de contenedor – durante un despliegue el pod antiguo y el nuevo se solapan, y tras un reinicio cAdvisor informa brevemente del cgroup antiguo y del nuevo. El pico real de un solo pod fue de 538 MiB. Los gráficos que calculan sum by (namespace, pod, container), como el panel propio, son seguros; un sum solo por nombre de contenedor no lo es.
🚨 Alertas – y por qué estaban mudas
| Alerta | Salta cuando (durante 5 min) |
|---|---|
| Disco root / disco data | más del 80 % ocupado |
| Memoria | más del 90 % ocupada |
| Carga de CPU | más del 85 % de ocupación |
| Nodo caído | el node exporter no responde |
| Memoria de contenedor | un contenedor por encima del 90 % de su límite de memoria |
| Limitación de contenedor | más del 50 % de los periodos de CPU limitados |
| OOM de contenedor / bucle de fallos | un OOM kill, o más de 2 reinicios en 15 minutos |
| Trivy desfasado / fallido | ningún escaneo de vulnerabilidades en 36 horas, o el último falló |
| Logs (Loki) | denegación de SELinux en modo enforcing, error de fail2ban, OOM del kernel, ningún log |
En septiembre de 2026, una comprobación en el servidor Nextcloud mayor mostró que ninguna de estas reglas había saltado jamás – no porque no pasara nada, sino porque no podían. Las reglas se habían creado por la API sin el campo condition, que nombra la consulta que decide la alerta. Grafana registraba «condition must not be empty» en cada evaluación, y las reglas estaban en execErrState: OK, que convertía cada error de evaluación en «todo bien». Dos ajustes, razonables cada uno por separado, hacían juntos que la alerta pareciera sana estando muerta. Desde entonces cada regla indica condition: "B". La lección: tras un cambio, comprobar el estado de cada regla en Grafana – «Normal» sin error de evaluación – en lugar de tomar la ausencia de correos como prueba.
Queda un hueco a propósito: noDataState y execErrState siguen en OK, para que un reinicio de Prometheus no envíe una docena de correos. Si el propio Prometheus se parase, todas las reglas seguirían tranquilamente en verde. Ese caso se cubre desde fuera de Grafana: Monit vigila Prometheus, Grafana, los exportadores node y MySQL, Loki y Alloy, y envía un correo si alguno se detiene.
🧰 Operación diaria
# Prometheus directly, from the host
curl -s 'http://127.0.0.1:9090/api/v1/targets' | jq '.data.activeTargets[] | {job: .labels.job, health}'
curl -s --get 'http://127.0.0.1:9090/api/v1/query' \
--data-urlencode 'query=max_over_time(sum(phpfpm_active_processes)[7d:1m])' | jq '.data.result'
# Grafana state
systemctl status grafana-server prometheus
journalctl -u grafana-server --since -1h # warnings and errors only (log level warn)- Cambiar un umbral significa cambiarlo en el rol y ejecutar el playbook; un cambio hecho en la interfaz web lo sobrescribiría la siguiente ejecución, y es intencionado.
- Un gráfico nuevo que deba quedarse va al JSON del panel en el repositorio (el panel propio) – para los paneles de la comunidad importados, a una copia con UID propio.
- Copias de seguridad: todo lo que importa – fuentes de datos, paneles, reglas de alerta – se recrea desde el repositorio. Las métricas en sí no se copian; 30 días de historia es el horizonte elegido.
🆘 Chuleta: síntoma → comando
La versión corta de este artículo para el momento en que algo va mal. Ejecutar en el host como root; <…> son marcadores.
| Síntoma | Comando |
|---|---|
| Grafana o Prometheus no responden | systemctl status grafana-server prometheus |
| 403 en /grafana | # country filter in the ingress: is the address in the list? |
| Un gráfico está vacío | curl -s http://127.0.0.1:9090/api/v1/targets | jq '.data.activeTargets[] | {job: .labels.job, health}' |
| Preguntar a Prometheus directamente | curl -s --get http://127.0.0.1:9090/api/v1/query --data-urlencode 'query=up' | jq '.data.result' |
| Ningún correo de alerta aunque algo va mal | # Grafana → Alerting → Alert rules: state "Error" or "No data"? |
| El propio Prometheus está parado | monit status svc_prometheus # Monit mails, Grafana cannot |
| Umbral o panel cambiado en la interfaz web | # overwritten by the next playbook run - change it in the role |
🔭 Lo que viene
- Logs y métricas en una sola vista: Loki está desde octubre de 2026; enlazar un pico de un gráfico con las líneas de log del mismo minuto es el siguiente paso.
- Hecho el 20 y 21 de octubre de 2026: una alerta sobre la cola de PHP-FPM (umbral sacado de 14 días de mediciones), y
/grafanadetrás de la misma lista de países que SSH y el admin de WordPress: desde cualquier otro sitio el ingress controller responde 403. - Paneles como código (Jsonnet/Grafonnet) cuando los paneles propios pasen de uno.
⚖️ Alternativas – y por qué Grafana + Prometheus
| Opción | Punto fuerte | Por qué no aquí |
|---|---|---|
| Netdata | Sin configuración, resolución por segundo | Por host, historia corta por defecto, menos adecuado para métricas de Kubernetes. |
| Zabbix / Icinga | Monitorización clásica por comprobaciones, alertas maduras | Las comprobaciones dicen «funciona o no», no «cuánto desde cuándo»; las métricas de contenedores requieren trabajo extra. Icinga se probó en un laboratorio. |
| Servicios de monitorización en la nube | Nada que operar | Las métricas salen del servidor; costes por host y por serie. |
| Prometheus + Grafana | Estándar para Kubernetes, modelo pull, abierto, paneles para cada exportador | Elegido. |
Un panel es un conjunto de preguntas que hizo otra persona. Las útiles aquí son aquellas cuyo estado normal se conoce – por eso este artículo indica «normal aquí» junto a cada gráfico. Y una alerta solo es tan buena como su última prueba: la línea más importante de este artículo es la de las reglas que parecieron verdes durante semanas sin poder saltar nunca.


