sábado, 27 de enero de 2024

Ansible - Estructura de proyectos I

Cuando trabajamos en proyectos de automatización con Ansible, lo normal es que tengamos una carpeta para cada uno de los proyectos. En dichas carpetas guardaremos el fichero de configuración de Ansible, el fichero de inventario, playbooks, etc.

En este post veremos como organizar dicha carpeta para aprovechar las funcionalidades de Ansible de la forma más simple y eficiente posible, además de ser la aproximación recomendada.

Hasta ahora, hemos tenido una carpeta donde, al menos, necesitamos el fichero de configuración de Ansible y el inventario. En el fichero de inventario, para comandos ad-hoc simples, hemos añadido en ocasiones variables, ya fuese a grupos o a hosts, para poder realizar determinadas operaciones. Para poder asignar de forma mucho más ordenada y clara variables a cada grupo de hosts, o a hosts individuales, Ansible recomienda que, en nuestra carpeta de proyecto, tengamos presentes las carpetas group_vars y host_vars. Con esto en mente, la estructura más básica de una carpeta de proyecto Ansible sería algo como lo siguiente:

Estructura mínima de un proyecto de Ansible.

Como es lógico y hemos visto hasta ahora, el contenido del fichero ansible.cfg debería tener las variables de configuración necesarias, en función de nuestro entorno y necesidades, pero al menos, debería incluir la ruta a nuestro fichero de inventario, el nombre del usuario remoto empleado para la conexión así como las opciones de escalado de privilegios que necesitemos.

¿Cual es la función de las carpetas group_vars y host_vars? contener ficheros cuyos nombres coincidirán con los nombres de grupos de hosts, en el caso de la carpeta group_vars, o de hosts individuales en el caso de la carpeta host_vars, de acuerdo con las entradas de nuestro inventario. En dichos ficheros podremos incluir variables, tanto de ansible como definidas por nosotros, para su uso durante la ejecución de playbooks o comandos ad-hoc. Es importante tener en cuenta que estos ficheros deben estar en formato YAML.

Para verlo con un ejemplo simple, supongamos que tenemos el grupo de hosts de bases de datos y el de servidores web. Definimos que, el paquete que debe instalarse en los servidores web, será la última versión disponible de Apache, lo cual podemos definir en el fichero correspondiente dentro de group_vars del siguiente modo:

Contenido de fichero group_vars.

El contenido del fichero, en formato YAML, contiene el nombre que he definido para la variable y el valor de la misma. Ahora, si uso un comando ad-hoc o un playbook en el que lanzo la instalación del paquete dado por el nombre de la variable sobre el grupo webservers, tengo lo siguiente:

Instalación de Apache usando variable.

Aunque no está la salida completa del comando, Ansible ha realizado la instalación de la última versión del paquete Apache en todos los hosts del grupo webservers. A partir de esto, entendemos lo siguiente:

  • Ansible busca por defecto la carpeta group_vars y, dentro de la misma, un fichero cuyo nombre coincida con el del grupo de hosts sobre el que estamos actuando.
  • Como no hemos definido nada relacionado con dicha carpeta en nuestro fichero de configuración, está claro que es una característica propia de Ansible.
  • Para usar una variable definida en un fichero de variables, la definimos entre dobles llaves {{}} como podemos ver en el comando anterior.

Si intentamos utilizar una variable de un grupo, con otro grupo diferente, recibiremos un error ya que, como acabamos de ver, Ansible buscará en el fichero de variables con el mismo nombre que nuestro grupo:

Usando una variable de otro grupo.

Como vemos, Ansible busca la variable en el fichero group_vars correspondiente al grupo sobre el que queremos actuar y, al no encontrarla, nos devuelve un error.

La única excepción a este comportamiento, es el fichero group_vars/all, el cual, si existe, será leido por Ansible en todas las ejecuciones. Así, por ejemplo, podemos incluir en el fichero group_vars/all la password para realizar sudo en nuestros hosts, de una manera similar a la siguiente:

Variables comunes a todos los hosts.

En este caso estamos definiendo una variable de Ansible, con lo que, al lanzar un comando ad-hoc simple como el módulo ping, será leido y utilizado directamente por Ansible:

Ejecución de comando ad-hoc.

Como podemos ver en la salida anterior, utilizamos el grupo dbservers, pero no la opción -k, porque estoy usando claves SSH, ni la opción -K, porque la password para sudo ya está definida en el fichero group_vars/all. Por tanto, Ansible busca la carpeta group_vars y, dentro de ella, leerá el fichero all y aplicará todas las variables que encuentre para, después, buscar el fichero con el nombre coincidente con el grupo de hosts sobre el que estamos actuando, para leer las variables definidas en el mismo.

Como es lógico, el directorio host_vars tiene un uso idéntico pero está pensado para hosts individuales, con lo que los ficheros que creemos en dicha carpeta, tendrán el nombre o dirección IP de hosts para los que queramos definir variables especiales.

Más adelante, veremos como usar vault con el inventario y como extenderlo aún más, con el uso de roles.