Usando ansible-playbook de una manera organizada para desplegar y aprovisionar

Para los que no conocen, Ansible es una herramienta que permite automatizar por medio de tareas declaradas, los despliegues y aprovisionamientos de servicios o aplicaciones en nuestros servidores o cluster, conectándose a ellos a través del protocolo SSH. Si no se entiende, la idea principal de la aplicación es que al momento de tener instalaciones limpias del sistema operativo, usando Ansible podremos actualizar el sistema, instalar paquetes y aplicaciones necesarias e incluso reiniciar servicios y agregar usuarios.

Ahora bien, en este sencillo tutorial, explicare como usar Ansible-Playbook de una manera organizada por lo que usare como ejemplo el compilar Nginx 1.9 en un servidor con CentOS 7 desde un equipo cliente con Fedora 21 (Si es lo que uso para el trabajo). 

Ansible esta escrito en python, por lo que podremos instalarlo utilizando pip o easy_install, también es posible instalarlo con el manejador de paquetes para Fedora conocido como YUM, previamente agregando los repositorio EPEL, también se deberá tener instalados los paquetes rpm-buildmakepython2-devel, pero en mi caso, instalaremos Ansible utilizando PIP, por lo que ejecutaremos en la línea de comandos lo siguiente:

$ pip install paramiko PyYAML Jinja2 httplib2 ansible

Recuerden que si es una instalación en limpio de Fedora 21 en la que instalaran Ansible, deberán tener instalado las Development Tools, y luego instalar el paquete python-pip desde el repositorio EPEL, lo pasos a seguir serían:

$ sudo yum group install "Development Tools" -y
$ sudo rpm -ivh http://mirror.globo.com/epel/7/x86_64/e/epel-release-7-5.noarch.rpm
$ sudo yum install python-pip -y

Comprobamos que se han instalado correctamente ansible y ansible-playbook, ejecutando en la terminal:

# ansible --version
ansible-playbook 1.9.1
configured module search path = None
# ansible-playbook --version
ansible 1.9.1
configured module search path = None

Llegado a este punto, y debido a que Ansible se conectará a los múltiples servidores que existan en nuestra infraestructura es necesario explicar que es Inventory (Inventario) y Playbook.

El Inventory o Inventario no es mas que un archivo donde indicamos las direcciones IP de los servidores en los que vamos a desplegar y aprovisionar nuestros servicios y aplicaciones, por defecto este archivo se encuentra en /etc/ansible/hosts. Puedes encontrar información detalla en la documentación oficial de Ansible: Inventory.

Los Playbook de Ansible son la base para la gestión de la configuración utilizando archivos y directorios que permiten organizar y orquestar una serie de tareas en los servidores , ya sea desde instalar un paquete del sistema o clonar un repositorio de Bitbucket. Puedes encontrar información extendida en la documentación oficial de Ansible: Playbook. YAML es el lenguaje utilizado para declarar la politica de procedimientos y/o tareas.

Conocido lo anterior, es hora de organizar nuestro directorio con los distintos archivos que contendrán las configuraciones y tareas que permitirán compilar Nginx 1.9 en un servidor con CentOS7 (es nuestro ejemplo a usar). Los archivos y directorios quedarán organizados de la siguiente manera:

# tree
.
├── inventory
│   └── production
├── README.md
├── roles
│   ├── common
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       └── hosts.j2
│   └── nginx
│       ├── files
│       │   └── nginx.service
│       ├── handlers
│       │   └── main.yml
│       ├── tasks
│       │   └── main.yml
│       ├── templates
│       │   ├── default.conf.j2
│       │   ├── nginx.conf.j2
│       │   └── virtualhost.conf.j2
│       └── vars
│           └── main.yml
└── site.yml

11 directories, 12 files

El archivo site.yml es el archivo principal que contendrá los elementos que componen nuestro Playbook, como por ejemplo, las variables, los hosts e incluso los roles. Los Roles en Ansible es una forma de cargar automáticamente variables, tareas y controladores basados en una estructura de archivos que permite el intercambio de roles con otros usuarios y organizar de una mejor manera los Playbook. Encontraras mas información en la documentación oficial de Ansible: Roles. El archivo site.yml esta constituido de la siguiente manera:

# File: site.yml
# Usage:
# ansible-playbook -i iventory/production site.yml

---
#Deploy:
- name: Provisioning instances
  hosts: Production
  sudo: no
  remote_user: root
  vars:
    - hostname_name: "localhost"
    - domain: "localhost"
    - remote_user: "root"
    - directory_build_nginx: "/root/nginx-build"
    - directory_webapp: "/home/"
    - version_nginx: 1.9.0
    - version_ngxspeed: 1.9.32.3
  roles:
    - common
    - nginx

Como se puede ver en el archivo site.yml se detallan todas las variables que usaran los roles de nuestro Playbook y desde la variable hosts invocamos la lista de servidores o instancias al que se conectará y ejecutaran las tareas.

El directorio inventory tiene un archivo al que llamamos production que contiene todas las direcciones IP de los servidores en donde serán ejecutados los aprovisionamientos de aplicaciones y/o servicios, internamente esta constituido de la siguiente manera:

# File: inventory/production
[Production]
192.168.1.110 ansible_ssh_host=192.168.1.110 ansible_ssh_port=22 ansible_ssh_private_key_file=~/.ssh/id_rsa

En este archivo indicamos la dirección IP (192.168.1.110) del servidor con la instalación de CentOS7, el puerto SSH y la private key con la que conectaremos al servidor en cuestión. Es de mencionar que antes de ejecutar los Playbook debemos agregar nuestra llave RSA publica a los servidores, esto lo logramos ejecutando en la terminal lo siguiente:

$ ssh-copy-id ansible @ 192.168.1.110

El usuario ansible indicado es con el que normalmente accedemos al servidor.

El directorio roles contiene las demás tareas y variables para preparar y compilar Nginx 1.9 en CentOS7 (recuerden es nuestro ejemplo para este tutorial). Solamente explicare el rol que permitirá ejecutar tareas para compilar Nginx 1.9 y esta formado por la siguiente estructura:

$ tree roles/nginx
roles/nginx
├── files
│   └── nginx.service
├── handlers
│   └── main.yml
├── tasks
│   └── main.yml
├── templates
│   ├── default.conf.j2
│   ├── nginx.conf.j2
│   └── virtualhost.conf.j2
└── vars
    └── main.yml

5 directories, 7 files

Directorios vars, tasks y handlers

El comportamiento para los directorios vars/, tasks/, handlers/, es que si existe el archivo main.yml se ejecutaran las tareas que están declaradas en esos archivos. Los directorios vars/ y handlers/, contendrán las variables para ese rol determinado y los servicios que se ejecutarán en una tarea declara, como por ejemplo reiniciar el servicio de Nginx. El mas importante de todos, es el archivo main.yml del directorio tasks/, en ese archivo están declaradas todas los tareas que serán ejecutadas para compilar e instalar correctamente Nginx.

Directorios files y templates

Para los archivos en los directorios files y templates , serán copiados al servidor, en este ejemplo, el archivo nginx.service es copiado en el directorio /lib/systemd/system usado por Systemd para controlar el servicio de Nginx. Los archivos contenidos en templates/ serán copiados en el directorio correspondiente a la configuración de Nginx, la diferencia entre los archivos que están en el directorio files/ y templates/: los que están en el directorio templates/ con extensión .j2, contienen una variable a reemplazar que al momento de ser copiados al servidor, se hace con el valor de la variable del archivo site.yml o del archivo main.yml del directorio vars/ en ese rol determinado.

¿Como declaro una tarea?

Las tareas se declaran utilizando la etiqueta name y luego el modulo correspondiente a la tarea a ejecutar. Los Modulos en Ansible son librerías que permiten simplificar tareas para controlar servicios, crear y copiar ficheros, instalar paquetes o ejecutar comandos en el servidor, por ejemplo, con el modulo get_url podremos obtener un archivo desde un servidor web, con el modulo shell podremos ejecutar el comando chmod del servidor y cambiar los permisos en un directorio cualquiera. Se puede encontrar mas información en la documentación de Ansible: Modules .

Existe también un listado de modulos en Ansible, organizados por categoría y que se puede usar para cualquier tarea a realizar desde crear instancias en AWS Ec2 , hasta instalar una paquete o modulo de NodeJS usando NPM.

Como ejemplo tenemos parte del archivo main.yml donde se declaran 3 tareas a ejecutar:

---
- name: Install packages necessary to build Nginx
  yum: name={{ item }} state=installed
  with_items:
    - gcc
    - gcc-c++

- name: Create a directory to build Nginx
  file: state=directory path={{ directory_build_nginx }}

- name: Download Ngx Pagespeed sources
  get_url: url=https://github.com/pagespeed/ngx_pagespeed/archive/release-{{ version_ngxspeed }}-beta.zip dest={{ directory_build_nginx }}/release-{{ version_ngxspeed }}-beta.zip

En la primera tarea a ejecutar al que llamamos - name: "Install packages necessary to build Nginx", esta tarea invoca al modulo YUM que permite instalar los paquetes gcc y gcc-c++ . Para la segunda tarea se invoca al modulo file que creará un directorio usando la variable declarada en el archivo main.yml del directorio vars/. La tercera tarea llamada - name: Download Ngx Pagespeed sources llama al modulo get_url y permite descargar un archivo .zip desde el servidor de Github.com sustituyendo los valores de las variables declaradas en el archivo principal site.yml y main.yml del directorio vars/.

Una vez que tenemos organizado todos los archivos y directorios es hora de ejecutar nuestro Playbook. Utilizaremos el comando ansible-playbook , este comando lee cada una de las tareas y variables declaradas en los archivos con extensión .yml y ejecuta los módulos, es posible pasarle argumentos desde donde declaramos las acciones a ejecutar. En la línea de comandos ejecutamos:

$ ansible-playbook -i inventory/production site.yml

Al comando anterior le hemos pasado el argumento -i donde le indicamos que el archivo que contiene las direcciones IP de los servidores a los que se conectara y ejecutara cada una de las tareas se encuentra en la ubicación inventory/production . La salida al ejecutar el comando sería parecido a lo siguiente:

PLAY [Provisioning instances] ************************************************* 

GATHERING FACTS *************************************************************** 
ok: [192.168.1.100]

TASK: [common | Change hostname] ********************************************** 
ok: [192.168.1.100]

TASK: [common | Copy hosts file] ***************************************************
ok: [192.168.1.100]

TASK: [nginx | Install packages necessary to build Nginx] *************************
ok: [192.168.1.100]

TASK: [nginx | Create a directory to build Nginx] ************************************
ok: [192.168.1.100]

PLAY RECAP ********************************************************************  
192.168.1.100            : ok=5    changed=0    unreachable=0    failed=0

Al finalizar la ejecución de todas las tareas encontraremos información sobre cuales de ellas se han ejecutado correctamente y cuales han fallado. Un dato adicional, Si agregamos como argumento -vvvv cambiamos el nivel de verbose al ejecutar el comando.

El ejemplo que he utilizado es actualmente un Ansible-Playbook que he construido y que nos permitirá compilar Nginx 1.9 con el modulo de soporte a Google PageSpeed en un servidor con CentOS7, puedes encontrar el repositorio en mi cuenta de Github.com.

Hasta acá este pequeño y mini tutorial, sobre una de las herramientas que sé que ha ayudado a muchos a reducir tiempos al momento de desplegar y aprovisionar servicios y aplicaciones en grandes infraestructuras.

Bien, si tienes una duda sobre esta herramienta publicala en la caja de comentarios.