Introducción
La automatización con plantillas mantiene un único origen mientras produce configuración a partir de las entradas de cada host. Generarás archivos desde variables y datos recopilados, transformarás una cabecera con un filtro integrado de Jinja, elegirás una rama condicional y cargarás configuración validada de nginx mediante un handler. Las solicitudes HTTP reales y un candidato inválido rechazado mostrarán la diferencia entre el texto de origen, los archivos administrados y el comportamiento activo del servicio.
Generar contenido administrado con variables de host y datos recopilados
En este paso, crearás una plantilla de página y generarás archivos reales distintos a partir del mismo origen en ambos destinos.
Los nodos preparados tienen nginx instalado como requisito previo. Tu trabajo es generar y aplicar configuración, en lugar de instalar otra vez el paquete. Las plantillas se procesan en el controlador usando el contexto de variables de cada destino y después se transfieren a los destinos administrados.
Entra en el proyecto del controlador y crea un directorio templates. mkdir -p crea el directorio si es necesario sin fallar cuando ya existe:
cd /home/labex/project
mkdir -p templates
Define entradas tipadas de los hosts. El entorno y el puerto de escucha difieren, y los valores maintenance_mode sin comillas son booleanos YAML:
cat > host_vars/web1.yml <<'YAML'
environment_label: development
service_port: 8081
maintenance_mode: true
YAML
cat > host_vars/web2.yml <<'YAML'
environment_label: production
service_port: 8082
maintenance_mode: false
YAML
Las expresiones Jinja entre llaves dobles insertan valores en el texto. El sufijo .j2 es una convención útil para los archivos de origen de plantillas, no un requisito del nombre del archivo administrado de destino. Crea un origen que use el nombre del host actual en el inventario, la variable del entorno y el dato descubierto de distribución:
cat > templates/page.txt.j2 <<'JINJA'
host={{ inventory_hostname }}
environment={{ environment_label }}
distribution={{ ansible_facts.distribution }}
JINJA
Usa el módulo template para generar el contenido. Copy normalmente transferiría los bytes del origen sin interpretar estas expresiones Jinja. Recopila los datos antes de consumir el valor de distribución y declara propiedad y permisos administrados explícitos:
cat > templates.yml <<'YAML'
- name: Render host-specific website content
hosts: all
gather_facts: true
become: true
tasks:
- name: Prepare the rendered site directory
ansible.builtin.file:
path: /var/www/template-site
state: directory
owner: root
group: root
mode: "0755"
- name: Render each host's website content
ansible.builtin.template:
src: templates/page.txt.j2
dest: /var/www/template-site/index.html
owner: root
group: root
mode: "0644"
YAML
Genera ambas páginas administradas:
ansible-playbook templates.yml --syntax-check
ansible-playbook templates.yml
Lee los archivos reales de los destinos:
ansible all -m ansible.builtin.command -a 'cat /var/www/template-site/index.html'
Cada página debería identificar su propio host, el entorno development/production correspondiente y distribution=Ubuntu. El controlador sigue teniendo un origen reutilizable, mientras que las dos salidas administradas difieren.
Añadir un filtro integrado y una rama condicional de configuración
En este paso, generarás una configuración válida de nginx que cambie el comportamiento HTTP según las entradas de cada host.
Un filtro Jinja transforma un valor mediante el símbolo de barra vertical. upper es un filtro integrado que convierte texto a mayúsculas; no es un complemento personalizado. Las instrucciones de control Jinja usan delimitadores de porcentaje y llaves. La rama if/else/endif siguiente selecciona una cabecera de mantenimiento según la entrada booleana.
Crea una plantilla completa de configuración de nginx. El puerto, la cabecera de entorno y la cabecera de mantenimiento proceden de datos por host. Los ajustes root e index sirven la página que ya has generado:
cat > templates/nginx.conf.j2 <<'JINJA'
events {}
http {
server_tokens off;
server {
listen {{ service_port }};
root /var/www/template-site;
index index.html;
add_header X-Environment "{{ environment_label | upper }}" always;
{% if maintenance_mode %}
add_header X-Maintenance "enabled" always;
{% else %}
add_header X-Maintenance "disabled" always;
{% endif %}
}
}
JINJA
Añade las tareas de configuración y servicio, junto con el handler de recarga con nombre, al playbook existente. El comando validate de la tarea template prueba un candidato generado temporal (%s) antes de sustituir la configuración administrada. Una configuración válida con cambios notifica al handler; la tarea service normal mantiene nginx en ejecución.
cat >> templates.yml <<'YAML'
- name: Render and validate the nginx candidate before replacement
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: "0644"
validate: /usr/sbin/nginx -t -c %s
notify: Reload nginx
- name: Keep the rendered site service running
ansible.builtin.service:
name: nginx
state: started
handlers:
- name: Reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
YAML
Aplica el playbook completo:
ansible-playbook templates.yml
La tarea de configuración debería cambiar ambos hosts y activar el handler de recarga. Inspecciona web1 en el puerto 8081 y web2 en el puerto 8082. curl -i incluye las cabeceras de respuesta junto con el cuerpo:
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
ansible web2 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8082/'
Web1 debería devolver X-Environment: DEVELOPMENT y X-Maintenance: enabled; web2 debería devolver X-Environment: PRODUCTION y X-Maintenance: disabled. Ambos cuerpos conservan la página correcta por host. Las fechas de las cabeceras varían; compara los valores basados en datos y el estado satisfactorio.
Cambiar entradas y rechazar un candidato inválido
En este paso, actualizarás una entrada persistente sin editar las plantillas, observarás el resultado activo real y demostrarás que la validación protege la configuración funcional.
Cambia el entorno guardado de web1 a staging y su modo de mantenimiento a false, conservando su puerto existente:
cat > host_vars/web1.yml <<'YAML'
environment_label: staging
service_port: 8081
maintenance_mode: false
YAML
Aplica las mismas plantillas de origen:
ansible-playbook templates.yml
La página y la configuración de Web1 deberían cambiar, notificando a su handler de recarga; web2 debería permanecer sin cambios. Inspecciona web1 otra vez:
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
Su cuerpo debería mostrar environment=staging, con X-Environment: STAGING y X-Maintenance: disabled. Las plantillas siguieron siendo reutilizables al cambiar la entrada.
Ahora prueba el rechazo de candidatos. cp guarda el origen válido del controlador para poder restablecerlo. Sustituye únicamente la plantilla de nginx por texto deliberadamente inválido:
cp templates/nginx.conf.j2 templates/nginx.conf.j2.good
printf 'invalid nginx configuration;\n' > templates/nginx.conf.j2
Se espera que la siguiente ejecución del playbook falle en la validación del candidato. El || de la shell ejecuta el mensaje únicamente después de una salida distinta de cero, permitiendo continuar este ejercicio de fallo intencionado:
ansible-playbook templates.yml || printf 'Expected candidate validation rejection\n'
Lee el fallo de la tarea: nginx debería rechazar el candidato temporal. La configuración administrada funcional debe permanecer en su lugar, por lo que ambos servicios activos deberían seguir respondiendo correctamente:
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
ansible web2 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8082/'
mv restablece el origen válido guardado con su nombre de archivo original. Ejecuta el playbook completo una vez más:
mv templates/nginx.conf.j2.good templates/nginx.conf.j2
ansible-playbook templates.yml
Ambos hosts deberían informar changed=0, failed=0 y unreachable=0, sin ningún handler de recarga programado. La validación protegió la configuración activa sin convertir una copia de un candidato inválido en el estado final deseado.
Resumen
Has generado contenido con variables de host y datos recopilados mediante el módulo template, usado el filtro integrado upper y una rama booleana de Jinja y aplicado configuración validada mediante un handler. Las entradas modificadas produjeron salidas activas distintas sin reescribir los orígenes. Un candidato inválido se rechazó antes de sustituir la configuración y las plantillas válidas restablecidas convergieron sin cambios adicionales.


