Uso de variables con Ansible

Buenos dias a tod@as!!

Para permitir mas flexibilidad en playbooks y roles, Ansible tiene la capacidad de trabajar con variables. Estas variables se pueden usar para recorrer una serie de valores determinados, acceder a informacion diversa, como el hostname o la ip de un sistema, e incluso reemplacer ciertas cadenas en una plantilla por valores específicos.

Las variables se pueden proporcionar a través del inventario, de un archivo de variables, o se pueden incluir por linea de comandos al llamar un playbook.

Hay que tener en cuenta que los nombres de las variables deben ser letras, números o guiones bajos. Y que las variables siempre deben comenzar con una letra.

Dicho esto, la pregunta es ¿cómo usar variables? ¿Y cómo conseguirlas en primer lugar?

Variables y loops

En general, los bucles son uno de los casos de uso mas comunes de variables. Si bien no estamos utilizando variables proporcionadas externamente, voy a intentar dar una primera idea de como usarlas.

Por ejemplo, voy a copiar un conjunto de archivos, es posible escribir una tarea para cada archivo o simplemente recorrerlos:

1
2
3
4
5
6
7
8
tasks:
  - name: Copia ficheros
    copy: 
      src: /etc/tmp/{{ item }}
      dest: /tmp/{{ item }}
    with_items:
      - prueba1.txt
      - prueba2.txt

Primer concepto básico: Las variables se pueden usar en los argumentos del módulo y se referencian entre llaves {{ variable }}

Variables y templates

Las variables se pueden usar también para substituir parámetros en ficheros de configuración con valores específicos del sistema, extraidos directamente en tiempo de ejecución. Imaginemos por un momento, un archivo que debe contener el hostname del servidor. Todas las máquinas son casi idénticas excepto por el nombre de host, por lo que esta variable no podrá estar predefinida, sinó que deberemos capturarla en tiempo de ejecución

En este caso, es mejor tener una copia del archivo de configuración, con una variable como place-maker en lugar de los nombres de host: aquí es donde las plantillas entran en juego:

1
2
$ cat template.j2
My host name is {{ ansible_hostname }}.

El módulo de Ansible para usar plantillas y sustitución de variables es el módulo template:

1
2
3
4
5
tasks:
  - name: copy template
    template: 
        src: template.j2 
        dest: /tmp/tmp.conf

Cuando esta tarea se ejecute, copiará el fichero template.j2 con el nombre tmp.conf y substituirá la variable {{ ansible_hostname }} por el nombre de host de cada servidor donde se ejecute.

Los templates tinen que tener la extensión .j2 y utilizan el lenguage jinja2. Podeis leer mas en la web oficial del proyecto

Usando variables dentro de condicionales

Las variables se pueden usar dentro de condiciones, lo que garantiza que ciertas tareas solo se ejecuten cuando, la variable solicitada se establece en un valor determinado:

1
2
3
4
5
6
7
8
9
10
tasks:
  - name: Install Apache on Solaris
    pkg5: 
      name: web/server/apache-24
    when: ansible_os_family == "Solaris"
 
  - name: Install Apache on RHEL
    yum:  
      name: httpd
    when: ansible_os_family == "RedHat"

En este caso, la primera tarea solo se aplica en servidores Solaris, mientras que la segunda solo se ejecuta en máquinas Red Hat.

Usando variables del sistema

Para usar variables en Ansible, evidentemente, tienen que haber sido definidas previamente. Hasta ahora hemos visto algunos casos de uso de variables, pero no cómo obtenerlas.

Por defecto, Ansible ya define un amplio conjunto de variables individuales para cada host. Cada vez que se ejecuta en un sistema, toda la información sobre el sistema se recopila y establece como una variable. Estas variables se pueden consultar a través del módulo setup:

1
2
3
4
5
6
7
8
9
10
11
12
$ ansible neon -m setup
neon | success >> {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.122.203"
        ], 
        "ansible_all_ipv6_addresses": [
            "fe80::5054:ff:feba:9db3"
        ], 
        "ansible_architecture": "x86_64", 
        "ansible_bios_date": "04/01/2014", 
...

Todas estas variables se pueden usar en templates, playbooks, roles o como comentábamos antes, en tareas condicionales.

Variables desde la línea de comandos

Otra manera de definir variables en Ansible es llamar a los playbooks con la opción --extra-vars o -e en su formato abreviado:

1
$ ansible-playbook --extra-vars "cli_var=production"

La referencia a esta variable, es otra vez, entre llaves {{ variable }}

1
2
$ cat template.j2
environment: {{ cli_var }}.

Variables en playbooks

Una forma de definir variables mas directamente, es en los playbooks, con la clave vars:

1
2
3
4
5
6
7
...
hosts: all 
vars:
  play_var: bar 
 
tasks:
...

Estas variables, también pueden ampliarse incluyendo un archivo .yml

1
2
3
4
5
6
...
hosts: all
include_vars: setupvariables.yml
 
tasks:
...

Incluir mas archivos con más variables resulta extremadamente útil cuando las variables para cada sistema se guardan en un archivo específico, como $HOSTNAME.yml, porque el archivo incluido puede volver a ser una variable:

1
2
3
4
5
6
...
hosts: all
include_vars: "{{ ansible_hostname }}.yml"
 
tasks:
...

En este caso, el playbook lee las variables específicas para cada host correspondiente, definidas en un fichero externo de variables.

Variables en el inventario

En ocasiones, puede cobrar sentido definir variables directamente en un fichero de inventario previamente definido:

1
2
3
[clients]
helium intevent_var=helium_123
neon invent_var=bar

El inventario trata más o menos cualquier argumento que no sea específico de Ansible como variable de host.

También es posible establecer variables para hostgroups completos:

1
2
3
4
5
6
[clients]
helium
neon
 
[clients:vars]
invent_var=group-foo

Un ejemplo claro de variables en el inventario seria cuando diferentes equipos usan el mismo conjunto de roles y playbooks, pero tienen diferentes configuraciones de máquina que necesitan un tratamiento específico en cada caso.

Configurar variables en el sistema: local facts

Cuando Ansible accede a un host remoto, busca el directorio /etc/ansible/facts.d y se leen todos los archivos que terminan en .fact:

1
2
3
4
$ cat /etc/ansible/facts.d/variables.fact 
[system]
foo: bar
dim: dum

Las variables que muestra el módulo setup serian las siguientes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ ansible neon -m setup
...
      "type": "loopback"
  }, 
  "ansible_local": {
      "variables": {
          "system": {
              "dim": "dum", 
              "foo": "bar"
          }
     }
  }, 
  "ansible_machine": "x86_64", 
...

Usar el resultado de una tarea como variable

Los resultados de una tarea durante la ejecución de un playbook también se pueden registrar dentro de una variable. Junto con los condicionales, esto permite que un playbook reaccione de una forma u otra en funcion de los resultados de las otras tareas.

Por ejemplo, necesitamos que el servicio httpd esté ejecutandose. Si el servicio no se ejecuta, todo el servidor debe apagarse inmediatamente para garantizar que no se produzcan corrupción en los datos. El playbook correspondiente verifica el servicio httpd, ignora los errores pero en su lugar analiza el resultado y apaga la máquina si el servicio no se puede iniciar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---
- name: register example
  hosts: all 
  sudo: yes
 
  tasks:
    - name: start service
      service: name=httpd state=started
      ignore_errors: True
      register: service_result
 
    - name: shutdown
      command: "shutdown -h +1m"
      when: service_result | failed

Otro ejemplo sería para eliminar el host del balanceador en caso de que el servidor web no esté disponible. O para verificar si la base de datos está disponible y de lo contrario cerrar inmediatamente el firewall frontal.

Conclusión

Las variables son una característica muy poderosa que nos ayuda a enriquecer la funcionalidad de Ansible. Junto con los templates y el lenguaje Jinja2, las posibilidades son casi infinitas.

Tarde o temprano, cada administrador tendrá que dejar atrás los simples roles y playbooks y comenzar a sumergirse en variables, templates y loops para hacer la automatización del sistema aún más fácil y dinámica.

Espero que esta pequeña guia os pueda ser de utilidad.

Un saludo!

Miquel.

0%