Introducción a la Gestión de Configuración: Escribiendo Playbooks de Ansible

Introducción

En pocas palabras, la gestión de la configuración del servidor (también conocida popularmente como Automatización de TI) es una solución para convertir la administración de su infraestructura en un código base, describiendo todos los procesos necesarios para implementar un servidor en un conjunto de scripts de aprovisionamiento que pueden ser versionados y reutilizados fácilmente. Puede mejorar enormemente la integridad de cualquier infraestructura de servidor con el tiempo.

En una guía anterior, hablamos sobre los principales beneficios de implementar una estrategia de gestión de configuración para su infraestructura de servidor, cómo funcionan las herramientas de gestión de configuración y qué tienen en común estas herramientas.

Esta parte de la serie le guiará a través del proceso de automatización del aprovisionamiento del servidor usando Ansible, una herramienta de gestión de configuración que proporciona un marco de automatización completo y capacidades de orquestación, manteniendo el objetivo de máxima simplicidad y minimalismo. Nos centraremos en la terminología del lenguaje, la sintaxis y las características necesarias para crear un ejemplo simplificado para automatizar completamente la implementación de un servidor web Ubuntu 18.04 utilizando Apache.

La siguiente lista contiene todos los pasos que necesitamos automatizar para alcanzar nuestro objetivo:

  1. Actualizar la caché de apt
  2. Instalar Apache
  3. Crear un directorio raíz de documentos personalizado
  4. Coloque un archivo index.html en la raíz de documentos personalizada
  5. Aplique una plantilla para configurar nuestro host virtual personalizado
  6. Reinicie Apache

Comenzaremos echando un vistazo a la terminología utilizada por Ansible, seguido de una visión general de las principales características del lenguaje que se pueden utilizar para escribir playbooks. Al final de la guía, encontrarás el contenido de un ejemplo completo de aprovisionamiento para automatizar los pasos descritos para configurar Apache en Ubuntu 18.04.

Nota: esta guía tiene la intención de familiarizarte con el lenguaje de Ansible y cómo escribir playbooks para automatizar la aprovisionamiento de tu servidor. Para obtener una visión más introductoria de Ansible, que incluye los pasos necesarios para instalar y comenzar a usar esta herramienta, así como cómo ejecutar comandos y playbooks de Ansible, consulta nuestra guía Cómo Instalar y Configurar Ansible en Ubuntu 18.04.

Empezando

Antes de pasar a una vista más práctica de Ansible, es importante que nos familiaricemos con la terminología y los conceptos importantes introducidos por esta herramienta.

Terminología

La siguiente lista contiene un resumen rápido de los términos más relevantes utilizados por Ansible:

  • Nodo de Control: la máquina donde está instalado Ansible, responsable de ejecutar la aprovisionamiento en los servidores que estás gestionando.
  • Inventario: un archivo INI que contiene información sobre los servidores que estás gestionando.
  • Playbook: un archivo YAML que contiene una serie de procedimientos que deben ser automatizados.
  • Tarea: un bloque que define un solo procedimiento a ser ejecutado, por ejemplo: instalar un paquete.
  • Módulo: un módulo típicamente abstrae una tarea del sistema, como tratar con paquetes o crear y cambiar archivos. Ansible tiene una multitud de módulos integrados, pero también puedes crear personalizados.
  • Rol: un conjunto de playbooks relacionados, plantillas y otros archivos, organizados de una manera predefinida para facilitar su reutilización y compartir.
  • Play: una aprovisionamiento ejecutada de principio a fin se llama un play.
  • Hechos: variables globales que contienen información sobre el sistema, como interfaces de red o sistema operativo.
  • Manejadores: se utilizan para activar cambios en el estado del servicio, como reiniciar o recargar un servicio.

Formato de Tarea

A task defines a single automated step that should be executed by Ansible. It typically involves the usage of a module or the execution of a raw command. This is how a task looks:

- name: This is a task
  apt: name=vim state=latest

La parte name es en realidad opcional, pero recomendada, ya que aparece en la salida del aprovisionamiento cuando se ejecuta la tarea. La parte apt es un módulo integrado de Ansible que abstrae la gestión de paquetes en distribuciones basadas en Debian. Esta tarea de ejemplo le indica a Ansible que el paquete vim debería tener su estado cambiado a latest, lo que hará que el gestor de paquetes instale este paquete en caso de que aún no esté instalado.

Formato del Playbook

Los Playbooks son archivos YAML que contienen una serie de directivas para automatizar el aprovisionamiento de un servidor. El siguiente ejemplo es un playbook simple que realiza dos tareas: actualiza la caché de apt e instala vim después:

---
- hosts: all
  become: true
  tasks:
     - name: Update apt-cache 
       apt: update_cache=yes

     - name: Install Vim
       apt: name=vim state=latest

YAML se basa en la indentación para serializar estructuras de datos. Por esa razón, al escribir playbooks y especialmente al copiar ejemplos, debes tener cuidado extra para mantener la indentación correcta.

Antes del final de esta guía veremos un ejemplo más realista de un playbook, explicado en detalle. La próxima sección te dará una visión general de los elementos y características más importantes que se pueden utilizar para escribir Playbooks de Ansible.

Escribiendo Playbooks

Ahora que estás familiarizado con la terminología básica y el formato general de los libros de jugadas y tareas en Ansible, aprenderemos sobre algunas características de los libros de jugadas que pueden ayudarnos a crear automatizaciones más versátiles.

Trabajando con Variables

Existen diferentes formas en las que puedes definir variables en Ansible. La forma más simple es usando la sección vars de un libro de jugadas. El ejemplo a continuación define una variable package que luego se utiliza dentro de una tarea:

---
- hosts: all
  become: true
  vars:
     package: vim
  tasks:
     - name: Install Package
       apt: name={{ package }} state=latest

La variable package tiene un ámbito global, lo que significa que se puede acceder desde cualquier punto del aprovisionamiento, incluso desde archivos incluidos y plantillas.

Usando Bucles

Los bucles se utilizan típicamente para repetir una tarea usando diferentes valores de entrada. Por ejemplo, en lugar de crear 10 tareas para instalar 10 paquetes diferentes, puedes crear una sola tarea y usar un bucle para repetir la tarea con todos los paquetes diferentes que desees instalar.

Para crear un bucle dentro de una tarea, incluye la opción with_items con un array de valores. El contenido se puede acceder a través de la variable de bucle item, como se muestra en el ejemplo a continuación:

- name: Install Packages
  apt: name={{ item }} state=latest
  with_items:
     - vim
     - git
     - curl  

También puedes usar una variable de array para definir tus elementos:

---
- hosts: all
  become: true
  vars:
     packages: [ 'vim', 'git', 'curl' ]
  tasks:
     - name: Install Package
       apt: name={{ item }} state=latest
       with_items: "{{ packages }}"

Usando Condicionales

Los condicionales pueden ser utilizados para decidir dinámicamente si una tarea debe ejecutarse o no, basándose en una variable o en una salida de un comando, por ejemplo.

El siguiente ejemplo solo apagará los sistemas basados en Debian:

- name: Shutdown Debian Based Systems
  command: /sbin/shutdown -t now
  when: ansible_os_family == "Debian"

El condicional when recibe como argumento una expresión a evaluar. La tarea solo se ejecuta en caso de que la expresión se evalúe como true. En nuestro ejemplo, probamos un dato para verificar si el sistema operativo pertenece a la familia Debian.

A common use case for conditionals in IT automation is when the execution of a task depends on the output of a command. With Ansible, the way we implement this is by registering a variable to hold the results of a command execution, and then testing this variable in a subsequent task. We can test for the command’s exit status (if failed or successful). We can also check for specific contents inside the output, although this might require the usage of regex expressions and string parsing commands.

El siguiente ejemplo muestra dos tareas condicionales basadas en la salida de un comando php -v. Probaremos el estado de salida del comando, ya que sabemos que fallará al ejecutarse en caso de que PHP no esté instalado en este servidor. La parte ignore_errors de la tarea es importante para asegurarse de que el aprovisionamiento continúe incluso cuando el comando falle en la ejecución.

- name: Check if PHP is installed
  register: php_installed
  command: php -v
  ignore_errors: true

- name: This task is only executed if PHP is installed
  debug: var=php_install
  when: php_installed|success
  
- name: This task is only executed if PHP is NOT installed
  debug: msg='PHP is NOT installed'
  when: php_installed|failed

El módulo debug utilizado aquí es un módulo útil para mostrar el contenido de variables o mensajes de depuración. Puede imprimir una cadena (cuando se usa el argumento msg) o imprimir el contenido de una variable (cuando se usa el argumento var).

Trabajando con Plantillas

Las plantillas se utilizan típicamente para configurar archivos de configuración, lo que permite el uso de variables y otras características destinadas a hacer que estos archivos sean más versátiles y reutilizables. Ansible utiliza el motor de plantillas Jinja2.

El siguiente ejemplo es una plantilla para configurar un host virtual de Apache, utilizando una variable para establecer el directorio de documentos para este host:

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot {{ doc_root }}

    <Directory {{ doc_root }}>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

El módulo integrado template se utiliza para aplicar la plantilla desde una tarea. Si nombraste el archivo de plantilla anterior vhost.tpl, y lo colocaste en el mismo directorio que tu libro de jugadas, así es como aplicarías la plantilla para reemplazar el host virtual de Apache por defecto:

- name: Change default Apache virtual host
  template: 
    src: vhost.tpl
    dest: /etc/apache2/sites-available/000-default.conf

Definición y Activación de Manipuladores

Los manipuladores se utilizan para activar un cambio de estado en un servicio, como un reinicio o una detención. Aunque pueden parecer bastante similares a las tareas regulares, los manipuladores solo se ejecutan cuando se han activado previamente desde una directiva notify en una tarea. Típicamente se definen como un array en una sección handlers del libro de jugadas, pero también pueden residir en archivos separados.

Tomemos en consideración nuestro ejemplo anterior de uso de plantilla, donde configuramos un host virtual de Apache. Si deseas asegurarte de que Apache se reinicie después de un cambio en el host virtual, primero necesitas crear un manipulador para el servicio de Apache. Así es como se definen los manipuladores dentro de un libro de jugadas:

handlers:
    - name: restart apache
      service: name=apache2 state=restarted
    
    - name: other handler
      service: name=other state=restarted

La directiva name aquí es importante porque será el identificador único de este controlador. Para activar este controlador desde una tarea, debes usar la opción notify:

- name: Change default Apache virtual host
  template: 
    src: vhost.tpl
    dest: /etc/apache2/sites-available/000-default.conf
  notify: restart apache

Hemos visto algunas de las características más importantes que puedes utilizar para comenzar a escribir playbooks de Ansible. En la próxima sección, profundizaremos en un ejemplo más realista de un playbook que automatizará la instalación y configuración de Apache en Ubuntu.

Playbook de Ejemplo

Ahora veamos un playbook que automatizará la instalación de un servidor web Apache en un sistema Ubuntu 18.04, como se discutió en la introducción de esta guía.

El ejemplo completo, que incluye el archivo de plantilla para configurar Apache y un archivo HTML para ser servido por el servidor web, se puede encontrar en Github. La carpeta también contiene un Vagrantfile que te permite probar el playbook en una configuración simplificada, utilizando una máquina virtual gestionada por Vagrant.

Contenido del Playbook

El contenido completo del playbook está disponible aquí para tu conveniencia:

playbook.yml
  1. ---
  2. - hosts: all
  3. become: true
  4. vars:
  5. doc_root: /var/www/example
  6. tasks:
  7. - name: Update apt
  8. apt: update_cache=yes
  9. - name: Install Apache
  10. apt: name=apache2 state=latest
  11. - name: Create custom document root
  12. file: path={{ doc_root }} state=directory owner=www-data group=www-data
  13. - name: Set up HTML file
  14. copy: src=index.html dest={{ doc_root }}/index.html owner=www-data group=www-data mode=0644
  15. - name: Set up Apache virtual host file
  16. template: src=vhost.tpl dest=/etc/apache2/sites-available/000-default.conf
  17. notify: restart apache
  18. handlers:
  19. - name: restart apache
  20. service: name=apache2 state=restarted

Examinemos cada parte de este playbook con más detalle:

hosts: all
El libro de jugadas comienza indicando que debe aplicarse a todos los hosts en su inventario (hosts: all). Es posible restringir la ejecución del libro de jugadas a un host específico o a un grupo de hosts. Esta opción puede ser sobrescrita en el momento de la ejecución.

become: true
La porción become: true indica a Ansible que utilice la escalada de privilegios (sudo) para ejecutar todas las tareas en este libro de jugadas. Esta opción puede ser sobrescrita tarea por tarea.

vars
Define una variable, doc_root, que luego se utiliza en una tarea. Esta sección podría contener múltiples variables.

tasks
La sección donde se definen las tareas reales. La primera tarea actualiza la caché de apt, y la segunda tarea instala el paquete apache2.

La tercera tarea utiliza el módulo integrado file para crear un directorio que servirá como nuestro directorio raíz de documentos. Este módulo se puede utilizar para gestionar archivos y directorios.

La cuarta tarea utiliza el módulo copy para copiar un archivo local al servidor remoto. Estamos copiando un archivo HTML simple para que sea servido como nuestro sitio web alojado por Apache.

handlers
Por último, tenemos la sección handlers, donde se declaran los servicios. Definimos el controlador restart apache que es notificado desde la cuarta tarea, donde se aplica la plantilla de Apache.

Ejecutando un Playbook

Una vez que hayas descargado el contenido de este playbook en tu nodo de control de Ansible, puedes usar ansible-playbook para ejecutarlo en uno o más nodos de tu inventario. El siguiente comando ejecutará el playbook en todos los hosts de tu archivo de inventario predeterminado, utilizando autenticación de par de claves SSH para conectarse como el usuario del sistema actual:

  1. ansible-playbook playbook.yml

También puedes usar -l para limitar la ejecución a un solo host o a un grupo de hosts de tu inventario:

  1. ansible-playbook -l host_or_group playbook.yml

Si necesitas especificar un usuario SSH diferente para conectarte al servidor remoto, puedes incluir el argumento -u usuario en ese comando:

  1. ansible-playbook -l host_or_group playbook.yml -u remote-user

Para obtener más información sobre cómo ejecutar comandos y playbooks de Ansible, consulta nuestra guía sobre Cómo Instalar y Configurar Ansible en Ubuntu 18.04.

Conclusión

Ansible es una herramienta minimalista de automatización de IT que tiene una curva de aprendizaje baja, utilizando YAML para sus scripts de aprovisionamiento. Tiene una gran cantidad de módulos integrados que se pueden utilizar para abstraer tareas como la instalación de paquetes y el trabajo con plantillas. Sus requisitos de infraestructura simplificados y su lenguaje sencillo pueden ser adecuados para aquellos que están comenzando con la gestión de configuraciones. Sin embargo, podría carecer de algunas funciones avanzadas que se pueden encontrar en herramientas más complejas como Puppet y Chef.

En la próxima parte de esta serie, veremos una visión práctica de Puppet, una herramienta popular y bien establecida de gestión de configuraciones que utiliza un DSL personalizado expresivo y potente basado en Ruby para escribir scripts de aprovisionamiento.

Source:
https://www.digitalocean.com/community/tutorials/configuration-management-101-writing-ansible-playbooks