Cómo instalar Ansible y usarlo de forma básica en Linux

Publicado el 09 enero 2018 por Drassill
La gestión de múltiples equipos es una tarea que dependiendo del volumen de éstos, puede resultar una tarea abrumadora y extremadamente ineficiente; especialmente si queremos aplicar el mismo cambio a todos estos. Imaginemos que tenemos 5 servidores Linux con SSH instalado para gestionarlos remotamente; y tenemos que hacer (por ejemplo) una simple tarea de actualización, junto con añadir un nuevo DNS a/etc/resolv.conf. Ambas tareas son simples, pero no deja de requerir tiempo pues requiere replicarlas en 5 servidores. Es por ello que en ciertos entornos es recomendado tener alguna herramienta de automatización de tareas... Existen varias herramientas, tales como Chef, Puppet o Ansible, pero hoy nos vamos a centrar en la que, en mi opinión, es la que tiene la menor curva de aprendizaje y la que es más sencilla de gestionar: Ansible.

Ansible es una herramienta de automatización de tareas que puede mandar comandos a múltiples máquinas ya sea desde la consola a la fuerza, o mediante unos ficheros, cuyo formato es YAML  (conocidos como playbooks), que siguen una estructura muy definida, en la que se establecen todo tipo de comandos, comportamientos, desencadenadores ante ciertos eventos, etc... Lo más común y lo ideal, es trabajar con dichos ficheros, pues se puede dejar todo configurado en estos y luego simplemente con decirle a Ansible que realice las acciones pertinentes que le digan éstos; pero a modo de introducción, es mejor comenzar con lo más básico y realizar tareas sencillas para luego, en caso de ver que la herramienta se adapta a nuestras necesidades, pasar a usar Ansible en profundidad.
Lo primero y necesario para poder gestionar todas las máquinas, es que todas tengan el paquete openssh-server instalado. Esto sí que es necesario hacer a mano. También es necesario tener dicho paquete instalado en la máquina controladora.
apt-get install openssh-server
La necesidad de dicho paquete radica en que todas las comunicaciones se hacen vía SSH, con lo que en caso de tener un firewall entre el controlador que enviará los comandos al resto, y las máquinas, habría que asegurarse de que éste no está bloqueando el puerto correspondiente (por defecto el 22). Aún así con esto no valdría; habría que habilitar el acceso de root por SSH a las otras máquinas; esta parte es subjetiva, ya que dependiendo de qué Linux estemos usando y la versión de éste, podemos tener habilitado el acceso remoto, pero lo ideal es asegurarse... Para ello habría que editar el fichero /etc/ssh/sshd_config y comprobar que el parámetro PermitRootLogin esté puesto a yes:
PermitRootLogin yes
Por supuesto habría que reiniciar el servicio SSH.
/etc/init.d/ssh restart
Ahora bien, hay que pensar en hacer las tareas lo más automatizadas posible, con lo que vamos a usar el método de autenticación de clave pública y privada para evitar tener que introducir la contraseña a cada servidor, ahorrándonos mucho tiempo y pudiendo hacer que las tareas de Ansible se ejecuten sin tener que estar nosotros delante autenticándonos cada vez que se conecte a un nuevo servidor. Esto puede parecer tedioso, pero hay que pensar que es un proceso se hará una sola vez, al igual que la instalación de SSH. Para ello, desde el servidor que usaremos como el controlador, es decir desde el que usaremos para enviar ordenes al resto con Ansible, generaremos un par de claves (una pública y una privada) RSA.
ssh-keygen -t rsa
Es importante matizar que cuando nos pregunten sobre qué contraseña ponerle a la clave privada para poder usarla, no poner ninguna, pues dicha clave privada, en un principio, no va a salir del equipo.
.
Ahora para exportar la clave pública a otro equipo se usaría el comando ssh-copy-id tal que así:
ssh-copy-id root@IP
Por ejemplo, si uno de los equipos tuviese la IP 192.168.1.8 haríamos:
ssh-copy-id root@19216818
Este comando habría que repetirlo para cada IP que queramos controlar. Obviamente, al copiar la clave pública, el equipo al queramos copiar la clave nos pedirá la clave de root de dicho equipo. Gracias a dicho comando ya podremos entrar a dichos equipos sin necesidad de contraseña, lo cual hará que cualquier tarea sea mucho menos tediosa; ahora bien, faltaría instalar Ansible en el equipo que queremos que "controle" al resto, es decir el equipo desde el que hemos enviado nuestra clave pública al resto. Afortunadamente instalar Ansible es tan sencillo como instalarlo de los repositorios:
apt-get install ansible
Ahora que tenemos Ansible instalado, lo primero que haremos será dirigirnos al fichero /etc/ansible/hosts; dicho fichero sería el encargado de recopilar los diferentes equipos que queremos controlar. En dicho fichero se pueden crear diferentes grupos que agrupan distintos hosts; grupos que se pueden llamar de forma individual, o que también pueden ser llamados todos a la vez.  Cada grupo tendrá la siguiente estructura:
[nombre_grupo]
IP1
IP2
...
Un ejemplo puramente teórico podría ser:
[WEB]
19216817
19216818
[ASTERISK]
19216819
A modo de ejemplo vamos a crear un solo grupo que tendrá la IP 192.168.1.8 dentro del susodicho, pues sabemos que dicha IP tiene nuestra clave pública y que podremos efectuar cualquier acción sobre el equipo con dicha IP sin preocuparnos en introducir la contraseña.
[PRUEBAS]
19216818
Ahora que tenemos el fichero hosts preparado, vamos a efectuar un par de comando básicos:
El primero sería una simple comprobación del contenido del fichero /etc/resolv.conf de cada equipo controlado por Ansible:
root@debian:~# ansible all -a \ 
"cat /etc/resolv.conf"
19216818 | SUCCESS | rc=0 >>
nameserver 19216811
Esto haría que lanzásemos una consulta del contenido del fichero /etc/resolv.conf a cada equipo bajo nuestro control, y que cada uno nos devolviese como respuesta la salida de dicho comando; ahora bien; qué significa la estructura arriba mostrada? Dicha estructura es tan sencilla como:
ansible nombre_grupo -a "comando de Linux"

A la hora de poner el nombre de grupo podemos poner el nombre del grupo al que queremos hacer referencia, como por ejemplo PRUEBAS, o por el contrario, si queremos hacer referencia a todos los grupos habría que simplemente poner el nombre all, tal y como hemos hecho arriba. Es decir que en nuestro anterior comando podríamos haber puesto el nombre del grupo PRUEBAS en vez de all y haber obtenido el mismo resultado. Al igual que podemos haber mandando dicho comando, podemos lanzar cualquier otro comando común de la consola, como podría ser un "ifconfig" o incluso un "apt-get update"; es decir que prácticamente con la prueba de concepto de arriba, podrían mandarse comandos de consola simples a todos los equipos, lo cual de por sí nos puede ahorrar una cantidad de tiempo considerable.
Ansible ofrece más opciones más allá de los comandos de shell, ya que si bien he omitido este concepto a propósito en el primer ejemplo, cuando se mandan ordenes a través de Ansible, se mandan gracias al uso de módulos que son llamados gracias al parámetro -m. La lista de módulos y sus diferentes parámetros es realmente enorme; lista que se pueden encontrar en la página oficial de Ansible como podéis ver en este enlace. Mencionarlos todos es prácticamente imposible, pero hay unos pocos que pueden resultar interesantes de mencionar; en concreto habrían 3 módulos que en mi opinión son interesantes conocer:
  • shell: Este módulo es el usado por defecto. Es decir que no especificar un módulo o poner que se quiere usar este módulo viene a ser lo mismo. Este módulo ejecuta directamente ordenes de shell.
  • service: Este es un módulo muy interesante, ya que se encarga de comprobar el estado de los servicios y manipularlos. Puede pararlos, arrancarlos e incluso añadirlos y quitarlos del arranque... En definitiva, es un módulo muy interesante a la hora de mantener servidores.
  • apt: Este módulo se dedica a usar apt para realizar actualizaciones de la caché, instalaciones, actualizaciones y eliminaciones de paquetes. 

El primer módulo ya hemos visto como usarlo y que simplemente requiere tener nociones de la shell, pero ¿Cómo usamos los módulos service y apt?
El módulo service tiene tres argumentos que podrían considerarse como los más importantes: El nombre del servicio, mediante el argumento name, el estado en el que queremos que esté mediante el comando state, y si queremos que se inicio no durante el proceso de arranque mediante el comando enabled.  Los estados pueden ser: started, stopped, restarted y reloaded; mientras que la habilitación del arranque durante el inicio sería: yes o no.
La mejor forma de entender los argumentos es viendo el uso de estos:
Imaginemos que queremos parar el servicio ntp en todos los equipos que controlamos; el comando que lograría dicho resultado sería:
root@debian:~# ansible all -m service -a \
"name=ntp state=stopped"
19216818 | SUCCESS => {
"changed": true,
"name": "ntp",
"state": "stopped",
Cada comando que lancemos que a cada máquina nos devolverá una serie de líneas, entre las cuales nos importará ver, por un lado que el comando se ha enviado exitosamente, lo cual se deduce al ver que devuelve un SUCCESS, y por otro lado hay una línea en la que veremos que dirá "changed"; en caso de dar como respuesta true, significará que antes se encontraba parado, mientras que al decir false nos estaría diciendo que ya se encontraba parado antes de que nosotros se lo ordenásemos. Otro ejemplo más completo, aprovechando que el servicio ntp se encuentra parado podría ser:
root@debian:~# ansible all -m service -a \
"name=ntp state=started enabled=yes"
19216818 | SUCCESS => {
"changed": true,
"enabled": true,
"name": "ntp",
"state": "started",
En este caso no solo le habríamos ordenado que arrancase, sino que además le estaríamos ordenando que se iniciase durante el proceso de arranque el equipo...  Este ejemplo se ha usado para el servicio NTP pero podría extrapolarse a cualquier otro sin problema alguno.
Veamos ahora el módulo apt. Este módulo funciona de la misma forma que el uso habitual de apt-get, con la diferencia que desde aquí podremos controlar pequeños detalles como que por ejemplo tengamos un paquete concreto siempre con la última versión. Este módulo tiene muchos parámetros, pero los más importantes serían: name, cuya utilidad es la misma que la usada en el módulo de service y que está presente en la gran mayoría de los módulos, state, que en este caso tiene unos estados diferentes a los del módulo service, y update_cache, que sería un equivalente al comando apt-get update. Los estados en este caso serían: latest (última versión del paquete), absent, present y  build-dep. El último estado simplemente comprobaría que el paquete X tiene todas las dependencias necesarias instaladas.
A sabiendas de dichos parámetros, podemos jugar con el módulo apt para, por un lado nos actualice la caché de los repositorios y que por otro lado nos instale (si no tenemos instalada ya) la última versión de NTP.
root@debian:~# ansible all -m apt -a \
"name=ntp update_cache=yes state=latest"
19216818 | SUCCESS => {
"cache_update_time": 1515448761,
"cache_updated": true,
"changed": false
}
Gracias a dicho módulo podemos mantener actualizados todos los equipos sin tener que ir uno por uno realizando dicha tarea.
Existen muchos más módulos cuya importancia varía dependiendo de las herramientas que estemos usando, pero estas tres serían las más básicas y con las que se podrían realizar las tareas más sencillas sin necesidad de introducirse en el mundo de los playbooks.
Como podéis observar el uso de Ansible puede ayudar muchísimo en la administración de varios equipos. Cierto es que en este caso se ha interactuado con uno solo y que hacer la misma tarea directamente en dicho equipo habría sido más sencillo ¿Pero y si fuesen 5, o 10, o incluso 100? Esta herramienta brilla en dicho tipo de entornos y es donde realmente se le saca partido.
Espero que con este post hayáis podido ver las posibilidades que ofrece Ansible y las ventajas que puede ofrecer para automatizar ciertas tareas y ahorrarnos horas de tareas repetitivas.
Saludos.