Introduction
L'automatisation par modèles conserve une seule source tout en produisant la configuration à partir des entrées de chaque hôte. Vous produirez des fichiers avec les variables et les faits, transformerez un en-tête avec un filtre Jinja intégré, sélectionnerez une branche conditionnelle et chargerez une configuration nginx validée par un handler. Des requêtes HTTP réelles et un candidat invalide rejeté montreront la différence entre le texte source, les fichiers gérés et le comportement du service actif.
Insérer les variables d'hôte et les faits dans le contenu géré
Dans cette étape, vous créerez un modèle de page unique et produirez des fichiers réels distincts à partir de cette même source sur les deux cibles.
Les nœuds préparés possèdent nginx installé comme prérequis. Votre travail consiste à produire et appliquer la configuration, plutôt qu'à réinstaller le paquet. Les modèles sont rendus sur le contrôleur avec le contexte de variables de chaque cible, puis transférés vers les destinations gérées.
Entrez dans le projet du contrôleur et créez un répertoire templates. mkdir -p crée le répertoire si nécessaire sans échouer lorsqu'il existe déjà :
cd /home/labex/project
mkdir -p templates
Définissez des entrées d'hôte typées. L'environnement et le port d'écoute diffèrent, et les valeurs maintenance_mode sans guillemets sont des booléens 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
Les expressions Jinja entre doubles accolades insèrent des valeurs dans le texte. Le suffixe .j2 est une convention utile pour les sources de modèles, et non une exigence pour le nom du fichier de destination géré. Créez une seule source utilisant le nom d'inventaire actuel, la variable d'environnement et le fait de distribution découvert :
cat > templates/page.txt.j2 <<'JINJA'
host={{ inventory_hostname }}
environment={{ environment_label }}
distribution={{ ansible_facts.distribution }}
JINJA
Utilisez le module template pour le rendu. Copy transférerait normalement les octets de la source sans interpréter ces expressions Jinja. Collectez les faits avant de consommer la valeur de distribution et déclarez explicitement les propriétaires et permissions gérés :
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
Produisez les deux pages gérées :
ansible-playbook templates.yml --syntax-check
ansible-playbook templates.yml
Lisez les véritables fichiers cibles :
ansible all -m ansible.builtin.command -a 'cat /var/www/template-site/index.html'
Chaque page doit identifier son propre hôte, l'environnement development/production correspondant et distribution=Ubuntu. Le contrôleur conserve une seule source réutilisable, tandis que les deux sorties gérées diffèrent.
Ajouter un filtre intégré et une branche de configuration conditionnelle
Dans cette étape, vous produirez une configuration nginx valide qui change le comportement HTTP selon les entrées de chaque hôte.
Un filtre Jinja transforme une valeur avec le symbole de barre verticale. upper est un filtre intégré qui convertit le texte en majuscules ; ce n'est pas un plugin personnalisé. Les instructions de contrôle Jinja utilisent des délimiteurs accolade-pourcentage. La branche if/else/endif ci-dessous sélectionne un en-tête de maintenance selon l'entrée booléenne.
Créez un modèle de configuration nginx complet. Le port, l'en-tête d'environnement et l'en-tête de maintenance proviennent tous des données par hôte. Les paramètres root et index servent la page déjà produite :
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
Ajoutez les tâches de configuration et de service ainsi que le handler de rechargement nommé au playbook existant. La commande validate de la tâche template teste un candidat rendu temporaire (%s) avant de remplacer la configuration gérée. Une configuration valide modifiée notifie le handler ; la tâche service ordinaire maintient nginx en cours d'exécution.
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
Appliquez le playbook complet :
ansible-playbook templates.yml
La tâche de configuration doit modifier les deux hôtes et déclencher le handler de rechargement. Examinez web1 sur le port 8081 et web2 sur le port 8082. curl -i inclut les en-têtes de réponse avec le corps :
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 doit renvoyer X-Environment: DEVELOPMENT et X-Maintenance: enabled ; web2 doit renvoyer X-Environment: PRODUCTION et X-Maintenance: disabled. Les deux corps conservent la page correcte propre à l'hôte. Les dates des en-têtes varient ; comparez les valeurs pilotées par les données et le statut réussi.
Modifier les entrées et rejeter un candidat invalide
Dans cette étape, vous mettrez à jour une entrée persistante sans modifier les modèles, observerez le véritable résultat actif et prouverez que la validation protège la configuration fonctionnelle.
Changez l'environnement enregistré de web1 en staging et son mode maintenance en false, en conservant son port existant :
cat > host_vars/web1.yml <<'YAML'
environment_label: staging
service_port: 8081
maintenance_mode: false
YAML
Appliquez les mêmes modèles source :
ansible-playbook templates.yml
La page et la configuration de web1 doivent changer et notifier son handler de rechargement ; web2 doit rester inchangé. Examinez de nouveau web1 :
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
Son corps doit afficher environment=staging, avec X-Environment: STAGING et X-Maintenance: disabled. Les modèles sont restés réutilisables malgré le changement d'entrée.
Testez maintenant le rejet d'un candidat. cp enregistre la source valide du contrôleur pour pouvoir la restaurer. Remplacez uniquement le modèle nginx par un texte volontairement invalide :
cp templates/nginx.conf.j2 templates/nginx.conf.j2.good
printf 'invalid nginx configuration;\n' > templates/nginx.conf.j2
La prochaine exécution du playbook doit échouer à la validation du candidat. Le || du shell exécute le message uniquement après un code de sortie non nul, permettant de poursuivre cet exercice d'échec intentionnel :
ansible-playbook templates.yml || printf 'Expected candidate validation rejection\n'
Lisez l'échec de tâche : nginx doit rejeter le candidat temporaire. La configuration gérée fonctionnelle doit rester en place ; les deux services actifs doivent donc toujours répondre correctement :
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 restaure la source valide enregistrée sous son nom de fichier d'origine. Exécutez encore une fois le playbook complet :
mv templates/nginx.conf.j2.good templates/nginx.conf.j2
ansible-playbook templates.yml
Les deux hôtes doivent indiquer changed=0, failed=0 et unreachable=0, sans handler de rechargement programmé. La validation a protégé la configuration active sans faire d'une copie d'un candidat invalide l'état final souhaité.
Résumé
Vous avez produit du contenu à partir des variables d'hôte et des faits avec le module template, utilisé le filtre intégré upper et une branche Jinja booléenne, et appliqué une configuration validée par un handler. Une entrée modifiée a produit une sortie active différente sans réécrire les sources. Un candidat invalide a été rejeté avant le remplacement, et les modèles valides restaurés ont convergé sans changement supplémentaire.


