Logs centralizados para aplicaciones Kubernetizadas

Cuando corremos una aplicación en Kubernetes (y en términos más generales en un arquitectura distribuida) donde potencialmente existe más de una instancia de nuestra aplicación resulta conveniente (o incluso imprescindible) poder acceder a los logs de nuestra aplicación en forma centralizada. O sea, en lugar de acceder a cada servidor/nodo donde corre nuestra aplicación, deberíamos poder acceder a una única ubicación y ver ahí mismo los logs de todas las instancias de nuestra aplicación.

Para implementar una solución de este tipo cuando nuestra aplicación corre en Kubernetes existen diferentes estrategias posibles. Voy a referir a continuación 3 estrategias que a mi parecer son bastante habituales, pero antes veamos algunas generalidades.

El tener un log centralizado típicamente implica dos cuestiones: por un lado recolectar los mensajes de log para enviarlos a un lugar central y por otro lado poder acceder a esos mensajes de una forma práctica. Adicionalmente podríamos mencionar el procesamiento de esos mensajes para poder consultarlos de forma más práctica/específica o incluso para accionar ante ciertos mensaje. Esta problemática es tan habitual en la actualidad que existen múltiples productos y servicios para implementarla. Entre las soluciones más populares podemos mencionar New Relic, Datadog, Sumologic y ELK. En general estas soluciones son mucho más amplias que el manejo de logs, son soluciones de monitoreo que incluyen los logs como una funcionalidad particular. Volviendo al foco de este artículo, nos vamos a concentrar puntualmente en el primer paso de este flujo: la recolección de los mensajes de log.

Estrategia Sidecar

Esta estrategia consisten en desplegar dentro cada uno de nuestros pods, a la par del contenedor que corre nuestra aplicación, un contener sidecar que tome los mensajes de log de nuestra aplicación y los envíe a la fuente central. Una form típica de implementar esto es haciendo que nuestra aplicación escriba el log en un archivo de un volumen compartido del cual luego serán leídos por el colector de logs que corre en el contenedor sidecar.

Esta estrategia resulta bastante transparente para la aplicación pero requiere montar el sidecar en cada manifiesto de deployment.

A modo de ejemplo, si utilizamos el servicio de logs centralizados de Sumologic, podemos utilizar esta imagen Docker para montar nuestro contenedor sidecar.

Estrategia Daemonset

En el contexto de kubernetes un daemonset es un pod que corre en cada nodo del cluster de Kubenetes. Entonces podemos desplegar un daemonset que colecte los logs leyendo el stdout de todos contenedores corriendo en el nodo.

Esta estrategia resulta muy económica de implementar, basta hacer que nuestra aplicación escriba los mensajes de log al stdout y desplegar el daemonset. Luego al agregado de nuevas aplicaciones y nodos es completamente transparente.

A modo de ejemplo, si utilizamos el servicio de log de Papertrail, podemos utilizar este daemonset para enviar los logs.

Estrategia Logger con handler/appender remoto

Esta estrategia no tiene nada que ver con Kubernetes y consiste en hacer que sea nuestra aplicación la que directamente envíe los mensajes de log al log central. La forma prolija de implementar esto es usando alguna biblioteca log como Log4J, Log4Net, etc y configurar esta biblioteca con un handler que envíe la información al log central.

Lo interesante de esta solución es que resulta muy simple de implementar. Pero al mismo tiempo tiene un potencial costo de performance que hace que esta estrategia no sea conveniente para algunos escenarios, o sea: si tenemos una aplicación web atendiendo pedidos de nuestros usuarios y en ese contexto el logger van a enviar los mensajes de log via HTTP dentro del mismo proceso podemos tener algún issue de performance. Sin embargo, si nuestra aplicación es un job que corre en background, puede que este costo del log no sea relevante.

A modo de ejemplo, si nuestra aplicación está construída en Ruby podemos utilizar la gema SemanticLogger con un appender http para enviar los Sumologic o Papertrail.

De la máquina del developer directo a Producción, sin escalas

Ayer estuve participando de un meetup donde Marcos Nils estuvo contando sobre su implementación de GitOps. Su presentación me pareció muy interesante pero lo que más me llamó la atención fue cuando comentó que en su organización no tienen ambiente de testing, ni staging, ni nada parecido, o sea: cada developer trabaja en su máquina, sube su código a Git y eso dispara una serie de pasos que terminan instalando una nueva versión en el ambiente productivo. No todos los pasos  son necesariamente automáticos, puede que haya algún trigger manual, pero el punto central es que la aplicación no es instalada en ningún otro ambiente más que el productivo.

Apenas escuché esto me resultó muy disruptivo, luego, en el intervalo hablé personalmente con Marcos y me siguió pareciendo disruptivo, ¡ja! Una estrategia de GitOps implica una alto de grado de automatización y el manejo de infraestructura como código, lo cual facilitaría mucho el armado de nuevos ambientes. Sin embargo, lo que me comentaba Marcos es que ellos hicieron un análisis y no encontraron la necesidad de un ambiente de prueba: tienen tests automatizados y algunas cosas las prueban directamente en producción utilizando usuarios particulares de forma de no interferir con los usuarios reales. La respuesta de Marcos me pareció razonable, pues tomaron las decisión con cierto fundamento y contando con toda una serie de medidas (automatización, monitoreo, alertas, etc) que les ayudan a mitigar algunos de los riesgos de no tener un ambiente de prueba.

Personalmente las veces en que me encontré con equipos/organizaciones sin ambiente de prueba, ha sido por la imposibilidad de armar dicho ambiente en lugar de ser consecuencia de una decisión analizada. Al mismo tiempo, en varios de los proyectos que he participado había un contexto de regulaciones que obligaban a contar con un ambiente de prueba/homologación, previo al ambiente productivo.

Pero quien sabe, tal vez algún día tenga la oportunidad de experimentar con una estrategia de este tipo.

Practicas DevOps: Monitoreo

Una problemática común para aquellos que trabajamos en el mundo del software es la disponibilidad de nuestras aplicaciones. En primera instancia es imprescindible entender las necesidades de disponibilidad que debe cumplir nuestra aplicación. Esto es una definición que viene dada por una restricción del negocio. En ese sentido hay aplicaciones que solo deben están disponibles en horario de oficina (de lunes a viernes a 9 a 18), mientras que hay otras que puede que solo requieran estar disponibles ciertos días por mes. Y obviamente también están aquellas aplicaciones que tienen que estar disponibles 7 x 24. Esta es una cuestión de negocio que tiene un impacto enorme en las cuestiones técnicas. Desde el punto de vista de desarrollo tenemos que tomar cierta precauciones en el diseño y codificación de nuestra solución. Al mismo tiempo también debemos observar ciertas cuestiones en lo que respecta a la arquitectura física de la solución. Finalmente debemos tener presente una serie de cuestiones operacionales como ser backups, failover, escalamiento, etc. Como consecuencia de varias de estas cuestiones surge la necesidad de ser capaces de detectar una interrupción del servicio ANTES que se entere el usuario final.  Más aún, lo ideal es detectar en forma anticipada una posible interrupción para intentar evitarla. Esto implica implementar una estrategia de monitoreo.
Cuando hablamos de monitoreo tenemos distintos niveles:
  • Capa 1: hardware / sistema operativo, aquí miramos cpu, memoria, disco, red, etc.
  • Capa 2: middleware, aqui miramos métricas particulares del midddleware como ser métricas de la JVM, del web server, la DB, etc
  • Capa 3: aplicación, aquí miramos cuestiones más concretas cercanas al dominio de nuestra app. Incluyo aquí tiempo de respuesta, tiempo de carga de las páginas, y también cuestiones como cantidad de usuarios con sesión activa, etc.
Para monitorear cada una de estas capas hay distintas alternativas. Cuando uno corre con una infraestructura de cloud, el proveedor de cloud típicamente provee monitoreo de capa 1. El monitoreo a este nivel es transparente para la aplicación.
Para capa 2 el monitoreo también puede hacerse de forma transparente, y las herramientas para hacerlo dependen en gran medida de cual sea nuestro middleware, o sea, no es lo mismo monitorear tomcat que nginx. Una herramienta de uso común aquí es Nagios (aunque Nagios también puedeusarse en capa 1).
Para capa 3 implementar monitoreo requiere algunos ajustes a nivel de aplicación, o sea, las soluciones tienen cierto grado de intrusión en nuestra aplicación y suelen requerir algunos cambios en nuestro código. Las soluciones son diversas y pueden mezclase. Un caso típico es utilizar Google Analytics para medir tiempos de respuesta y permanencia de los usuarios en ciertas páginas.
Obviamente existen algunas soluciones que proveen la posibilidad de monitorear las 3 capas de manera unificada. A mi parecer la solución más popular a en este segmento es New Relic.
Por otro lado el monitoreo implica 2 cuestiones centrales:
  1. Recolección de datos
  2. Ejecución de acciones ante determinadas situaciones. De mínima tenemos el envió de alertas, pero también podríamos activar acciones de escalamiento.
Lo mencionado anteriormente sobre los niveles de monitoreo aplica a la recolección.  Respecto de la acciones a ejecutar, ahí también tenemos diferencia en la solución de monitoreo elegida. En este sentido, si la aplicación va a estar disponible 7×24, es posible que el envío de mails no sea suficiente y tengamos que echar mano de mensajes a directos a un teléfono de guardia.
Continuará…