02 - Versionamento e Imutabilidade
video: https://youtu.be/... no frontmatter da nota.Versionamento e Imutabilidade
Versionamento e imutabilidade são os dois princípios que transformam IaC de automação conveniente em sistema confiável de produção. O versionamento coloca toda a definição de infraestrutura em Git, com histórico completo de quem mudou o quê, quando e por quê; a imutabilidade estende o mesmo raciocínio aos artefatos executáveis, servidores e imagens que, uma vez criados, não são mais alterados, apenas substituídos por versões novas. Juntos, eles eliminam a categoria de problemas em que produção é um estado irreproduzível que só existe por acumulação de decisões não registradas. Em produção, o valor aparece em três momentos: ao revisar uma mudança antes de ela tocar o mundo real, ao investigar um incidente com linha do tempo confiável e ao reverter, de forma limpa, uma mudança que causou regressão. Este arquivo cobre os dois princípios, o fluxo de trabalho que os materializa e os trade-offs entre servidores mutáveis e imutáveis.
Conceito: o repositório como fonte de verdade
Quando a infraestrutura vive em Git, o repositório passa a responder qualquer pergunta sobre o estado do sistema. Qual regra de security group protege o banco: olha-se o código. Quem aprovou a mudança de ontem: olha-se o pull request. Como produção estava no mês passado, antes do incidente: olha-se o commit anterior. Essa propriedade, fonte de verdade, é o que separa uma operação auditável de uma operação baseada em memória oral.
O versionamento também define o contrato de confiança entre times: nada existe em produção sem correspondência no repositório, e nada entra no repositório sem revisão. Quando esse contrato vale, a discussão técnica acontece no pull request, com evidência e contexto, e a produção reflete a decisão aprovada, não a decisão do último que teve acesso ao console.
O paralelo com software é direto e proposital. Se a infraestrutura é código, ela herda o modelo mental do desenvolvimento: branches para mudanças em andamento, tags para releases, revert para desfazer, blame para investigar. Times que já dominam Git transportam a competência inteira para infraestrutura com pouco atrito, e a curva de aprendizado do IaC se concentra na parte de infraestrutura, não na parte de código.
O problema que resolve: o servidor como caixa preta
Servidores mutáveis operados manualmente são caixas pretas acumuladoras. Cada patch aplicado, cada pacote instalado e cada ajuste de configuração ficam gravados apenas na máquina, sem histórico e sem explicação; com o tempo, o sistema passa a depender de estados locais que ninguém sabe reproduzir. A frase clássica da área resume o risco: se o servidor não pode ser recriado, ele não está em produção, está de refém.
A caixa preta cobra o preço mais alto no pior momento possível. Durante um incidente, o time precisa responder o que mudou recentemente, e sem versionamento a resposta é um palpite; durante uma reconstrução de emergência, o time precisa reproduzir a máquina, e sem imutabilidade a resposta é tentativa e erro. Versionamento e imutabilidade existem para que essas duas perguntas tenham resposta mecânica.
Existe ainda o problema da divergência ao longo do tempo. Uma frota de dez servidores mutáveis provisionados há dois anos contém, na prática, dez versões diferentes do sistema operacional, das bibliotecas e da configuração, porque cada um absorveu patches e intervenções próprios. Essa heterogeneidade invisível é fonte clássica de bugs que aparecem em uma instância e nunca se reproduzem em outra.
O fluxo de mudança versionada
O fluxo canônico replica o desenvolvimento de software. A mudança nasce em um branch descritivo, avança por pull request com plano de execução anexado, recebe revisão, é executada por pipeline em ambientes na ordem de menor para maior risco e termina em commit aplicado à produção. Em cada etapa, o artefato é o mesmo: código que descreve infraestrutura.
A proteção de branch é o mecanismo que sustenta o contrato. Branch principal protegido, aprovação obrigatória, checagens automatizadas de formatação e validação, e proibição de push direto transformam o repositório em portão único de mudança. Sem proteção, o fluxo existe só no papel, e o console volta a ser o caminho real.
O fluxo também precisa lidar com emergências sem destruir a disciplina. A política comum abre um caminho expresso: a mudança emergencial entra por commit imediato com revisão retroativa, acompanhada de issue de reconciliação obrigatória em janela curta. O importante é que emergências sejam exceções rastreáveis, e não a porta de entrada rotineira de mudança sem revisão.
# Pipeline GitHub Actions: validação, plano e apply de infraestrutura
name: infra-pipeline
on:
pull_request:
paths: ["infra/**"] # roda apenas quando o IaC muda
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: terraform fmt -check -recursive # estilo consistente
- run: terraform validate # sintaxe e semântica
- run: terraform plan -out=tfplan # plano anexado ao PR
apply:
needs: validate
if: github.ref == 'refs/heads/main' # só na main, após merge
runs-on: ubuntu-latest
environment: producao # exige aprovação humana
steps:
- uses: actions/checkout@v4
- run: terraform apply -auto-approve tfplan
Imutabilidade: substituir em vez de alterar
A imutabilidade aplica a lógica do versionamento aos artefatos executáveis. Numa infraestrutura mutável, a correção entra no servidor existente: pacote atualizado, arquivo editado, serviço reiniciado. Numa infraestrutura imutável, a correção gera um novo artefato, imagem ou container, e o servidor antigo é descartado e substituído; nunca se edita o que está em execução.
O ganho central é a eliminação da divergência acumulada. Um servidor imutável nasce de uma imagem construída por pipeline, idêntica às irmãs da mesma versão, e permanece idêntico até a morte. Não existem snowflakes porque não existe tempo para eles se formarem: a instância vive dias ou semanas, não anos, e a heterogeneidade da frota passa a ser apenas a lista de versões em rollout.
A imutabilidade também muda a natureza do rollback. Voltar atrás deixa de ser desfazer mudanças dentro do servidor, operação frágil e sem garantia de estado limpo, e passa a ser reexecutar a versão anterior da imagem, operação determinística. O estado que se restaura não é o servidor, é a versão do artefato.
O custo do modelo é estrutural e precisa ser aceito: cada mudança exige novo build, novo rollout e, no caso de VMs, substituição de instâncias com orquestração de drain, health check e destruição gradual. Auto Scaling Groups, grupos de instâncias gerenciados e controladores de rollout de Kubernetes existem, em grande parte, para pagar esse custo de forma automatizada e segura.
Golden images: o pipeline da imagem
A golden image é o artefato da imutabilidade: uma imagem de máquina construída por pipeline, contendo sistema operacional endurecido, dependências, agentes e configuração base, versionada e testada antes de virar base de frota. Packer e EC2 Image Builder são as ferramentas dominantes para construí-la de forma repetível e auditável, e a imagem vira insumo do IaC, referenciada por id ou por filtro de versão.
O ciclo de vida da imagem replica o ciclo de release de software. Uma mudança de dependência abre pull request no repositório de imagens, a pipeline constrói uma versão nova, executa testes de smoke e varredura de vulnerabilidade, publica com tag imutável e a frota adota a versão por rollout gradual. A imagem anterior permanece disponível para rollback imediato, e versões antigas são aposentadas por política de retenção, não por esquecimento.
A fronteira entre imagem e configuração dinâmica é uma decisão de desenho: o que for igual a toda a frota entra na imagem; o que variar por instância ou por ambiente entra como user data, parâmetros ou gerenciamento de configuração no boot. Colocar na imagem o que muda por ambiente multiplica builds sem ganho; deixar na configuração dinâmica o que é comum reacende a mutabilidade.
# Packer HCL: construção de golden image versionada
packer {
required_plugins {
amazon = {
version = ">= 1.3.0"
source = "github.com/hashicorp/amazon"
}
}
}
source "amazon-ebs" "app" {
region = "sa-east-1"
instance_type = "t3.small"
ami_name = "app-base-${local.timestamp}" # tag imutável por build
ssh_username = "ec2-user"
ami_tags = {
Version = var.versao # versão semântica vinda da pipeline
GitSha = var.git_sha # rastreabilidade até o commit
}
}
build {
sources = ["source.amazon-ebs.app"]
provisioner "shell" {
inline = [
"sudo yum update -y", # patch de segurança
"sudo amazon-linux-extras install -y docker",
"sudo systemctl enable docker" # estado base congelado
]
}
}
Mutable versus immutable: comparação
| Dimensão | Infraestrutura mutável | Infraestrutura imutável |
|---|---|---|
| Unidade de mudança | Patch no servidor em execução | Nova imagem substituindo a anterior |
| Divergência da frota | Acumula com o tempo | Zerada a cada substituição |
| Rollback | Desfazer mudanças, frágil | Reexecutar imagem anterior, determinístico |
| Tempo de aplicação | Minutos, direto no host | Ciclo de build e rollout completo |
| Testabilidade da mudança | Limitada ao próprio servidor | Testada antes, na imagem |
| Debug pós-incidente | Estado local pode desaparecer | Instância substituída preservada para forense |
| Custo de armazenamento | Baixo, estado vivo | Maior, múltiplas versões de imagem |
| Exigência de automação | Opcional, funciona à mão | Estrutural, sem rollout automático não escala |
A leitura prática da tabela é de transição, não de fé: operações começam mutáveis por velocidade e migram para imutáveis quando a divergência da frota e o custo dos incidentes superam o custo do build. A migração típica ocorre por partes, primeiro serviços stateless em contêineres e Auto Scaling Groups, depois camadas de estado com estratégias próprias de reconstrução e réplicas.
Há também um ponto de articulação entre os dois mundos: servidores imutáveis ainda precisam de parametrização no boot, endereço, papel, credenciais efêmeras, e essa camada é exatamente onde gerenciamento de configuração e user data operam no modelo imutável. A imutabilidade não elimina configuração; ela restringe a configuração a um momento único e reproduzível, o nascimento da instância.
Rollback e recuperação versionada
O rollback em infraestrutura versionada é um revert de commit seguido de plano e apply, e a garantia de que a reversão reproduz o estado anterior vem justamente do versionamento: o commit antigo descreve integralmente o estado desejado de antes. Sem estado descrito, rollback é improvisação; com estado descrito, rollback é execução rotineira do fluxo normal em ordem inversa.
O rollback de infraestrutura imutável é ainda mais direto: reexecutar a versão anterior da imagem no rollout, mantendo o plano de dados intocado. A armadilha clássica é a incompatibilidade entre versão de código e versão de schema de banco, e por isso rollbacks de schema pedem compatibilidade bidirecional durante um período de transição, tema tratado nos módulos de banco deste material.
Em recuperação de desastre, o versionamento é o que transforma o plano em exercício realista: recriar a região a partir do código é o teste completo da fonte de verdade, e times maduros executam esse teste de forma programada. Se a reconstrução regional depende de estado que não está no repositório, o teste revela, e a lacuna é fechada antes do desastre, e não durante.
Erros comuns e boas práticas
- Deixar exceções manuais em produção sem reconciliação no código; a exceção não registrada vira drift e bomba-relógio para a próxima execução.
- Revisar apenas a sintaxe do IaC e não o plano; o pull request precisa mostrar o que muda no mundo real, não só o que muda no texto.
- Aplicar patches diretamente na golden image publicada; imagem é imutável, e correção gera versão nova, nunca edição da existente.
- Manter frota mutável sem inventário de versão; se o modelo mutável persistir, registre versão, patches e intervenções por instância.
- Construir golden images sem testes; imagem sem smoke test e sem varredura de vulnerabilidade é dívida empacotada e distribuída em escala.
- Usar tags latest ou referências não versionadas para imagens; referência imprecisa destrói a rastreabilidade que a imutabilidade promete.
- Descartar instâncias substituídas em incidentes antes da forense; preserve o artefato de diagnóstico antes de encerrar o recurso.
- Confundir imutabilidade com ausência de configuração; a parametrização no boot continua existindo e precisa de versionamento próprio.
- Permitir push direto no branch principal do repositório de infraestrutura; o fluxo de revisão só vale se for o único caminho para produção.