Use etiquetas de Ansible para ahorrar tiempo en las ejecuciones de libros de jugadas

Como usuario frecuente de Ansible, siempre busco formas de simplificar mis playbooks y ahorrar tiempo al depurar playbooks. Una de mis características favoritas para escribir playbooks robustos de Ansible es su compatibilidad con etiquetas. Este artículo presenta etiquetas, revisa algunos escenarios de etiquetas comunes y describe un uso más avanzado.
¿Qué son las balizas?
Palabras clave son metadatos que puede adjuntar a tareas en un libro de jugadas de Ansible. Le permiten apuntar selectivamente a ciertas tareas en tiempo de ejecución, diciéndole a Ansible que realice (o no realice) ciertas tareas. Si bien normalmente ejecuta un libro de jugadas completo de Ansible de principio a fin, puede ser extremadamente útil para completar tareas específicas en un libro de jugadas bajo demanda.
Como ejemplo, el libro de jugadas a continuación define dos etiquetas: hello
y goodbye
. Puede usar estas etiquetas para controlar cómo Ansible ejecuta el libro de jugadas:
---
- hosts: localhost
gather_facts: False
tasks:
- name: Hello tag example
debug:
msg: "Hello!"
tags:
- hello
- name: No tag example
debug:
msg: "How are you?"
- name: Goodbye tag example
debug:
msg: "Goodbye!"
tags:
- goodbye
Puede decirle a Ansible que ejecute solo etiquetas con el -t
Donde --tags
bandera:
$ ansible-playbook basic_example.yml --tags hello
PLAY [localhost]
*******************************************
TASK [Hello tag example]
*******************************************
ok: [localhost] => {
"msg": "Hello!"
}
PLAY RECAP
*******************************************
localhost: ok=1 changed=0 unreachable=0
failed=0 skipped=0 rescued=0 ignored=0
También puede decirle a Ansible que ignore las tareas con ciertas etiquetas usando el --skip-tags
bandera:
$ ansible-playbook basic_example.yml --skip-tags goodbye
PLAY [localhost]
*******************************************
TASK [Hello tag example]
*******************************************
ok: [localhost] => {
"msg": "Hello!"
}
TASK [No tag example]
*******************************************
ok: [localhost] => {
"msg": "How are you?"
}
PLAY RECAP
*******************************************
localhost: ok=2 changed=0 unreachable=0
failed=0 skipped=0 rescued=0 ignored=0
Finalmente, puede enumerar las etiquetas en su libro de jugadas usando el --list-tags
bandera:
$ ansible-playbook basic_example.yml --list-tags
playbook: basic_example.yml
play #1 (localhost): localhost TAGS: []
TASK TAGS: [goodbye, hello]
Asegúrese de que una tarea se ejecute siempre (o nunca)
Al usar etiquetas en sus libros de jugadas, encontrará que a veces tiene tareas que siempre desea completar, incluso cuando usa --tags
Donde --skip-tags
. Por ejemplo, imagine un libro de jugadas que instala, configura y verifica el software de su empresa. Tiene una tarea que obtiene la última versión de la aplicación para instalar al realizar una solicitud a un punto final web. Utiliza etiquetas para distinguir cada paso, en caso de que desee ejecutar una determinada parte del libro de jugadas para ahorrar tiempo. Sin embargo, desea que la tarea de versión de software se ejecute siempre porque contiene información que es relevante para todas las demás tareas. Si un usuario apunta solo a tareas marcadas con configure
, el libro de jugadas siempre debe ejecutar la tarea de versión de software para obtener información crítica. Puedes lograr esto con el always
etiqueta:
---
- hosts: appservers
gather_facts: False
tasks:
- name: Obtain latest software version from API
uri:
url: "http://config-server.example.com:8080/api/v1/appVersion"
register: app_version
delegate_to: localhost
tags:
- always
- name: Install software
package:
name: "{{ app_version.json.version }}"
state: present
tags:
- install
- name: Configure software
template:
src: templates/app_config.json.j2
dest: /etc/app/app_config.json
mode: 0644
owner: root
group: root
tags:
- configure
- name: Verify software
command: /usr/local/sbin/test_app.sh {{ app_version.json.version }}
tags:
- verify
Cuando no se transmite ninguna baliza, todo el libro de jugadas se ejecuta, como se esperaba:
$ ansible-playbook -i inventory.ini always_runs.yml
PLAY [appservers]
*************************************************************
TASK [Obtain latest software version from API]
*************************************************************
ok: [appserver.example.com -> localhost]
TASK [Install software]
*************************************************************
ok: [appserver.example.com]
TASK [Configure software]
*************************************************************
ok: [appserver.example.com]
TASK [Verify software]
*************************************************************
changed: [appserver.example.com]
PLAY RECAP
*************************************************************
appserver.example.com : ok=4 changed=1 unreachable=0
failed=0 skipped=0 rescued=0 ignored=0
Cuando el configure
se pasa, la tarea de configuración se ejecuta como se esperaba. La tarea de versión de software también se ejecuta porque tiene la always
etiqueta:
$ ansible-playbook -i inventory.ini always_runs.yml -t configure
PLAY [appservers]
*************************************************************
TASK [Obtain latest software version from API]
*************************************************************
ok: [appserver.example.com -> localhost]
TASK [Configure software]
*************************************************************
ok: [appserver.example.com]
PLAY RECAP
*************************************************************
appserver.example.com : ok=2 changed=0 unreachable=0
failed=0 skipped=0 rescued=0 ignored=0
Ansible también proporciona la never
etiqueta, que hace lo contrario de always
. Garantiza que una tarea nunca se ejecutará a menos que el usuario la apunte específicamente. Imagine que no desea que se ejecute la tarea de verificación a menos que el usuario del libro de jugadas la llame específicamente. el never
la etiqueta soluciona este problema:
- name: Verify software
command: /usr/local/sbin/test_app.sh {{ app_version.json.version }}
tags:
- verify
- never
Incluso si no se especifica ninguna etiqueta, la tarea de verificación no se ejecuta porque tiene la never
etiqueta:
$ ansible-playbook -i inventory.ini always_runs.yml
PLAY [appservers]
*************************************************************
TASK [Obtain latest software version from API]
*************************************************************
ok: [appserver.example.com -> localhost]
TASK [Install software]
*************************************************************
ok: [appserver.example.com]
TASK [Configure software]
*************************************************************
ok: [appserver.example.com]
PLAY RECAP
*************************************************************
appserver.example.com : ok=3 changed=0 unreachable=0
failed=0 skipped=0 rescued=0 ignored=0
Una tarea etiquetada siempre se ejecuta si se llama explícitamente:
$ ansible-playbook -i inventory.ini always_runs.yml -t verify
PLAY [appservers]
*************************************************************
TASK [Obtain latest software version from API]
*************************************************************
ok: [appserver.example.com -> localhost]
TASK [Verify software]
*************************************************************
changed: [appserver.example.com]
PLAY RECAP
*************************************************************
appserver.example.com : ok=2 changed=1 unreachable=0
failed=0 skipped=0 rescued=0 ignored=0
Herencia de etiquetas
Escribir un solo libro de jugadas monolítico es raro. Probablemente tenga libros de jugadas que importen otros libros de jugadas, incluyan tareas de otros archivos o utilicen una estructura de roles para organiza tu código. Comprender cómo se transmiten las etiquetas en este jerarquía de las importaciones es importante porque las etiquetas se complican cuando se usan comprender Donde importaciones.
[ Learn more by signing up for the free webinar Ansible 101: An introduction to automating everything with training. ]
Ansible diferencia entre estático importaciones y dinámico comprender. Al usar una importación (como import_tasks
), Ansible también aplica etiquetas asociadas a todas las tareas importadas. Por ejemplo, considere el siguiente código, que refactoriza el único libro de jugadas de la última sección en dos archivos YAML: playbook.yml
y install.yml
. (Omití las tareas de configuración y validación en aras de la brevedad). La principal playbook.yml
usos import_tasks
para disparar en las tareas en el install.yml
archivar:
# playbook.yml
---
- hosts: appservers
gather_facts: False
tasks:
- name: Obtain latest software version from API
uri:
url: "http://config-server.example.com:8080/api/v1/appVersion"
register: app_version
delegate_to: localhost
tags:
- always
- import_tasks: install.yml
tags:
- install
# install.yml
---
- name: Install software
package:
name: "{{ app_version.json.version }}"
state: present
Aquí está el lanzamiento del libro de jugadas:
$ ansible-playbook -i inventory.ini playbook.yml -t install
PLAY [appservers]
*************************************************************
TASK [Obtain latest software version from API]
*************************************************************
ok: [appserver.example.com -> localhost]
TASK [Install software]
*************************************************************
ok: [appserver.example.com]
PLAY RECAP
*************************************************************
appserver.example.com : ok=2 changed=0 unreachable=0
failed=0 skipped=0 rescued=0 ignored=0
Observe cómo la tarea en install.yml
corrió a pesar de no tener el install
etiqueta. Esto sucede porque el install
etiqueta aplicada en el principal playbook.yml
es heredado por todas las tareas importadas por import_tasks
. Sin embargo, si cambio import_tasks
en include_tasks
, obtengo un comportamiento muy diferente:
# playbook.yml
---
- hosts: appservers
gather_facts: False
tasks:
- name: Obtain latest software version from API
uri:
url: "http://config-server.example.com:8080/api/v1/appVersion"
register: app_version
delegate_to: localhost
tags:
- always
- include_tasks: install.yml
tags:
- install
El resultado:
$ ansible-playbook -i inventory.ini playbook.yml -t install
PLAY [appservers]
*************************************************************
TASK [Obtain latest software version from API]
*************************************************************
ok: [appserver.example.com -> localhost]
TASK [include_tasks]
*************************************************************
included:
/home/[...]/ansible_tags/dynamic_imports/install.yml
for appserver.example.com
PLAY RECAP
*************************************************************
appserver.example.com : ok=2 changed=0 unreachable=0
failed=0 skipped=0 rescued=0 ignored=0
Observe la install
la tarea ya no se está ejecutando. Cuando una dinámica include_task
se utiliza, las etiquetas se aplican sólo a include
él mismo. Tareas dentro de la include
no heredan etiquetas, por lo que también deben establecerse en las tareas incluidas:
# install.yml
---
- name: Install software
package:
name: "{{ app_version.json.version }}"
state: present
tags:
- install
Agregar etiquetas a cada tarea manualmente en un archivo incluido puede ser tedioso y propenso a errores, por lo que Ansible usa el apply
palabra clave para ayudarte. Ver el Documentación para más información.
Use etiquetas para simplificar la depuración del libro de jugadas
Una excelente manera de apreciar la utilidad de las etiquetas es aprovecharlas al escribir y depurar libros de jugadas. En el siguiente ejemplo, Ansible devuelve un error con respecto a una variable no definida durante la tarea de instalación:
TASK [Install software]
*********************************
fatal: [appserver.example.com]: FAILED! =>
{"msg": "The task includes an option with an undefined variable.
The error was: 'dict object' has no attribute 'version'nnThe error
appears to be in '/home/ansible/debug_tags.yml': line 21, column 7,
but maynbe elsewhere in the file depending on the exact syntax
problem.nnThe offending line appears to be:nnn
- name: Install softwaren
^ heren"}
entonces agrego debug
etiquetas a las tareas que quiero realizar para facilitar la resolución de problemas. En este caso, agregué una tarea de depuración para imprimir la variable guardada y también agregué una debug
etiqueta a la tarea de instalación. Esto me permite ejecutar solo tareas específicas al solucionar problemas, lo que me ahorra tiempo ya que no tengo que ejecutar todo el libro de jugadas:
---
- hosts: appservers
gather_facts: False
tasks:
- name: Obtain latest software version from API
uri:
url: "http://config-server.example.com:8080/api/v1/appVersion"
register: app_version
delegate_to: localhost
tags:
- always
- name: Print out software version API response
debug:
msg: "{{ app_version }}"
tags:
- debug
- name: Install software
package:
name: "{{ app_version.json.version }}"
state: present
tags:
- install
El uso de esta lógica de depuración muestra rápidamente que me estoy refiriendo a la clave de diccionario incorrecta (appversion.json.version
en lugar de appversion.json.softwareVersion
):
$ ansible-playbook -i inventory.ini debug_tags.yml -t debug
PLAY [appservers]
*************************************************************
TASK [Obtain latest software version from API]
*************************************************************
ok: [appserver.example.com -> localhost]
TASK [Print out software version API response]
*************************************************************
ok: [appserver.example.com] => {
"msg": {
"changed": false,
"connection": "close",
"content_length": "51",
"content_type": "application/json; charset=utf-8",
"cookies": {},
"cookies_string": "",
"date": "Thu, 14 Oct 2021 17:00:41 GMT",
"elapsed": 0,
"failed": false,
"json": {
"softwareVersion": "my-app=7.68.0-1ubuntu2.7"
},
"msg": "OK (51 bytes)",
"redirected": false,
"status": 200,
"url": "http://config-server.example.com:8080/api/v1/appVersion"
}
}
PLAY RECAP
*************************************************************
appserver.example.com : ok=2 changed=0 unreachable=0
failed=0 skipped=0 rescued=0 ignored=0
Por lo general, elimino estas etiquetas de depuración después de que mi libro de jugadas se está ejecutando y antes de confirmarlo para el control de fuente. Sin embargo, como en la sección anterior, también puede dejar tareas de depuración adicionales en sus libros de jugadas y simplemente marcarlas con el never
etiqueta.
Envoltura
Encontrar formas de estructurar y ejecutar mejor sus playbooks de Ansible es esencial para desarrollar una estrategia de automatización flexible. Ansible ofrece muchas formas diferentes de lograr esto; las importaciones, las inclusiones, los roles y otros patrones le permiten escribir código sólido y reutilizable.
Las etiquetas proporcionan una forma rápida de apuntar a tareas específicas de Ansible. Sabiendo utilizar correctamente las balizas, aprovecha always
y never
etiquetas y comprender los matices de la herencia de etiquetas mejorará su experiencia en Ansible y le permitirá diseñar una mejor automatización.
Artículos de interés