Renderizar e validar modelos de configuração

AnsibleBeginner
Pratique Agora

Introdução

A automação com modelos mantém uma única origem enquanto produz configurações a partir das entradas de cada host. Você renderizará arquivos a partir de variáveis e fatos, transformará um cabeçalho com um filtro Jinja integrado, escolherá um ramo condicional e carregará uma configuração nginx validada por meio de um handler. Solicitações HTTP reais e um candidato inválido rejeitado mostrarão a diferença entre texto de origem, arquivos gerenciados e comportamento do serviço ativo.

Renderizar variáveis de host e fatos no conteúdo gerenciado

Nesta etapa, você criará um modelo de página e renderizará arquivos reais distintos a partir da mesma origem nos dois destinos.

Os nós preparados têm nginx instalado como pré-requisito. Seu trabalho é renderizar e aplicar configurações, em vez de instalar o pacote novamente. Os modelos são renderizados no controlador usando o contexto de variáveis de cada destino e depois transferidos aos destinos gerenciados.

Entre no projeto do controlador e crie um diretório templates. mkdir -p cria o diretório se necessário, sem falhar quando ele já existe:

cd /home/labex/project
mkdir -p templates

Defina entradas tipadas de host. O ambiente e a porta de escuta diferem, e os valores maintenance_mode sem aspas são booleanos 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

Expressões Jinja dentro de chaves duplas inserem valores no texto. O sufixo .j2 é uma convenção útil para origens de modelos, não uma exigência para o nome do arquivo de destino gerenciado. Crie uma origem que use o nome atual no inventário, a variável de ambiente e o fato descoberto da distribuição:

cat > templates/page.txt.j2 <<'JINJA'
host={{ inventory_hostname }}
environment={{ environment_label }}
distribution={{ ansible_facts.distribution }}
JINJA

Use o módulo template para renderizar. Copy normalmente transferiria os bytes da origem sem interpretar essas expressões Jinja. Colete fatos antes de usar o valor de distribuição e declare explicitamente a propriedade e as permissões gerenciadas:

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

Renderize as duas páginas gerenciadas:

ansible-playbook templates.yml --syntax-check
ansible-playbook templates.yml

Leia os arquivos reais nos destinos:

ansible all -m ansible.builtin.command -a 'cat /var/www/template-site/index.html'

Cada página deve identificar seu próprio host, o ambiente development/production correspondente e distribution=Ubuntu. O controlador ainda tem uma origem reutilizável, enquanto as duas saídas gerenciadas diferem.

Acrescentar um filtro integrado e um ramo condicional de configuração

Nesta etapa, você renderizará uma configuração nginx válida que altera o comportamento HTTP conforme as entradas de cada host.

Um filtro Jinja transforma um valor usando o símbolo de barra vertical. upper é um filtro integrado que converte texto em maiúsculas; não é um plugin personalizado. As instruções de controle Jinja usam delimitadores de porcentagem e chave. O ramo if/else/endif abaixo seleciona um cabeçalho de manutenção com base na entrada booleana.

Crie um modelo completo de configuração nginx. A porta, o cabeçalho de ambiente e o cabeçalho de manutenção vêm de dados por host. As configurações root e index servem a página que você já renderizou:

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

Acrescente as tarefas de configuração e serviço e o handler nomeado de recarga ao playbook existente. O comando validate da tarefa template testa um candidato renderizado temporário (%s) antes de substituir a configuração gerenciada. Uma configuração válida alterada notifica o handler; a tarefa service comum mantém o nginx em execução.

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

Aplique o playbook completo:

ansible-playbook templates.yml

A tarefa de configuração deve alterar os dois hosts e acionar o handler de recarga. Inspecione web1 na porta 8081 e web2 na porta 8082. curl -i inclui os cabeçalhos de resposta junto ao corpo:

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 deve retornar X-Environment: DEVELOPMENT e X-Maintenance: enabled; web2 deve retornar X-Environment: PRODUCTION e X-Maintenance: disabled. Os dois corpos mantêm a página correta por host. As datas dos cabeçalhos variam; compare os valores baseados em dados e o status de sucesso.

Alterar entradas e rejeitar um candidato inválido

Nesta etapa, você atualizará uma entrada persistente sem editar os modelos, observará o resultado ativo real e comprovará que a validação protege a configuração funcional.

Altere o ambiente salvo de web1 para staging e seu modo de manutenção para false, mantendo a porta existente:

cat > host_vars/web1.yml <<'YAML'
environment_label: staging
service_port: 8081
maintenance_mode: false
YAML

Aplique os mesmos modelos de origem:

ansible-playbook templates.yml

A página e a configuração de web1 devem mudar, notificando seu handler de recarga; web2 deve permanecer inalterado. Inspecione web1 novamente:

ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'

Seu corpo deve mostrar environment=staging, com X-Environment: STAGING e X-Maintenance: disabled. Os modelos continuaram reutilizáveis quando a entrada mudou.

Agora teste a rejeição de um candidato. cp salva a origem válida no controlador para que possa ser restaurada. Substitua apenas o modelo nginx por texto deliberadamente inválido:

cp templates/nginx.conf.j2 templates/nginx.conf.j2.good
printf 'invalid nginx configuration;\n' > templates/nginx.conf.j2

Espera-se que a próxima execução do playbook falhe na validação do candidato. O || do shell executa a mensagem apenas após uma saída diferente de zero, permitindo que este exercício de falha intencional continue:

ansible-playbook templates.yml || printf 'Expected candidate validation rejection\n'

Leia a falha da tarefa: o nginx deve rejeitar o candidato temporário. A configuração gerenciada funcional deve permanecer no lugar, portanto os dois serviços ativos ainda devem responder corretamente:

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 restaura a origem válida salva para seu nome de arquivo original. Execute o playbook completo mais uma vez:

mv templates/nginx.conf.j2.good templates/nginx.conf.j2
ansible-playbook templates.yml

Os dois hosts devem informar changed=0, failed=0 e unreachable=0, sem handler de recarga agendado. A validação protegeu a configuração ativa sem tornar uma cópia de um candidato inválido o estado final desejado.

Resumo

Você renderizou variáveis de hosts e fatos com o módulo template, usou o filtro integrado upper e um ramo booleano Jinja e aplicou uma configuração validada por meio de um handler. Uma entrada alterada produziu uma saída ativa diferente sem regravar as origens. Um candidato inválido foi rejeitado antes da substituição, e os modelos válidos restaurados convergiram sem alterações extras.