Dominando a Arquitetura de Ansible
Se você quer evoluir suas automações sem virar refém de “copiar/colar”, a virada está em tratar estrutura como parte do código: inventário, organização de diretórios, papéis, variáveis e idempotência.
1) Inventário e resolução de contexto: onde a ordem realmente importa
A primeira decisão arquitetural do Ansible é: como os hosts entram no sistema e como as variáveis chegam neles. O que parece “detalhe” vira custo operacional quando o inventário cresce.
-
Use um formato previsível de inventário (ex.:
iniouYAML) e defina grupos por intenção (ex.:web,db,edge), não por acaso. - Separe variáveis globais (aplicadas a tudo) de variáveis por grupo e variáveis por host. Isso reduz o “efeito colateral” de valores acidentalmente sobrescritos.
- Entenda que a prioridade de variáveis é determinística. Se você deixa a resolução implícita, o comportamento muda conforme arquivos e ordens — e isso mata a confiança no projeto.
2) Organização de diretórios: tratando playbooks como produto
Uma arquitetura saudável não depende de “memória”. Eu organizo o repositório para permitir que qualquer pessoa: (1) encontre onde alterar, (2) saiba o que impacta, e (3) replique padrões sem inventar moda.
-
Mantenha playbooks no topo, com nome que explique o objetivo:
site.yml,deploy-web.yml,hardening.yml. - Use roles para encapsular comportamento e permitir reuso. Cada role deve ter uma responsabilidade clara (ex.: “nginx”, “firewall”, “base-os”).
- Centralize group_vars e host_vars no nível que faz sentido para o time. Evite espalhar variáveis em múltiplos locais quando a regra é “variável de ambiente”.
- Coloque templates, arquivos estáticos e handlers dentro da role que consome, não no “meio do caminho”.
ansible/
├─ ansible.cfg
├─ inventories/
│ ├─ prod/
│ │ ├─ hosts.ini
│ │ └─ group_vars/
│ │ ├─ all.yml
│ │ └─ web.yml
│ └─ staging/
│ ├─ hosts.ini
│ └─ group_vars/
│ ├─ all.yml
│ └─ web.yml
├─ playbooks/
│ ├─ site.yml
│ ├─ deploy-web.yml
│ └─ hardening.yml
├─ roles/
│ ├─ base-os/
│ │ ├─ tasks/main.yml
│ │ ├─ handlers/main.yml
│ │ └─ templates/
│ │ └─ ...
│ ├─ nginx/
│ │ ├─ tasks/main.yml
│ │ ├─ handlers/main.yml
│ │ └─ templates/
│ │ └─ nginx.conf.j2
│ └─ firewall/
│ ├─ tasks/main.yml
│ └─ defaults/main.yml
└─ group_vars/ (opcional, se você preferir um nível global)
3) Roles e playbooks: responsabilidade, composição e previsibilidade
A “arquitetura” do Ansible se concretiza na composição entre playbooks e roles. O playbook descreve o que será feito (em que grupos/hosts), enquanto a role descreve como.
- No playbook, use roles de forma explícita e mínima. Evite “papel infinito” que faz tudo e dificulta testes.
-
Dentro da role, siga um fluxo claro em
tasks/main.yml: pré-requisitos → configuração → serviços → validação. Isso aumenta legibilidade e facilita auditoria. - Prefira handlers para ações reativas (ex.: reiniciar serviço apenas quando muda configuração). Isso evita churn e reduz downtime desnecessário.
-
Use
defaultsevarscom intenção: defaults são “opcionais e seguros”; vars são “fixos do componente”. Se tudo vira default, você perde governança.
4) Idempotência e execução: como garantir que o resultado é estável
A arquitetura não é só organização: é também como você garante que repetidas execuções mantêm o mesmo estado. No Ansible, isso é idempotência na prática.
- Use módulos declarativos sempre que possível. Evite depender de comandos externos para alterações críticas.
- Garanta que tarefas que alteram arquivos sejam feitas com módulos adequados (ex.: template/copy) e ativem handlers apenas quando houver mudança real.
-
Defina e respeite check mode e valide com
diffquando aplicável. Isso reduz surpresas em pipelines. - Estruture tarefas sensíveis com condições claras e variáveis bem definidas. Quando a condição fica “mágica”, a manutenção vira adivinhação.
# roles/nginx/tasks/main.yml
- name: Instalar pacote do nginx
ansible.builtin.package:
name: nginx
state: present
- name: Gerar configuração do nginx
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: "0644"
notify: Reiniciar nginx
- name: Garantir que o serviço está habilitado
ansible.builtin.service:
name: nginx
enabled: true
state: started
# roles/nginx/handlers/main.yml
- name: Reiniciar nginx
ansible.builtin.service:
name: nginx
state: restarted
Resultado esperado: a execução repetida só reinicia o serviço quando o template realmente muda, mantendo o comportamento previsível.
Próximo passo
Se você gostou do nível de clareza que uma boa arquitetura entrega, continua comigo: leia outros posts para consolidar padrões de repositório, governança de variáveis e práticas de execução.