Introduction
Un nouveau paquet Courtyard Sessions présente une page d’accueil convaincante, mais il manque le téléchargement destiné aux visiteurs. Cet environnement indépendant démarre avec le site original sain sur les deux serveurs. Améliorez le déploiement Ansible préparé pour restaurer la version utilisable précédente après l’échec d’un candidat et arrêter avant de modifier un autre nœud.
Terminez d’abord Mettre à jour un hôte à la fois, ou possédez une pratique équivalente de son outil d’inspection, de la délégation, des lots séquentiels, des opérations copy/file et des vérifications HTTP complètes. Ce laboratoire enseigne la capture du lien réel et la récupération avec block/rescue. La durée est estimée par l’auteur, sans mesure auprès de débutants.
Restaurer après un échec et arrêter le déploiement
Dans cette étape, vous allez enregistrer la véritable version précédente, récupérer le nœud actuel après une vérification échouée et arrêter explicitement le lot suivant.
Ouvrez Website. Le titre original est Good things, around the corner. Utilisez The program et Download the program (CSV) pour vérifier les deux séances initiales. La passerelle et les deux serveurs SSH sont indépendants du laboratoire précédent ; aucun ancien fichier ou processus n’est nécessaire.
Passez à Controller, qui démarre dans /home/labex/project. Les fichiers préparés inspect.yml et rollout.yml sont les outils du laboratoire précédent. Lisez le paquet choisi et l’état courant :
cat group_vars/all.yml
ls packages/courtyard-incomplete
ansible-playbook inspect.yml
release_id sélectionne courtyard-incomplete. Sa page d’accueil et son identité existent, mais pas schedule.csv. Les deux serveurs réels annoncent encore courtyard-preview. Conservez tous les octets des paquets fournis : ajouter un CSV au candidat masquerait son défaut au lieu de pratiquer la récupération. Ne lancez pas encore le déploiement sans protection.
Un block regroupe des tâches susceptibles d’échouer ; ses tâches rescue s’exécutent après l’échec ordinaire d’une tâche du bloc. Enregistrez current avant le bloc avec stat, qui lit les métadonnées du système de fichiers. follow: false lit le lien lui-même plutôt que son répertoire cible. La valeur enregistrée previous.stat.lnk_source est la véritable cible précédente : la récupération restaure ce qui fonctionnait, sans supposer un répertoire fixe.
La récupération doit restaurer le lien, demander la page d’accueil et le CSV complets restaurés et comparer les deux au paquet précédent. Le filtre basename extrait le nom du répertoire de version depuis la cible enregistrée, afin que les comparaisons existantes avec lookup('file', ...) lisent le paquet local correspondant. Seul un nœud restauré sain peut revenir dans le pool. Si une ressource restaurée manque ou est incorrecte, cette tâche de rescue échoue et le nœud reste retiré ; l’autre serveur continue à servir les visiteurs.
Un rescue réussi efface l’effet de l’échec original dans les décisions de gestion d’erreurs d’Ansible. Ainsi, any_errors_fatal seul n’arrête pas les lots suivants après une récupération réussie. Un fail explicite en fin de rescue annonce le refus du candidat et arrête le lot suivant. Une sortie non nulle est le résultat attendu, même si le site précédent fonctionne à nouveau. Un YAML invalide et des hôtes SSH inaccessibles ne déclenchent pas ce mécanisme ; ce n’est pas une garantie générale de retour arrière.
Remplacez rollout.yml par cette version complète. cat > remplace uniquement ce playbook de l’apprenant ; le délimiteur du here-document entre guillemets préserve les expressions Ansible. Conservez les espaces montrés :
cat > rollout.yml <<'YAML'
- name: Release one website backend at a time
hosts: website
order: sorted
gather_facts: false
serial: 1
any_errors_fatal: true
tasks:
- name: Remember the actual previous release link
ansible.builtin.stat:
path: "{{ release_root }}/current"
follow: false
register: previous
- name: Update this backend or recover it
block:
- name: Drain this backend from the pool
ansible.builtin.uri:
url: "{{ pool_api }}/pool"
method: POST
body_format: json
body:
node: "{{ inventory_hostname }}"
enabled: false
delegate_to: controller
- name: Create this release directory
ansible.builtin.file:
path: "{{ release_root }}/releases/{{ release_id }}"
state: directory
mode: '0755'
- name: Copy the selected package over SSH
ansible.builtin.copy:
src: "{{ package_root }}/{{ release_id }}/"
dest: "{{ release_root }}/releases/{{ release_id }}/"
mode: '0644'
- name: Activate the selected release
ansible.builtin.file:
src: "{{ release_root }}/releases/{{ release_id }}"
dest: "{{ release_root }}/current"
state: link
- name: Read the real candidate homepage
ansible.builtin.uri:
url: "http://{{ hostvars[inventory_hostname].ansible_host }}:{{ backend_port }}/"
return_content: true
delegate_to: controller
register: homepage
- name: Require the complete candidate homepage
ansible.builtin.assert:
that: homepage.content == lookup('file', package_root ~ '/' ~ release_id ~ '/index.html', rstrip=False)
- name: Read the real candidate download
ansible.builtin.uri:
url: "http://{{ hostvars[inventory_hostname].ansible_host }}:{{ backend_port }}/schedule.csv"
return_content: true
delegate_to: controller
register: download
- name: Require the complete approved program
ansible.builtin.assert:
that: download.content == lookup('file', package_root ~ '/' ~ approved_release ~ '/schedule.csv', rstrip=False)
- name: Rejoin only after both business checks
ansible.builtin.uri:
url: "{{ pool_api }}/pool"
method: POST
body_format: json
body:
node: "{{ inventory_hostname }}"
enabled: true
delegate_to: controller
rescue:
- name: Restore the previous release link
ansible.builtin.file:
src: "{{ previous.stat.lnk_source }}"
dest: "{{ release_root }}/current"
state: link
- name: Read the restored homepage
ansible.builtin.uri:
url: "http://{{ hostvars[inventory_hostname].ansible_host }}:{{ backend_port }}/"
return_content: true
delegate_to: controller
register: restored_home
- name: Require the complete previous homepage
ansible.builtin.assert:
that: restored_home.content == lookup('file', package_root ~ '/' ~ (previous.stat.lnk_source | basename) ~ '/index.html', rstrip=False)
- name: Read the restored download
ansible.builtin.uri:
url: "http://{{ hostvars[inventory_hostname].ansible_host }}:{{ backend_port }}/schedule.csv"
return_content: true
delegate_to: controller
register: restored_download
- name: Require the complete previous program
ansible.builtin.assert:
that: restored_download.content == lookup('file', package_root ~ '/' ~ (previous.stat.lnk_source | basename) ~ '/schedule.csv', rstrip=False)
- name: Rejoin only after both restoration checks
ansible.builtin.uri:
url: "{{ pool_api }}/pool"
method: POST
body_format: json
body:
node: "{{ inventory_hostname }}"
enabled: true
delegate_to: controller
- name: Stop subsequent batches after recovery
ansible.builtin.fail:
msg: Candidate failed; the previous release is healthy again. Stop this rollout.
YAML
Validez la syntaxe avant de modifier l’un des serveurs :
ansible-playbook rollout.yml --syntax-check
Essayez ensuite la version incomplète. Cette commande doit échouer après la récupération :
ansible-playbook rollout.yml
Lisez immédiatement le code final. $? contient le code de sortie de la commande précédente et echo l’affiche. Une autre commande remplacerait cette valeur conservée par le shell :
echo $?
Le code attendu est non nul, généralement 2. La vérification de la nouvelle page réussit, le téléchargement candidat renvoie HTTP 404, la page et le CSV restaurés passent, puis Stop subsequent batches after recovery échoue volontairement. HTTP 404 identifie le téléchargement manquant. La connexion SSH reste utilisable pour restaurer. web2 ne doit pas commencer les tâches de publication.
Relancez l’outil d’observation :
ansible-playbook inspect.yml
Les deux serveurs doivent annoncer courtyard-preview et rester actifs. Passez à Website, rechargez, ouvrez The program, téléchargez à nouveau le CSV et vérifiez la disponibilité des deux séances originales. Plan a visit doit toujours afficher les informations d’accessibilité.
Le site restauré propose toujours les deux séances originales et le téléchargement complet.

Pour une erreur courante de YAML ou de nom de fichier, utilisez ls, corrigez le playbook complet et relancez la vérification syntaxique. Si une modification précédente a basculé le lien avant que la récupération fonctionne, enregistrez d’abord le playbook correct ci-dessus. Le même déploiement sain déjà appris permet de restaurer le paquet original connu en le choisissant à la fois comme version et comme programme attendu. Les guillemets réunissent les deux affectations dans un seul argument --extra-vars :
ansible-playbook rollout.yml --extra-vars "release_id=courtyard-preview approved_release=courtyard-preview"
Cette commande de restauration doit réussir. Confirmez les deux nœuds avec inspect.yml, puis relancez le candidat incomplet avec la commande originale ansible-playbook rollout.yml pour démontrer l’échec et la récupération demandés. Ne réinitialisez pas la VM et ne réécrivez pas le candidat. Gardez un playbook réutilisable : les requêtes du pool doivent employer le pool_api fourni, les chemins les variables release_root et package_root, et les tâches les modules directs présentés ici.
Résumé
Vous avez restauré le vrai site précédent après un échec réel de téléchargement manquant. Les deux vérifications de restauration commandent la réintégration, et un échec explicite arrête les lots Ansible suivants même lorsque rescue réussit.



