O Modelo Mental de Infraestrutura Declarativa
Cada ferramenta abordada nesta seção - Ansible, Pulumi, Terraform encapsulado em Python, ou um script boto3 desenvolvido internamente - é julgada pelo mesmo modelo subjacente, quer sua documentação diga isso explicitamente ou não.
Esse modelo é infraestrutura declarativa: você descreve o que deve existir, um processo de reconciliação compara essa descrição com o que realmente existe, e apenas a diferença é tratada.
Esta é uma forma diferente de programar do que a maioria dos engenheiros aprende inicialmente, onde o código é uma sequência de passos executados em ordem, e tornar-se confortável com a mudança é o que realmente torna os playbooks do Ansible, os programas Pulumi e os planos do Terraform previsíveis em vez de misteriosos.
Resumo
- Ferramentas de infraestrutura declarativa pegam uma descrição do estado desejado e reconciliam a infraestrutura real em direção a ele, em vez de executar uma sequência fixa de passos imperativos.
- Por que Importa: Scripts imperativos ("crie isso, depois aquilo") falham imprevisivelmente em reexecuções e após falhas parciais; a reconciliação declarativa é projetada para ser reexecutada com segurança a partir de qualquer ponto de partida.
- Conceitos Chave: estado desejado, idempotência, loop de reconciliação, drift, arquivo de estado, plan/apply.
- Quando Usar: Qualquer infraestrutura que será criada mais de uma vez, tocada por mais de uma pessoa, ou que precise convergir após falha parcial - o que na prática significa quase toda infraestrutura real além de um protótipo solo.
- Limitações / Trade-offs: Ferramentas declarativas trocam controle processual granular por previsibilidade, e introduzem uma nova superfície de falha própria - drift do arquivo de estado, bloqueio e staleness do plan/apply.
- Tópicos Relacionados: gerenciamento de configuração, GitOps, provisionamento de nuvem, política-como-código.
Fundamentos
Um script imperativo diz ao computador exatamente o que fazer, em ordem: crie este bucket, então anexe esta política, então crie esta fila.
Execute-o duas vezes e a segunda execução geralmente falha, porque o bucket já existe e a maioria das APIs rejeita uma chamada de criação duplicada diretamente.
Uma ferramenta declarativa, em vez disso, pede para você descrever o estado final que deseja - "este bucket deve existir, com versionamento ativado" - e deixa a sequenciação e o tratamento de duplicatas para a própria ferramenta.
Idempotência é a propriedade que torna isso seguro: uma operação idempotente produz o mesmo resultado final quer seja executada uma ou dez vezes, então reexecuções após uma falha, um soluço de rede, ou uma nova tentativa de CI nunca são destrutivas por padrão.
O mecanismo por trás da idempotência é um loop de reconciliação: lê o estado atual do mundo real, compara-o com o estado desejado declarado, calcula a diferença, e age apenas sobre essa diferença.
Se o bucket já existe com versionamento ativado, o loop de reconciliação não vê diferença e não faz nada - esse comportamento de "no-op na reexecução" é a assinatura prática de um sistema corretamente idempotente.
Uma analogia simples: um termostato não executa "ligue o aquecedor por exatamente 4 minutos" como um script fixo.
Ele compara continuamente a temperatura atual do ambiente com a temperatura alvo e age apenas sobre a lacuna, que é por isso que funciona corretamente independentemente da temperatura inicial do ambiente.
Ansible, Pulumi e Terraform (com ou sem um wrapper Python) são todas implementações dessa mesma ideia de reconciliação, diferindo principalmente em onde elas mantêm seu registro do estado atual e como elas expressam o estado desejado em código.
Mecânicas e Interações
O loop de reconciliação precisa de duas entradas para calcular uma diferença: o estado desejado (seu código) e um registro do estado atual.
As ferramentas diferem acentuadamente em como obtêm a segunda entrada, e essa diferença explica a maioria de suas peculiaridades comportamentais.
Terraform e Pulumi mantêm um arquivo de estado explícito - um registro serializado de cada recurso que a ferramenta acredita ter criado e seus últimos atributos conhecidos - e eles comparam seu código com esse arquivo, não necessariamente com a infraestrutura ao vivo, a menos que uma etapa de atualização (refresh) seja executada primeiro.
Ansible, por outro lado, é majoritariamente sem estado: cada tarefa consulta diretamente o alvo ao vivo (este arquivo está presente, este pacote está instalado) e só então decide se age, que é por que os playbooks do Ansible tendem a ser naturalmente idempotentes por tarefa sem um arquivo de estado separado para gerenciar.
Essa distinção é a fonte mais comum de confusão para engenheiros que mudam entre ferramentas: "drift" no Terraform significa que o arquivo de estado discorda da infraestrutura real (alguém mudou algo no console), enquanto "drift" no Ansible significa que o alvo ao vivo discorda do estado declarado do playbook, verificado a cada execução.
Plan before apply existe porque uma diferença contra um registro de estado desatualizado ou incompleto pode estar errada, e a etapa de plano é o ponto de revisão humana onde uma ação destrutiva proposta (excluir um recurso com estado, por exemplo) é capturada antes de ser executada.
# A forma da reconciliação, independente da API de qualquer ferramenta específica
def reconcile(desired: dict, get_current: callable, apply_change: callable) -> dict:
current = get_current() # lê o estado do mundo real
diff = {k: v for k, v in desired.items() if current.get(k) != v}
if diff:
apply_change(diff) # age apenas sobre a diferença
return {"changed": bool(diff), "delta": diff}Esta é a forma que toda ferramenta declarativa implementa internamente, seja um binário Go de 500 linhas (Terraform) ou um provedor de recursos Python (Pulumi) - o ponto é o padrão de comparação-então-apenas-delta, não qualquer implementação específica.
Bloqueio (Locking) é o outro detalhe mecânico que vale a pena internalizar: como o arquivo de estado (ou o alvo ao vivo, para Ansible) é compartilhado, aplicaçãos concorrentes de dois engenheiros ou dois trabalhos de CI competindo contra a mesma infraestrutura corromperão ou entrarão em conflito nesse registro compartilhado, a menos que a ferramenta imponha um bloqueio durante a aplicação.
Considerações Avançadas e Aplicações
Em escala, o modelo de reconciliação tem que lidar com um caso que a simples analogia do termostato não aborda: falha parcial durante a aplicação, onde alguns recursos em um lote são bem-sucedidos e outros falham.
Uma ferramenta bem comportada deixa o arquivo de estado refletindo exatamente o que foi realmente criado, então a diferença da próxima execução pega corretamente apenas o trabalho restante, em vez de tentar novamente recursos já criados ou ignorar silenciosamente a falha.
Detecção de drift torna-se uma preocupação operacional de primeira classe assim que a infraestrutura é gerenciada por mais de uma equipe ou mais de uma ferramenta - uma mudança manual no console, um script separado usando boto3 diretamente, ou um recurso expirado podem fazer com que os estados declarado e real diverjam silenciosamente, sem nenhum erro até que o próximo plano o apresente como uma mudança inesperada.
Política-como-código estende o portão de plan/apply: em vez de um humano inspecionar uma diferença de texto, uma verificação de política automatizada inspeciona o delta planejado e bloqueia a aplicação se violar uma regra (tags obrigatórias ausentes, um grupo de segurança excessivamente permissivo, um bucket S3 público), transformando a etapa de revisão em um portão testável e repetível em vez de uma decisão manual.
Configurações multi-ambiente (dev, staging, prod) levam o modelo adiante: cada ambiente precisa de seu próprio registro de estado e, muitas vezes, de seus próprios parâmetros de estado desejado, e a disciplina que impede uma aplicação acidental em produção é quase inteiramente sobre manter o estado isolado por ambiente, não sobre o código ser fundamentalmente diferente.
Python entra nesse cenário em dois papéis distintos que valem a pena distinguir: como um runtime IaC de primeira classe (Pulumi, onde Python é a linguagem de estado desejado), ou como uma camada de orquestração em torno do ciclo de plan/apply de outra ferramenta (um script que executa terraform plan, verifica a política e condicionalmente executa terraform apply) - o modelo de reconciliação subjacente é idêntico de qualquer forma.
| Abordagem | Força | Fraqueza | Melhor Ajuste |
|---|---|---|---|
| Script boto3 imperativo | Controle total, sem ferramentas extras | Não idempotente por padrão, sem detecção de drift, sem etapa de plano | Tarefas únicas pequenas, scripts de cola |
| Ansible | Sem agente, naturalmente idempotente por tarefa, sem arquivo de estado para gerenciar | Grafo de dependência embutido mais fraco entre recursos do que Terraform/Pulumi | Gerenciamento de configuração, convergência ad-hoc |
| Terraform (HCL ou via wrapper Python) | Modelo de estado maduro, vasto ecossistema de provedores, fortes diffs de plano | HCL é uma linguagem separada para aprender (a menos que encapsulado) | Multi-cloud, IaC padrão de organização com portões de plano rigorosos |
| Pulumi (Python nativo) | Linguagem de programação real - loops, funções, testes | Ecossistema de provedores menor que Terraform em algumas áreas | Equipes que desejam IaC como código Python genuíno e testável |
Equívocos Comuns
- "Declarativo apenas significa usar YAML ou um arquivo de configuração em vez de um script." O formato do arquivo é incidental; o que importa é se um loop de reconciliação compara o estado desejado com o estado atual, em vez de um script simplesmente executando passos em ordem, independentemente do que já existe.
- "Idempotente significa que a operação é somente leitura ou inofensiva." Operações idempotentes podem absolutamente criar, modificar ou excluir recursos - a garantia é apenas que repetir a mesma operação não produz um resultado final diferente ou duplicado.
- "Se o plano do Terraform não mostra mudanças, a infraestrutura definitivamente corresponde ao código." O plano apenas compara o código com o arquivo de estado; se alguém mudou o recurso real fora do Terraform e nenhuma atualização foi executada, o próprio arquivo de estado pode estar desatualizado, mesmo que o plano pareça limpo.
- "Ansible não tem estado, então não pode detectar drift." Ansible verifica o estado ao vivo em cada execução de tarefa em vez de um registro em cache, que é um mecanismo diferente para o mesmo objetivo, não uma ausência de consciência de estado.
- "Plan e apply são apenas um diálogo de confirmação." A etapa de plano é um cálculo completo da diferença real contra um snapshot de estado específico; tratá-lo como um carimbo de borracha ignora o único local onde mudanças destrutivas são visíveis antes de serem executadas.
FAQs
O que exatamente significa "idempotente" para código de infraestrutura?
- Executar a mesma declaração duas vezes produz o mesmo estado final em ambas as vezes.
- A segunda execução deve relatar "sem mudanças" se nada realmente mudou.
- Não significa que a operação é segura ou reversível - apenas que a repetição não duplica ou corrompe o resultado.
Por que Terraform e Pulumi precisam de um arquivo de estado?
As APIs de nuvem geralmente não podem responder "quais recursos meu código criou", apenas "o que existe". O arquivo de estado é o próprio registro da ferramenta que vincula as declarações de recursos do seu código aos IDs de recursos reais, que é o que torna possíveis diffs precisos e atualizações direcionadas.
O Ansible tem um equivalente a um arquivo de estado do Terraform?
Não por padrão - a maioria dos módulos Ansible consulta o alvo ao vivo diretamente em cada execução em vez de consultar um registro em cache, que é por que os playbooks Ansible são naturalmente idempotentes por tarefa sem gerenciamento de estado separado.
O que é drift de infraestrutura, mecanicamente?
Drift é qualquer lacuna entre o estado que uma ferramenta acredita existir (seu arquivo de estado, para Terraform/Pulumi, ou o alvo ao vivo, para Ansible) e o que está realmente implantado - comumente causado por mudanças manuais no console ou por uma segunda ferramenta modificando os mesmos recursos.
Por que "plan before apply" é considerado tão importante?
A etapa de plano calcula e exibe a diferença exata que a aplicação atuará, que é o único ponto onde uma revisão humana ou de política automatizada pode detectar uma mudança destrutiva inesperada (como a exclusão acidental de um recurso) antes que ela aconteça.
Como o Pulumi difere do Terraform se ambos são declarativos?
Pulumi expressa o estado desejado como Python nativo (ou outra linguagem de propósito geral) com loops, condicionais e funções reais; Terraform usa sua própria linguagem HCL, embora CDKTF permita gerar configuração Terraform a partir de Python como uma camada sobre ela.
Posso escrever código de infraestrutura idempotente sem nenhuma ferramenta IaC?
Sim - um script boto3 que verifica a existência antes de criar, e verifica a configuração atual antes de atualizar, é idempotente pela mesma definição, apenas sem um arquivo de estado compartilhado ou uma etapa de plano.
O que acontece se uma aplicação falhar no meio do caminho?
Um modelo de estado corretamente implementado registra exatamente quais recursos foram bem-sucedidos antes da falha, então a diferença da próxima aplicação visa apenas os recursos restantes, ainda não criados - este é um dos principais benefícios práticos do modelo de reconciliação sobre um script linear.
Por que as equipes adicionam verificações de política sobre plan/apply?
Porque um humano revisando uma grande diferença de plano pode perder um problema sutil, mas importante (um bucket público, uma tag ausente); uma verificação de política automatizada inspeciona a mesma diferença programaticamente e bloqueia a aplicação em violações específicas e definidas.
O bloqueio de estado é realmente necessário para uma equipe pequena?
Torna-se necessário assim que duas pessoas ou dois trabalhos de CI podem executar apply na mesma infraestrutura concorrentemente - sem um bloqueio, ambos podem ler o mesmo estado desatualizado e aplicar mudanças conflitantes.
Como este modelo se relaciona especificamente com ferramentas de gerenciamento de configuração?
Gerenciamento de configuração (Ansible) aplica o mesmo modelo declarativo/idempotente no nível da configuração de uma única máquina (pacotes, arquivos, serviços) em vez do nível de provisionamento de recursos de nuvem inteiros, mas o modelo mental de reconciliação é idêntico.
Qual é o maior risco prático do modelo declarativo?
Tratar o arquivo de estado (ou a verificação do alvo ao vivo, para Ansible) como infalível - se ele sair de sincronia com a realidade e ninguém perceber, a diferença do próximo plano pode estar perigosamente errada, propondo "corrigir" algo que um humano mudou intencionalmente fora de banda.
Relacionados
- Noções Básicas de Automação de Infraestrutura - exemplos práticos de idempotência e plan-before-apply
- Ansible - o modelo de convergência sem estado, por tarefa
- Pulumi (IaC Python) - infraestrutura declarativa como Python nativo
- Terraform com Python - CDKTF e encapsulamento da CLI do Terraform
- Provisionamento de Recursos de Nuvem - aplicando o modelo a recursos reais de nuvem
- Testando Código de Infraestrutura - validando planos antes que eles sejam aplicados
Versões do Stack: Esta página foi escrita para Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, e uv 0.6+.