Dependencia de roles y gestión de errores con Ansible

0
4428

En este tutorial veremos cómo gestionar las dependencias de los roles de un playbook de ansible, y como configurar una tarea para que sea idempotente en múltiples ejecuciones del playbook.

Índice de contenidos

1. Introducción

Recientemente he leído Ansible up and running: Automating configuration management and deployment the easy way de Lorin Hochstein en el que he aprendido algunas cosas chulas que quería compartir con vosotros. Vamos al lío 😀

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.2
  • Vagrant 1.8.1
  • Ansible 1.9.3

3. Instalación del entorno

La instalación del entorno es muy simple: hay que instalar Vagrant y Ansible y elegir el directorio de trabajo en el que deseamos realizar la prueba. En este tutorial explico como instalar estas herramientas.

4. Gestión de dependencias en un rol

Al crear un playbook de Ansible en mi proyecto, aprovisionabamos la máquina con los roles ordenados, de forma que las dependencias fueran en un módulo anterior al módulo que las necesitaba. Esto puede suponer un problema ya que necesitas tener un conocimiento previo del funcionamiento del aprovisionamiento para ejecutar tareas específicas.

Por ejemplo, si instalamos Sonar con PostgreSQL y tú quieres probar (o reutilizar) el módulo de Sonar en otro proyecto, necesitarías saber que depende del rol que instala PostgreSQL para su funcionamiento.

Con la gestión de dependencias de los roles lo que se pretende es justo evitar eso. Si quieres ejecutar un rol, ese rol sabe todas las dependencias que necesita para que pueda ser ejecutado fácilmente. Vamos a realizar un ejemplo donde comprobamos la gestión de dependencias del rol sonar. Para ello nos creamos nuestra carpeta Ansible dónde van tanto nuestros playbooks como nuestros roles, y nos servimos de Vagrant para provisionar las máquinas.

En el directorio elegido ejecutamos el comando Vagrant init para configurar el aprovisionamiento de nuestra máquina virtual y lo dejamos de la siguiente manera:

Vagrantfile

En este fichero le decimos la box de la que queremos partir, hacemos la redirección al puerto 9000 de la máquina virtual y configuramos la memoria que va a tener la máquina virtual. Por último, definimos el aprovisionamiento indicando el playbook y el inventory que vamos a utilizar.

Comenzamos creando el playbook:

ansible/playbook.yml

Este es un playbook sencillo en el que se ejecuta sólo un rol. Vamos a crear este rol ahora 😀

ansible/roles/sonar/tasks/main.yml

Este es un rol con el que descargamos sonar, creamos su usuario y su grupo y nos aseguramos de que esté funcionando como servicio. Este rol necesita de un fichero de configuración que tenemos definido en el siguiente fichero:

ansible/roles/sonar/templates/sonar.properties

Este fichero contiene las propiedades del Sonar, así como la configuración para que la base de datos no se cree en memoria.

Pues ya tendríamos nuestro sonar montado, aunque si nosotros tratáramos de ejecutar este rol fallaría porque le falta la configuración del idioma, el programa unzip para descomprimir el sonar y PostgreSQL. Nosotros necesitamos crear estos roles pero no queremos modificar el playbook, ya que nosotros lo que queremos instalar es Sonar. Si Sonar necesita alguna dependencia debería de ser este quien las gestionara. ¿Cómo hacemos eso? añadiendo un nuevo fichero dentro del rol sonar que sea el que conozca la dependencia:

ansible/roles/sonar/meta/main.yml

Con este fichero le estamos indicando que para poder ejecutar el rol de sonar necesita ejecutar antes los siguientes roles. La ventaja es que esta información está dentro del playbook de sonar de forma que si no tiene ese rol creado no será capaz de ejecutar sonar por falta de dependencia.

Creamos el rol de locales encargado de actualizar los idiomas y aplicarlos:

ansible/roles/locales/tasks/main.yml

Instalamos Java en la máquina virtual porque es un prerrequisito de Sonar:

ansible/roles/java8/tasks/main.yml

Creamos el rol de unzip para instalarlo:

ansible/roles/unzip/tasks/main.yml

Creamos también el rol de postgresql:

ansible/roles/postgres/tasks/main.yml

¡Qué no se nos olvide almacenar las variables de ejecución del playbook!

ansible/environments/ndevelopment/group_vars/sonar

Si ahora levantamos la máquina virtual veremos como primero se ejecutan las dependencias del módulo y por último se ejecuta sonar. Después podremos acceder a través de nuestro navegador preferido ejecutando localhost:9000/sonar

5. Gestión del cambio de tareas

Ahora vamos a ver otra cosa chula de ansible. Normalmente cuando ejecutamos un playbook queremos que este sea idempotente, es decir, que deje la máquina exactamente en el mismo estado independientemente de las veces que se ejecute ese playbook en el host remoto. La mayoría de los módulos de ansible soporta la idempotencia, aunque si nosotros ejecutamos comandos directamente en el servidor remoto estos no suelen ser idempotentes. Como prueba, si nosotros volvemos a provisionar la máquina que acabamos de crear, nos daría un error en una tarea de sonar precisamente porque no es idempotente. Esta tarea ejecuta el comando mv a un directorio y cuando lo realiza por segunda vez, como el directorio ya se encuentra allí, nos da un error.

Ansible también nos ofrece una solución a esto por medio de las cláusulas «changed_when» y «failed_when» donde puedes especificar de forma exacta cuando consideras que esa tarea ha fallado o ha cambiado el estado de la máquina remota. Este método es mucho menos intrusivo que poner directamente un «ignore_errors: yes» ya que nosotros decidimos qué es cambio y qué es fallo.

Para verlo en funcionamiento cambiemos la tarea «move sonar to its right place» del rol de sonar para que quede la siguiente manera:

ansible/roles/sonar/tasks/main.yml

En primer lugar necesitamos registrar la salida del comando, en nuestro caso en una variable result. Luego indicamos como condición del fallo que la tarea sea considerado un error si en la salida pone algo diferente a que el directorio no esté vacío, porque esto quiere decir que la tarea ya se realizó previamente.

Volvemos a ejecutar el aprovisionamiento de ansible (vagrant provision) y comprobamos como ahora no falla.

6. Conclusiones

Espero que este tutorial sirva para organizar mejor nuestros playbook y para gestionar los errores en tareas no idempotentes, como ejecutar comandos directamente en la máquina remota. Puedes obtener el código del ejemplo desde mi repositorio de github.

7. Referencias

Dejar respuesta

Please enter your comment!
Please enter your name here