Ansible (III): roles y templates

Tras una breve pausa derivada del hecho de que me he casado :D, seguimos apuntando cosillas en el blog. En este artículo vamos a crear un pequeño rol que nos permita gestionar nuestros backups como debería ser.

Ya comentamos en el post anterior, todo lo que hacía el script y ahora vamos a crear el rol.

Instalación de Backupninja

Nuestro rol va a realizar principalmente dos acciones: en una va a instalar backupninja y en la segunda le va a aplicar la configuración que nosotros queremos. Así que nos vamos al fichero tasks/main.yml y comenzamos a editarlo:

---
# tasks file for backupninja

- name: Install backupninja package
  ansible.builtin.package:
    name: backupninja
    state: present

Ese conjunto de líneas serían una tarea completa. El formato yaml utilizado por Ansible no necesita usar paréntesis puesto que determina a que pertenece cada línea en función de la indentación. Vamos a proceder a explicarlo:

  • name: El primer name se corresponde al nombre que tiene la tarea y que se va a mostrar en la terminal cuando ejecutemos el rol.

  • ansible.builtin.package: Se corresponde con el nombre del módulo de Ansible que estamos utilizando. En este caso, package es el módulo embebido dentro de Ansible que nos permite instalar paquetes sin importar de que distribución GNU/Linux estemos utilizando. Podemos ver más información aquí. Cada módulo recibe una serie de parámetros para funcionar y en este caso le pasamos el nombre del paquete y el estado en el que queremos que esté (present significa que el paquete se instalará en caso de no estarlo).

Plantillas

Nuestro rol ya instalaría backupninja y ahora vamos a preparar las plantillas con nuestra configuración en ../templates. Ansible utiliza Jinja2 para las plantillas así que deberemos conocer su sintaxis aunque es bastante intuitiva.

Voy a crear la plantilla tar.j2 basándonos en los ficheros de configuración que crea backupninja como ejemplo.

# {{ ansible_managed }}

when = everyday at 02

backupname = "bananapi"
backupdir = "/media/disk/backup/tmp"

compress = gzip

includes = "/media/disk/datos /var/www/nextcloud"
excludes = "/tmp /proc /sys /dev /srv /misc /net /selinux"

DATEFORMAT = %Y.%m.%d

Este fichero de configuración crearía un backup a las dos de la mañana, con el nombre de bananapi y un formato de fecha YYYY.MM.dd en el directorio /media/disk/backup/tmp con el contenido de los directorios /media/disk/datos y /var/www/nextcloud . También añadiría un comentario en la primera línea imprimiendo la variable ansible_managed . En Jinja2 las variables se utilizan entre dobles llaves.

Aunque esta plantilla sería funcional para nuestras necesidades, no le serviría a otros así que vamos a utilizar más variables. Ahora nuestra plantilla tar.j2 sería así:

# {{ ansible_managed }}

when = {{ ninja_when }}

backupname = {{ ninja_backupname }}
backupdir = {{ ninja_backupdir }}

compress = {{ ninja_compress }}

includes = {{ ninja_includes }}
excludes = {{ ninja_excludes }}

DATEFORMAT = %Y.%m.%d

Despliegue de la configuración

Tras crear la plantilla ahora debemos crear una nueva tarea que despliegue la configuraciónn y para ello volvemos a editar el fichero ~/tasks/main.yml

---
# tasks file for backupninja

- name: Install backupninja package
  ansible.builtin.package:
    name: backupninja
    state: present

- name: Deploy TAR template
  ansible.builtin.template:
    src: tar.j2
    dest: "/etc/backup.d/10.tar"
    mode: '0600'
    owner: root
    group: root

En este caso hemos añadido una nueva tarea que utiliza el módulo template y crea un fichero 10.tar en /etc/backup.d/ utilizando la plantilla tar.j2 como fuente. El fichero además tendrá permisos 600 y pertenecerá a root:root.

Variables

La plantilla que hemos creado antes tiene variables declaradas que no han sido definidas en ningún sitio por lo que si ahora desplegáramos la configuración ésta no sería la correcta. Ansible soporta variables de muchísimas maneras y recomendamos leer su documentación. En nuestro caso vamos a definir ciertas variables en el ~/defaults/main.yml:

ninja_when: "everyday at 02"
ninja_backupname: ""
ninja_backupdir: ""
ninja_compress: "gzip"
ninja_includes: ""
ninja_excludes: "/tmp /proc /sys /dev /srv /misc /net /selinux"

Como se puede observar, he declarado todas las variables aunque no todas tienen un valor. He pretendido darles un valor sólo a las variables que suelen mantenerse entre servidores aunque pueden ser sobreescritas sin necesidad de modificar el rol. Debemos recordar que en la medida de lo posible un rol debe ser independiente del servidor en el que se vaya a instalar.

Ejecución del rol

Ahora ya tenemos una pequeña versión preliminar de una parte del rol y vamos a proceder a ejecutarla desde un playbook. De esta forma nos vamos a la carpeta padre del rol y creamos dos ficheros: uno para nuestro playbook (test-backupninja.yml) y otro con variables (test-backupninja-vars.yml).

---
- name: Backupninja test
  hosts: banana
  become: true

  pre_tasks:
    - ansible.builtin.include_vars: "test-backupninja-vars.yml"

  roles:
    - backupninja

Como vemos en el archivo de variables cambiamos el valor de ninja_when para sobreescribir el que viene por defecto.

---
ninja_when: "everyday at 01"
ninja_backupname: "bananapi"
ninja_backupdir: "/media/disk/backup/tmp"
ninja_includes: "/media/disk/datos /var/www/nextcloud"
ninja_excludes: "/tmp /proc /sys /dev /srv /misc /net /selinux"

En el primer YAML añadimos las variables que nos faltan mediante el módulo include_tasks y después ejecutamos el rol. Vamos a probarlo y a ver el resultado:

ansible-playbook -i hosts test-backupninja.yml 

PLAY [Backupninja test] **********************************************************************

TASK [Gathering Facts] ***********************************************************************
ok: [banana]

TASK [include_vars] **************************************************************************
ok: [banana]

TASK [backupninja : Install backupninja packages] ********************************************
ok: [banana] => (item=backupninja)

TASK [backupninja : Deploy TAR template] *****************************************************
changed: [banana]

PLAY RECAP ***********************************************************************************
banana                     : ok=4    changed=1    unreachable=0    failed=0   

Como resultado de la ejecución podemos ver que Backupninja ya estaba instalado y que se ha producido un cambio en la plantilla y si nos logueamos podemos ver que se ha desplegado de forma correcta (sustituyéndose el when a “everyday at 01”).

# Ansible managed

when = everyday at 01

backupname = bananapi
backupdir = /media/disk/backup/tmp

compress = gzip

includes = /media/disk/datos /var/www/nextcloud
excludes = /tmp /proc /sys /dev /srv /misc /net /selinux

DATEFORMAT = %Y.%m.%d

Hasta aquí llegamos con el despleigue del rol de Backupninja. En la próxima entrega añadiremos dos pasos más y configuraremos el rol para que sea multi-distribución.

Documentación

Revisado a 01/05/2023