Ansible

Diferencia con Terraform
Algunos devops prefieren usar terraform para realizar modificaciones, actualizaciones o para implementar aplicaciones.
En cambio usar terraform para aprovisionar o hacer cambios en la infraestructura. (Aprovisionar Recursos).
Ansible es una herramienta utilizada para implementar aplicaciones, así como para instalar, configurar o actualizar software en múltiples servidores de manera automatizada. Pertenece a la categoría de herramientas de Infraestructura como Código (IaC), lo que permite gestionar y automatizar la infraestructura mediante scripts o playbooks.

Casos de uso

Supongamos que tienes 3 máquinas virtuales y necesitas instalar una herramienta como Java Maven, además de implementar una aplicación. Hacer esto manualmente en cada máquina no solo consumiría mucho tiempo, sino que también aumentaría el riesgo de errores humanos.

Con Ansible, puedes utilizar una máquina principal (llamada Control Node) para automatizar estas tareas. En el Control Node, se crea un playbook (un archivo en formato YAML) que contiene las instrucciones para instalar Maven e implementar la aplicación. Luego, Ansible se encarga de enviar (push) estas instrucciones a las máquinas virtuales objetivo (llamadas Managed Nodes) y ejecutarlas de manera remota.

Características clave de Ansible

  1. Arquitectura sin agentes (Agentless):
    • Los Managed Nodes no requieren la instalación de ningún software adicional (agente). Ansible se comunica con ellos a través de protocolos estándar como SSH (para Linux) o WinRM (para Windows).
  2. Idempotencia:
    • Ansible es idempotente, lo que significa que si ejecutas una tarea o un playbook varias veces, el resultado final será el mismo que si lo hubieras ejecutado una sola vez. Si una tarea ya se ha ejecutado previamente y el sistema está en el estado deseado, Ansible no realizará cambios adicionales. Esto garantiza que solo se realicen modificaciones cuando sea necesario.
  3. Módulos:
    • Las tareas en Ansible se ejecutan mediante módulos, que son pequeños programas que realizan una función específica (por ejemplo, instalar un paquete, copiar un archivo o reiniciar un servicio). Estos módulos se envían desde el Control Node a los Managed Nodes, se ejecutan y, una vez completados, se eliminan automáticamente de los nodos de destino.
  4. Inventario (Inventory):
    • Para gestionar las máquinas en las que Ansible debe actuar, se utiliza un archivo de inventario. Este archivo, generalmente ubicado en /etc/ansible/hosts, contiene las direcciones IP o nombres de host de los Managed Nodes. También puede incluir grupos de servidores para aplicar tareas de manera selectiva.
  5. Autenticación:
    • Para que Ansible pueda conectarse a los Managed Nodes y realizar cambios, es necesario proporcionar credenciales de autenticación. Esto se hace comúnmente mediante el uso de claves SSH (public key y private key). La clave pública se coloca en los Managed Nodes, mientras que la clave privada se guarda en el Control Node. Esto permite una conexión segura y sin contraseñas.

Ejemplo de configuración básica

Archivo de inventario (/etc/ansible/hosts):
[webservers]
192.168.1.10
192.168.1.11
192.168.1.12

Playbook de ejemplo (install_maven.yml):

  • name: Instalar Java Maven e implementar una aplicación
    hosts: webservers
    tasks:
    • name: Instalar Java Maven
      apt:
      name: maven
      state: present
    • name: Copiar la aplicación al servidor
      copy:
      src: /ruta/local/aplicacion.jar
      dest: /ruta/destino/aplicacion.jar

Ejecución del playbook:
ansible-playbook -i /etc/ansible/hosts install_maven.yml

Instalación:

sudo apt update
sudo apt install ansible
ansible –version

Crear las claves ssh en cada maquina Master y Nodos
ssh-keygen
ir a la carpeta /root/.ssh/
mostrar el archivo.pub
copiar el contenido del archivo.pub
-Ir a la carpeta /root/.ssh/ de las maquinas Node. Si el archivo authorized_keys no esta creado, crearlo y pegar el contenido del archivo.pub de la maquina Control-Node. -> guardar.

-Agregar las direccines Ip de los Nodos al Control-Node
Ir a la carpeta /etc/ansible/
editar el archivo hosts
Agregar:
[webservers]
192.168.1.10 por ejemplo esa

-Editar el archivo ansible.conf
agregar la linea
host_key_cheching = False
-> guardar

probar la conexion ssh
ssh root@ip_del_nodo

Probar la conexion con los nodos
ansible all -m ping
debe dar un mensaje de Success

Play y Playbook
Play: cuando se escribimos un solo programa pequeño que realiza solo una tarea.
Playbook: Cuando combinamos multiples tareas
Crear una carpeta -> acceder a la carpeta
Crear el archivo script.yaml
Ejemplo del contenido de un playbook

---
- name: Install and start Apache
  hosts: web
  become: true

  tasks:
    - name: Install Apache package
      apt:
        name: apache2
        state: present

    - name: Start Apache service
      service:
        name: apache2
        state: started
        enabled: true
    - name: Print variables
      debug: 
        msg: "Hellow {{ name }}" -> variable

hosts: web -> web es el nombre del inventario de IPs
tasks: -> listado de tareas a realizar
name: Install Apache package -> es el nombre de esa tarea

Opciones de state en el módulo apt (para sistemas Debian/Ubuntu)
El módulo apt (para manejar paquetes en Debian/Ubuntu) admite varios valores para state:

stateDescripción
presentInstala el paquete si no está instalado.
latestInstala o actualiza el paquete a la versión más reciente disponible.
absentElimina el paquete del sistema.
fixedMantiene la versión actual del paquete y evita que se actualice.

Para ejecutar un playbook
ansible-playbook script.yaml

Ansible ayudará con 3 tareas principales.

  1. Instalacion de software -> automatiza la instalacion de software en varios servidores
  2. Administracion y configuracion de servidores.
  3. Implementar aplicaciones web o cualquier tipo de aplicacion en un servidor y se puede automatizar.

Ansible se instala solo en la maquina local.
Tan pronto se ejecute el comando del playbook Ansible se conectará a los servidores remotos usando ssh y ejecutará los comandos necesarios por eso se denomina agentless. No hace falta instalar un agente en todos los servidores.

Para escribir un ansible playbook se debe seguir las convenciones de yaml
Con el siguinete comando se agrega el repositorio al Ubuntu

sudo apt-add-repository ppa:ansible/ansible
sudo apt update -y
sudo apt install ansible -y
ansible --version

Estructura de carpetas

Tenemos un servidor remoto con una direccion ip publica.
-Archivo hosts: aca se escriben las ip o los nombres de dominio de o de los servidores remotos. Este archivo va dentro de una carpeta llamada inventory y dentro de una carpeta de nombre representativo. (a la izquierza de la imagen). Pueden haber mas de un archivo hosts.
-roles y tasks: el archivo mail.yml es una tarea presente dentro de un rol y el nombre del rol es python.
El playbook de ansible puede tener varios roles y cad rol puede tener una tarea. roles->python->tasks-main.yml.
-dentro del archivo main.yml de cada tarea se encuentran las instrucciones que se requiere ejecutar en el servidor remoto.
-El archivo en el ejemplo vm-setup-playbook.yml es el archivo que contiene dos elementos principales. Uno: hosts -> el o los host a los que se va a conectar (hosts: all -> todos los host), que estan en el archivo hosts. Dos: el remote_user: ubuntu . Es el nombre de usuario del servidor remoto. Tres: roles: -python -> es el nombre del rol que se debe ejecutar

Para ejecutar ese playbook se hace con el comando siguiente:
1 ansible-playbook
2 especificar la ruta del host que queremos correr –inventory inventory/vm-setup-playbook/hosts
3 el nombre el playbook vm-setup-playbook.yml
quedando el comando de la siguiente manera:

ansible-playbook  --inventory inventory/vm-setup-playbook/hosts vm-setup-playbook.yml

El contenido muy básico de los archivos principales e ansible son
hosts:

[default]
18.206.57.41 ansible_ssh_private_key_file=~/.ssh/wordpress-server.key

Muy recomendable guardar la llave privada en la carpeta ~.ssh/
Si tu clave está en otra ubicación, revisa tu configuración SSH en:
cat ~/.ssh/config
Otros comandos:

    ssh-keygen
    sh.copy-id -i ansible-demo.pub richard@ip_remota

main.yml:

- name: Creates directory
  file:
    path: ./basic-http-server
    state: directory

vm-setup-playbook.yml:

---
- name: Example Ansible playbook
  hosts: all
  remote_user: ubuntu
  roles:
    - directory

En caso de no usar el par de llaves, se requerirá usuario y password de los hosts remotos

Como escribir YAML

Identacion o sangria:

Cuando se escribe un archivo yaml se comienta con 3 guiones seguidos —
Ejemplo para crear un directorio

- name: Creates directory
  file:
    path: ./basic-http-server
    state: directory

en la siguiente línea viene el nombre – name: Create Directory -> comienza con un guion. Esto define el nombre de lo que hace la tarea.
La tercera línea depende del playbook de ansible que se quiere implementar. Por ejemplo file: (file: es el nombre del módulo de ansible). El modulo file en ansible es reponsable de crear o eliminar directorios. Va separado de un tab.
Las siguientes líneas definen los atributos hijos. Siempre que se use un modulo file: se debe definir los atributos hijos que son path: y state:
Van separados con 2 tabs.
En este ejemplo se quiere crear un directorio con el módulo file: , entonces se requiere especificar el path o ruta del directorio a crear. Y el state: va a definir si se va a crear o a eliminar.

Ansible Handlers

Los handlers en Ansible son tareas especiales que solo se ejecutan cuando son notificadas por otra tarea dentro del playbook. Se utilizan comúnmente para reiniciar servicios después de que se realicen cambios en la configuración.
Cómo funcionan los Handlers?
Se definen dentro de la sección handlers de un playbook.
Se activan solo si una tarea los “notifica”.
Se ejecutan al final del bloque de tareas, pero solo si han sido notificados.

#mail.yml
- name: Configurar servidor web
  hosts: servidores
  become: yes
  tasks:
    - name: Copiar archivo de configuración de Nginx
      copy:
        src: nginx.conf
        dest: /etc/nginx/nginx.conf
      notify: Reiniciar Nginx  # Notifica al handler si el archivo cambia

  handlers:
    - name: Reiniciar Nginx
      service:
        name: nginx
        state: restarted

#ansible-handlers-playbook.yml
- name: Example Ansible playbook for Handlers
  hosts: all
  become: yes
  remote_user: ubuntu
  roles:
    - handlers

  handlers:
    - name: Restart Nginx
      service:
        name: nginx
        state: restarted

Variable en Ansible

Ejemplo:

- name: Ejemplo de Variables en Ansible
  hosts: servidores
  become: yes
  vars:
    usuario: "admin"
    puerto_ssh: 2222
  tasks:
    - name: Mostrar el usuario y el puerto
      debug:
        msg: "Usuario: {{ usuario }}, Puerto SSH: {{ puerto_ssh }}"

Las variables se definen con la palabra reservada vars: y seguidamente se asigna un nombre y un valor.
Para usarlas se debe llamar con la palabra reservada tasks: y dentro un paramtro debug: llamando a la variable asignandole un nombre y luego llamando a la o las variables definidas

Boolean variable
is_enabled: false

    - name: Boolean variable
      debug:
        msg: "Variable is true"
      when: is_enabled

Si la variable es verdadera imprime el valor de la variable msg, solo cuando la variable en particular es true (when: is_enabled)

Variables Lista : contiene elementos únicos

 fruits:
      - apple
      - banana
      - orange
    - name: List variable - Print list of fruits
      debug:
        var: fruits

Para acceder a un elemento en particular

    - name: List variable - Reference individual item in list
      debug:
        var: fruits[0]de la lista se utiliza indice

Dictionary Variable: objetos JSON para organizar variables de forma estructurada. Son útiles cuando necesitas agrupar datos relacionados, como configuraciones de usuarios, puertos o servicios.

    fruit_prices:
      apple: 0.5
      banana: 0.25
      orange: 0.75
   - name: Dictionary Variable - Accessing all dictionary variable
      debug:
        var: fruit_prices

Acceder a un elemento particular en variable diccionario

    - name: Dictionary Variable - Accessing individual specific fields
      debug:
        var: fruit_prices.apple

Obtener el valor de un elemento del diccionario y asignarlo a otra variable

    - name: Get the price of an apple
      command: echo "{{ fruit_prices['apple'] }}"
      #Register the price of a apple to new variable - apple_price
      register: apple_price_as_registered_var

    - name: Print the value of register variable
      debug:
        var: apple_price_as_registered_var.stdout

Diccionario de variables anidadas
Referencing nested variable

    fruit_basket:
      - name: John
        fruits:
          - apple
          - orange
      - name: Jane
        fruits:
          - banana
          - apple
          - orange
    - name: Get the value of apple from the nested variable
      debug:
        var: fruit_prices[fruit_basket[0].fruits[0]]

Filtros Jinja2
Jinja2 en Ansible permite manipular datos usando filtros. Estos filtros se aplican con | (pipe) y son muy útiles para transformar variables dentro de tus playbooks y templates.
[[Filtros Jinja2]]

Jinja 2 filter on variables

    - name: Using Jinja 2 filters on variables
      debug:
        var: fruit_prices.keys() | list | map('upper') | list

En el ejemplo, se accede a la key de fruit_prices dictionary, luego se convierte esas keys en listas | list -> | es un filtro de Jinja que convierte esas keys en lista, luego se usa el mismo filtro | para convertirlo en mayúsculas | map('upper') , y luego se convertirá nuevamente en lista | list.
Al ejecutarlo, pasamos de esto:
fruit_prices:
      apple: 0.5
      banana: 0.25
      orange: 0.75
 a esto ->
 “APPLE”
 “BANANA”
 “ORANGE”

Accessing variable inside playbook from my-vars.yml

    - name: Get the value of variable from my-vars.yml
      debug:
        var: vars_from_my_vars_yml

Si se requiere crear variables dentro de un archivo var.yml fuera del playbook.
En el playbook:

  vars_files:
    - my-vars.yml

se crea el archivo my-vars.yml
y dentro tiene el siguiente contenido:

---
vars_from_my_vars_yml: "This is variable value is coming from my-vars.yml file"
other_variable: "foo"

Para acceder a esa variable en particular desde el playbook:

#Accessing variable inside playbook from my-vars.yml
    - name: Get the value of variable from my-vars.yml
      debug:
        var: vars_from_my_vars_yml

Pasar variables en tiempo de ejecucion (runtime)

#Defining and accessing the variable at RunTime
    - name: Get the value from run time
      debug:
        var: version

var: version no esta declarada en ninguna parte.
Se debe llamar de la siguiente forma:

## Run playbook with run time variables

ansible-playbook --inventory inventory/ansible-variable-playbook/hosts ansible-variables-playbook.yml --extra-vars '{"version":"1.0","other_variable":"foo-world"}'

Pasar el archivo de variables desde el command line:
En lugar de pasar una variable individual en runtime, se pasa el nombre completo del archivo my-vars.yml en tiempo de ejecucion.

#Defining and accessing the variable at RunTime
    - name: Print the value of variable when var file is passed at run time
      debug:
        var: other_variable

Se ejecuta asi:

## Run playbook with vars from YAML file
ansible-playbook --inventory inventory/ansible-variable-playbook/hosts ansible-variables-playbook.yml --extra-vars "@my-vars.yml"

Variable de Precedencia:
Como ocurre la precedencia de variables cuando hay múltiples variables con el mismo nombre:
Ejemplo:
En el archivo my-vars.yml tenemos la variable -> other_variable: “foo, pero en la ejecucion del comando se esta pasando la misma variable con valor diferente -> “other_variable”:”foo-world”.
ansible-playbook –inventory inventory/ansible-variable-playbook/hosts ansible-variables-playbook.yml –extra-vars ‘{“version”:”1.0″,“other_variable”:”foo-world”}’
Un valor esta en el archivo my-vars.yml y el otro valor se esta pasando por el runtime desde la linea de comandos.
En este caso predomina el valor pasado en la línea de comandos.

Variables de entorno

$PATH $HOME $USER
El playbook de ansible permite actualizar y utilizar las variables de entorno cuando se esta escribiendo un playbook.
Con la ayuda de Ansible se puede configurar variables de entorno personalizadas que sean necesarias para el playbook y se pueden usar a nivel de tareas o en a nivel de playbook global.
Para crear una variable de entorno se hace con la palabra clave environment: , y debajo un nombre de variable con un valor, como por ejemplo EXAMPLE: “Foo bar”. Esa variable de entorno estará disponible para alas tareas creadas dentro de el playbook.

 environment:
    EXAMPLE: "Foo bar"
    MY_VAR1: "variable value1"

También se puede definir una variable de entorno que no sea global pero que sea muy especifica para una tarea en particular, se puede hacer es la siguiente forma:

# Environment variable only for tasks
    - name: Environment var for only task Level
      environment:
        MY_TASK_LEVEL_VARIABLE: "Hello World!"
      ansible.builtin.command: "echo $MY_TASK_LEVEL_VARIABLE"

Para llamar a la variable:

 tasks:
    # Environment variable at Playbook Level
    - name: Environment var at Playbook Level
      ansible.builtin.command: "echo $EXAMPLE"

Y de esta forma se puede acceder a ambas variables de entorno:

#Testing Environment variable
    - name: Testing both playbook level as well as environment level vars.
      ansible.builtin.command: "echo $EXAMPLE $MY_TASK_LEVEL_VARIABLE "

De es manera solo se podria acceder a la variable $EXAMPLE, pero no a la variable $MY_TASK_LEVEL_VARIABLE porque esa variable no es global y queda fuera del alcance de esta tarea en particular.
Ejemplo de el playbook

---
- name: Example Ansible playbook for Handlers
  hosts: all
  become: yes
  remote_user: ubuntu
  roles:
    - custom-role

  environment:
    EXAMPLE: "Foo bar"
    MY_VAR1: "variable value1"

  tasks:
    # Environment variable at Playbook Level
    - name: Environment var at Playbook Level
      ansible.builtin.command: "echo $EXAMPLE"

    # Environment variable only for tasks
    - name: Environment var for only task Level
      environment:
        MY_TASK_LEVEL_VARIABLE: "Hello World!"
      ansible.builtin.command: "echo $MY_TASK_LEVEL_VARIABLE"

    #Testing Environment variable
    - name: Testing both playbook level as well as environment level vars.
      ansible.builtin.command: "echo $EXAMPLE $MY_TASK_LEVEL_VARIABLE "

Comando para ejecutar el playbook:

ansible-playbook --inventory inventory/ansible-env-variable-playbook/hosts ansible-env-variable-playbook.yml -v

-v pára poder ver el resultado de las tareas

Condicionales en Ansible

En Ansible se tiene la condicional when.
Se ejecutará un codigo siempre que se cumpla con una condicion flag.
Por ejemplo teniendo una variable booleana en true se podrá instalar apache.
archivo vars

---
install_apache_flag: true

archivo main.yml

---
## Boolean Conditional check for installing Apache
- name: Install Apache web server
  apt:
    name: apache2
    state: absent
  when: install_apache_flag

En este casa so instalará apache cuando la variable install_apache_flag este en true. De lo contrario apache no se instalará usando ansible.

Condiciones Multiples

## Putting multiple condition using ansible_facts
- name: Combine conditions using ansible facts
  debug:
    var: ansible_facts['kernel_version']
  when:
    - ansible_facts['os_family'] == "Debian"
    - ansible_facts['distribution_major_version'] == "20"

En este caso existen 2 condiciones when. Solo cuando las dos estén en true se ejecutará la sentencia.

Condicional AND
Se puede especificar multiples condiciones pero en lugar de colocar la condicion en la siguiente línea, se puede utilizar el operador AND y especificar la condicion en una sola línea.

## Setting complex condition
- name: Setting complex condition
  debug:
    var: ansible_facts['kernel_version']
  when: ansible_facts['os_family'] == "Debian" and ansible_facts['distribution_major_version'] | int >= 20

Condicional ON
Se trata de colocar una condición a un valor particular y ese valor puede ser almacenado dentro de una variable y podamos especificar condiciones basadas en el valor de una variable en especifico. Ejemplo:
Registrar una variable:

## Register a variables and evaluate the value using when condition
- name: Register a variable
  ansible.builtin.command: cat /home/ubuntu/test-file.txt
  register: test_file_content

Acá se registra una variable con la palabra reservada register: y estamos especificando una variable con el nombre que es el contenido de un archivo -> register: test_file_content. Y dentro de esa variable especifica se almacenará el valor de ese archivo de texto especifico -> /home/ubuntu/test-file.txt este archivo debería estar presente en el host remoto con un valor

Poner (PUT) una condición en una variable

- name: Use the variable in conditional statement
  debug:
    var: test_file_content.stdout
  when: test_file_content.stdout.find('hi') != -1 #and test_file_content is succeeded

Acá se coloca una condicion when y se esta especificando una condicion. Buscar la palabra ‘hi’ en el archivo test_file_content.
test_file_content.stdout
‘est_file_content es una variable que almacena la salida de una tarea anterior que probablemente ejecutó un comando usando command, shell o script.
.stdout hace referencia al contenido de la salida estándar del comando ejecutado. stdout -> Standard Output.
Se verifica si la palabra “hi” está dentro del contenido de stdout.
.find(‘hi’) devuelve la posición de la primera aparición de “hi” en la salida estándar o -1 si no se encuentra.
Si “hi” está presente, la tarea se ejecuta.

Condicinal With Loop (Bucles)

## When with Loop
- name: Run with items greater than 5
  ansible.builtin.command: echo {{ item }}
  loop: [ 0, 2, 4, 6, 8, 10 ]
  when: item > 5

Palabra clave loop y se especifica la matriz de valores loop: [ 0, 2, 4, 6, 8, 10 ] y dependiendo de la condicion va a imprimir ciertos valores de la matriz. En el ejemplo solo imprimirá los valores mayores a 5 when: item > 5.
Se evalúa la condicion y se compara con cada valor de la matriz, lo que coincida se guardará en la variable {{ item }} y se imprimirá ansible.builtin.command: echo {{ item }}.

Ansible Facts
Los Ansible Facts son variables del sistema que Ansible recopila automáticamente sobre los hosts en los que se ejecuta. Estas variables contienen información detallada sobre el sistema operativo, hardware, red, almacenamiento y más. Es como un metadato de esa maquina remota en particular.

#Conditionals based on ansible_facts
- name: How to use ansible_facts
  debug:
    var: ansible_facts['distribution']
    #var: ansible_facts['distribution_major_version']
    #var: ansible_facts['kernel_version']
    #var: ansible_facts
  when: ansible_facts['os_family'] == "Debian"

Evaluar multiple task

## Putting multiple condition using ansible_facts
- name: Combine conditions using ansible facts
  debug:
    var: ansible_facts['kernel_version']
  when:
    - ansible_facts['os_family'] == "Debian"
    - ansible_facts['distribution_major_version'] == "20"

El ejempo tiene una condicion when con dos condiciones especificas que estan evaluando ansible_facts que son la familia del sistema operativo os_family si es Debian. Y en segundo lugar la version de la distribucion que sea igual a la version 20. Si estas dos condiciones ansible_facts se cumplen mostrará en pantalla la version del kernel var: ansible_facts[‘kernel_version’].

Otro ejemplo con AND condition:

## Setting complex condition
- name: Setting complex condition
  debug:
    var: ansible_facts['kernel_version']
  when: ansible_facts['os_family'] == "Debian" and ansible_facts['distribution_major_version'] | int >= 20

Registrar una variable y asignarle un valor a esa variable.

## Register a variables and evaluate the value using when condition
- name: Register a variable
  ansible.builtin.command: cat /home/ubuntu/test-file.txt
  register: test_file_content

- name: Use the variable in conditional statement
  debug:
    var: test_file_content.stdout
  when: test_file_content.stdout.find('hi') != -1 #and test_file_content is succeeded

Se registra una variable register: test_file_content y luego se evalua el contenido de esa variable when: test_file_content.stdout.find(‘hi’) != -1 #and test_file_content is succeeded. Se espera que exista el archivo ansible.builtin.command: cat /home/ubuntu/test-file.txt . Es decir, se busca el valor ‘hi’ en el archivo dentro de la variable y luego se evalua la condicion.
En este caso se utiliza el comando cat y se registra en la variable test_file_content y con el stdout.find se compara el valor. Luego imprime el valor de la variable para que sepamos que la condicion ha sido evaluada.

Ejemplo con loop

## When with Loop
- name: Run with items greater than 5
  ansible.builtin.command: echo {{ item }}
  loop: [ 0, 2, 4, 6, 8, 10 ]
  when: item > 5

Multiple Ansible_facts dentro de la condicional when

## Conditionals based on ansible_facts
- name: How to use ansible_facts
  debug:
    var: ansible_facts['distribution']
    #var: ansible_facts['distribution_major_version']
    #var: ansible_facts['kernel_version']
    #var: ansible_facts
  when: ansible_facts['os_family'] == "Debian"

Tareas y Roles


En Ansible, un role es una forma estructurada de organizar código para facilitar la reutilización y modularidad. Los roles permiten dividir un playbook en componentes reutilizables, facilitando la gestión de configuraciones complejas.
La imagen muestra el la estructura simple de una playbook ansible.
Dentro de la carpeta roles existendos carpetas. custom-roles e install-apache. Cada uno es un rol identificado por un nombre de rol.
Cada rol debe estar dentro del playbook principal.

---
- name: Example Ansible playbook for Handlers
  hosts: all
  become: yes
  remote_user: ubuntu
  roles:
    - custom-role
    - install-apache

En Ansible, los roles dentro de un playbook principal se ejecutan en el orden en que aparecen.
Si algún role depende de otro, es importante colocarlos en el orden correcto para evitar errores.
Las tareas dentro de un role también siguen el orden en tasks/main.yml.
Si un role depende de otro, puedes definirlo en meta/main.yml para garantizar el orden.

Ansible Tasks

Es una acción específica que se ejecuta en los servidores objetivo dentro de un playbook o un role.
Cada tarea usa un módulo para realizar una acción, como instalar paquetes, copiar archivos o reiniciar servicios.
Se debe crear una carpeta dentro del rol al que pertenecerá con el nombre tasks y contendrá al menos un archivo main.yml donde se definen las tareas del role.
Ejemplo tarea main.yml del rol install-apache:

#main.yml
- hosts: servidores_web
  tasks:
    - name: Instalar Apache
      apt:
        name: apache2
        state: present

    - name: Copiar archivo de configuración
      copy:
        src: config.conf
        dest: /etc/apache2/sites-available/config.conf

    - name: Reiniciar Apache después de la configuración
      service:
        name: apache2
        state: restarted

1️ Se instala Apache.
2 Se copia el archivo de configuración.
3 Se reinicia el servicio para aplicar los cambios.

Subtasks dentro de tasks
Ejemplo:
Se tiene el mail.yml con el contenido:

---
- import_tasks: packages.yml
- import_tasks: message.yml
- name: Start nginx service
  service:
    name: nginx
    state: started

Este main.yml depende de las subtareas packages.yml y message.yml y son tareas que se definen en la misma carpeta de tasks:

packages.ymlmessage.yml

– name: Install required packages
apt:
name:
– nginx
– git
– curl
state: latest
update_cache: yes

– name: Set message file
copy:
content: “{{ message }}”
dest: /var/www/html/index.html
Entonces deben importarse en el main.yml
  • import_tasks: packages.yml
  • import_tasks: message.yml y luego se procede a ejecutar la tarea principal en este caso iniciar nginx server.

Jinja2 en Ansible

En Ansible, Jinja2 se usa dentro de archivos YAML o archivos de plantilla .j2 para generar contenido dinámico.
Jinja2 usa {{ }} para expresiones y {% %} para estructuras de control.
Beneficios: Configuracion dinámica y scripts dinamicos.
Simpre que se quieran usar deberan ser configurados en la plantilla jinja2.

Crear una plantilla jinja2
Cualquier plantilla jinja2 se debe crear con el nombre de la plantilla y la extension .j2 y puede tener cualquier nombre my_config.j2
ejemplo:

server.document-root        = "/var/www/html"
server.upload-dirs          = ( "/var/cache/lighttpd/uploads" )
server.errorlog             = "/var/log/lighttpd/error.log"
server.pid-file             = "/run/lighttpd.pid"
server.username             = "www-data"
server.groupname            = "www-data"
server.port                 = {{ server_port }}

La plantilla jinja2 tiene la configuracion de un servidor http. server.port = {{ server_port }} -> se esta intentando colocar el valor dinamico para el puerto del servidor (interpolation syntax).
La interpolacion comienza con llaves dobles en ambos lados y en medio la variable dinamica que será proporcionado por el ansible playbook.

Como llamar a la plantilla Jinja2 y pasarle el valor de la variable dinamica dentro de la interpolacion:
en el main.yml principal

- name: Generate configuration file
  template:
    src: myconfig.j2
    dest: /etc/lighttpd/lighttpd.conf
    owner: root
    group: root
    mode: '0644'
  vars:
    server_port: 9090

En primer lugar se debe usar la palabra clave template: para que el ansible playbook sepa que se va a utilizar una plantilla jinja2. Luego se debe especificar el nombre del archivo.j2 src: myconfig.j2 y la ruta preferiblemente en el mismo directorio.
Lo tercero mas importante es pasar el valor de la variable, en este caso server_port: , por lo que se debe usar la palabra clave vars: dentro del playbook y seguidamente el valor que se requiere asiganar.

Estrategias de despliegue:

En Ansible, los despliegues pueden implementarse de diferentes maneras según los requerimientos de disponibilidad, continuidad del negocio y gestión del cambio. Los tres enfoques principales son Single Deployment, Multiple Deployment y Blue/Green Deployment.
Single Deployment (Despliegue Único)
Este es el enfoque más básico y directo. Consiste en ejecutar los playbooks de Ansible en un único entorno de producción.
Características:
Se actualizan los servidores en vivo.
No hay una versión previa a la que se pueda regresar fácilmente.
Tiempo de inactividad potencial si la implementación falla.

Despliegue en Múltiples Servidores (Multi Server Deployment)
Este enfoque implica tener varios entornos (por ejemplo, desarrollo, prueba y producción). Se prueba cada actualización en un entorno antes de pasar al siguiente.
Reduce el riesgo de fallos en producción.
Facilita pruebas antes del despliegue final.
Puede requerir más recursos para gestionar múltiples entornos.
Cuándo usarlo:
Para entornos empresariales que requieren validaciones antes de producción.
Cuando se desea implementar cambios de forma progresiva.

Despliegue Blue/Green
En este modelo, se tienen dos entornos idénticos: Blue (actual) y Green (nuevo). El tráfico se dirige inicialmente a Blue. Cuando se implementa una nueva versión en Green y se prueba correctamente, el tráfico se cambia a Green, minimizando el tiempo de inactividad.
Reducción del riesgo: si algo sale mal, se puede volver al entorno anterior rápidamente.
Casi cero tiempo de inactividad.
Puede requerir más recursos para mantener dos entornos activos.
Cuándo usarlo:
Para aplicaciones críticas con alta disponibilidad.
Cuando se necesita un rollback rápido en caso de error.
Siempre que se tienen muchos servidores, es difícil recordar las direcciones IP de cada uno. En ese caso, en Ansible se crean categorías, como Blue y Green.
Categoría Green: En la categoría Green, tenemos dos servidores que están funcionando activamente en el ambiente de producción y atendiendo las solicitudes de los clientes.
Categoría Blue: La categoría Blue también está en el ambiente de producción, pero no está activa atendiendo solicitudes de los clientes.
Podemos decir que Green es activo y Blue es pasivo.
Este modelo permite que, al momento de realizar un nuevo despliegue o release en producción, se implemente primero en los servidores de la categoría Blue, ya que estos no están sirviendo solicitudes de los clientes.
Una vez finalizado el despliegue en los servidores Blue, el tráfico se redirige desde los servidores Green hacia los servidores Blue, y estos pasan a formar parte de la categoría Green y los que estaban en Green pasarían a la categoría Blue.

El beneficio de este enfoque es que no se necesita tiempo de inactividad en el servidor de producción. Las aplicaciones estarán siempre disponibles, y solo es necesario intercambiar el rol de los servidores Blue y Green.
Como implementar usando el playbook de Ansible?
Usando los flgs –limit blue o –limit green al final del comando para ejecutar el playbook.
Ejemplo:
ansible-playbook --inventory inventory/ansible-deployment-strategy/hosts ansible-deployment-strategy.yml --limit blue
Ejemplo del archivo hosts

[blue]
# Server-1
52.59.205.47 ansible_ssh_private_key_file=/Users/rahulwagh/.ssh/aws_ec2_terraform
# Server-2
18.194.203.12 ansible_ssh_private_key_file=/Users/rahulwagh/.ssh/aws_ec2_terraform

[green]

# Server-3 3.75.236.253 ansible_ssh_private_key_file=/Users/rahulwagh/.ssh/aws_ec2_terraform # Server-4 3.123.154.105 ansible_ssh_private_key_file=/Users/rahulwagh/.ssh/aws_ec2_terraform

Manejo de errores en Ansible

Escenario 1: Copiar un archivo a dos servidores (Server-1 y Server-2).
Si Server-1 está fuera de línea, el archivo no podrá copiarse en él. Aunque Server-2 esté en línea, si ocurre un error durante la copia, el playbook fallará si no se ha implementado un mecanismo de manejo de errores.
Para evitar que el playbook se detenga por completo, se puede utilizar la opción ignore_errors en la tarea correspondiente. Esto permitirá que Ansible ignore los errores y continúe con la ejecución del playbook.

---
# 1. ignore_errors - Ignoring the error
- name: index.html copy with ignore_error
  template: src=index.html dest=/home/ubuntu
  register: copy_result
  ignore_errors: true

Otro escenario ocurre cuando es necesario que la ejecución de un playbook falle forzosamente.

Por ejemplo, supongamos que se intenta copiar un archivo de configuración esencial para un despliegue en producción, pero la ruta de destino es incorrecta. Como resultado, el archivo no se copia en el lugar requerido, lo que podría causar fallos en la aplicación. En este caso, es fundamental que el playbook se detenga inmediatamente para evitar problemas mayores.

Para este tipo de escenario, se puede utilizar la opción any_errors_fatal, que forzará la detención del playbook en caso de error. Esto permite corregir el problema antes de continuar con la implementación.

#any_errors_fatal
- name: index.html copy with failed_when
  template: src=index.html dest=/home/ubuntu
  failed_when:
    - '"Could not find or access" not in copy_result.msg'
    - copy_result.failed == true
  any_errors_fatal: false

Se debe proporcional una condicional. “failed_when: – copy_result.failed == true”

Manejo de errores con changed_when: dice cuando algo ha cambiado en el servidor remoto

#any_errors_fatal
- name: index.html copy with failed_when
  template: src=index.html dest=/home/ubuntu
  failed_when:
    - '"Could not find or access" not in copy_result.msg'
    - copy_result.failed == true
  any_errors_fatal: false
#
#
- name: Create a file if it does not exist
  command: touch /home/ubuntu/myfile.txt
  register: file_created
  changed_when: file_created.rc == 0

Ansible Vault: Manejo Seguro de Credenciales y Datos Sensibles

Ansible Vault es una herramienta integrada en Ansible que permite cifrar y proteger información sensible, como contraseñas, claves de acceso, archivos de configuración y datos críticos dentro de los playbooks.
Principales Características
Cifrado de Datos Sensibles: Protege credenciales y archivos con una clave de cifrado.
Integración con Playbooks: Permite usar variables encriptadas dentro de los playbooks sin exponer información sensible.
Control de Acceso: Solo quienes tengan la clave correcta pueden descifrar la información.
Compatibilidad con Git: Ideal para almacenar configuraciones en repositorios sin comprometer la seguridad.
Se utiliza cuando se requiere cifrar datos que no se quieren mostrar en formato de texto simple mientrs se trabaja en el playbook.
Los comandos básicos para operar en un Ansible Vault son:
Create Vault:

ansible-vault create group_vars/my_vault.yml

Edit Vault:

ansible-vault edit my_vault.yml

View Vault:

ansible-vault view my_vault.yml

Como utilizar Ansible Vault en el playbook

  1. Ejecutando el playbook con el parámetro --ask-vault-pass
    Se debe ingresar el password en la línea de comandos.
ansible-playbook playbook.yml --ask-vault-pass
  1. Almacenar la contraseña en un archivo de contraseñas de Ansible Vault (--vault-password-file)
    Si no se desea ingresar la contraseña manualmente en cada ejecución, se puede almacenarla en un archivo de texto plano y proporcionarlo al ejecutar el playbook:
ansible-playbook playbook.yml --vault-password-file=password.txt

Esto evita tener que ingresar la contraseña cada vez.
⚠️ Importante: El archivo que contiene la contraseña no debe subirse a Git por razones de seguridad.

  1. Uso de Variables de Entorno
    También es posible definir la contraseña en una variable de entorno para evitar ingresarla manualmente o almacenarla en un archivo visible:
export ANSIBLE_VAULT_PASSWORD_FILE=./ansible-vault.pass

Esto permite que Ansible use automáticamente la contraseña desde la variable de entorno sin necesidad de pasarla por archivo de texto ni ingresarla en la terminal.

Crear un vault
En la consola
ansible-vault create group_vars/my_vault.yml
Pedirá una contraseña y confirmar contraseña.
Se abrirá un editor vim.
Tecla i para INSERT
escribir my_secret: “Mi vault secreto”
:wq -> para salir

Este archivo de Vault puede contener por ejemplo: coneciones a bases de datos, contraseñas a bases de datos, credenciales, etc.
Para usarlo se debe tener el nombre de la variable my_secret

---
- name: Show vault variable value
  debug:
    var: my_secret

Para usarlo en el playbook se debe establecer los parametros e indicar la ruta del archivo .yml que contiene el Vault y el parametro –ask-vault-pass

ansible-playbook --inventory inventory/ansible-vault/hosts ansible-vault-playbook.yml -e @group_vars/my_vault.yml --ask-vault-pass

Cambiar la contraseña del Ansible Vault con la palabra clave rekey
ansible-vault rekey group_vars/my_vault.yml

Generar una contraseña base64

openssl rand -base64 2048 > pass_file/ansible-vault.pass

Crear un Ansible Vault con la contrasela base64 generada:

ansible-vault create group_vars/my_vault_with_bas64_pass.yml --vault-password-file=pass_file/ansible-vault.pass

Se abrirá un editor vim.
Tecla i para INSERT
escribir my_secret: “Mi vault secreto”
:wq -> para salir

Ver el contenido de el Vault con el archivo pass

ansible-vault view group_vars/my_vault_with_bas64_pass.yml --vault-password-file=pass_file/ansible-vault.pass

Exportar la ruta del archivo de password a Variables de Entorno ANSIBLE_VAULT_PASSWORD_FILE -> nombre de la variable
Una vez exportata la contraseña a las variables de entorno, no se requerirá la contraseña.

export ANSIBLE_VAULT_PASSWORD_FILE=/ruta_completa/pass_file/ansible-vault.pass

Para verla:
echo $ANSIBLE_VAULT_PASSWORD_FILE

Etiquetado:

Agregar un comentario

Tu dirección de correo electrónico no será publicada. Los campos requeridos están marcados *