miércoles, 1 de noviembre de 2023

Introducción a Ansible - Inventario y variables

Hoy, después de mucho tiempo, continuamos la serie sobre Ansible analizando el inventario y como asignar variables a los hosts, o grupos de hosts, que forman parte del mismo.

Como vimos en la entrada anterior, al utilizar Ansible, este busca su fichero de configuración ansible.cfg en una serie de rutas. Dentro de este fichero podremos especificar la ruta y nombre del fichero de inventario usando la opción de configuración inventory. Recordemos que el fichero de configuración por defecto de Ansible, /etc/ansible/ansible.cfg, fija como inventario el fichero /etc/ansible/hosts.

El fichero de inventario debe contener el nombre o dirección IP de todos los sistemas bajo el control de Ansible y como vimos, podemos hacer grupos con ellos de forma muy simple especificando etiquetas. Así, comenzamos a realizar pruebas con una serie de contenedores y comprobamos que podíamos acceder correctamente a los mismos. En ese momento, el contenido del fichero de inventario era el siguiente:

Fichero de inventario.

Revisando la imagen anterior podemos observar que hay definidos 5 hosts, cuyas direcciones IP están en el rango 172.17.0.2-172.17.0.6 y un grupo adicional llamado all, el cual está formado por el rango de direcciones IP 172.17.0.2-172.17.0.7. Usando un comando ad-hoc, podemos comprobar las direcciones IP de cada contenedor del siguiente modo:

Direcciones IP de cada contenedor usado.

Por tanto, cada uno de los contenedores que estoy empleando están registrados en el inventario, lo que me permite hacer comandos ad-hoc o utilizar playbooks con cada uno de ellos individualmente. Como vemos, en la definición de inventario no es necesario tener definidos los hosts en una lista inicial y luego usarlos en grupos, como en mi caso donde, el contenedor con la IP 172.17.0.7, solo aparece en el grupo all. Si elimino dicha entrada del grupo all y lanzo un comando ad-hoc directamente contra esa dirección IP el resultado es el siguiente:

Comando ad-hoc contra 172.17.0.7.

El resultado anterior nos muestra un punto muy importante sobre el inventario y es que, es necesario  que todos los hosts estén incluidos en el inventario si queremos poder controlarlos mediante Ansible. 

Si resumimos lo poco que hemos visto hasta ahora del fichero de inventario, podemos decir que es un fichero de texto plano, en el cual registramos las direcciones IP o nombres de todos los hosts que queremos controlar con Ansible.

Una de las características de Ansible en lo referente al fichero de inventario, es que podemos definir categorías o grupos dentro del mismo identificándolas con una etiqueta. En el fichero que hemos usado hasta ahora solo hay una etiqueta llamada [all], que engloba todas las direcciones IP, pero por ejemplo, si hacemos lo siguiente:

Nuevo grupo de hosts.

Ahora podemos repetir el comando ad-hoc empleando el módulo ping, pero especificando que el grupo sobre el que queremos lanzar dicho comando es el nuevo grupo:

Comando ad-hoc con el nuevo grupo.

¿Que nos muestra esta prueba tan sencilla? que podemos poner nombres de etiquetas, totalmente arbitrarias, para agrupar los hosts que luego controlaremos desde nuestro nodo de gestión. Así, por ejemplo, podemos necesitar una clasificación como la siguiente:

Un inventario "más complejo".

Ahora, al comprobar la conectividad con el grupo de hosts webservers:

Comprobación de un grupo de hosts.

En este caso, la salida del comando, muestra que solamente se ha lanzado la tarea contra los hosts que forman parte de dicho grupo.

En general, y de forma mucho más simple, siempre que queramos consultar la lista de hosts que forman parte de un grupo, podremos usar directamente el comando ansible, con la opción --list-hosts, de la siguiente manera:

Listando los miembros de los grupos de hosts del inventario.

De esta manera, podemos consultar los miembros de un grupo sin necesidad de editar el fichero de inventario. La salida del comando nos mostrará todas las direcciones IP, o nombres de los hosts, que forman dicho grupo.

Como es lógico, recibiremos un mensaje de error si intentamos lanzar alguna acción sobre un grupo no está presente en el fichero de inventario:

Grupo no existente.
 
Esto se debe a que, en general, el comando ansible buscará en el fichero de inventario el patrón que aparece al final del comando, ya que se entiende que se corresponde con el nombre del host o grupo de hosts sobre los que queremos actuar. Esto lo comprobamos del siguiente modo:
 
 
Usando expresiones para especificar los hosts.

Como vemos en la salida anterior, claramente el comando ansible buscará en el fichero de inventario los patrones que coincidan con el que pasamos por línea de comandos.

Bueno, y ahora la duda es ¿y el inventario sirve para algo más? Afortunadamente, el fichero de inventario nos permite establecer configuraciones específicas para hosts o comunes a grupos de hosts. Para esto, podemos definir variables de configuración en el inventario en el caso de ser necesario, tanto por host individual como por grupos. Imaginemos que necesitamos instalar un paquete determinado en un grupo de hosts, para esto podríamos hacer lo siguiente:

Uso de variables en fichero de inventario.
 
Como podemos ver he definido una variable, con un nombre arbitrario, para especificar el paquete de servidor de base de datos que quiero instalar en cada uno de los hosts que forman parte de dicho grupo. Ahora, puedo lanzar la ejecución del mismo en todos los hosts del grupo usando un comando ad-hoc como el siguiente:

Instalación de paquete especificando una variable.

Aunque no se muestra toda la salida, el resultado es la instalación del paquete especificado en la variable db_package que está definida en el fichero de inventario.

En el comando ad-hoc usamos el módulo apt, al que pasamos dos argumentos; el primero, state=present, establece que lo que queremos es que el estado final del sistema incluya el paquete; el cual está especificado por el segundo argumento, dado por name y que es igual a la variable que hemos definido. Como podemos ver, las variables las especificamos entre llaves dobles {{ NOMBRE_VARIABLE }}, aunque ya veremos más adelante como trabajar con variables.

Pero esto, podríamos pensar, es bastante incómodo de manajer ya que, si tengo 20 hosts en dicho grupo, tendré que añadir la variable a todos y cada uno de ellos. Afortunadamente, para evitar este trabajo innecesario, podemos definir variables de grupos del siguiente modo:

Definiendo variables para grupos de hosts.

Por ejemplo, supongamos que en una serie de hosts, el usuario que se emplea para las conexiones desde el servidor Ansible no es el mismo que en el resto. Para controlar este tipo de situaciones, Ansible nos deja especificar opciones de configuración de ansible en nuestro fichero de inventario, por ejemplo del siguiente modo:

Especificando el usuario de conexión
Un punto importante a tener en cuenta, ya que estamos hablando de usuarios de conexión, es que por defecto, ansible usará para conectarse con el host remoto el usuario con el que se está ejecutando el comando. Si queremos cambiar este comportamiento, es necesario que fijemos la opción de configuración remote_user en la sección defaults del fichero de configuración general ansible.cfg o bien la variable ansible_user, como una variable específica para aquellos hosts os grupos que utilicen un usuario diferente, dentro del inventario.
 
Con el cambio anterior, al intentar conectar a los hosts del grupo webservers, encontramos el siguiente fallo ya que el usuario especificado no existe:
 
Fallo de conexión al usar un usuario incorrecto

Por tanto, si queremos hacer algo así, necesitamos crear el usuario primero, lo que podríamos hacer del siguiente modo. Primero definimos dos variables, que podemos llamar admin_user, para el usuario, y admin_password, para la password de dicho usuario. Estas variables las definimos para los hosts del grupo webservers, añadiéndolas del siguiente modo:

Variables para creación de usuario

Ahora creamos dicho usuario en las máquinas mediante un comando ad_hoc como el siguiente:

Creación del nuevo usuario de conexión

Para poder usar este nuevo usuario, como administrador de las máquinas que forman parte del grupo webservers, añadimos un fichero con una regla de sudoers usando un comando ad-hoc como el siguiente:

Añadiendo un fichero con una regla específica de sudo para el nuevo usuario

Tras esto, al volver a cambiar la variable ansible_user en el fichero de inventario, podemos comprobar que podemos conectar sin problemas con los hosts del grupo webservers:

Comprobación usando el nuevo usuario

En resumen, hemos visto como usar el fichero de inventario y hemos comenzado a usar variables, definidas en este caso en el fichero de invetnario, así como hemos usado varios módulos y empleado variables en los mismos.

Espero que, en breve, más.