Introdução
Um novo pacote do Courtyard Sessions tem uma página inicial convincente, mas falta o download para visitantes. Este ambiente independente começa com o site original funcionando nos dois servidores. Melhore a implantação Ansible preparada para restaurar a versão utilizável anterior quando um candidato falhar e parar antes de atualizar outro nó.
Conclua primeiro Atualize um host por vez ou tenha prática equivalente com sua inspeção, delegação, lotes sequenciais, operações copy/file e verificações HTTP completas. Este laboratório ensina a registrar o link real e recuperar com block/rescue. A duração é uma estimativa do autor, não um tempo medido com iniciantes.
Restaure uma versão com falha e pare a implantação
Nesta etapa, você registrará a versão anterior real, recuperará o nó atual após uma verificação malsucedida e interromperá explicitamente o lote seguinte.
Abra a aba Website. O título original é Good things, around the corner. Use The program e Download the program (CSV) para conferir as duas sessões originais. O gateway e os dois servidores SSH são independentes do laboratório anterior; não precisam de arquivos nem processos anteriores.
Mude para a aba Controller, que começa em /home/labex/project. Os arquivos preparados inspect.yml e rollout.yml são as ferramentas do laboratório anterior. Leia o pacote selecionado e o estado atual:
cat group_vars/all.yml
ls packages/courtyard-incomplete
ansible-playbook inspect.yml
release_id seleciona courtyard-incomplete. Sua página inicial e identidade existem, mas não há schedule.csv. Os dois servidores reais ainda informam courtyard-preview. Preserve os bytes de todos os pacotes: acrescentar um CSV ao candidato esconderia o defeito, em vez de praticar a recuperação. Ainda não execute a implantação sem proteção.
Um block agrupa tarefas que podem falhar; suas tarefas rescue são executadas depois que uma tarefa do bloco falha normalmente. Registre current antes do bloco com stat, que lê metadados do sistema de arquivos. follow: false lê o próprio link, e não o diretório de destino. O valor salvo previous.stat.lnk_source é o destino anterior real: a recuperação restaura o que estava em execução, em vez de supor um diretório fixo.
A recuperação deve restaurar o link, solicitar a página e o CSV completos restaurados e compará-los com o pacote anterior. O filtro basename extrai o nome do diretório de versão do destino salvo para que as comparações existentes com lookup('file', ...) leiam o pacote local correspondente. Só um nó restaurado e saudável é recolocado. Se algum recurso restaurado estiver ausente ou seu conteúdo estiver errado, essa tarefa de rescue falha e o nó permanece fora; o outro servidor continua atendendo os visitantes.
Um rescue bem-sucedido remove o efeito da falha original nas decisões de tratamento de erros do Ansible. Por isso, any_errors_fatal sozinho não interrompe os lotes posteriores quando a recuperação tem sucesso. Um fail explícito no fim de rescue informa a rejeição e interrompe o lote seguinte. A saída diferente de zero é intencional, embora o site anterior volte a funcionar. YAML inválido e hosts SSH inacessíveis não acionam esse mecanismo de rescue; ele não é uma garantia geral de reversão.
Substitua rollout.yml por esta versão completa. cat > substitui apenas este playbook do aluno; o delimitador entre aspas preserva as expressões do Ansible. Mantenha os espaços mostrados:
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
Valide a sintaxe antes de alterar qualquer servidor:
ansible-playbook rollout.yml --syntax-check
Depois tente publicar a versão incompleta. Espera-se que este comando falhe após recuperar o site:
ansible-playbook rollout.yml
Leia imediatamente o estado de saída. $? contém o estado de saída do comando anterior, e echo o imprime. Outro comando substituiria esse valor guardado pelo shell:
echo $?
Espere um estado diferente de zero, normalmente 2. A verificação da página nova tem sucesso; o download do candidato retorna HTTP 404; a página e o CSV restaurados passam; depois Stop subsequent batches after recovery falha intencionalmente. HTTP 404 identifica o download ausente. A conexão SSH continua disponível para a recuperação. web2 não deve entrar nas tarefas de publicação.
Execute novamente a ferramenta de observação:
ansible-playbook inspect.yml
Ambos os servidores devem mostrar courtyard-preview e permanecer ativos. Mude para Website, recarregue, entre em The program, baixe novamente o CSV e confira que as duas sessões originais continuam disponíveis. Plan a visit deve continuar mostrando as informações de acessibilidade.
O site restaurado continua oferecendo as duas sessões originais e o download completo.

Para um erro comum de YAML ou nome de arquivo, use ls, corrija o playbook inteiro e repita a verificação sintática. Se uma edição anterior alterou o link antes de a recuperação funcionar, primeiro salve o playbook correto acima. Você pode usar a mesma implantação saudável já aprendida para restaurar o pacote original conhecido, selecionando-o tanto como versão quanto como programação esperada. As aspas mantêm essas duas atribuições em um único argumento --extra-vars:
ansible-playbook rollout.yml --extra-vars "release_id=courtyard-preview approved_release=courtyard-preview"
Esse comando de restauração deve ter sucesso. Confirme os dois nós com inspect.yml e depois repita o candidato incompleto com o comando original ansible-playbook rollout.yml para demonstrar a falha e a recuperação exigidas. Não redefina a VM nem reescreva o candidato. Mantenha o playbook reutilizável: as solicitações do pool devem usar o pool_api fornecido, os caminhos devem usar release_root e package_root, e as tarefas devem usar os módulos diretos mostrados.
Resumo
Você recuperou o site anterior real após uma falha verdadeira de download ausente. Ambas as verificações de restauração controlam a reinclusão, e uma falha explícita interrompe os próximos lotes do Ansible mesmo quando rescue termina com sucesso.



