Herramientas de usuario

Herramientas del sitio


informatica:ciberseguridad:cursos:curso_ansible_automatizacion_it:conceptos_avanzados

¡Esta es una revisión vieja del documento!


Ansible: Conceptos avanzados

Sección perteneciente al curso Ansible Automatización IT

¿Qué es YAML y por qué es importante conocerlo?

YAML es un formato de serialización de datos , legible por humanos, que está inspirado en lenguajes como XML.

YAML es un superconjunto del formato JSON, que permite añadir estructuras más complejas e incluso comentarios.

En el mundo Ansible, todos los playbooks están escritos en YAML.

Reglas generales:

  • Extensión .yaml (recomendada) o .yml
  • Las tabulaciones no están permitidas, solo los espacios.
  • Es necesario identar (sangrar) el código uno o más espacios.
  • Todas las claves/propiedades son case-sensitive (se distingue mayúsculas de minúsculas).
  • Los comentarios empiezan con un símbolo #.

Ejemplo de un playbook en YAML:

hosts: webservers

sudo: yes

vars:
  app_name: PleaseDeployMe
  repo_url: https://github.com/username/reponame.git
  repo_remote: origin
  repo_version: master
  webapps_dir: /deployed
  virtualenv_root: /deployed/PleaseDeployMe/mac
  
  tasks:
    - name: git pull project
      git: repo={{repo_url}} dest={{webapps_dir}}/{{app_name}} version=master

Tipos de datos

Cadenas de texto

---
foo: Esta es una cadena de texto

Los tres guiones indican el inicio del fichero.

Para meter caracteres especiales, hay que meterlos entre comillas:

---
foo: Esta es una cadena de texto "\n" estoy en una línea nueva.

Booleanos

True es lo mismo que On; False es lo mismo que Off.

---
foo: True
bar: False
light: On
TV: Off

Números

Soporta números decimales, hexadecimales…

---
foo: 12345
bar: 0x12d4
plop: 023332

Array / listas

---
items: [1, 2, 3, 4, 5]
names: ["one", "two", "three", "four", "five"]

También se podría escribir en el siguiente formato:

items:
  - 1
  - 2
  - 3
  - 4
  - 5

Diccionarios

---
foo: {thing1: huey, thing2: louie, thing3: dewey}

Los diccionarios se pueden anidar con otros elementos:

---
foo: 
  bar:
    - bar
    - rgb
    - plob

El diccionario bar tiene dentro una lista de 3 elementos.

Variables

Permiten almacenar y reusar información. Las variables se procesan en orden, de mayor a menor preferencia:

  1. Variables por línea de comandos
  2. Variables en tareas
  3. Variables en roles y secciones include
  4. Variables creadas con la directiva register
  5. Variables en los inventarios
  6. Variables en las plays
  7. Host facts
  8. Variables por defecto en los roles

En la documentación oficial de Ansible se habla hasta de 15 niveles de prioridad.

Reglas a la hora de definir variables en Ansible:

  • Deben comenzar con letra
  • Pueden contener letras, números y guiones bajos.

Tareas, Plays y Playbooks I

Una tarea es la aplicación de un módulo en un playbook para realizar una acción específica sobre un equipo.

Algunas tareas:

  • file: un directorio debe existir.
  • yum: un paquete tiene que estar instalado.
  • service: un servicio tiene que estar ejecutándose.
  • template: se quiere cargar una configuración en un template.
  • get_url: se quiere descargar un fichero de una URL.
  • git: se quiere clonar el código fuente desde un repositorio.

Ejemplo de la sección de tareas en un fichero YAML de Ansible:

tasks:
- name: httpd package is present
  yum:
    name: httpd
    state: latest
    
- name: latest index.html file is present
  copy:
    scr: files/index.html
    dest: /var/www/html/
    
- name: restart httpd
  service:
    name: httpd
    state: restarted

Plays y Playbooks

Una play consiste en un conjunto ordenado de tareas que se ejecutan en los hosts seleccionados sobre un inventario. Un playbook es un fichero que contiene una o más plays:

A continuación vemos un ejemplo de un Playbook con una sola play:

---
- name: install and start apache
  hosts: web
  become: yes
  vars:
    http_port: 80
    
  tasks:
  - name: httpd package is present
    yum:
      name: httpd
      state: latest
    
  - name: latest index.html file is present
    copy:
      scr: files/index.html
      dest: /var/www/html/

Ansible permite importar playbooks con la palabra clave import_playbook.

Ejemplo de nuestro primer playbook (primer_playbook.yaml):

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  gather_facts: false # para que no traiga los facts por defecto
  
  tasks:
  - name: comprobar conexión
    ping:
      data: functionando

Lo ejecutamos:

ansible-playbook -i /home/pepito/inventario -u ansible_user --key-file /home/pepito/.ssh/id_rsa primer_playbook.yaml

También podríamos añadirle la opción -v para obtener más información de la salida:

ansible-playbook -i /home/pepito/inventario -u ansible_user --key-file /home/pepito/.ssh/id_rsa primer_playbook.yaml -v

Poner ejemplo de la salida del comando anterior.

Podemos revisar la sintaxis del Playbook sin ejecutarlo:

ansible-playbook primer_playbook.yaml --syntax-check

Tareas, Plays y Playbooks II

Vamos a coger el playbook de la sección anterior y complicarlo un poco más añadiéndole otra play:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  gather_facts: false # para que no traiga los facts por defecto
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

- name: segunda play
  hosts: servidor_web
  gather_facts: false
  
  tasks:
  - name: listar directorios
    command: ls
    
  - name: instalar nginx
    apt: name=nginx state=present

Antes de ejecutar, revisamos la sintaxis del fichero:

ansible-playbook primer_playbook.yaml --syntax-check

Si todo va bien, ejecutamos el playbook:

ansible-playbook -i /home/pepito/inventario -u ansible_user --key-file /home/pepito/.ssh/id_rsa primer_playbook.yaml -v

Vemos que falla porque no el usuario no tiene permisos para instalar el paquete nginx. Podemos indicar que necesitamos elevar privilegios con become: yes:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  gather_facts: false # para que no traiga los facts por defecto
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

- name: segunda play
  hosts: servidor_web
  become: yes
  gather_facts: false
  
  tasks:
  - name: listar directorios
    command: ls
    
  - name: instalar nginx
    apt: name=nginx state=present

En la máquina destino, tenemos que evitar que pida la contraseña el usar sudo. Para ello, hay que editar el fichero /etc/sudoers y la línea que comienza por %sudo dejarla de la siguiente manera:

%sudo ALL=(ALL) NOPASSWD: ALL

Modificamos más el playbook para incluir variables:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  gather_facts: false # para que no traiga los facts por defecto
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

- name: segunda play
  hosts: servidor_web
  become: yes
  gather_facts: false
  vars:
    state: absent
  
  tasks:
  - name: listar directorios
    command: ls
    
  - name: instalar nginx
    apt: name=nginx state={{ state }}

Lo que añadamos entre llaves ({{}}) se sustituirá por el valor que le hayamos dado. En este caso state es absent, así que es lo mismo que si hubiéramos puesto:

    apt: name=nginx state=absent

Si ejecutamos ahora ese playbook, desinstalará nginx.

Qué son los handlers y para qué se usan

Los handlers son tareas especiales que se ejecutan al final de una play si son notificadas por otra tarea cuando ocurre un cambio.

tasks:
- name: httpd package is present
  yum:
    name: httpd
    state: latest
  notify: restart httpd
    
- name: latest index.html file is present
  copy:
    src: files/index.html
    dest: /var/www/html
    
handlers:
- name: restart httpd
  service:
    name: httpd
    state: restarted

Los handlers se definen al final del Playbook

Seguimos trabajando con nuestro playbook:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  gather_facts: false # para que no traiga los facts por defecto
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

- name: segunda play
  hosts: servidor_web
  become: yes
  gather_facts: false
  vars:
    state: present
  
  tasks:
  - name: listar directorios
    command: ls
    
  - name: instalar nginx
    apt: name=nginx state="{{ state }}"
    
  - name: copiar index
    copy:
      src: index.html
      dest: /var/www/html
      
   - name: reiniciar nginx
     service:
       name: nginx
       state: restarted

Lo ejecutamos:

ansible-playbook -i /home/pepito/inventario -u ansible_user --key-file /home/pepito/.ssh/id_rsa primer_playbook.yaml -vv

Si observamos el resultado, vemos que el servidor web nginx se reinicia, pero esto no nos interesa. Queremos que se reinicie solo cuando sea necesario. Aquí entran en escena los handlers:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  gather_facts: false # para que no traiga los facts por defecto
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

- name: segunda play
  hosts: servidor_web
  become: yes
  gather_facts: false
  vars:
    state: present
  
  tasks:
  - name: listar directorios
    command: ls
    
  - name: instalar nginx
    apt: name=nginx state="{{ state }}"
    notify: reiniciar nginx # Mismo nombre que el handler
    
  - name: copiar index
    copy:
      src: index.html
      dest: /var/www/html

   handlers:     
   - name: reiniciar nginx
     service:
       name: nginx
       state: restarted

Si lanzamos ahora el playbook, conseguiremos que solo se reinice nginx cuando se instale.

Vamos a pasar variables por línea de comando con la opción -e:

ansible-playbook -i /home/pepito/inventario -u ansible_user --key-file /home/pepito/.ssh/id_rsa primer_playbook.yaml -vv -e "state=absent"

Ansible, en lugar de usar la varible state definida en el playbook, aplica la que pasamos por la línea de comandos. Por tanto, se eliminará nginx.

Condicionales

Ansible permite incluir condiciones a la hora de ejecutar las tareas basándose en la evaluación de una variable, fact u otra tarea previa en tiempo de ejecución.

---
- name: install Apache webserver
  hosts: all
  tasks:
    - name: Install Apache on Ubuntu Server
      apt: name=apache2 state=present
      become: yes
      when: ansible_os_family == "Debian" and ansible_distribution_version == "18.04"

Trabajando con nuestro primer_playbook.yaml:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  # gather_facts: true 
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

- name: segunda play
  hosts: servidor_web
  become: yes
  gather_facts: false
  vars:
    state: present
  
  tasks:
  - name: listar directorios
    command: ls
    when: ansible_os_family == "RedHat"
    
  - name: instalar nginx
    apt: name=nginx state="{{ state }}"
    notify: reiniciar nginx # Mismo nombre que el handler
    
  - name: copiar index
    copy:
      src: index.html
      dest: /var/www/html

   handlers:     
   - name: reiniciar nginx
     service:
       name: nginx
       state: restarted

Poner ejemplo de salida

Veremos ahora el uso de condicionales utilizando una variable que hemos usado como salida de una tarea:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  # gather_facts: true 
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

- name: segunda play
  hosts: servidor_web
  become: yes
  gather_facts: false
  vars:
    state: present
  
  tasks:
  - name: listar directorios
    command: ls
    register: contenido # registro de la salida de 'ls'
    
  - name: comprobar ficheros
    debug:
      msg: "El directorio no está vacío"
    when: contenido.stdout != ""
    
  - name: instalar nginx
    apt: name=nginx state="{{ state }}"
    notify: reiniciar nginx # Mismo nombre que el handler
    
  - name: copiar index
    copy:
      src: index.html
      dest: /var/www/html

   handlers:     
   - name: reiniciar nginx
     service:
       name: nginx
       state: restarted

Poner ejemplo de salida de ejecución del anterior playbook

Tags

Los tags permiten ejecutar un subconjunto de tareas en un playbook.

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  # gather_facts: true 
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

- name: segunda play
  hosts: servidor_web
  become: yes
  gather_facts: false
  vars:
    state: present
  
  tasks:
  - name: listar directorios
    command: ls
    register: contenido # registro de la salida de 'ls'
    tags:
      - listar
    
  - name: comprobar ficheros
    debug:
      msg: "El directorio no está vacío"
    when: contenido.stdout != ""
    
  - name: instalar nginx
    apt: name=nginx state="{{ state }}"
    notify: reiniciar nginx # Mismo nombre que el handler
    
  - name: copiar index
    copy:
      src: index.html
      dest: /var/www/html

   handlers:     
   - name: reiniciar nginx
     service:
       name: nginx
       state: restarted

Lo ejecutamos añadiendo la opción --tags:

ansible-playbook -i /home/pepito/inventario -u ansible_user --key-file /home/pepito/.ssh/id_rsa primer_playbook.yaml -vv --tags "listar"

Poner ejemplo de salida del comando anterior.

También podríamos incluir tags en una play entera:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  # gather_facts: true 
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

- name: segunda play
  hosts: servidor_web
  become: yes
  tags: segunda_play
  gather_facts: false
  vars:
    state: present
  
  tasks:
  - name: listar directorios
    command: ls
    register: contenido # registro de la salida de 'ls'
    tags:
      - listar
    
  - name: comprobar ficheros
    debug:
      msg: "El directorio no está vacío"
    when: contenido.stdout != ""
    
  - name: instalar nginx
    apt: name=nginx state="{{ state }}"
    notify: reiniciar nginx # Mismo nombre que el handler
    
  - name: copiar index
    copy:
      src: index.html
      dest: /var/www/html

   handlers:     
   - name: reiniciar nginx
     service:
       name: nginx
       state: restarted
ansible-playbook -i /home/pepito/inventario -u ansible_user --key-file /home/pepito/.ssh/id_rsa primer_playbook.yaml -vv --tags "segunda_play"

Poner ejemplo de salida del comando anterior.

Templates I

Ansible integra el motor de templating jinja2 para:

  • Fijar y modificar variables
  • Utilizar lógica condicional
  • Generar ficheros de configuración utilizando variables

Insertar una variable:

INTERFACES="{{ dhcp_interface }}"

Iterar sobre una lista:

search ws.nsrc.org
{$ for host in use_dns_servers %}
nameserver {{ host }}
{% endfor %}

Se puede probar jinga online por ejemplo desde este Jinja2 live parser

Jinja2 permite utilizar filtros para transformar información almacenada en variables.

Templates II

Usaremos jinja2 con Ansible. Vamos a crear un fichero template_ejemplo.j2:

Hola!

Nginx en la versión {{ version }} se está ejecutando en {{ servidor }}

Modificamos nuestro Playbook para añadir una tercera play:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  # gather_facts: true 
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

- name: segunda play
  hosts: servidor_web
  become: yes
  tags: segunda_play
  gather_facts: false
  vars:
    state: present
  
  tasks:
  - name: listar directorios
    command: ls
    register: contenido # registro de la salida de 'ls'
    tags:
      - listar
    
  - name: comprobar ficheros
    debug:
      msg: "El directorio no está vacío"
    when: contenido.stdout != ""
    
  - name: instalar nginx
    apt: name=nginx state="{{ state }}"
    notify: reiniciar nginx # Mismo nombre que el handler
    
  - name: copiar index
    copy:
      src: index.html
      dest: /var/www/html

- name: tercera play
  hosts: all
  become: no
  tags: tercera_play
  vars:
    version: "5.13"
    servidor: "Ubuntu"

  tasks:
    - name: imprimir template
      template:
        src: template_ejemplo.js # fichero con template Jinja2
        dest: /home/ansible/archivo.txt

   handlers:     
   - name: reiniciar nginx
     service:
       name: nginx
       state: restarted

Revisamos sintaxis:

ansible-playbook --syntax-check primer_playbook.yaml

Lanzamos el playbook centrándonos en la última play que hemos hecho:

ansible-playbook -i /home/pepito/inventario -u ansible_user --key-file /home/pepito/.ssh/id_rsa primer_playbook.yaml -vv --tags "tercera_play"

Si todo ha funcionado bien, se habrá creado el fichero /home/ansible/archivo.txt con el contenido:

Hola!

Nginx en la versión 5.13 se está ejecutando en Ubuntu.

Vamos a añadir más variables en la tercera play:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  # gather_facts: true 
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

- name: segunda play
  hosts: servidor_web
  become: yes
  tags: segunda_play
  gather_facts: false
  vars:
    state: present
  
  tasks:
  - name: listar directorios
    command: ls
    register: contenido # registro de la salida de 'ls'
    tags:
      - listar
    
  - name: comprobar ficheros
    debug:
      msg: "El directorio no está vacío"
    when: contenido.stdout != ""
    
  - name: instalar nginx
    apt: name=nginx state="{{ state }}"
    notify: reiniciar nginx # Mismo nombre que el handler
    
  - name: copiar index
    copy:
      src: index.html
      dest: /var/www/html

- name: tercera play
  hosts: all
  become: no
  tags: tercera_play
  vars:
    version: "5.13"
    servidor: "Ubuntu"
    coches: ["Mercedes", "Nissan", "Renault", "Ford"]

  tasks:
    - name: imprimir template
      template:
        src: template_ejemplo.j2 # fichero con template Jinja2
        dest: /home/ansible/archivo.txt

    - name: ejemplo loop con templates
      template:
        src: template_ejemplo_2.j2
        dest: /home/ansible/archivo_2.txt

   handlers:     
   - name: reiniciar nginx
     service:
       name: nginx
       state: restarted

Creamos la plantilla template_ejemplo_2.j2:

La lista de coches contiene las siguientes marcas:

{% for item in coches %}

  {{ item }}
{% endfor %}

Revisamos sintaxis:

ansible-playbook --syntax-check primer_playbook.yaml

Lanzamos el playbook centrándonos en la última play que hemos hecho:

ansible-playbook -i /home/pepito/inventario -u ansible_user --key-file /home/pepito/.ssh/id_rsa primer_playbook.yaml -vv --tags "tercera_play"

En el equipo destino tendremos un nuevo archivo /home/ansible/archivo_2.txt:

La lista de coches contiene las siguientes marcas:

  Mercedes
  
  Nissan
  
  Renault
  
  Ford

Si no queremos el espacio tras cada elemento, usaríamos un guion antes del cierre del foreach:

{%- endfor %}

Si queremos que se transforme el texto a todo mayúscula, podemos usar el filtro upper:

La lista de coches contiene las siguientes marcas:

{% for item in coches %}

  {{ item | upper }}
{% endfor %}

Finalmente, veremos inyección de variables en código HTML. Creamos el fichero template_ejemplo_3.j2:

<html>
    <h1>Nginx se está ejecutando en {{ ansible_hostname }}</h1>
</html>

En nuestro Playbook vamos a añadir una tercera tarea en la tercera play:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  # gather_facts: true 
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

- name: segunda play
  hosts: servidor_web
  become: yes
  tags: segunda_play
  gather_facts: false
  vars:
    state: present
  
  tasks:
  - name: listar directorios
    command: ls
    register: contenido # registro de la salida de 'ls'
    tags:
      - listar
    
  - name: comprobar ficheros
    debug:
      msg: "El directorio no está vacío"
    when: contenido.stdout != ""
    
  - name: instalar nginx
    apt: name=nginx state="{{ state }}"
    notify: reiniciar nginx # Mismo nombre que el handler
    
  - name: copiar index
    copy:
      src: index.html
      dest: /var/www/html

- name: tercera play
  hosts: all
  become: yes # para tener permisos para escribir en la carpeta del servidor web
  tags: tercera_play
  vars:
    version: "5.13"
    servidor: "Ubuntu"
    coches: ["Mercedes", "Nissan", "Renault", "Ford"]

  tasks:
    - name: imprimir template
      template:
        src: template_ejemplo.j2 # fichero con template Jinja2
        dest: /home/ansible/archivo.txt

    - name: ejemplo loop con templates
      template:
        src: template_ejemplo_2.j2
        dest: /home/ansible/archivo_2.txt

     - name: mostrar un nuevo fichero index
       template:
         src: template_ejemplo_3.j2
         dest: /var/www/html/index.html
         mode: 0755

   handlers:     
   - name: reiniciar nginx
     service:
       name: nginx
       state: restarted

Revisamos sintaxis:

ansible-playbook --syntax-check primer_playbook.yaml

Lanzamos el playbook centrándonos en la última play que hemos hecho:

ansible-playbook -i /home/pepito/inventario -u ansible_user --key-file /home/pepito/.ssh/id_rsa primer_playbook.yaml -vv --tags "tercera_play"

Bloques y gestión de errores

Los bloques crean grupos lógicos de tareas y además permiten gestionar errores.

Para ignorar errores podemos usar la etiqueta ignore_errors:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  # gather_facts: true 
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

- name: segunda play
  hosts: servidor_web
  become: yes
  tags: segunda_play
  gather_facts: false
  vars:
    state: present
  
  tasks:
  - name: listar directorios
    command: lz # Ponemos un comando incorrecto a propósito
    register: contenido # registro de la salida de 'ls'
    tags:
      - listar
    ignore_errors: yes
    
  - name: comprobar ficheros
    debug:
      msg: "El directorio no está vacío"
    when: contenido.stdout != ""
    
  - name: instalar nginx
    apt: name=nginx state="{{ state }}"
    notify: reiniciar nginx # Mismo nombre que el handler
    
  - name: copiar index
    copy:
      src: index.html
      dest: /var/www/html

- name: tercera play
  hosts: all
  become: yes # para tener permisos para escribir en la carpeta del servidor web
  tags: tercera_play
  vars:
    version: "5.13"
    servidor: "Ubuntu"
    coches: ["Mercedes", "Nissan", "Renault", "Ford"]

  tasks:
    - name: imprimir template
      template:
        src: template_ejemplo.j2 # fichero con template Jinja2
        dest: /home/ansible/archivo.txt

    - name: ejemplo loop con templates
      template:
        src: template_ejemplo_2.j2
        dest: /home/ansible/archivo_2.txt

     - name: mostrar un nuevo fichero index
       template:
         src: template_ejemplo_3.j2
         dest: /var/www/html/index.html
         mode: 0755

   handlers:     
   - name: reiniciar nginx
     service:
       name: nginx
       state: restarted

Al ejecutar, dará error cuando llegue a “listar directorios”, pero continuará la ejecución porque le hemos dicho que los ignores.

Vamos a crear dos bloques en nuestro playbook:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  # gather_facts: true 
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

- name: segunda play
  hosts: servidor_web
  become: yes
  tags: segunda_play
  gather_facts: false
  vars:
    state: present
  
  tasks:
  - name: listar directorios y comprobar ficheros
    block:
    - name: listar directorios
      command: lz # Ponemos un comando incorrecto a propósito
      register: contenido # registro de la salida de 'ls'
      tags:
        - listar

    - name: comprobar ficheros
      debug:
        msg: "El directorio no está vacío"
      when: contenido.stdout != ""

  - name: instalar nginx y copiar index
    block:
    - name: instalar nginx
      apt: name=nginx state="{{ state }}"
      notify: reiniciar nginx # Mismo nombre que el handler
    
    - name: copiar index
      copy:
        src: index.html
        dest: /var/www/html

- name: tercera play
  hosts: all
  become: yes # para tener permisos para escribir en la carpeta del servidor web
  tags: tercera_play
  vars:
    version: "5.13"
    servidor: "Ubuntu"
    coches: ["Mercedes", "Nissan", "Renault", "Ford"]

  tasks:
    - name: imprimir template
      template:
        src: template_ejemplo.j2 # fichero con template Jinja2
        dest: /home/ansible/archivo.txt

    - name: ejemplo loop con templates
      template:
        src: template_ejemplo_2.j2
        dest: /home/ansible/archivo_2.txt

     - name: mostrar un nuevo fichero index
       template:
         src: template_ejemplo_3.j2
         dest: /var/www/html/index.html
         mode: 0755

   handlers:     
   - name: reiniciar nginx
     service:
       name: nginx
       state: restarted

Vamos a hacer también una gestión más inteligente de los errores con la etiqueta rescue:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  # gather_facts: true 
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

- name: segunda play
  hosts: servidor_web
  become: yes
  tags: segunda_play
  gather_facts: false
  vars:
    state: present
  
  tasks:
  - name: listar directorios y comprobar ficheros
    block:
    - name: listar directorios
      command: lz # Ponemos un comando incorrecto a propósito
      register: contenido # registro de la salida de 'ls'
      tags:
        - listar

    - name: comprobar ficheros
      debug:
        msg: "El directorio no está vacío"
      when: contenido.stdout != ""
 

    rescue: 
      - name: listar directorios
        commando: ls 
 

  - name: instalar nginx y copiar index
    block:
    - name: instalar nginx
      apt: name=nginx state="{{ state }}"
      notify: reiniciar nginx # Mismo nombre que el handler
    
    - name: copiar index
      copy:
        src: index.html
        dest: /var/www/html
 

- name: tercera play
  hosts: all
  become: yes # para tener permisos para escribir en la carpeta del servidor web
  tags: tercera_play
  vars:
    version: "5.13"
    servidor: "Ubuntu"
    coches: ["Mercedes", "Nissan", "Renault", "Ford"]

  tasks:
    - name: imprimir template
      template:
        src: template_ejemplo.j2 # fichero con template Jinja2
        dest: /home/ansible/archivo.txt

    - name: ejemplo loop con templates
      template:
        src: template_ejemplo_2.j2
        dest: /home/ansible/archivo_2.txt

     - name: mostrar un nuevo fichero index
       template:
         src: template_ejemplo_3.j2
         dest: /var/www/html/index.html
         mode: 0755

   handlers:     
   - name: reiniciar nginx
     service:
       name: nginx
       state: restarted

Cuando ejecutamos el playbook, al llegar al bloque “listar directorios”, da error, entonces salta a la sección rescue y ejecuta lo que está definido ahí.

También podríamos usar la etiqueta always si queremos que algo se ejecute haya errores o no:

...
  tasks:
  - name: listar directorios y comprobar ficheros
    block:
    - name: listar directorios
      command: lz # Ponemos un comando incorrecto a propósito
      register: contenido # registro de la salida de 'ls'
      tags:
        - listar

    - name: comprobar ficheros
      debug:
        msg: "El directorio no está vacío"
      when: contenido.stdout != ""

    rescue: 
      - name: listar directorios
        commando: ls 

    always:
      - name: mensaje de always
        debug:
          msg: "Este código se ejecuta siempre"

Bucles

Los bucles se usan para realizar una tarea múltiples veces, como por ejemplo crear muchos usuarios, instalar varios paquetes, etc.

Se emplea la palabra reservada loop.

Vamos a modificar la primera play de nuestro playbook de ejemplo:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  # gather_facts: true 
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

  - name: echo items
    command: echo "{{ item }}"
    loop:
      - uno
      - dos

- name: segunda play
  hosts: servidor_web
  become: yes
  tags: segunda_play
  gather_facts: false
  vars:
    state: present
  
  tasks:
  - name: listar directorios y comprobar ficheros
    block:
    - name: listar directorios
      command: lz # Ponemos un comando incorrecto a propósito
      register: contenido # registro de la salida de 'ls'
      tags:
        - listar

    - name: comprobar ficheros
      debug:
        msg: "El directorio no está vacío"
      when: contenido.stdout != ""
 

    rescue: 
      - name: listar directorios
        commando: ls 
 

  - name: instalar nginx y copiar index
    block:
    - name: instalar nginx
      apt: name=nginx state="{{ state }}"
      notify: reiniciar nginx # Mismo nombre que el handler
    
    - name: copiar index
      copy:
        src: index.html
        dest: /var/www/html
 

- name: tercera play
  hosts: all
  become: yes # para tener permisos para escribir en la carpeta del servidor web
  tags: tercera_play
  vars:
    version: "5.13"
    servidor: "Ubuntu"
    coches: ["Mercedes", "Nissan", "Renault", "Ford"]

  tasks:
    - name: imprimir template
      template:
        src: template_ejemplo.j2 # fichero con template Jinja2
        dest: /home/ansible/archivo.txt

    - name: ejemplo loop con templates
      template:
        src: template_ejemplo_2.j2
        dest: /home/ansible/archivo_2.txt

     - name: mostrar un nuevo fichero index
       template:
         src: template_ejemplo_3.j2
         dest: /var/www/html/index.html
         mode: 0755

   handlers:     
   - name: reiniciar nginx
     service:
       name: nginx
       state: restarted

Al ejecutar el playbook, veremos en la salida que se muestra esos dos elementos (uno y dos) para cada uno de los equipos del inventario.

Añadimos más cosas:

(...)
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

  - name: echo items
    command: echo "{{ item }}"
    loop:
      - uno
      - dos

  - name: listar inventario
    debug: 
      msg: "{{ item }}"
    loop: "{{ groups['all'] }}"
 
(...)

Mostrar salida del comando anterior

Vamos a usar ahora diccionarios:

# Este es nuestro primer playbook
- name: primera play
  hosts: all
  become: yes
  gather_facts: true
  
  tasks:
  - name: comprobar conexión
    ping:
      data: funcionando

  - name: echo items
    command: echo "{{ item }}"
    loop:
      - uno
      - dos

  - name: listar inventario
    debug: 
      msg: "{{ item }}"
    loop: "{{ groups['all'] }}"
    
  - name: crear usuarios
    user:
      name: "{{ item.name }}"
      state: present
      groups: "{{ item.groups }}"
    loop:
      - { name: 'user1', groups: 'sudo'}
      - { name: 'user2', groups: 'sudo'}
 
(...)

Revisamos sintaxis:

ansible-playbook --syntax-check primer_playbook.yaml

Lanzamos el playbook centrándonos en la última play que hemos hecho:

ansible-playbook -i /home/pepito/inventario -u ansible_user --key-file /home/pepito/.ssh/id_rsa primer_playbook.yaml -vv 

Si vamos al equipo/s destino, se habrían creado los usuarios user1 y user2 (en Linux podemos mirar el contenido del fichero /etc/passwd.

Si quisiéramos eliminar los usarios, cambiaríamos el valor de state a absent:

(...)
  - name: crear usuarios
    user:
      name: "{{ item.name }}"
      state: absent
      groups: "{{ item.groups }}"
    loop:
      - { name: 'user1', groups: 'sudo'}
      - { name: 'user2', groups: 'sudo'}
(...)

Qué son los Roles en Ansible

Los roles permiten organizar, paquetizar y reusar nuestro código de forma más práctica.

Tendríamos una estructura de carpetas donde incluiremos los roles que queramos.

  • site.yaml
  • roles/
    • common/
      • files/: archivos a desplegar en el equipo objetivo.
      • templates/
      • tasks/
      • handlers/
      • vars/
      • defaults/
      • meta/: permite incluir dependencias entre roles
    • apache/
      • files/
      • templates/
      • tasks/
      • handlers/
      • vars/
      • defaults/

Creando nuestro primer Rol

Crearemos la carpeta principal roles:

mkdir roles

Dentro de ella, vamos a crear el rol nginx, que será otra carpeta más:

mkdir nginx

Dentro de nginx, crearemos el resto de carpetas que espera ansible:

mkdir defaults files handlers tasks templates

Por tanto, si nos vamos al directorio padre, tendríamos la siguiente estructura de carpetas:

├── roles
│   └── nginx
│       ├── defaults
│       ├── files
│       ├── handlers
│       ├── tasks
│       └── templates

Partimos del siguiente playbook:

# Este es nuestro primer playbook
- name: primera play
  become: yes
  hosts: servidor_web
  tags: primera_play
  gather_facts: false 
  vars:
    state: present
  
  tasks:

  - name: instalar nginx
    apt: name=nginx state="{{ state }}"
    notify: reiniciar nginx # Mismo nombre que el handler
    
  - name: crear index
    template:
      src: template_ejemplo_3.j2
      dest: /var/www/html/index.html
      mode: 0755

  - name: copiar script
    copy:
      src: simple_script.sh
      dest: /tmp/simple_script.sh

   handlers:     
   - name: reiniciar nginx
     service:
       name: nginx
       state: restarted

Vamos a mover los distintos elemenos de ese playbooks a donde deben estar. Por ejemplo, las tareas irán a roles/nginx/tasks/main.yaml:

---
  - name: instalar nginx
    apt: name=nginx state="{{ state }}"
    notify: reiniciar nginx # Mismo nombre que el handler
    
  - name: crear index
    template:
      src: template_ejemplo_3.j2
      dest: /var/www/html/index.html
      mode: 0755

  - name: copiar script
    copy:
      src: simple_script.sh
      dest: /tmp/simple_script.sh

Colocaremos ahora el template que usamos en el Playbook (template_ejemplo_3.j2) en roles/nginx/templates/

Hacemos lo mismo con el script simple_script.sh, pero a la ruta roles/nginx/files.

Ahora es el turno de los handlers. Añadiremos un fichero main.yaml con el contenido:

---
  - name: reiniciar nginx
    service:
      name: nginx
      state: restarted

Nos quedan las variables, crearemos el fichero roles/nginx/defaults/main.yaml:

---
state: present

Nos falta incluir este nuevo rol en algún sitio. En la carpeta raíz, creamos uno llamado, por ejemplo, nginx_rol.yaml:

---
  - hosts: web # Solo aplicamos a uno de los hosts
    become: yes
    roles:
      - nginx # Rol que vamos a incluir

Algo tarde, pero no es mal momento para comentar que podemos crear un fichero de configuración de Ansible: ansible.cfg:

[defaults]
inventory = /home/pepito/inventario
remote_user = ansible
private_key_file = /home/pepito/.ssh/id_rsa

Ahora es más cómodo lanzar el playbook de la siguiente manera:

ansible-playbook nginx_role.yaml -vv

Recursos

informatica/ciberseguridad/cursos/curso_ansible_automatizacion_it/conceptos_avanzados.1683573465.txt.gz · Última modificación: por tempwin