Está en la página 1de 64

LUIS USQUIANO MORENO

INTEGRACIÓN DE VAGRANT
CON ANSIBLE
Página | 1
Contents
Objetivos y Justificación de este proyecto ............................................................................. 3
Contexto ................................................................................................................................. 3
Vagrant ................................................................................................................................... 4
Ansible .................................................................................................................................... 4
SSH ........................................................................................................................................ 4
Recordatorio sobre las llaves pública-privada....................................................................... 4
Dos posibles escenarios ........................................................................................................ 5
Herramientas que se han utilizado para la realización de este proyecto: ............................ 6
Nuestro primer servidor con Vagrant utilizando Windows .................................................... 6
Comprobación de las máquinas Virtuales creadas con Vagrant ........................................ 15
Servicio SMB gestionado por Vagrant ................................................................................. 17
Reconfigurar nuestras máquinas Virtuales con Vagrant ..................................................... 20
Emplear emuladores de terminales para agilizar el trabajo ................................................ 21
Destruir una máquina virtual con Vagrant ........................................................................... 21
Aprovisionar máquinas virtuales creadas con Vagrant con Shell Scripts ........................... 22
Vagrant junto a otras tecnologías ........................................................................................ 26
SSH en Ansible .................................................................................................................... 26
Generar un par de llaves pública-privada en nuestro controlador. ..................................... 26
Configurar el servidor SSH .................................................................................................. 28
Exportar nuestra llave pública.............................................................................................. 28
Desactivar las conexiones SSH por contraseña ................................................................. 29
Ansible y el papel de los inventarios.................................................................................... 30
Utilizando comandos ad-hoc de Ansible ............................................................................. 32
Volver a levantar nuestras máquinas virtuales con Vagrant ............................................... 34
Automatización de Tareas con Ansible utilizando Playbooks ............................................. 35
Playbook alternativo para crear usuarios y grupos en diferentes equipos ......................... 48
Configuración de ~/.ssh/config y /etc/hosts ......................................................................... 50
Servidor NFS y Cliente......................................................................................................... 52
Actualizar las Host Keys de cada nodo ............................................................................... 54
Servidor NTP con Ansible .................................................................................................... 60
Conclusiones ........................................................................................................................ 64

Página | 2
Objetivos y Justificación de este proyecto
Con este proyecto pretendo dar a conocer las ventajas que ofrece Ansible para la
automatización de tareas, como puede ser el aprovisionamiento y configuración de algunos
servicios para servidores en un entorno virtualizado gestionado por Vagrant.
Escogí este tema por su esencia netamente administrativa, que es la tarea que más hemos
realizado a lo largo del módulo.
A medida que iba investigando sobre Ansible, nacido en el año 2012, había otra palabra que,
en ocasiones, se mencionaba para levantar los nodos que iban a ser gestionados después
por él. Era Vagrant. En sí mismo, Vagrant da para la creación de un proyecto en solitario,
debido a todo lo que nos puede ofrecer combinándolo con otras tecnologías como Ansible
(este es el caso), Shell Scripts, Chef, Puppet o Docker. Y básicamente eso es lo que he
hecho, un proyecto que demuestra lo bien que se pueden compenetrar estas dos tecnologías.
Mi intención con este proyecto es explicaros el funcionamiento de estas dos tecnologías y
mostraros el código que he tenido que realizar para el levantamiento de una infraestructura
informática con Vagrant y su aprovisionamiento y gestión de tareas de forma automatizada
utilizando Ansible. Utilizar Ansible y Vagrant se resume en crear scripts y en eso se apoya
este proyecto.
Cabe mencionar también, que ambas tecnologías utilizan SSH para su funcionamiento, por
lo que me ha tocado profundizar conocimientos sobre este protocolo.
Durante el proceso de creación de este proyecto me he quedado estancado en muchas
partes, recibiendo diversos errores en la terminal, pero que con el tiempo los he ido
solventando y mi intención es reproducir esos mismos errores a lo largo de este documento
y exponer la solución.

Contexto
En el pasado los administradores de sistemas solían conectarse a los servidores que
gestionaban a través una conexión segura empleando para ello SSH, pero de uno en uno. A
medida que Internet creció, la solicitud de servicios a través de la World Wide Web también.
Los pocos servidores que gestionaban empezaron a multiplicarse. ¿Qué pasaba si querías
replicar la infraestructura de servidores que ya tenías montada, con todo el software y la
configuración editada a tus necesidades? Reproducir cada nodo tomaría demasiado tiempo
sin mencionar el coste.

Página | 3
Vagrant
Vagrant es una tecnología que puede levantar máquinas virtuales a través de un fichero
escrito en lenguaje Ruby. Sin embargo, no hace falta ningún conocimiento previo de Ruby
para utilizarlo. Como he dicho en líneas anteriores Vagrant se puede combinar con otras
tecnologías para abastecer a las máquinas creadas con el software y los servicios de Red e
Internet que queramos, entre otras cosas. Sin embargo, Vagrant necesita un hipervisor para
trabajar. De forma nativa emplea VirtualBox, al ser un software de código abierto y libre, pero
puede emplear otros hipervisores como KVM (GNU-LINUX), VMware o Hyper-V de Microsoft.

Ansible
Ansible es una herramienta para la automatización de tareas en nodos, ya sean servidores o
dispositivos de red. Empleando Ansible puedes gestionar diferentes nodos a través del
protocolo SSH y ejecutar una tarea o lista de tareas en los nodos especificados (nodos que
crearemos con Vagrant). Así pues, desde un solo equipo podemos gestionar diez, veinte o
doscientos equipos a los que nos podamos conectar a través de SSH.
Ansible puede convertir un script complejo en un playbook fácil de leer y codificar escrito en
YAML. Y ese es el motivo por el que Ansible se ha convertido en una de las herramientas
más utilizadas en el mundo de los servidores. Es fácil de aprender a diferencia de otras
tecnologías que se crearon para su mismo cometido. No necesitas tener instalado ningún tipo
de software en los nodos que van a ser controlados, pues Python y OpenSSH Server (los
únicos requisitos) ya vienen instalados por defecto en la mayoría de distribuciones
GNU/Linux.
En ansible el equipo que gestiona al resto de equipos se le conoce como controlador y a los
equipos controlados se le conoce como nodos.

SSH
A través de un fichero llamado ‘Vagrantfile’ crearemos nuestras máquinas virtuales con
Vagrant. Vagrant creará en cada una de ellas un usuario con el nombre Vagrant y contraseña
Vagrant. Dicho usuario está configurado para que pueda utilizar el comando sudo sin
contraseña con privilegios de superusuario. Sin embargo, no necesitaremos utilizar la
aplicación de VirtualBox para acceder a nuestras nuevas máquinas virtuales dado que desde
el mismo terminal podremos conectarnos a ellas a través de una sesión de SSH.
Por defecto, Vagrant crea un par de llaves/claves pública-privada para facilitarnos la conexión
SSH con llave pública. Sin embargo, dado que utilizaremos Ansible, y este también utiliza
SSH para realizar tareas en nuestros equipos creados (nodos), lo mejor que podemos hacer
-y por mejorar la seguridad- es crear nuestro propio par de llaves pública-privada.

Recordatorio sobre las llaves pública-privada


Para hacer una conexión remota con SSH podemos utilizar contraseñas (no recomendado) o
emplear un par de llaves pública-privada. Una clave privada puede encriptar un fichero y se
requiere de una clave pública para desencriptarla (y viceversa). Todo lo que se envíe en
nuestra sesión de SSH será encriptado y desencriptado por este par de llaves. Ahora, como
soy yo el que quiere conectarse a nuestras máquinas creadas, necesitaré crear dichas claves.

Página | 4
La clave privada se quedará en mi ordenador (/home/luis/.ssh/id_rsa) y la clave pública
(/home/luis/.ssh/id_rsa.pub) la podré enviar a cualquier ordenador/servidor al que me quiera
conectar (tendremos que añadir nuestra clave pública a este fichero para que el servidor nos
pueda autenticar /home/maquinaVirtual/.ssh/authorized_keys).

Dos posibles escenarios


Tengo una buena y una mala noticia. La buena noticia es que Vagrant es una herramienta
multiplataforma por lo que da igual el sistema operativo que tengamos, lo podemos utilizar
en cualquier escenario, así tengamos un Mac OS X, un Windows o un GNU Linux. La mala
noticia es que de momento no podemos utilizar un Windows de forma nativa para controlar
con Ansible nuestros diferentes nodos. Para remediar este problema se puede dar uso a
pequeños trucos como instalar el WSL1 (windows subsystem for linux) en Windows, que es
una herramienta que nos permite tener instalada una distribución de Linux como Ubuntu en
nuestro Windows y así con este poder controlar nuestros nodos. Sin embargo, no olvidemos
que Vagrant necesita VirtualBox para trabajar y no es posible activar el WSL sin antes
desactivar el sistema de virtualización de VirtualBox en favor del sistema de virtualización de
Windows (Hyper-V) y así poder ejecutar WSL. En otras palabras, si activamos WSL,
VirtualBox dejará de funcionar.2 Tenemos un problema de incompatibilidad de tecnologías.
Sin embargo, tengo dos soluciones: la primera sería realizar todo el proyecto sobre un sistema
GNU Linux (por ejemplo, dentro de un Manjaro) y que nuestro equipo Linux tome el papel de
controlador, cree con Vagrant los nodos y los aprovisione con Ansible. La segunda opción
sería utilizar Windows de forma nativa, pero utilizar Vagrant para crear el controlador y los
nodos que hagan falta. Obviamente, para que podamos aprovisionar con Ansible este se
deberá instalar en el controlador (una máquina virtual controlará otras máquinas virtuales).
Las dos opciones suenan tentativas y utilizaré primero la segunda para explicar el
funcionamiento básico de Vagrant y después emplearé la primera opción para entrar de lleno
en el proyecto con ejemplos “reales” de la utilización de Vagrant y Ansible en una empresa.

1
What is the Windows Subsystem for Linux?
2
Are WSL 2 and VirtualBox currently mutually exclusive?

Página | 5
Escenario uno y dos respectivamente.

Herramientas que se han utilizado para la realización de este


proyecto:

- Windows 10 y Powershell
- Manjaro y la terminal Bash en entornos Linux
- Cualquier emulador de terminal que pueda dividir la terminal en varias partes y así
facilitarnos el trabajo: Tilix en Linux y Cmder en Windows.
- Visual Studio Code con complementos para YAML y VIM como IDEs
- Virtualbox
- Vagrant
- Ansible

Nuestro primer servidor con Vagrant utilizando Windows


Para explicar el funcionamiento de Vagrant necesitaré utilizar cualquier terminal que nos
provee Windows como CMD o Powershell. También necesitaremos tener instalado algún
entorno de desarrollo como Visual Studio Code para realizar nuestros scripts de Vagrant.
No hace falta decir que también necesitamos tener instalado Virtualbox y Vagrant en nuestro
equipo. Con las herramientas anteriores instaladas en el equipo empecemos:
Voy a crear con Vagrant una máquina GNU Linux que sirva de controlador y un nodo para
que sea controlado por este. A medida que vayamos avanzando y agregando nuevas líneas
a nuestro script iré explicando el funcionamiento de los mismos.

Página | 6
Empecemos por crear un directorio donde se va alojar todo nuestro proyecto. En este
directorio crearemos nuestro fichero de Vagrant para configurar nuestras máquinas
virtuales.

Comprobemos que tenemos instalado Vagrant en nuestro equipo con el siguiente comando:

Para crear nuestro Vagrantfile ejecutaremos el siguiente comando:

Vagrant nos avisa que ya hemos creado nuestro fichero y que ya está listo para levantar la
máquina. Cosa que es mentira. Vagrant solo ha creado un fichero básico que hay que
configurarlo más a fondo. Para abrir el fichero que Vagrant acaba de crear y si tienes instalado
el Visual Studio Code escribe el siguiente comando:

Página | 7
Esta es una plantilla bien documentada por Vagrant con la mayoría de opciones que
podemos configurar. Y esta es una tabla con los parámetros que quiero que Vagrant cree
las máquinas virtuales.

Máquinas Virtuales

Nombre máquina controlador nodo

Hostname controlador nodo

IP 192.168.50.10 192.168.50.20

Tipo de Red Privada Privada

Nombre de Red ASIR ASIR

Tipo de Sincronización de NFS -


Carpetas con la máquina Real

Nombre en VirtualBox controlador(50.10) nodo(50.20)

CPU asignado 1 1

Memoria asignada 512MB 512MB

Distribución GNU Linux Debian Debian

Página | 8
Este sería el script sin los comentarios el cual vamos a personalizar a nuestras
necesidades.

Vagrant.configure("2") do |config|
config.vm.box = "base"
end

Eliminemos la línea del medio y empecemos.

Vagrant.configure("2") do |config|
end

Dado que vamos a configurar dos máquinas virtuales las vamos a definir del siguiente
modo:

Vagrant.configure("2") do |config|
config.vm.define "controlador" do |cont|
end
config.vm.define "nodo" do |nodo|
end
end

El “2” hace referencia a la versión de Vagrant que estamos utilizando.


La palabra clave “define” se utiliza cuando queremos configurar más de una máquina virtual
en nuestro fichero.
De momento tenemos creados dos bloques de configuración. Uno para el controlador y otro
para el nodo. “Controlador” y “nodo” serán los nombres con que Vagrant identificará a
nuestras máquinas virtuales.
Ahora bien, dado que nuestro controlador y nodo van a compartir algunas configuraciones
las podemos definir fuera de estos bloques. Así crearemos uno para configuraciones
globales.

Vagrant.configure("2") do |config|
# configuración general
config.vm.box = "debian/buster64"
# configuración controlador
config.vm.define "controlador" do |cont|
end
# configuración nodo
config.vm.define "nodo" do |nodo|
end
end

Página | 9
config.vm.box = “debian/buster” hace referencia a la Box que vamos a utilizar para
nuestras dos máquinas. Un box es una máquina virtual utilizada por Vagrant muy optimizada
como punto de partida para customizar sobre ella nuestra máquina virtual. Con esto me refiero
a que, si deseamos crear un Debian, debemos utilizar un box con la versión del Debian que
queramos levantar. Los nombres de estos boxes las podemos conseguir desde el catálogo
de Vagrant. 3 En dicho catálogo buscaremos el nombre del sistema operativo o distribución
de GNU/Linux que queramos utilizar. Ojo, no confundamos un box con una ISO de un sistema
operativo (que es lo que utilizamos normalmente cuando queremos levantar una máquina en
virtualbox).

Vamos a configurar otros parámetros en nuestro Vagrantfile como por ejemplo el hostname,
el tipo de red y la IP que utilizarán nuestras máquinas virtuales.

Vagrant.configure("2") do |config|
# configuración general
config.vm.box = "debian/buster64"
# configuración controlador
config.vm.define "controlador" do |cont|
cont.vm.hostname = "controlador"
cont.vm.network "private_network", ip: "192.168.50.10",
virtualbox__intnet: "asir"

3
Vagrant Cloud

Página | 10
end
# configuración nodo
config.vm.define "nodo" do |nodo|
nodo.vm.hostname = "controlador"
nodo.vm.network "private_network", ip: "192.168.50.20",
virtualbox__intnet: "asir"
end
end

Se podría configurar el tipo de red como pública con “public_network” y así nuestras
máquinas virtuales serían un equipo más en nuestro segmento de red. También podríamos
dejar que un servidor DHCP sea el que asigne automáticamente las IPs a nuestras máquinas
virtuales. Para ello deberíamos reemplazar la línea donde configuramos la red a por esta:

cont.vm.network "private_network", type: "dhcp"

El nombre que le damos a nuestra IP privada es opcional.


Sigamos configurando nuestro Vagrantfile a nuestras necesidades. Supongamos que
queremos que exista una carpeta compartida-sincronizada entre la máquina real y el
controlador. Virtualbox por defecto tiene un sistema de carpetas compartidas, pero que a
veces da muchos fallos. Con Vagrant podemos implementar protocolos como SMB en
Windows o NFS / rsync en equipos tipo UNIX. Dado que nuestra máquina real es un Windows
utilizaremos SMB.

Vagrant.configure("2") do |config|
# configuración general
config.vm.box = "debian/buster64"
# configuración controlador
config.vm.define "controlador" do |cont|
cont.vm.hostname = "controlador"
cont.vm.network "private_network", ip: "192.168.50.10",
virtualbox__intnet: "asir"
cont.vm.network "private_network", type: "dhcp"
cont.vm.synced_folder ".", "/home/vagrant/proyecto", type: "smb"
end
# configuración nodo
config.vm.define "nodo" do |nodo|
nodo.vm.hostname = "controlador"
nodo.vm.network "private_network", ip: "192.168.50.20",
virtualbox__intnet: "asir"
end
end

Página | 11
Al momento de levantar nuestras máquinas Vagrant solicitará permisos a Windows para
instalar un servicio SMB pidiéndonos usuario y contraseña de administrador.
Nos queda por configurar opciones como la memoria, la CPU y el nombre que tendrán las
máquinas en Virtualbox.

Vagrant.configure("2") do |config|
# configuración general
config.vm.box = "debian/buster64"
# configuración controlador
config.vm.define "controlador" do |cont|
cont.vm.hostname = "controlador"
cont.vm.network "private_network", ip: "192.168.50.10",
virtualbox__intnet: "asir"
cont.vm.synced_folder ".", "/home/vagrant/proyecto", type: "smb"
cont.vm.provider "virtualbox" do |vb|
vb.name = "controlador(50.10)"
vb.cpus = 1
vb.memory = 512
end
end
# configuración nodo
config.vm.define "nodo" do |nodo|
nodo.vm.hostname = "controlador"
nodo.vm.network "private_network", ip: "192.168.50.20",
virtualbox__intnet: "asir"
nodo.vm.provider "virtualbox" do |vb|
vb.name = "nodo(50.20)"
vb.cpus = 1
vb.memory = 512
end
end
end

Bien. Ya tenemos nuestro Vagrantfile con sintaxis en Ruby que va a levantar el controlador y
el nodo. Para comprobar que no hemos cometido ningún fallo de sintaxis guardamos el fichero
y ejecutamos el siguiente comando en la terminal:

Página | 12
Para levantar las dos máquinas ejecutamos el siguiente comando:

Vagrant empezará a descargar la Box si es la primera vez que la utilizamos en nuestro equipo.

Como dije anteriormente. Vagrant montará un servicio de compartición de ficheros SMB entre
la máquina real (Windows) y el controlador. Para ello antes nos solicitará permisos de
administrador.

Vagrant empezará a configurar las máquinas con los parámetros que establecimos en nuestro
fichero Vagrantfile.

Página | 13
Vagrant ha creado exitosamente nuestras dos máquinas virtuales. Para comprobarlo
ejecutaremos el siguiente comando:

Página | 14
Comprobación de las máquinas Virtuales creadas con Vagrant
Podemos comprobar que las especificaciones de las máquinas virtuales son las que
establecimos desde nuestro Vagrantfile desde la aplicación VirtualBox

Página | 15
Podemos cerrar VirtualBox porque no lo utilizaremos más ya que podemos ejecutar las
conexiones hacia nuestras máquinas virtuales a través de SSH. Para acceder a nuestra
máquina controladora ejecutaremos el siguiente comando:

Efectivamente, comprobamos que nos encontramos en un GNU Linux Debian

Página | 16
Servicio SMB gestionado por Vagrant
Comprobemos que nuestro sistema de compartición de ficheros funciona correctamente
creando algunos documentos.

Comprobación en el directorio del proyecto de la máquina real.

Al parecer todo funciona correctamente. Con esta herramienta (SMB) podríamos estar
trabajando perfectamente a través de Windows y todo el trabajo quedaría también reflejado
en nuestro Debian. Vagrant puede utilizar también Rsync y NFS para entornos Linux. Es bien
conocido que utilizar el sistema de compartición de carpetas y ficheros por defecto de
VirtualBox puede dar muchos dolores de cabeza porque solo funciona cuando le apetece
(esto lo he experimentado en clase muchas veces) 4.

Comprobación de la red:

4
NFS, rsync, and shared folder performance in Vagrant VMs

Página | 17
vagrant@controlador:~/proyecto$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group
default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP
group default qlen 1000
link/ether 08:00:27:8d:c0:4d brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic eth0
valid_lft 84921sec preferred_lft 84921sec
inet6 fe80::a00:27ff:fe8d:c04d/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP
group default qlen 1000
link/ether 08:00:27:ec:75:5c brd ff:ff:ff:ff:ff:ff
inet 192.168.50.10/24 brd 192.168.50.255 scope global eth1
valid_lft forever preferred_lft forever
inet6 fe80::a00:27ff:feec:755c/64 scope link
valid_lft forever preferred_lft forever

La primera es la interfaz loopback. La segunda es la tarjeta de red con la que nos podemos
comunicar con nuestro equipo Windows y salir a internet (configurada en NAT). La tercera
tarjeta de red virtual es la que utilizamos para generar la red privada virtual (configurada en
Red Interna). Cuya IP, efectivamente, es: 192.168.50.10

La puerta de enlace

Página | 18
Vagrant utiliza SSH para que la máquina anfitriona se pueda comunicar desde la terminal con
los diferentes hosts que vaya creando. Podemos comprobar que nuestra llave pública (que
creó Vagrant) se encuentra en el fichero ‘authorized_keys’ del equipo remoto al que nos
queremos conectar.

Comprobación de la memoria RAM en MibiBytes

Vagrant crea la cuenta ‘vagrant’ con privilegios de root a través de sudo. Vagrant ha
configurado sudoers para que el usuario realice operaciones con sudo sin tener que
especificar ninguna contraseña. Comprobación:

vagrant@controlador:~$ groups
vagrant cdrom floppy sudo audio dip video plugdev netdev

Página | 19
Acceder al nodo a través de SSH (sin utilizar comandos de Vagrant)

Hostname…

Reconfigurar nuestras máquinas Virtuales con Vagrant


¡Ups! Dado que cometí un error al ponerle el mismo hostname (controlador) al nodo (en
realidad debería tener configurado el hostname como ‘nodo’). Vamos a ver cómo podemos
solucionar esta pequeña errata. Volvamos a nuestro Vagrantfile y editemos la línea 24.

Página | 20
Guardamos nuestro fichero y ejecutamos el siguiente comando para que Vagrant reconozca
la nueva configuración.

Emplear emuladores de terminales para agilizar el trabajo


Sería conveniente que nuestra terminal se pueda dividir en las partes que queramos para así
facilitarnos la tarea de realizar las diferentes conexiones SSH a nuestras máquinas virtuales.
Para ello tenemos los emuladores de terminales. En Linux podemos utilizar Tilix5, en
Windows podemos utilizar Cmder6.

Destruir una máquina virtual con Vagrant


Si quisiéramos destruir nuestras máquinas virtuales por alguna razón el comando que
tendríamos que ejecutar sería el siguiente (en el caso de querer eliminar el controlador):

5
Tilix
6
Cmder

Página | 21
También se puede especificar como parámetro el id asignado por Vagrant a la máquina
virtual. También se le puede pasar más de un parámetro al comando:

Para que Vagrant no nos pida confirmación al momento de eliminar las máquinas virtuales se
debe añadir la opción -f

Aprovisionar máquinas virtuales creadas con Vagrant con Shell


Scripts
La grandeza de Vagrant es que la podemos combinar con otras tecnologías como Shell
scripts, Puppet, Chef, Salt, Ansible o Docker. Dado que nuestro objetivo es que Ansible esté
instalado en el controlador para este ejemplo utilizaremos un Shell script para este cometido.

#!/bin/bash
sudo apt update
sudo apt install vim git python3 ansible -y

Lo guardaremos en la carpeta de nuestro proyecto como ‘controlador.sh’ y ahora sigamos


editando nuestro Vagrantfile para aprovisionar nuestro controlador.

Vagrant.configure("2") do |config|
# configuración general
config.vm.box = "debian/buster64"
# configuración controlador
config.vm.define "controlador" do |cont|
cont.vm.hostname = "controlador"
cont.vm.network "private_network", ip: "192.168.50.10",
virtualbox__intnet: "asir"
cont.vm.synced_folder ".", "/home/vagrant/proyecto", type: "smb"
cont.vm.provider "virtualbox" do |vb|
vb.name = "controlador(50.10)"
vb.cpus = 1
vb.memory = 512
end

Página | 22
cont.vm.provision "shell", path: "controlador.sh"
end
# configuración nodo
config.vm.define "nodo" do |nodo|
nodo.vm.hostname = "nodo"
nodo.vm.network "private_network", ip: "192.168.50.20",
virtualbox__intnet: "asir"
nodo.vm.provider "virtualbox" do |vb|
vb.name = "nodo(50.20)"
vb.cpus = 1
vb.memory = 512
end
end
end

Podemos aprovechar para crear otro shell script que actualice los repositorios del nodo y que
a su vez instale Python, que es un requisito para que Ansible pueda controlarlo (nodo.sh)

#!/bin/bash
sudo apt update
sudo apt install vim git python3 -y

Vagrant.configure("2") do |config|
# configuración general
config.vm.box = "debian/buster64"
# configuración controlador
config.vm.define "controlador" do |cont|
cont.vm.hostname = "controlador"
cont.vm.network "private_network", ip: "192.168.50.10",
virtualbox__intnet: "asir"
cont.vm.synced_folder ".", "/home/vagrant/proyecto", type: "smb"
cont.vm.provider "virtualbox" do |vb|
vb.name = "controlador(50.10)"
vb.cpus = 1
vb.memory = 512
end
cont.vm.provision "shell", path: "controlador.sh"
end
# configuración nodo
config.vm.define "nodo" do |nodo|
nodo.vm.hostname = "nodo"

Página | 23
nodo.vm.network "private_network", ip: "192.168.50.20",
virtualbox__intnet: "asir"
nodo.vm.provider "virtualbox" do |vb|
vb.name = "nodo(50.20)"
vb.cpus = 1
vb.memory = 512
end
nodo.vm.provision "shell", path: "nodo.sh"
end
end

Guardamos nuestro fichero y para empezar el aprovisionamiento de nuestras máquinas


ejecutamos el siguiente comando:

Vagrant empezará a actualizar los repositorios de las dos máquinas virtuales e instalará
Ansible en el controlador (junto a git y vim). Además, debe instalar la versión 3 de Python en
el nodo. Vamos a comprobarlo.

Página | 24
vagrant@controlador:~$ ansible --version
ansible 2.7.7
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/vagrant/.ansible/plugins/modules',
'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3/dist-packages/ansible
executable location = /usr/bin/ansible
python version = 3.7.3 (default, Dec 20 2019, 18:57:59) [GCC 8.3.0]
vagrant@controlador:~$

Comprobación de la instalación de Git

Comprobación de instalación de Vim

Comprobación de la actualización de los repositorios

Podríamos actualizar los paquetes con ‘sudo apt upgrade’ pero no nos hará falta para lo que
vamos a hacer. Salgamos de la sesión y comprobemos que todo esté bien en el nodo.

Página | 25
Vagrant junto a otras tecnologías
Vale hasta aquí ha sido comprobada la esencia de Vagrant. ¿De qué me sirve virtualizar de
forma automática dos, diez, veinte máquinas virtuales si no podré después automatizar el
aprovisionamiento? Pues para eso combinamos Vagrant con otras tecnologías. Si ya sabes
programar Shell scripts te sentirás muy cómodo escribiendo playbooks de Ansible. Con
Ansible puedes escribir scripts (playbooks) de forma sencilla sin tener que escribir Shell
scripts complejos que intenten satisfacer la misma tarea. Una vez aprovisionado todo nuestro
entorno podemos dejar del lado Vagrant para poder utilizar toda la fuerza que tiene Ansible.
¿Quieres configurar firewalls en 20 nodos con un solo script? ¿Quieres instalar servicios como
un stack completo de LAMP de forma sencilla en los nodos que haga falta? No hace falta que
te conectes manualmente a tus servidores uno a uno para actualizar al último parche de
seguridad. Todo eso lo puedes hacer desde un simple playbook o utilizando comandos ad-
hoc de Ansible.

SSH en Ansible
Ansible también utiliza SSH para realizar todas las tareas que queramos realizar. Por lo que
debemos generar nuestro par de llaves pública/privada y exportar nuestra llave pública a los
nodos que queramos controlar. Se puede utilizar contraseñas para una conexión SSH, pero
no es recomendable por temas de seguridad.7

Generar un par de llaves pública-privada en nuestro


controlador.

7
5 Linux SSH Security Best Practices To Secure Your Systems

Página | 26
La passphrase la dejaremos en blanco. RSA es el tipo de algoritmo que vamos a utilizar (se
podría utilizar otros). Se puede especificar el número de bits que utilizará el algoritmo para la
encriptación, pero con los parámetros del comando anterior son más que suficientes para
nuestra prueba. Nuestra llave privada generada se ha guardado en la ruta ~/.ssh/id_rsa.pub.
Es esta llave la que debemos exportar a nuestro nodo. Para ello utilizaremos el siguiente
comando:

Sin embargo, al intentar exportar nuestra llave pública nos topamos con un error en la salida
del comando.

vagrant@controlador:~$ ssh-copy-id 192.168.50.20


/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed:
"/home/vagrant/.ssh/id_rsa.pub"
The authenticity of host '192.168.50.20 (192.168.50.20)' can't be established.
ECDSA key fingerprint is
SHA256:DuOJ1cJ0Q6/7IDnVYI5Wh0SNwpsQP+RQAEEHVNybN3U.
Are you sure you want to continue connecting (yes/no)? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that
are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is
to install the new keys
vagrant@192.168.50.20: Permission denied (publickey).

Página | 27
Configurar el servidor SSH
Permiso denegado. Para solucionar esto debemos configurar antes el fichero de
configuración del servidor SSH del nodo. Cerremos la conexión y accedamos al nodo para
poder configurar el SSH.

Debemos configurar la última línea y cambiar la opción (PasswordAuthentication) a ‘yes’.


Guardemos el fichero.

Reiniciemos el servicio SSH (ssh o sshd en Debian, sshd en CentOs)

Exportar nuestra llave pública


Cerremos la conexión SSH y accedamos al controlador. Intentemos ejecutar el comando
antes fallido:

Página | 28
Ingresemos la contraseña del usuario con el que deseamos acceder a través de ssh (vagrant).

Date cuenta de que no hace falta escribir el comando ‘ssh-copy-id vagrant@192.168.50.20’


dado que el programa toma como cuenta de usuario la cuenta con la que he hecho login
(vagrant).
Comprobemos que podemos hacer SSH al nodo desde el controlador.

Desactivar las conexiones SSH por contraseña


Por seguridad deberíamos desactivar las conexiones SSH a través de contraseña en el nodo.

vagrant@nodo:~$ sudo vim /etc/ssh/sshd_config

Editamos la última opción y la cambiamos a por ‘no’


Reiniciamos el servicio SSH como lo hicimos antes. Ahora nadie podrá acceder al nodo si no
tiene una llave pública alojada en el fichero ‘authorized_keys’

Con todo esto preparado tenemos casi nuestro entorno preparado para poder realizar algunas
pruebas con Ansible. Si quisiéramos utilizar Ansible contra nuestro propio controlador
tendríamos que importar nuestra llave pública creada a nuestro fichero ‘authorized_keys’. Al
ejecutar el comando ‘ssh-copy-id 192.168.50.10’ nos toparemos otra vez con el error que
experimentamos con el nodo. Para solventar esto tendríamos que editar nuestro fichero de
configuración del servidor SSH, reiniciar el servicio SSH y volver a ejecutar el comando.

Página | 29
Ansible y el papel de los inventarios
Bien. Ya podemos empezar a utilizar Ansible. La forma con la que le decimos de la existencia
de nuestros nodos a Ansible es a través de un inventario (inventory). Este inventario es un
fichero sin formato en el que anotaremos los nodos (por dominio o IP o ambos). Podemos
crear grupos de nodos e inclusive almacenar variables relacionadas con los nodos o grupos.
Aquí pongo un ejemplo de un inventario.

[controlador]
192.168.50.10
[basesDeDatos]
192.168.50.20
192.168.50.30
[servidoresWeb]
192.168.50.40
192.168.50.50
[servidoresCorreo]
192.168.50.60
192.168.50.70
[debian:children]
controlador
basesDeDatos
basesDeDatos
[centos:children]
servidoresWeb
servidoresCorreo
[debian:vars]
ansible_user=vagrant
ansible_port=2222 # puerto personalizado para realizar la conexión SSH

Los nodos se agrupan a través de las cabeceras entre corchetes. Si quisiéramos añadir
variables a un nodo o a un grupo en específico habría que añadir :vars a la cabecera. Para
agrupar grupos utilizamos :children.

El fichero se encuentra en la siguiente ruta:

/etc/ansible/hosts

Sin embargo, podemos crear nuestro propio inventario y decirle explícitamente a Ansible que
lo utilice en vez del inventario general.
Vamos a crear nuestro propio inventario. Le podemos poner el nombre que queramos, pero
yo le llamaré hosts. Lo guardaré en el mismo directorio donde se encuentra nuestro
Vagrantfile.

Página | 30
La variable ‘ansible_user:vagrant’ especifica el usuario con el que queremos hacer la
conexión SSH.
Bien ahora necesitamos decirle a Ansible que utilice este inventario. Para ello utilizamos el
fichero de configuración que se encuentra en la ruta: /etc/ansible/ansible.cfg
Sin embargo, dado que estamos en un entorno de pruebas, dejemos ese fichero en paz y
creemos nuestro propio fichero de configuración. Este debe estar en el directorio de nuestro
proyecto con el nombre ansible.cfg

En el debemos escribir lo siguiente:

Página | 31
Utilizando comandos ad-hoc de Ansible
Si tenemos todo bien configurado hasta el momento (conexiones ssh y los ficheros hosts y
ansible.cfg) ya podemos utilizar comandos ad-hocs de ansible. Para comprobar la conexión
con nuestro nodo ejecutaremos el siguiente comando (ping):

Podemos utilizar el parámetro all para seleccionar todos los nodos que se encuentren en el
inventario (incluyendo el controlador)

Podemos utilizar IPs y una lista de IPs separándolos a través de comas.

Con -m le estamos diciendo a Ansible que vamos a utilizar un módulo y ping al nombre del
módulo.
Estos son ejemplos de algunos comandos ad-hocs:
Ver la memoria de todos los nodos (incluido en controlador)

Página | 32
Aquí estamos utilizando el módulo shell, que es eso, ejecutar comandos shell con Ansible.
con la opción -a especificamos los parámetros del módulo shell (free -h)

Obtener los hostnames de los nodos:

Como vemos no hace falta especificar -m shell. Ansible ya sabe que queremos ejecutar
comandos shell.

Instalar htop en ambos nodos:

vagrant@controlador:~/proyecto$ ansible all -b -m apt -a "name=htop state=present"

Con la opción -b (become) le decimos a Ansible que el siguiente comando lo ejecute con
privilegios de superusuario (sudo) y empleamos el módulo apt (gestor de paquetes de
Debian). Luego le pasamos los parámetros con -a. En ‘name’ especificamos el paquete que
queremos configurar (htop) y en ‘state’ especificamos qué queremos hacer con el paquete
(present para que se instale). Si htop estuviese instalado ya, podríamos cambiar el estado a
por ‘absent’ para desinstalarlo de ambos nodos. En la salida del comando veremos que el
programa ha sido instalado en ambos nodos. Comprobémoslo:

Vista desde el controlador (utilizando powershell):

Página | 33
Vista desde el nodo (utilizando Cmder)

Vamos viendo la esencia de Ansible. Podemos controlar a través de SSH diferentes nodos
según como hayamos configurado nuestro inventario. Esto nos elimina el trabajo engorroso
de conectarnos nodo por nodo para realizar nuestras operaciones en ellos. En nuestro caso
solo tenemos dos nodos configurados (controlador y nodo) pero esto podría no ser así y que
algún día tengamos que administrar una docena de servidores. Ansible reduce este trabajo
permitiéndonos administrarlos desde un solo equipo.

Volver a levantar nuestras máquinas virtuales con Vagrant


Otra cosa, un servidor en teoría nunca debería apagarse pues siempre debería estar a la
escucha de la petición de servicios. Aunque hasta ahora no hayamos configurado ningún
nodo para que preste servicios a otros equipos, supongamos que hemos decidido
apagar/reiniciar nuestro equipo real. Esto hará que nuestras máquinas virtuales creadas por
Vagrant dejen de estar operativas. Encendamos el equipo, abramos PowerShell y
comprobémoslo y vayamos al directorio de nuestro proyecto (D:\proyectoWindows) y
ejecutemos el siguiente comando.

Página | 34
El estado de nuestras máquinas es de ‘running’. Cualquiera pudiera decir de que están
activas. Pero si intentamos conectarnos a través de SSH…

Debemos volver a utilizar el comando ‘vagrant up’ para que nuestras máquinas estén
operativas otra vez.

Hemos avanzado ya cierto trecho de este proyecto. Ahora quedaría por ver la utilidad de los
playbooks (así se llaman los scripts en Ansible). Un playbook es un fichero con formato .yml
o .yaml escrito en lenguaje YAML. En dicho playbook se escriben ‘tasks’ o ‘plays’ que
básicamente son operaciones que se realizarán en nuestros nodos.

Automatización de Tareas con Ansible utilizando Playbooks


Para la realización del siguiente ejercicio con Ansible utilizaré un Manjaro como controlador y
desde él configuraremos dos nodos que serán dos Debian versión Buster. He creado un par
de claves pública-privada en mi Manjaro y desde nuestro fichero de configuración de Vagrant
exportaremos nuestra llave pública a cada uno de los Debian para poder controlarlos con
Ansible.

Página | 35
En este ejercicio veremos la automatización de creación de usuarios y grupos en dos nodos
distintos de dos empresas ficticias. Aparte de esto vamos a ver cómo podríamos subir
ficheros/directorios con la documentación de la empresa en cada carpeta personal de cada
usuario con los permisos correspondientes. Aquí un esquema con la información de los
grupos y usuarios de cada nodo:

Creemos un directorio para nuestro nuevo proyecto, nos posicionamos en su ruta. Ahora
creemos nuestro nuevo Vagrantfile:

# -*- mode: ruby -*-


# vi: set ft=ruby :

Vagrant.configure("2") do |config|
config.vm.box = "debian/buster64"
config.vm.synced_folder '.', '/vagrant', disabled: true
config.ssh.insert_key = false
config.ssh.forward_agent = true
config.ssh.private_key_path = ["~/.vagrant.d/insecure_private_key",
"~/.ssh/id_rsa"]
config.vm.provision "file", source: "~/.ssh/id_rsa.pub", destination:
"~/.ssh/authorized_keys"

config.vm.provider :virtualbox do |v|


v.memory = 256
v.cpus = 1
v.linked_clone = true
end

Página | 36
boxes = [
{ :name => "nodo1", :ip => "192.168.52.10" },
{ :name => "nodo2", :ip => "192.168.52.20" },
]

boxes.each do |opts|
config.vm.define opts[:name] do |config|
config.vm.hostname = opts[:name]
config.vm.network :private_network, ip: opts[:ip]

if opts[:name] == "nodo2"
config.vm.provision "ansible" do |ansible|
ansible.playbook = "playbooks/grupos_usuarios.yml"
ansible.inventory_path = "inventory"
ansible.limit = "nodos"
end
end
end
end
end

Explicaré algunas líneas nuevas de nuestro fichero:

config.vm.box = "debian/buster64"

Con esta línea le decimos a Ansible que nuestros nodos utilizarán un Debian versión Buster.
Recordemos que los boxes las encontramos en el catálogo de Vagrant Cloud. 8

config.vm.provision "file", source: "~/.ssh/id_rsa.pub", destination:


"~/.ssh/authorized_keys"

Esta línea nos permite exportar un fichero o directorio desde la máquina local hacia nuestros
nodos. En este caso exportando mi llave pública a cada uno de nuestros Debian.

boxes = [
{ :name => "nodo1", :ip => "192.168.52.10" },
{ :name => "nodo2", :ip => "192.168.52.20" },
]

8
Hashicorp Vagrant Cloud

Página | 37
A través de este array podemos crear los nodos que queramos y asignarle los atributos
correspondientes a cada nodo. Es una forma más limpia de crear nodos comparado con
nuestro primer ejercicio.

if opts[:name] == "nodo2"
config.vm.provision "ansible" do |ansible|
ansible.playbook = "playbooks/grupos_usuarios.yml"
ansible.inventory_path = "inventory"
ansible.limit = "nodos"
end
end

Aquí utilizamos un condicional para aprovisionar nuestros nodos con un playbook llamado
“grupos_usuarios.yml”. Hasta que nuestro último nodo no esté en funcionamiento Vagrant
no aprovisionará nada con Ansible. Es necesario decirle explícitamente a Vagrant la ruta
donde se encuentra nuestro inventario. La palabra clave ‘limit’ se emplea para decirle a
Vagrant a que nodo / grupo-de-nodos queremos que se aplique este aprovisionamiento.
En el mismo directorio de nuestro proyecto creamos un fichero nuestro inventario. Agrupamos
nuestros nodos en un grupo llamado ‘nodos’ y creamos una cabecera ‘nodos:vars’ para
almacenar variables relacionadas con nuestro grupo. Con la última variable le decimos a
Ansible la ruta de nuestra llave privada para hacer las conexiones SSH. Este fichero tendrá
el nombre de ‘inventory’.

[nodos]
192.168.52.10
192.168.52.20
[nodos:vars]
ansible_user=vagrant
ansible_ssh_private_key_file=~/.ssh/id_rsa

Ahora crearemos nuestro fichero de configuración ‘ansible.cfg’ donde le decimos a Ansible


la ruta de nuestro inventario.

[defaults]
inventory=inventory
deprecation_warnings=False

Ahora nos queda crear nuestro script de Ansible, conocido como playbook. Allí escribiremos
los ‘plays’ o ‘tasks’ que se realizarán en nuestros diferentes nodos para aprovisionarlos. Es
importante la forma en que usamos los espacios (o sangrías) en YAML9.

9
Yaml Tutorial | Learn YAML in 18 mins

Página | 38
---
- hosts: nodos
become: true
vars:
Nodo1Grupos: ["Gestion", "Marketing"]
Nodo2Grupos: ["Programadores", "Sistemas"]
Gestion: ['Ana','Maia','Lorenzo','Ricardo']
Marketing: ['Jose','Laura']
Programadores: ['Jon','Luis','Sara']
Sistemas: ['Alejandro','Oscar','Cristian']
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
tasks:
- name: crear Grupos nodo1
group:
state: present
name: "{{ item }}"
with_items: "{{ Nodo1Grupos }}"
when: ansible_facts.hostname == "nodo1"
- name: GESTION usuarios
user:
state: present
shell: /bin/bash
create_home: true
name: "{{ item }}"
group: Gestion
comment: Creado con Ansible
# la contraseña se puede crear con el programa mkpasswd
password:
$6$BsSaanpDOmQNTrw2$Krvyq7NhVe2qtghwJCcHBbuKpeij.eEdrSdLGN3Z6wSrSxEJ/0
u3eZ6JHGenbTXt8NCodmxxeChCv8HpFlds61
# hash de la contraseña luis (mkpasswd --method=SHA512)
with_items: "{{ Gestion }}"
when: ansible_facts.hostname == "nodo1"
- name: Marketing usuarios
user:
state: present
shell: /bin/bash
create_home: true
name: "{{ item }}"
group: Marketing
with_items: "{{ Marketing }}"
when: ansible_facts.hostname == "nodo1"

Página | 39
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
- name: crear Grupos nodo2
group:
state: present
name: "{{ item }}"
with_items: "{{ Nodo2Grupos }}"
when: ansible_facts.hostname == "nodo2"
- name: PROGRAMADORES usuarios
user:
state: present
shell: /bin/bash
create_home: true
name: "{{ item }}"
group: Programadores
with_items: "{{ Programadores }}"
when: ansible_facts.hostname == "nodo2"
- name: SISTEMAS usuarios
user:
state: present
shell: /bin/bash
create_home: true
name: "{{ item }}"
group: Sistemas
with_items: "{{ Sistemas }}"
when: ansible_facts.hostname == "nodo2"

---
Significa el inicio de un fichero escrito en YML.

- hosts: nodos
Especifica el nodo o grupo de nodos configurado en nuestro inventario.

vars:
Bloque donde se especificarán las variables. Para un playbook más limpio y organizado se
podría especificar un fichero externo que contenga dichas variables.
En este bloque estamos especificando una lista de grupos para cada nodo. Gestión y Marketing
para el nodo1 y Programadores y Administradores para el segundo nodo.

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
Las líneas comentadas empiezan con un # y Ansible no las tomará en cuenta.

Página | 40
tasks:
Con esta sección se definen las tareas que Ansible irá ejecutando en cada uno de los nodos. Se
podría usar roles de Ansible, que es la estructura de ficheros y directorios recomendada por
Ansible para tener así un playbook principal que llame a otros playbooks y obtener así un fichero
limpio y organizado. Esto es parecido a escribir un programa en cualquier lenguaje de
programación. Para tener un orden de nuestro programa lo escribimos en diferentes ficheros los
cuales llamaremos desde un fichero principal. Sin embargo, dado que estamos solamente en un
ejemplo, tengo todo el código en un solo fichero para facilitar el entendimiento del funcionamiento
de este playbook. No usaré roles.

- name: crear Grupos nodo1


El nombre del play es opcional, pero nos facilita la comprensión de qué hace nuestro play al
momento de leerlo. Este nombre aparecerá en la salida estándar (pantalla | terminal) al
momento de ejecutar dicha tarea.

group:
state: present
name: "{{ item }}"
with_items: "{{ Nodo1Grupos }}"
when: ansible_facts.hostname == "nodo1"

Group10 Es un módulo de Ansible para crear/modificar/eliminar grupos. Con ‘state: present’ le


decimos a Ansible que cree un grupo. El estado podría ser ‘absent’ para eliminarlos del nodo.
Con la opción ‘name’ especificamos el nombre del grupo. Si tuviéramos más de un grupo
almacenado en un array (como es este caso) hay que realizar iteraciones para que Ansible los
cree uno a uno. Con la palabra clave ‘with_items’ seleccionamos el array que contiene los grupos.
Utilizamos el condicional ‘when’ para que la tarea solo afecte al nodo uno. Empleamos
‘ansible_facts’ que es un array que que guarda información acerca de nuestro nodo.11

- name: GESTION usuarios

Empieza un nuevo play. Este creará los usuarios con el módulo user.12
Queda demostrado la singularidad de YAML. Es un lenguaje fácil de leer. No hace falta explicar
nada más acerca de lo que hace este playbook. Si quisiéramos encriptar nuestra contraseña
podríamos emplear Ansible Vault.13

“Ansible Vault encrypts variables and files so you can protect sensitive content such as passwords or keys rather
than leaving it visible as plaintext in playbooks or roles. To use Ansible Vault you need one or more passwords to
encrypt and decrypt content. If you store your vault passwords in a third-party tool such as a secret manager, you

10
Ansible.builtin.group – Add or remove groups
11
Discovering variables: facts and magic variables
12
Ansible.builtin.user – Manage user accounts
13
Ansible Vault

Página | 41
need a script to access them. Use the passwords with the ansible-vault command-line tool to create and view
encrypted variables, create encrypted files, encrypt existing files, or edit, re-key, or decrypt files. You can then
place encrypted content under source control and share it more safely.”

Bien, ahora solo nos queda incluir información acerca de la empresa en cada directorio ‘home’ de
cada usuario. Podríamos incluir esta tarea en este mismo playbook, añadiendo más líneas. Pero
lo haré de otra forma más adelante. Aquí un vistazo de la estructura de nuestro proyecto.

‘info_empleados.yml’ es otro playbook que utilizaremos después. Los otros ficheros con relación
a la información a la empresa se utilizarán también en el siguiente playbook.
Para levantar toda nuestra infraestructura y crear los usuarios y grupos con tan solo un comando
ejecutaremos lo siguiente:

Página | 42
Página | 43
Página | 44
Página | 45
Nos quedaría aprovisionar los nodos con la información de los empleados en el directorio personal
de cada usuario. Para ello vamos a crear un nuevo playbook:

---
- hosts: nodos
become: true
vars:
Gestion: ['Ana','Maia','Lorenzo','Ricardo']
Marketing: ['Jose','Laura']
Programadores: ['Jon','Luis','Sara']
Sistemas: ['Alejandro','Oscar','Cristian']
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
tasks:
- name: Copiar información de los trabajadores al grupo Gestion
copy:
src: "../files/"
dest: "/home/{{ item }}"
mode: 0644
owner: "{{ item }}"
group: Gestion
with_items: "{{ Gestion }}"
when: ansible_facts.hostname == "nodo1"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
- name: Copiar información de los trabajadores al grupo Marketing
copy:
src: "../files/"
dest: "/home/{{ item }}"
mode: 0644
owner: "{{ item }}"
group: Marketing
with_items: "{{ Marketing }}"
when: ansible_facts.hostname == "nodo1"

Solo aprovisionaré al nodo uno. Para aprovisionar el segundo con Ansible solo bastaría con editar
este playbook (copiar y pegar) y cambiar los valores correspondientes en los grupos y usuarios.

[luis@luis-manjaro grupos_usuarios]$ ansible-playbook playbooks/info_empleados.yml

Página | 46
Página | 47
Playbook alternativo para crear usuarios y grupos en diferentes
equipos
Este playbook hace la misma función de crear usuarios y grupos, pero sin declarar variables.

---
- hosts: all
become: true
tasks:
- name: crear grupos
group:
name: "{{ item }}"
state: present
with_items:
- programadores
- administradores
when: ansible_facts.hostname == "nodo"
- name: crear usuarios programadores
user:
state: present
shell: /bin/bash
group: programadores
name: "{{ item }}"

Página | 48
with_items:
- Sofia
- Carla
- Rodolfo
when: ansible_facts.hostname == "nodo"
- name: crear usuarios administradores
user:
state: present
shell: /bin/bash
group: administradores
name: "{{ item }}"
with_items:
- Ricardo
- Jose
- Ansible
when: ansible_facts.hostname == "nodo"

Página | 49
Las líneas en azul (skipping) son nodos a los que el task no le afecta.

Configuración de ~/.ssh/config y /etc/hosts


Escribir repetitivamente ‘ssh vagrant@192.168.52.10’ o ‘vagrant@192.168.52.20’ sobre la
consola puede lograr a ser un fastidio. Podemos decirle a nuestro cliente SSH que cada vez que
nos conectemos a nuestros nodos lo haga con el usuario vagrant sin tener que escribirlo en la
consola:

Página | 50
El resultado:

Página | 51
Servidor NFS y Cliente
En este ejercicio vamos a configurar un nodo para que sirva de servidor NFS. Un servidor NFS
nos permite compartir ficheros a través de la red. Configuraré dos nodos CentOS, uno funcionará
como servidor NFS y el otro como cliente. En esta práctica veremos el funcionamiento de nuevos
módulos de Ansible.

En nuestro primer nodo instalaremos los paquetes que sean necesarios para que funcione como
servidor NFS, crearemos el directorio a compartir y le asignaremos los permisos pertinentes,
activaremos los servicios que hagan falta para el funcionamiento de esta práctica, copiaremos el
archivo de configuración NFS desde nuestro equipo local al servidor. Una vez la configuración
esté realizada reiniciaremos el servicio.
En nuestro segundo nodo habrá que instalar los paquetes que hagan falta para que le permitan
dar uso de un servidor NFS, crearemos un punto de montaje de un directorio que utilizará para
acceder a los recursos compartidos y lo montaremos en el sistema.

Si quisiéramos eliminar los nodos anteriores:

Página | 52
Creamos un nuevo directorio para nuestro proyecto y nos posicionamos en él.

Creamos nuestro Vagrantfile

El código:

# -*- mode: ruby -*-


# vi: set ft=ruby :
Vagrant.configure("2") do |config|
config.vm.box = "centos/7"
config.vm.synced_folder '.', '/vagrant', disabled: true
config.ssh.insert_key = false
config.ssh.forward_agent = true
config.ssh.private_key_path = ["~/.vagrant.d/insecure_private_key",
"~/.ssh/id_rsa"]
config.vm.provision "file", source: "~/.ssh/id_rsa.pub",
destination: "~/.ssh/authorized_keys"

config.vm.provider :virtualbox do |v|


v.memory = 256
v.cpus = 1
v.linked_clone = true
end

boxes = [
{ :name => "nodo1", :ip => "192.168.52.10" },
{ :name => "nodo2", :ip => "192.168.52.20" },
]

boxes.each do |opts|

Página | 53
config.vm.define opts[:name] do |config|
config.vm.hostname = opts[:name]
config.vm.network :private_network, ip: opts[:ip]
end
end
end

Levantamos las máquinas virtuales

Una vez levantadas intentemos acceder a los nodos a través de SSH y…

Actualizar las Host Keys de cada nodo


Los Host Keys de cada nodo han cambiado (la llave pública de cada máquina virtual es distinta,
aunque tengan el mismo hostname e IP) y el cliente SSH nos advierte que los fingerprints han
cambiado. Por lo tanto, podríamos estar accediendo a un equipo remoto distinto (que es cierto).
Para solucionar este problema actualicemos los fingerprints y eliminemos las que ya no nos sirven
(las del ejercicio anterior).

Página | 54
[luis@luis-manjaro nfs_servidor_cliente]$ ssh-keygen -R nodo1
# Host nodo1 found: line 3
/home/luis/.ssh/known_hosts updated.
Original contents retained as /home/luis/.ssh/known_hosts.old

[luis@luis-manjaro nfs_servidor_cliente]$ ssh-keygen -R nodo2


# Host nodo2 found: line 3
/home/luis/.ssh/known_hosts updated.
Original contents retained as /home/luis/.ssh/known_hosts.old

Intentémoslo otra vez…

[luis@luis-manjaro nfs_servidor_cliente]$ ssh nodo1

[luis@luis-manjaro nfs_servidor_cliente]$ ssh nodo2

Página | 55
Así quedaría nuestro inventario. Nodo1 se podría cambiar por ServidorNFS y nodo2 como
ClienteNFS.

Fichero de configuración de este proyecto:

Creamos nuestro playbook que montará nuestro servidor NFS

---
- hosts: nodo1
become: true
tasks:
- name: Instalar Nfs-utils
yum:
name: nfs-utils
state: latest
- name: Crear directorio a compartir
file:
path: /home/vagrant/nfs_test
state: directory
owner: vagrant
group: vagrant
mode: '0775'
- name: Habilitar los servicios rpcbind nfslock nfs
service:
name: "{{ item }}"
enabled: true
with_items:
- rpcbind
- nfslock
- nfs
- name: Exportar fichero exports.j2
template:
src: exports.j2 # localizado en el directorio ./templates del
proyecto
dest: /etc/exports
owner: root
group: root
mode: '0644'
- name: Aplicar cambios NFS
shell: systemctl restart nfs; exportfs -a

Página | 56
Creamos un playbook que configure el cliente NFS

---
- hosts: nodo2
become: true
tasks:
- name: Instalar Nfs-utils
yum:
name: nfs-utils
state: latest
- name: Crear directorio que se sincronizará con el Servidor
file:
path: /mnt/compartido
state: directory
owner: vagrant
group: vagrant
mode: '0755'
- name: Montar el directorio del nodo1 en el nodo2
shell: mount 192.168.52.10:/home/vagrant/nfs_test
/mnt/compartido

Este es el contenido del fichero template.j2 que se encuentra alojado en un directorio llamado
‘templates’ dentro de nuestro proyecto. Es fichero de configuración del servidor NFS que se va
alojar en la ruta: /etc/exports

/home/vagrant/nfs_test 192.168.52.0/24(rw,sync,no_root_squash)

Contenido del Proyecto nfs_servidor_cliente:

Página | 57
Levantemos nuestras máquinas:

Establecer la conexión SSH en cada nodo antes de ejecutar nuestros playbooks

Ejecutamos el playbook que afecta solo al servidor

Página | 58
Ejecutamos el playbook que solo afecta al cliente

Página | 59
Comprobaciones:

Generemos ficheros

Cerremos sesión y conectémonos al cliente

Servidor NTP con Ansible


Nework Time Protocol (NTP) es un protocolo de internet para sincronizar los relojes de los
sistemas informáticos a través del enrutamiento de paquetes con latencia variable.

Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

API_VERSION = "2"
BOX_NAME = "centos/7"
BOX_IP = "192.168.50.4"
DOMAIN = "nip.io"
PRIVATE_KEY = "~/.ssh/id_rsa"
PUBLIC_KEY = '~/.ssh/id_rsa.pub'

Vagrant.configure(API_VERSION) do |config|
config.vm.box = BOX_NAME
config.vm.network "private_network", ip: BOX_IP
config.vm.host_name = BOX_IP + '.' + DOMAIN
config.ssh.insert_key = false

Página | 60
config.ssh.private_key_path = [PRIVATE_KEY, "~/.vagrant.d/insecure_private
_key"]
config.vm.provision "file", source: PUBLIC_KEY, destination: "~/.ssh/autho
rized_keys"

config.vm.provider "virtualbox" do |v|


v.memory = "2024"
v.cpus = "2"
end

config.vm.provider "vmware_fusion" do |v|


v.vmx["memsize"] = "2024"
v.vmx["numvcpus"] = "2"
end

end

Inventario
[boxes]
192.168.50.4
[boxes:vars]
ansible_connection=ssh
ansible_user=vagrant
ansible_private_key_file=~/.ssh/id_rsa

Configuración NTP
# {{ ansible_managed }}
driftfile /var/lib/ntp/drift
restrict default nomodify notrap nopeer noquery
restrict 127.0.0.1
restrict ::1
{% for item in ntp_servers %}
server {{ item }} iburst
{% endfor %}
includefile /etc/ntp/crypto/pw
keys /etc/ntp/keys
disable monitor

Página | 61
Playbook
---

- hosts: boxes
gather_facts: true
become: true
become_method: sudo

vars:
ntp_servers:
- "0.centos.pool.ntp.org"
- "1.centos.pool.ntp.org"
- "2.centos.pool.ntp.org"
- "3.centos.pool.ntp.org"

handlers:
- name: reiniciar ntp
service:
name: ntpd
state: restarted

tasks:
- debug:
msg: "Me estoy conectando a {{ ansible_nodename }} el cual
corre sobre {{ ansible_distribution }} {{
ansible_distribution_version }}"
- yum:
name: ["ntp","ntpdate"]
state: installed
- template:
src: ./ntp.conf.j2
dest: /etc/ntp.conf
notify: reinciar ntp

Comprobación:

Página | 62
Con la opción -i indicamos explícitamente la ruta del fichero inventario (hosts) que vamos a utilizar.

Los handlers se utilizan para lanzar tareas al final de un playbook. Sin embargo, necesitan una
condición para ejecutarse: que haya habido algún cambio en el nodo (instalación de algún
paquete/servicio, creación de usuarios/grupos, subir o eliminar algún fichero). Empleamos la
palabra clave ‘notify’ más el nombre del ‘handler’ para llamarlo.

Página | 63
Conclusiones
Más que un proyecto, esto ha sido, relativamente, un manual del funcionamiento de Vagrant y
Ansible donde he puesto en práctica casi todo el conocimiento que he adquirido sobre estas dos
tecnologías a través de determinados scripts. He expuesto la solución a alguno de los problemas
con los que me iba topando en el curso de la realización de este trabajo. No puedes aprender sin
antes equivocarte y es por eso que resaltar mis errores -o mi ignorancia en aquel entonces- me
parece algo sumamente importante.
Ansible es una tecnología que se demanda cada vez más en el sector IT de entorno GNU Linux y
es de obligatorio conocimiento si deseas obtener algún certificado Linux de Red Hat14.
Gracias a Ansible he reforzado conocimientos de Seguridad a través del empleo de protocolos
como SSH, la administración de sistemas GNU-Linux con sus módulos correspondientes, la
programación de scripts para el despliegue de una infraestructura de servidores-clientes y la
gestión de los mismos con playbooks de Ansible, modificando el comportamiento de la
ejecución del código en función del valor de determinadas variables.
En GitHub podemos encontrar muchos repositorios con proyectos de Ansible muy útiles como
por ejemplo levantar un LAMP stack o un Wordpress con un simple comando.
Aunque en este proyecto me haya centrado en nodos Linux también existen módulos de
Ansible para controlar nodos Windows.
En cuanto Vagrant está bien para el desarrollo de un entorno de pruebas, pero para un entorno
más real se debe emplear otras tecnologías -dado las limitaciones de VirtualBox- como la nube
que nos ofrece DigitalOcean.
Vuelvo a enfatizar la importancia de Ansible. No hay casi ningún libro sobre entornos GNU Linux
moderno que no le dediquen al menos unas páginas dada su relevancia que tiene hoy en día.

14
Ofertas de trabajo que requieren Ansible

Página | 64

También podría gustarte