Infraestrutura como código para quem tem cinco servidores
Ferramenta de infraestrutura declarativa tem fama de coisa para operação grande. O ganho aparece bem antes disso — e o motivo não é automação, é ter escrito em algum lugar o que existe.
Equipe EasyOps Cloud · · 7 min de leitura
Infraestrutura como código carrega a fama de ser coisa de operação grande. Com cinco servidores, a impressão é de que criar tudo pelo painel resolve — e resolve, no dia em que você cria.
O problema aparece meses depois, e não é de automação. É de memória: ninguém lembra por que aquela máquina tem 8 GB em vez de 4, qual regra de firewall foi adicionada às pressas em março, ou o que exatamente precisa existir para o ambiente funcionar.
O ganho real de declarar infraestrutura não é criar rápido. É ter escrito, em um lugar com histórico, o que existe e por quê.
O que muda na prática
Com a infraestrutura descrita em arquivo versionado, quatro coisas passam a ser possíveis:
- Revisar antes de aplicar. A mudança aparece como diferença de texto, e alguém pode olhar antes de virar realidade.
- Saber quem mudou o quê e quando. O histórico do repositório responde.
- Recriar do zero. Ambiente de homologação idêntico ao de produção deixa de ser aspiração.
- Documentação que não desatualiza, porque ela é a própria configuração.
Nenhum desses depende de escala. O quarto item, em especial, vale mais para equipe pequena — que é justamente quem não tem alguém dedicado a manter documentação.
Sobre a ferramenta
O Terraform mudou de licença em 2023, e a comunidade criou o OpenTofu como continuação sob licença aberta. Os dois são compatíveis na prática, e a escolha hoje é mais sobre a qual você quer estar sujeito no futuro — assunto do artigo sobre mudança de licença em open-source.
Para quem começa agora, o OpenTofu é a opção sem essa preocupação.
# a sintaxe e os conceitos são os mesmos
tofu init
tofu plan
tofu applyA estrutura mínima
# main.tf
terraform {
required_providers {
meuprovedor = {
source = "registry.opentofu.org/exemplo/meuprovedor"
version = "~> 1.0"
}
}
}
variable "regiao" { default = "br-sp" }
variable "tamanho" { default = "2vcpu-4gb" }
resource "meuprovedor_servidor" "web" {
count = 2
nome = "web-0${count.index + 1}"
imagem = "ubuntu-24.04"
regiao = var.regiao
plano = var.tamanho
tags = ["producao", "web"]
}
output "ips" {
value = meuprovedor_servidor.web[*].ipv4
}O fluxo tem três comandos e uma regra:
tofu init # baixa os provedores
tofu plan # mostra o que faria — LEIA
tofu apply # aplicaA regra é ler o plan. Sempre. Ele é o equivalente ao --dry-run do rsync descrito
no artigo sobre rsync: a diferença entre uma mudança conferida e uma surpresa.
O state, que é onde mora o risco
A ferramenta guarda um arquivo de estado com o mapeamento entre o que está
descrito e o que existe de verdade. Perder esse arquivo significa que a
ferramenta deixa de reconhecer os recursos — e um apply seguinte tenta criar
tudo de novo.
Deixá-lo apenas na máquina de quem executou é o erro que mais causa dor. Ele deve ficar em armazenamento compartilhado, com versionamento:
terraform {
backend "s3" {
bucket = "infra-state"
key = "producao/terraform.tfstate"
region = "br-sp"
endpoint = "https://s3.suaregiao.exemplo"
}
}Um bucket S3 com versionamento ativo resolve os dois problemas de uma vez: a equipe compartilha o mesmo estado, e uma versão anterior existe se algo corromper a atual.
Dois cuidados que não são opcionais. O state contém valores sensíveis em texto
claro — senha gerada, chave de acesso —, então o bucket precisa ser privado e o
arquivo nunca entra no versionamento de código. E com mais de uma pessoa
aplicando, é preciso travamento, senão dois apply simultâneos corrompem o
estado.
Os comandos que destroem sem avisar
Vale conhecer antes de tropeçar.
tofu destroy apaga tudo que está sob gestão. Existe para ambiente efêmero e é
perigoso em produção. Vale proteger o que não pode sumir:
resource "meuprovedor_servidor" "banco" {
# ...
lifecycle {
prevent_destroy = true
}
}Mudança que força recriação. Alguns atributos não podem ser alterados no lugar,
e a ferramenta resolve destruindo e recriando. No plan, isso aparece como must
be replaced ou -/+. Em um servidor de banco, significa perder o disco.
Ler o plan procurando especificamente por destroy e replace é o hábito que evita
o pior. Quando aparecer sem que você tenha pedido, pare e entenda antes de
seguir.
Alteração pelo painel. Mexer manualmente em um recurso gerido cria divergência. Na próxima execução, a ferramenta desfaz a mudança manual — ela considera o arquivo como verdade. Ou você adota a ferramenta como caminho único, ou vai ter surpresa.
tofu plan -refresh-only # o que mudou fora da ferramenta?Onde parar
Uma decisão que economiza bastante trabalho: declarar a infraestrutura, não a configuração de dentro das máquinas.
Servidor, disco, IP, regra de firewall, bucket, registro de DNS — tudo isso é descrição estável, muda pouco, e ganha com versionamento.
Já pacote instalado, arquivo de configuração e serviço em execução mudam com outra frequência e têm ferramenta própria. Tentar resolver os dois com a mesma coisa produz configuração cheia de scripts embutidos, difícil de ler e de testar.
A divisão que funciona: OpenTofu cria a máquina, Ansible configura o que está dentro. Ou, para operação pequena, uma imagem pré-configurada e um script de inicialização.
Quando um script resolve melhor
Vale a honestidade. Para uma máquina única, que foi criada uma vez e não muda, declarar em HCL adiciona uma ferramenta, um state para cuidar e um conceito novo para a equipe — em troca de pouco.
Nesses casos, um script de provisionamento versionado entrega boa parte do benefício:
#!/usr/bin/env bash
# provisiona.sh — o que precisa existir, em ordem
set -euo pipefail
# ... chamadas à API do provedor, ou comandos da CLIEle não tem reconciliação nem detecção de divergência, mas registra a intenção e tem histórico. Para muita operação pequena, isso já é a maior parte do valor.
O ponto de virada costuma ser quando aparece o segundo ambiente. No momento em que você precisa de homologação parecida com produção, a ferramenta declarativa passa a compensar de imediato — porque a alternativa é recriar tudo à mão e torcer para não esquecer nada.
Um caminho de adoção
Adotar de uma vez, importando tudo que já existe, é trabalhoso e frustrante. O caminho mais suave:
- Comece pelo próximo recurso novo. Não migre o que está de pé.
- Deixe o state no bucket desde o primeiro dia. Migrar depois é chato.
- Guarde o `plan` no histórico das mudanças relevantes. Ele documenta o que se pretendia fazer.
- Importe o que já existe aos poucos, quando cada recurso precisar de alteração de qualquer forma.
- Trate o repositório de infraestrutura como código de produção: revisão, histórico e ninguém aplicando direto sem olhar.
Em seis meses, sem nenhum esforço concentrado, a maior parte da infraestrutura estará descrita — e a pergunta "por que essa máquina é assim?" passa a ter resposta no histórico, em vez de depender da memória de quem estava lá.
O que ele não resolve
Vale delimitar, porque a expectativa inflada é o que mais frustra quem adota.
Não substitui backup. Recriar a infraestrutura não recria o dado. Um apply que
recria o servidor de banco entrega uma máquina vazia — e o conteúdo vem da
rotina de restauração testada, que continua sendo responsabilidade separada.
Não garante que a máquina está configurada. Ele cria o recurso; o que roda dentro é outra camada.
Não impede erro. Um apply com a configuração errada aplica a configuração
errada, rapidamente e em todas as máquinas. A proteção é o plan lido com
atenção, não a ferramenta.
Não elimina o painel. Investigação, console de emergência e ações pontuais continuam acontecendo por lá. O objetivo é que a *descrição* seja o código, não que a interface deixe de existir.
Entendidos os limites, o que sobra é bastante: um lugar único que responde o que existe, por que existe, e o que muda quando alguém propõe uma alteração. Para equipe pequena, esse registro costuma valer mais que a automação em si — porque o problema real raramente é criar servidor, é lembrar do que foi feito.
Um detalhe que evita retrabalho
Vale fixar as versões desde o início — tanto da ferramenta quanto dos
provedores. Sem isso, a mesma configuração produz resultados diferentes em
máquinas diferentes, e uma atualização silenciosa do provedor pode introduzir
mudança que aparece como recriação de recurso no plan seguinte.
terraform {
required_version = "~> 1.8"
required_providers {
meuprovedor = {
source = "registry.opentofu.org/exemplo/meuprovedor"
version = "= 1.4.2"
}
}
}O arquivo de bloqueio gerado pelo init também deve ser versionado. Ele registra
as versões exatas que foram usadas, o que garante que quem executar amanhã
obtenha o mesmo comportamento de quem executou hoje.
É o mesmo raciocínio de fixar a versão da imagem de container em vez de usar latest,
tratado no artigo sobre Docker em produção: reprodutibilidade custa uma linha e
evita a categoria de problema mais difícil de diagnosticar, que é aquela em que
nada mudou do seu lado.