Detén una actualización defectuosa

AnsibleBeginner
Practicar Ahora

Introducción

Un paquete nuevo de Courtyard Sessions tiene una página inicial convincente, pero le falta la descarga para visitantes. Este entorno independiente empieza con el sitio original funcionando en ambos servidores. Mejora el despliegue Ansible preparado para restaurar la versión utilizable anterior cuando falle un candidato y detenerse antes de actualizar otro nodo.

Completa primero Actualiza un host a la vez o ten práctica equivalente con su inspección, delegación, lotes seriales, operaciones copy/file y comprobaciones HTTP completas. Este laboratorio enseña a registrar el enlace real y a recuperar con block/rescue. La duración es una estimación del autor, no un tiempo medido con principiantes.

Restaura una versión fallida y detén el despliegue

En este paso, registrarás la versión anterior real, recuperarás el nodo actual tras una comprobación fallida y detendrás explícitamente el siguiente lote.

Abre la pestaña Website. El título original es Good things, around the corner. Usa The program y Download the program (CSV) para comprobar las dos sesiones originales. La puerta de enlace y los dos servidores SSH son independientes del laboratorio anterior; no necesitan archivos ni procesos previos.

Cambia a la pestaña Controller, que comienza en /home/labex/project. Los archivos preparados inspect.yml y rollout.yml son las herramientas del laboratorio anterior. Lee el paquete seleccionado y el estado actual:

cat group_vars/all.yml
ls packages/courtyard-incomplete
ansible-playbook inspect.yml

release_id selecciona courtyard-incomplete. Existen su página inicial e identidad, pero no schedule.csv. Los dos servidores reales siguen indicando courtyard-preview. Conserva los bytes de todos los paquetes: añadir un CSV al candidato ocultaría el defecto en lugar de practicar la recuperación. Todavía no ejecutes el despliegue sin protección.

Un block agrupa tareas que pueden fallar; sus tareas rescue se ejecutan después de que falle normalmente una tarea del bloque. Registra current antes del bloque mediante stat, que lee metadatos del sistema de archivos. follow: false lee el enlace en sí y no el directorio de destino. El valor guardado previous.stat.lnk_source es el destino anterior real: la recuperación restaura lo que estaba ejecutándose en lugar de suponer un directorio fijo.

La recuperación debe restaurar el enlace, solicitar la página y el CSV completos restaurados y compararlos con el paquete anterior. El filtro basename extrae el nombre del directorio de versión del destino guardado para que las comparaciones existentes con lookup('file', ...) lean el paquete local correspondiente. Solo se reincorpora un nodo restaurado y saludable. Si falta algún recurso restaurado o su contenido es incorrecto, esa tarea de rescue falla y el nodo permanece fuera; el otro servidor sigue atendiendo visitantes.

Un rescue correcto elimina el efecto del fallo original en las decisiones de tratamiento de errores de Ansible. Por ello, any_errors_fatal por sí solo no detiene los lotes posteriores cuando la recuperación tiene éxito. Un fail explícito al final de rescue informa del rechazo y detiene el siguiente lote. La salida distinta de cero es intencionada, aunque el sitio anterior vuelva a funcionar. El YAML inválido y los hosts SSH inalcanzables no activan este mecanismo de rescue; no es una garantía general de reversión.

Sustituye rollout.yml por esta versión completa. cat > reemplaza solo este playbook del alumno; el delimitador entre comillas conserva las expresiones de Ansible. Mantén los espacios 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

Valida la sintaxis antes de cambiar cualquiera de los servidores:

ansible-playbook rollout.yml --syntax-check

Intenta después publicar la versión incompleta. Se espera que este comando falle tras recuperar el sitio:

ansible-playbook rollout.yml

Lee inmediatamente el estado final. $? contiene el estado de salida del comando anterior y echo lo imprime. Otro comando sustituiría ese valor guardado por el shell:

echo $?

Espera un estado distinto de cero, normalmente 2. La comprobación de la página nueva tiene éxito; la descarga del candidato devuelve HTTP 404; la página y el CSV restaurados pasan; después Stop subsequent batches after recovery falla intencionadamente. HTTP 404 identifica la descarga ausente. La conexión SSH sigue disponible para recuperar. web2 no debe entrar en las tareas de publicación.

Vuelve a ejecutar la herramienta de observación:

ansible-playbook inspect.yml

Ambos servidores deben mostrar courtyard-preview y permanecer activos. Cambia a Website, recarga, entra en The program, descarga otra vez el CSV y comprueba que siguen disponibles las dos sesiones originales. Plan a visit debe seguir mostrando la información de accesibilidad.

El sitio restaurado sigue ofreciendo las dos sesiones originales y la descarga completa.

Sitio restaurado de Courtyard Sessions con su programa original

Ante un error habitual de YAML o nombre de archivo, usa ls, corrige el playbook completo y repite la comprobación sintáctica. Si una edición previa cambió el enlace antes de que funcionase la recuperación, guarda primero el playbook correcto anterior. Puedes usar el mismo despliegue saludable ya aprendido para restaurar el paquete original conocido, seleccionándolo tanto como versión como programa esperado. Las comillas mantienen estas dos asignaciones en un solo argumento --extra-vars:

ansible-playbook rollout.yml --extra-vars "release_id=courtyard-preview approved_release=courtyard-preview"

Ese comando de restauración debe terminar correctamente. Confirma ambos nodos con inspect.yml y repite después el candidato incompleto mediante el comando original ansible-playbook rollout.yml para demostrar el fallo y la recuperación exigidos. No restablezcas la VM ni reescribas el candidato. Mantén el playbook reutilizable: las solicitudes del grupo deben usar el pool_api suministrado, las rutas deben usar release_root y package_root, y las tareas deben emplear los módulos directos mostrados.

Resumen

Has recuperado el sitio anterior real tras un fallo auténtico de descarga ausente. Ambas comprobaciones de restauración controlan la reincorporación, y un fallo explícito detiene los siguientes lotes de Ansible aunque rescue termine correctamente.