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
- 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).
- 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.
- 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.
- 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.
- Para gestionar las máquinas en las que Ansible debe actuar, se utiliza un archivo de inventario. Este archivo, generalmente ubicado en
- 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
- name: Instalar Java Maven
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:
state | Descripción |
---|---|
present | Instala el paquete si no está instalado. |
latest | Instala o actualiza el paquete a la versión más reciente disponible. |
absent | Elimina el paquete del sistema. |
fixed | Mantiene 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.
- Instalacion de software -> automatiza la instalacion de software en varios servidores
- Administracion y configuracion de servidores.
- 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 variableis_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.yml | message.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
- 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
- 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.
- 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