Monit como watchdog configurado con Ansible

En este tutorial veremos Monit como herramienta para monitorizar el estado de los servicios, así como tomar las acciones pertinentes en caso de que no esté como esperábamos.

Índice de contenidos


1. Introducción

En este tutorial nos introduciremos en el apasionante mundo de la monitorización de aplicaciones, para lo que utilizaremos Monit como watchdog. Un watchdog en informática es una herramienta que registra uno (o varios) servicios, que deben estar periódicamente enviándole información para asegurar que están disponibles. Entendemos por servicio desde nuestro servidor web, desde Apache o Nginx hasta nuestras propias aplicaciones web.

He de comentar que si queremos un sistema de monitorización más avanzado de nuestras aplicaciones, deberíamos de ver la serie de tutoriales de mi compañero David donde habla de la monitorización de logs con Elasticsearch, Logstash y Kibana. Este tipo de monitorización es complementaria y específica de nuestra aplicación, mientras que Monit actúa como watchdog de gran cantidad de servicios (de mensajes, de ficheros, de bases de datos, FTP…)

Como suele ser costumbre en mis tutoriales, probaremos esta herramienta de monitorización en una máquina virtual creada con la combinación de Vagrant y Virtual Box y aprovisionada con Ansible.

El objetivo de este tutorial es enseñarnos como funciona Monit, monitorizando un servidor web, concretamente un Nginx. Para ello, crearemos una máquina virtual donde instalaremos Monit y Nginx, y comprobaremos como cuando paramos el servicio, este vuelve a levantarse sin nuestra intervención gracias a Monit.


2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro Retina 15′ (2.5 Ghz Intel Core I7, 16GB DDR3).
  • Sistema Operativo: Mac OS El Capitán 10.11.16
  • Vagrant 1.8.1
  • Virtual Box 5.0.14
  • Ansible 2.1.0.0
  • Monit 5.6
  • Nginx 1.4.1

3. Instalación de Vagrant, Virtual Box y Ansible

Como mencionábamos en la introducción, antes de ponernos manos a la obra, necesitamos tener Vagrant, Virtual Box y Ansible instalado. En este tutorial explico cómo se instalan estas herramientas. A partir de aquí suponemos que ya tienes esas herramientas instaladas y funcionando.


4. Creación de la máquina virtual

La creación de la máquina virtual es muy sencilla. Basta con irnos al directorio dónde queramos crear la máquina virtual y configurar el Vagrantfile que usará Vagrant para crearnos nuestra máquina virtual. Nuestro Vagrantfile tendrá la siguiente estructura:

Vagrantfile

En esta configuración, como siempre elegimos la “box” de Vagrant a partir de la que se va a crear la máquina virtual. Luego, se configuran los puertos para realizar la conexión con la máquina, de forma que cuando en nuestra máquina anfitrión pongamos la url localhost:8888 estaremos accediendo al puerto 80 de la máquina virtual (192.168.33.25:80).

Por último, tenemos la configuración de Ansible en la que decimos que después de crear la máquina ejecute el playbook watchdog_nginx.yml, y que la salida sea muy verbosa. Esto quiere decir que nada más crear la máquina virtual va a ejecutar los roles que, como decíamos en la introducción, se encargarán de instalarnos Nginx y Monit.


5. Creación de los roles de Ansible

A continuación nos queda crear los roles de Ansible que vamos a ejecutar cuando se cree la máquina. Para ello dentro del directorio dónde tenemos nuestro Vagrantfile, y siendo fieles a la propiedad que hemos establecido en el mismo dónde indicábamos el playbook (ansible.playbook = “ansible/watchdog_nginx.yml”), creamos el directorio “ansible” dónde guardaremos todos nuestros archivos de Ansible.


5.1. Playbook

El playbook que vamos a ejecutar tiene el siguiente formato:

ansible/watchdog_nginx.yml

Aquí le decimos que en el host Monit, nos ejecute como usuario privilegiado los roles de Nginx y de Monit. El nombre Monit se lo pusimos como hostname en la configuración de Vagrant.

Si recordáis los dos elementos imprescindibles para aprovisionar con Ansible son: Qué quiero ejecutar (esa información viene dada en el playbook) y dónde quiero ejecutarlo (información que viene dada en el inventory). Pero si nosotros no hemos definido un inventory, ¿cómo sabe la máquina que dónde tiene que ejecutar el playbook es la máquina que acabamos de crear? Esto se sabe debido a que Vagrant crea un fichero inventory auto-generado que está en la ruta .vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory que es relativa al directorio dónde tenemos creado nuestro Vagrantfile.


5.2. Rol de Nginx

Este rol solo instala Nginx en la máquina y le añade una directiva para que el servidor web actúe como proxy inverso de la aplicación objetivo, en este caso del propio Monit.

Las tareas que se ejecutan en este rol son:

ansible/roles/nginx/tasks/main.yml

Como vemos en el rol, usamos el módulo copy por lo que en su parámetro src esperará encontrar el fichero virtualhost-conf en el directorio ansible/roles/nginx/files. También tenemos una cláusula notify, que espera que haya un fichero main.yml en el directorio ansible/roles/nginx/handlers que contenga una tarea que tenga el nombre específico “restart nginx”, que se encarga de hacer reiniciar el servicio. Vamos a crearlos 😉

ansible/roles/nginx/files/virtualhost-conf
ansible/roles/nginx/handlers/main.yml

Con esto ya tendríamos el rol de Nginx creado. ¡Vamos con el de Monit!


5.3. Rol de Monit

El rol de Monit se encarga de instalar Monit y de copiar un fichero de configuración que usará para saber que servicios tiene que monitorizar, definirá las reglas de como hacerlo, y avisará en función del estado del servicio, en este caso por email usando el servidor de correo de Google.

Las tareas que tiene este rol son:

ansible/roles/monit/tasks/main.yml

Vemos como este rol también tiene la cláusula notify, por lo que esperamos tener dentro del directorio ansible/roles/monit/handlers un fichero llamado main.yml con una tarea de nombre “restart monit”

A diferencia del rol de Nginx que usaba el módulo copy vemos que en la segunda tarea usamos el módulo template. La diferencia principal es que espera encontrar el fichero monitrc dentro del directorio ansibleroles/monit/templates y que este fichero actúa como plantilla donde puedes añadir variables.

¡Vamos a crear estos ficheros!

ansible/roles/monit/handlers/main.yml

Y ahora la plantilla de la configuración de Monit

ansible/roles/monit/templates/monitrc

Como vemos, este fichero viene comentado (los comentarios son el texto que está después de #) explicando para que sirve cada directiva parte del fichero. De todas formas vamos a hacer un repaso rápido de por qué hemos usado esas directivas.

  • set daemon [seconds] with start delay [seconds]: Indica que Monit sea ejecutado en background, además de darle un determinado timeout antes de empezar, y también se establece el tiempo en segundos en el que el watchdog comprueba la disponibilidad de los servicios que monitoriza.
  • set logfile [logfile]: indica dónde van a estar los logs de Monit.
  • set idfile [idfile]: indica dónde se va a encontrar un id único para la instancia del Monit.
  • set statefile [statefile]: establece el fichero dónde se guardan los estados que guarda Monit de los servicios monitorizados en cada ciclo.
  • set mailserver [mail-server] port [port-number] username [username] password [pwd] using tlsv1 with timeout 30 seconds: Indica el servicio de mailing que se va a utilizar. En nuestro caso, usamos el servicio de mensajería de Google, para lo cual damos las credenciales de una cuenta de gmail que va a ser usada con para ENVIAR el mail.

    Si te preocupa el hecho de poner información sensible, como la contraseña de un correo en un fichero de Ansible, puedes mirar este tutorial sobre como cifrar información sensible con ansible-vault.

  • set alert [mail-to-alert]: Esta directiva indica los correos electrónicos que van a RECIBIR la alerta. Si no especificas nada más a la derecha de la directiva, se enviará un correo electrónico por evento diferente, aunque también es posible enviar correos solo para eventos específicos. SI quieres saber más sobre los eventos de Monit, consulta el siguiente enlace. Para este ejemplo no queremos ningún filtro por evento así que lo dejamos como está.
  • set mail-format [format]: Aquí especificamos el contenido del mensaje del mail
  • set httpd port [port-number] and use address [monit-host] allow [monit-host] allow [monit-access-user]:[monit-access-password]: Con esta propiedad permitimos el acceso al servicio web desde el puerto 2812 (puerto por defecto de Monit). El motivo por el cual hemos montado un Nginx que haga de proxy inverso es porque Monit viene con una aplicación web para ver la monitorización. Es normal añadir una capa de seguridad entre nuestra aplicación e Internet, y no exponer directamente los puertos de nuestra aplicación hacia afuera, dejando solo disponibles los puertos 80 y 443.
  • check process [unique name] [PIDFILE [path] | MATCHING [regex]: Esta directiva nos permite monitorizar un servicio en base a unas reglas específicas y realizar una acción respecto al servicio monitorizado en caso de que esté en un estado que no sea el esperado (reiniciar el servicio, ejecutar un script…).

    En nuestro caso comprobamos que el puerto 80 está disponible, y por lo tanto Nginx levantado. En caso de que nos de una respuesta negativa en dos ciclos de Monit (cada ciclo viene definido por el intervalo que pusimos en la primera directiva) se intenta arrancar el servicio.

    Como vemos en la plantilla, le indicamos como se para y como se arranca el servicio, así como el pid (el id del proceso) que estamos monitorizando. Por último, iteramos los términos que implican las acciones que va a realizar dada una precondición.

Como vemos, las plantillas de Ansible son un mecanismo muy potente que usa por debajo el sistema de plantillas de jinja2 para permitirnos iterar y poder ajustarnos a las necesidades específicas de cada servicio.

¿Dónde están definidas las variables de la plantilla? Como norma general, y dado que las variables de Ansible pueden estar definidas en varios sitios, yo os recomiendo que todas las variables que usa un rol estén definidas en el directorio defaults dentro del mismo rol. Por lo que nuestras variables estarían en la ruta: ansible/roles/monit/defaults/main.yml

ansible/roles/monit/defaults/main.yml

Como comprobaréis el valor de las propiedades monit_sender_account_email, monit_sender_account_password y monit_alert_recipients debe ser definido por vosotros en cada caso o no funcionará. En mi caso usaré dos correos electrónicos para que uno envíe los mensajes de Monit y otro los reciba.


6. Comprobación

Ahora que ya tenemos nuestra configuración de la máquina virtual levantada, y el aprovisionamiento que va a realizar Ansible preparado, en el directorio dónde tenemos el Vagrantfile ejecutamos el comando:

Terminal

Y esperamos a que suceda la magia 😀

Una vez termine el playbook, deberíamos de ser capaces de ver en el navegador con la ruta localhost:8888 la página por defecto de instalación de Nginx.

Default image from Nginx

Si ahora vamos a la ruta localhost:8888/monit tras introducir las credenciales de acceso (hemos puesto de usuario admin y de contraseña monit) accederemos a la aplicación web que nos ofrece Monit y veremos como el estado del servicio de Nginx es satisfactorio.

Monit Interface

Ahora vamos a hacer una pequeña prueba en la que paramos manualmente el servicio, y esperamos que Monit intente levantar el servicio de forma automática sin nuestra intervención. Para ello vamos a acceder a la máquina virtual por ssh y vamos a parar el servicio de Nginx, ejecutando los siguientes comandos desde el directorio en el que está nuestro Vagrantfile:

Terminal

Ten en cuenta que como hemos parado el servicio del Nginx, la aplicación web que veíamos tiene que dejar de ser accesible. Es decir, que si recargas la página en el instante después de haber parado el servicio ya no verás la interfaz web. Entonces, ¿Cómo sabemos si el servicio Nginx se ha levantado o sigue caído sin hacer F5 cada poco tiempo? ¿Recuerdas las notificaciones por mail que tenemos que recibir cuando se produzca un evento? Pues vamos a ver como en el correo nos llega un mensaje de que el servicio está caído, y como al cabo de un corto periodo el servicio vuelve a estar operativo.

Mail Events

Comprobamos que Nginx vuelve a estar operativo volviendo a localhost:8888

Default image from Nfinx after restart

Y como justo después de parar el servicio y esperar a que Monit lo restaure no hemos realizado ningún comando:

Auto restart from Nginx


7. Conclusión

Tener la seguridad de que tus aplicaciones son capaces de intentar recuperar su estado ideal de forma automática no tiene precio cuando estás en producción y cada instante que tu aplicación no está en el aire son potenciales clientes que estás perdiendo.

Monit te ofrece una gran potencia con una configuración muy sencilla y es rápido y fácil de instalar, por lo que si estás empezando a monitorizar tus aplicaciones, sin duda es una buena forma de comenzar. 😀

El código completo (también el rol de Nginx y de Monit que no se han mostrado) están en mi repositorio de GitHub, de forma que solo hay que bajárselo y seguir las instrucciones desde el punto 6.


8. Referencias