Lo que no se define no se puede medir. Lo que no se mide, no se puede mejorar. Lo que no se mejora, se degrada siempre
— William Thomson Kelvin
| | Este post no es continuación de logs-metricas.html pero están muy relacionados. De hecho sin el primerono se me habría ocurrido este otro, sin embargo como ya he dicho son independientes. |
Kibana (ELK)
Kibana es una interfaz de usuario gratuita y abierta que te permite visualizar los datos de Elasticsearch y navegaren el Elastic Stack. Realiza lo que desees, desde rastrear la carga de búsqueda hasta comprenderla forma en que las solicitudes fluyen por tus apps (https://www.elastic.co/es/kibana)
Kibana es el interface que usamos para visualizar los datos (como por ejemplo logs) que Elasticsearch ingesta dediferentes fuentes.
Un uso típico puede ser el ver de forma centralizada todos los logs que va generando un stack de servicios de talforma que podamos dar un contexto único a los mismos, realizar búsquedas complejas e incluso diseñar diagramasexplotando dichos datos. Así en el post logs-metricas.html contaba cómo hacer unaanotación para servicios Grails tal que añadieran información en el contexto del log sobre la duración del métodojunto con los parámetros recibidos en el mismo. Esta meta-información puede llegar al Kibana de tal forma que no sólopuedas realizar búsquedas en el texto sino en esta meta-información.
Visualmente, esto sería un ejemplo de una línea de log en la consola web de Kibana junto con su meta-información:
t @containerId rm26xpdn3qyx4i0ugphtplq8o/43b08f1b4da4
t @id 35625187335574879357932181753924573842110613149578166277
t @log_group docker-logs
t @log_stream service_1.2.rm26xpdn3qyx4i0ugphtplq8o/43b08f1b4da4
t @message {"timestamp":"2020-08-15T10:50:49.748+0000","level":"INFO","thread":"http-apr-8080-exec-4","logger":"{...}"}
t @owner 602122916959
t @payload.className com.puravida.service.HelloController
# @payload.duration 1,680
t @payload.methodName hello
t @replica 2
t @service service_1
@timestamp Aug 15, 2020 @ 10:50:49.748
t _id 35625187335574879357932181753924573842110613149578166277
t _index cwl-2020.08.15
# _score -
t _type docker-logs-production
t context default
t correlationId 8f9b465a-d08c-4d95-b65b-82398d3dc127
t level INFO
t logger com.puravida.service.HelloController
t message com.puravida.service.HelloController.hello: duration=1680 ms;
t thread http-apr-8080-exec-4
timestamp Aug 15, 2020 @ 10:50:49.748
t userId - not logged -
Como puedes ver en este registro de Elasticsearch no sólo tenemos el message
generado sino una serie de meta-camposextras como el @payload generado por nuestra anotación.
Gracias al motor de búsqueda de Elasticsearch desde Kibana puedes filtrar logs que contengan un valor de interés enestos metacampos, por ejemplo puedes filtrar:
@payload.className:HelloController
y obtener todos los logs generados por este controller y extraer de ellos el campo @payload.duration
Realizar gráficas que usen este campo junto, en un intervalo de tiempo definido en @timestamp es cuestión de minutos.
Telegram
A día de hoy asumo que prácticamente todo el mundo conoce Telegram (si estás leyendo este post probablemente es porqueincluso eres usuario de este sistema de mensajería).
Una de las características de Telegram sobre otros sistemas es la posibilidad de crear canales y bots de forma simple(y gratuita) incluso privados.
| | En https://core.telegram.org/bots#6-botfather tienes la documentación oficial sobre cómo crear bots |
En primer lugar (y siendo usuarios de esta plataforma) crearemos un bot mediante el BotFather (el bot de Telegramque nos sirve para crear y gestionar nuestros bots):
buscar desde la aplicación "botfather" y comenzar un diálogo con él
crear un bot siguiendo los pasos (dar un nombre y un id terminado en "bot" básicamente)
obtener el
token
. No hace falta guardarlo en lugar seguro pues podremos consultarlo en cualquier momento, pero nolo compartas más que con gente de confianza y ojo no guardarlo en un repositorio git público.
En segundo lugar crearemos un canal privado, por ejemplo "Puravida Alerts" y añadir al bot como administrador del canal. Puedes restringir sus permisos para que solamente pueda publicar mensajes. Así mismo hay que añadir a otros usuariosque puedan estar interesados en las alarmas.
Como el canal será privado (a no ser que quieras que cualquiera pueda suscribirse y ver las alertas) necesitaremosconocer su ID, que a diferencia del público donde es el @nombre_del_canal. Para ello tendremos que usar el interfaceweb de Telegram (desconozco si se puede con el móvil, yo no lo he conseguido). Simplemente navegaremos hasta el canalcreado con el navegador y nos fijaremos en la url que tiene asignada el canal:
https://web.telegram.org/#/im?p=cXXXXXXX_yyyyyyyyy
El id del canal será "XXXXXXX", es decir el número comprendido entre c
y _
en la URL
Alertas
El stack ELK (Kibana+Elasticsearch) cuenta con un ecosistema de plugins bastante extenso del cual vamos a usar paralas alarmas el de opendistro (https://opendistro.github.io/for-elasticsearch/)
Si quieres probarlo primero antes de instalarlo en tu stack, puedes usar este docker-compose como punto de partida:
(1)
version: '3'
services:
odfe-node1:
image: amazon/opendistro-for-elasticsearch:1.9.0
container_name: odfe-node1
environment:
- cluster.name=odfe-cluster
- node.name=odfe-node1
- discovery.seed_hosts=odfe-node1,odfe-node2
- cluster.initial_master_nodes=odfe-node1,odfe-node2
- bootstrap.memory_lock=true # along with the memlock settings below, disables swapping
- "ES_JAVA_OPTS=-Xms512m -Xmx512m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536 # maximum number of open files for the Elasticsearch user, set to at least 65536 on modern systems
hard: 65536
volumes:
- odfe-data1:/usr/share/elasticsearch/data
ports:
- 9200:9200
- 9600:9600 # required for Performance Analyzer
networks:
- odfe-net
odfe-node2:
image: amazon/opendistro-for-elasticsearch:1.9.0
container_name: odfe-node2
environment:
- cluster.name=odfe-cluster
- node.name=odfe-node2
- discovery.seed_hosts=odfe-node1,odfe-node2
- cluster.initial_master_nodes=odfe-node1,odfe-node2
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
volumes:
- odfe-data2:/usr/share/elasticsearch/data
networks:
- odfe-net
kibana:
image: amazon/opendistro-for-elasticsearch-kibana:1.9.0
container_name: odfe-kibana
ports:
- 5601:5601
expose:
- "5601"
environment:
ELASTICSEARCH_URL: https://odfe-node1:9200
ELASTICSEARCH_HOSTS: https://odfe-node1:9200
networks:
- odfe-net
volumes:
odfe-data1:
odfe-data2:
networks:
odfe-net:
| 1 | Sí, es el mismo que viene en la web de opendistro pero así hago este post más extenso |
Básicamente levanta dos nodos de Elasticsearch y uno de Kibana con los plugins configurados. Una vez levantado puedesacceder a localhost:9200
y usar admin:admin
para logearte.
La idea principal va a consistir en crear:
un monitor que inspeccione los registros cada cierto tiempo
un trigger que se ejecute dentro de este monitor cuando los registros cumplan una condicion
un destination a donde enviar la alarma
Destination
En primer lugar crearemos un destination (objetivo principal de este post) Puravida Telegram
:
name: Puravida Telegram
type: Custom
custom attributes:
-- host: api.telegram.org
-- path: botELTOKENDELBOT-INCLUIDO-LOS-DOS-PUNTOS/sendMessage
- header information: -- Content-Type: application/json
Es importante escribir bien la url:- texto fijo "bot"- el token obtenido con BotFather incluidos los dos puntos, tal que XXXXXXX:YYYYYYYYYY- terminar en /sendMessage
| | Así mismo es necesario añadir un header con el Content-Type como application/json |
Monitor
A continuación crearemos un Monitor
name: Slow HelloWorld
schedule: every 10 mnts
y usando el interface visual definiremos el monitor:
index: cwl-* (o el que hayas definido)
time field: @timestamp
crearemos una expresión para indicar el filtro a aplicar sobre los documentos:
En este ejemplo le estamos indicando que seleccione la duración máxima del @payload.duration en los registrosde la última hora.
Trigger
Podemos definir tantos triggers como queramos con diferente severidad y a diferentes destinations. En nuestro casovamos a crear un trigger de severidad 1 al PuraVida Telegram
destination creado anteriormente
Lo único que tenemos que hacer es darle un nombre, establecer una severidad e indicar la condición de disparo,por ejemplo:
IS ABOVE 500
lo cual enviará la alarma cuando la ejecución de HelloController
sea superior a 500 mills
Añadiremos un action
indicando un name a usar y seleccionando el destination PuraVida Telegram
.Así mismo nos interesará personalizar el mensaje a enviar incluyendo por ejemplo la duración que ha hecho dispararla alarma por lo que cambiaremos el texto propuesto por defecto:
Message
{
"chat_id":"-100EL_ID_DEL_CHAT",
"text": "{{ctx.monitor.name}} alert\n Some HelloWorld takes {{#ctx.results}}{{#aggregations}}{{when.value}}{{/aggregations}}{{/ctx.results}} millisecs to be completed"
}
| | Fíjate que el canal se compone de -100
y el id extraído de la URL del mismo |
Mediante el uso de {{
y }}
(handlebars) podremos incluir información relativa al evento. En este caso vamos aenviar el valor del aggregation: when
Para otro tipo de alarmas tienes que buscar qué información puedes extraer del contexto, básicamente inspeccionando lapropiedad hits
del mismo.
Desde esta consola puedes realizar una prueba de envío usando el enlace que aparece debajo Send test message
.Si todo está bien configurado recibirás una notificación en el canal indicado.
Conclusión
Si todo lo anterior ha ido correctamente a partir de ahora tendrás un job que se ejecutará según le hayas programadoy si se cumple alguna de las condiciones impuestas recibirás un mensaje en el canal.
Obviamente el destination puede ser un canal de #Slack por ejemplo, pero con los cientos de canales que tengo abiertosahí al final no les presto atención a ninguno.
TODO
Con el API de Telegram es muy fácil enviar stickers así que me anoto como tarea pendiente en lugar de enviar unmensaje de texto, definir varios niveles de criticidad y enviar un sticker diferente para cada nivel
Top comments (0)