Executar comandos de manutenção e tratar falhas

AnsibleBeginner
Pratique Agora

Introdução

Programas de manutenção existentes precisam de uma integração cuidadosa com a automação declarativa. Você distinguirá command, shell e script, passará um caminho com espaço de forma segura, classificará resultados somente leitura e protegerá a execução com base em uma saída real de script. Um pipeline e comprovantes delegados ao controlador tornarão observáveis o local de execução e a propagação de falhas. Você também provocará e recuperará uma falha real de entrada ausente.

Executar um comando direto com argumentos seguros

Nesta etapa, você inspecionará uma entrada gerenciada com command, preservará um caminho contendo um espaço e classificará corretamente a operação somente leitura.

Prefira módulos declarativos como copy e file para gerenciar estado. Command, shell e script são úteis ao integrar um executável ou programa de manutenção existente. command executa um executável gerenciado sem um shell, portanto pipes e redirecionamentos não são interpretados. Sua forma argv mantém cada argumento separado, incluindo um caminho com espaços.

Cada destino preparado tem /home/automation/maintenance input.txt contendo uma linha de dados aprovados e um pré-requisito maintenance-ready. O controlador fornece um script existente em scripts/ para uso posterior. Preserve essas entradas.

Entre no projeto do controlador:

cd /home/labex/project

Crie um playbook usando argv. O caminho com espaço é uma entrada única da lista YAML. register captura o resultado do executável, incluindo stdout e seu código de retorno numérico rc. Zero normalmente significa sucesso; um código de retorno diferente de zero normalmente faz a tarefa falhar.

cat > maintenance.yml <<'YAML'
- name: Inspect maintenance inputs safely
  hosts: all
  gather_facts: false
  tasks:
    - name: Read the managed input with a space-safe argument list
      ansible.builtin.command:
        argv:
          - /usr/bin/cat
          - /home/automation/maintenance input.txt
      register: input_result
      changed_when: false

    - name: Retain the inspected input in a managed record
      ansible.builtin.copy:
        dest: /home/automation/command-result.txt
        content: "{{ input_result.stdout }}\n"
        mode: "0640"
YAML

O comando cat é somente leitura, portanto changed_when false classifica seu resultado como sem alterações. Essa configuração muda o relato, não o que um comando realmente faz; ela não deve ser usada para ocultar alterações de um programa que modifica o estado. A tarefa copy usa o stdout retornado e grava um registro gerenciado real com uma quebra de linha final explícita.

Verifique e execute o playbook:

ansible-playbook maintenance.yml --syntax-check
ansible-playbook maintenance.yml

As duas tarefas de leitura devem informar ok, enquanto a primeira cópia do registro altera cada destino. Inspecione os registros reais:

ansible all -m ansible.builtin.command -a 'cat /home/automation/command-result.txt'

Cada um deve conter Approved maintenance data. Nesta versão do core, command pode expandir argumentos de variáveis de ambiente por meio de Python mesmo sem invocar um shell. Use expand_argument_vars false quando precisar de argumentos literais com prefixo de dólar; isso é desnecessário para este caminho fixo.

Transferir um script do controlador e proteger a execução pela saída real

Nesta etapa, você invocará o script de manutenção fornecido no controlador nos dois destinos e evitará execuções repetidas desnecessárias.

Inspecione o programa fornecido antes de executá-lo:

cat scripts/host-maintenance.sh

Ele verifica a entrada maintenance-ready do destino antes de criar qualquer saída. A ausência de prontidão imprime um erro e termina com 42. Caso contrário, grava um relatório protegido contendo o nome real do host de destino e seu argumento de rótulo. Sua pequena implementação em shell é uma entrada de integração existente; use módulos declarativos para a configuração comum de arquivos.

O módulo script transfere uma origem do controlador para o destino e a executa lá. Isso difere de command, cujo executável já deve existir no nó gerenciado. O rótulo entre aspas abaixo continua sendo um argumento único com espaço. O caminho creates é verificado no destino gerenciado: quando esse relatório já existe, o script é pulado.

Acrescente a tarefa de script:

cat >> maintenance.yml <<'YAML'

    - name: Run the supplied controller script once per target report
      ansible.builtin.script:
        cmd: /home/labex/project/scripts/host-maintenance.sh 'Maintenance checked'
        creates: /home/automation/maintenance-run/report.txt
      register: maintenance_result
      failed_when: maintenance_result.rc is defined and maintenance_result.rc != 0
YAML

O rc do resultado registrado controla failed_when. A proteção is defined considera um resultado que não inclua um código de retorno de execução. Não use ignore_errors nem failed_when false para transformar uma falha real do script em sucesso aparente.

Execute o playbook estendido:

ansible-playbook maintenance.yml

Os dois destinos devem criar relatórios. Leia as saídas reais:

ansible all -m ansible.builtin.command -a 'cat /home/automation/maintenance-run/report.txt'

Cada um deve conter seu próprio nome de host e label=Maintenance checked. Repita o mesmo playbook:

ansible-playbook maintenance.yml

O script deve ser pulado porque seu relatório no destino existe, e os dois resumos devem informar changed=0 sem destinos failed/unreachable. Creates é uma proteção baseada em existência, não um mecanismo de reconciliação de conteúdo: ele não corrige um relatório existente corrompido nem o regenera automaticamente quando as entradas do script mudam.

Propagar falhas e delegar comprovantes ao controlador

Nesta etapa, você usará shell apenas para um pipeline real, armazenará comprovantes distintos no controlador e exercitará uma falha visível de script sem suprimi-la.

Um pipe conecta a saída de um comando à entrada de outro e, portanto, precisa de shell. O pipefail do Bash faz com que um comando malsucedido em um pipeline produza uma falha, em vez de permitir que um comando wc final bem-sucedido a oculte. O pipeline abaixo apenas conta linhas da entrada; changed_when false é apropriado porque ele lê sem alterar o estado do destino. failed_when também rejeita uma contagem de linhas inesperada.

Crie um diretório de comprovantes no controlador. Cada cópia delegada usará o nome original do inventário em um arquivo separado, impedindo que os hosts sobrescrevam um comprovante compartilhado:

mkdir -p receipts

Acrescente as tarefas de pipeline e comprovante. delegate_to localhost executa apenas a tarefa do comprovante no controlador; inventory_hostname ainda identifica o destino original. become false mantém a criação de arquivos no controlador sob a conta comum labex.

cat >> maintenance.yml <<'YAML'

    - name: Count managed input lines with explicit pipeline failure propagation
      ansible.builtin.shell: |
        set -o pipefail
        cat '/home/automation/maintenance input.txt' | wc -l
      args:
        executable: /bin/bash
      register: line_result
      changed_when: false
      failed_when: line_result.rc != 0 or (line_result.stdout | trim) != '1'

    - name: Store a per-target receipt on the controller
      ansible.builtin.copy:
        dest: "/home/labex/project/receipts/{{ inventory_hostname }}.txt"
        content: |
          host={{ inventory_hostname }}
          input_lines={{ line_result.stdout | trim }}
        mode: "0640"
      delegate_to: localhost
      become: false
YAML

Execute o fluxo completo de manutenção:

ansible-playbook maintenance.yml

Inspecione os dois comprovantes no controlador. Cada um deve identificar seu próprio host original e input_lines=1:

cat receipts/web1.txt receipts/web2.txt

Agora exercite o ramo de falha real do script em web1. Remova seu relatório protegido por creates e a entrada de prontidão com o módulo file; essas são alterações temporárias intencionais dentro do nó de treinamento:

ansible web1 -m ansible.builtin.file -a 'path=/home/automation/maintenance-run/report.txt state=absent'
ansible web1 -m ansible.builtin.file -a 'path=/home/automation/maintenance-ready state=absent'

A próxima execução deve fazer a tarefa de script falhar em web1 com rc 42 e a mensagem de entrada ausente. || permite que o fluxo no Terminal continue após essa saída esperada diferente de zero do playbook; ele não muda a classificação da falha no Ansible:

ansible-playbook maintenance.yml || printf 'Expected missing-readiness failure\n'

Inspecione o caminho do relatório; web1 não deve ter um relatório de sucesso fabricado após a falha do script:

ansible web1 -m ansible.builtin.stat -a 'path=/home/automation/maintenance-run/report.txt'

Restaure a entrada de prontidão a partir de uma origem exata no controlador e execute novamente o fluxo completo:

printf 'Ready\n' > ready.txt
ansible web1 -m ansible.builtin.copy -a 'src=/home/labex/project/ready.txt dest=/home/automation/maintenance-ready mode=0644'
ansible-playbook maintenance.yml

Web1 deve regenerar seu relatório com sucesso. A repetição final deve ter zero alterações, destinos inacessíveis e falhas:

ansible-playbook maintenance.yml

As inspeções somente leitura ainda executam, o script protegido pula relatórios existentes e cada comprovante delegado permanece no controlador. Isso preserva os sinais reais de erro enquanto mantém estável uma execução repetida bem-sucedida.

Resumo

Você usou argv para execução gerenciada direta, script para transferência de programas do controlador ao destino e shell apenas para um pipeline real com propagação explícita de falhas. Relatar fielmente as inspeções somente leitura como sem alterações, usar uma proteção creates real e manter os códigos de saída sem suprimir erros tornou os resultados significativos. Comprovantes delegados por host demonstraram a execução no controlador, e o exercício de entrada ausente terminou em um fluxo restaurado e estável.