Durante muito tempo, meu contato com Kubernetes foi do tipo “já mexi”: subi um pod aqui, apliquei um manifesto ali, vi um deploy acontecer. Mas existe uma distância enorme entre usar uma ferramenta e entender o que ela faz por baixo. Eu venho de backend Java e automação de testes, e decidi atravessar essa distância de propósito construindo, do zero, uma plataforma GitOps completa e local.
Este artigo é o registro dessa construção. Não é um tutorial de “cole esse comando”; é uma explicação de por que cada peça existe e como elas se encaixam. Ao final, você terá visto uma infraestrutura nascer de um único comando, deploys acontecerem a partir de um git push, e métricas de uma aplicação Java fluírem até um dashboard tudo versionado, tudo reproduzível.
O que vamos construir
Antes do código, o mapa mental. A plataforma tem quatro camadas que se encaixam.
O Terraform provisiona o cluster e instala o motor de GitOps. É a fundação como código.
O kind roda um cluster Kubernetes real dentro de containers Docker, localmente.
O ArgoCD observa um repositório Git e garante que o cluster reflita exatamente o que está versionado.
Por fim, Prometheus e Grafana coletam e visualizam métricas, incluindo métricas customizadas de uma API Spring Boot.
O fio que costura tudo é uma ideia só: o Git é a fonte da verdade. Você não muda o cluster na mão; você muda arquivos no Git, e o cluster se ajusta.
Por que GitOps?
Vale parar um instante nessa ideia, porque ela é o coração de tudo.
No modelo tradicional, você aplica mudanças no cluster diretamente — um kubectl apply aqui, um kubectl scale ali. O problema é que o cluster vira uma caixa-preta. Ninguém sabe ao certo por que ele está do jeito que está, quem mudou o quê ou como reproduzir aquele estado em outro ambiente.
GitOps inverte isso. O estado desejado do cluster vive num repositório Git. Uma ferramenta (aqui, o ArgoCD) fica continuamente comparando o que deveria estar rodando (o Git) com o que está rodando (o cluster) e corrige qualquer diferença.
Na prática, isso significa que toda mudança passa a ter auditoria automática, porque ela é registrada como um commit com autor, data e motivo. Também significa que qualquer ambiente pode ser reproduzido a partir do mesmo repositório, que voltar atrás é tão simples quanto executar um git revert e que ninguém precisa de acesso direto ao cluster para fazer deploy: basta commitar.
Guarde essa ideia, porque vamos vê-la acontecer na prática.
Camada 1: o cluster como código com Terraform
O primeiro instinto de quem começa é criar o cluster na mão. O comando existe e é simples. Mas isso já quebra a promessa da reprodutibilidade — amanhã você não lembra exatamente como criou.
Por isso, desde o início, o cluster nasce de Terraform. O trecho central declara três providers e o cluster:
terraform {
required_providers {
kind = { source = "tehcyx/kind", version = "~> 0.9" }
helm = { source = "hashicorp/helm", version = "~> 2.17" }
kubernetes = { source = "hashicorp/kubernetes", version = "~> 2.35" }
}
}
resource "kind_cluster" "this" {
name = "gitops-lab"
wait_for_ready = true
kind_config {
kind = "Cluster"
api_version = "kind.x-k8s.io/v1alpha4"
node { role = "control-plane" }
node { role = "worker" }
node { role = "worker" }
}
}
Enter fullscreen mode Exit fullscreen mode
Repare em duas decisões. Primeiro, o cluster tem três nós (um control-plane e dois workers) em vez de um só. Isso não é firula: com múltiplos nós, você vê o Kubernetes distribuir cargas entre eles, o que torna conceitos como alta disponibilidade concretos em vez de teóricos.
Segundo, as versões dos providers estão fixadas. Isso é o que garante que um terraform apply daqui a seis meses produza o mesmo resultado de hoje. Reprodutibilidade não é acidente; é uma escolha.
A partir daí, o mesmo Terraform instala o ArgoCD via Helm, já apontando os providers para o cluster recém-criado. Um único terraform apply entrega o cluster e o motor de GitOps prontos.
Camada 2: ensinando o ArgoCD a observar o Git
Com o ArgoCD instalado, ele está de pé, mas ocioso — não sabe o que observar. É preciso apresentá-lo a um repositório. Isso se faz com um objeto chamado Application.
A Application é a ponte. Ela diz três coisas: de onde puxar, para onde aplicar e como se comportar.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx
namespace: argocd
spec:
source:
repoURL: https://github.com/usuario/gitops-manifests.git
targetRevision: main
path: apps/nginx
destination:
server: https://kubernetes.default.svc
namespace: nginx-demo
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Enter fullscreen mode Exit fullscreen mode
As duas linhas mais importantes deste arquivo são prune: true e selfHeal: true. Elas são o que torna o GitOps realmente vivo.
Com selfHeal, se alguém alterar o cluster manualmente, o ArgoCD detecta a divergência em relação ao Git e desfaz a mudança. O Git vence.
Já prune garante que, se um recurso for removido do repositório, ele também será removido do cluster. O cluster espelha exatamente o conteúdo do Git, nada mais.
O momento GitOps: escalar com um commit
Aqui a teoria vira prática, e é o momento que fixa o conceito.
Com o nginx rodando com uma réplica, fiz uma mudança que normalmente exigiria um comando no cluster: aumentar para três réplicas. Só que, em GitOps, isso não é um comando. É uma edição de arquivo:
spec:
replicas: 3 # antes era 1
Enter fullscreen mode Exit fullscreen mode
Um git commit e um git push depois, o ArgoCD detectou a diferença e criou dois novos pods — sozinho. Eu nunca rodei kubectl scale. O cluster simplesmente convergiu para o que o Git passou a dizer.
O teste que mais ensina veio a seguir. Executei um kubectl scale manual, forçando o cluster de volta a uma réplica. Por um instante, os pods começaram a ser removidos. Então o selfHeal entrou em ação: o ArgoCD percebeu que o cluster havia divergido do Git, que ainda dizia três réplicas, e recriou automaticamente os pods. Minha alteração manual foi desfeita em poucos segundos.
Essa é a garantia que dá segurança a ambientes de produção reais: não existe “conserta rápido no cluster e esquece”. Toda mudança passa pelo Git ou acaba sendo revertida.
Camada 3: observabilidade e o diferencial da métrica de negócio
Uma plataforma que você não consegue enxergar é uma plataforma que você não controla. Por isso a última camada é observabilidade.
Instalei o kube-prometheus-stack, um pacote que traz Prometheus, Grafana e Alertmanager já integrados, mantendo o padrão GitOps: ele entra no cluster como mais uma Application do ArgoCD.
Um detalhe técnico importante aqui é que charts Helm muito grandes, como esse, exigem a opção ServerSideApply=true no ArgoCD, porque seus CRDs ultrapassam o limite de tamanho da aplicação tradicional. É o tipo de detalhe que normalmente só aparece durante a prática.
Mas coletar métricas genéricas de CPU e memória é apenas o básico. O que realmente diferencia uma plataforma é medir lógica de negócio. E foi aqui que meu background em Java entrou como vantagem.
Criei uma API Spring Boot simples que expõe uma métrica customizada: um contador que incrementa a cada chamada de um endpoint.
@RestController
public class HelloController {
private final Counter helloCounter;
public HelloController(MeterRegistry registry) {
this.helloCounter = Counter.builder("demo_hello_requests_total")
.description("Total de chamadas ao endpoint /hello")
.register(registry);
}
@GetMapping("/hello")
public String hello() {
helloCounter.increment();
return "Olá do GitOps Lab!";
}
}
Enter fullscreen mode Exit fullscreen mode
Com Spring Boot Actuator e Micrometer, expor essa métrica no formato que o Prometheus entende exige muito pouca configuração.
A ponte final entre a aplicação e o Prometheus é um recurso chamado ServiceMonitor, responsável por informar ao Prometheus quais pods devem ser monitorados. Existe, porém, um detalhe que costuma bloquear muita gente: o ServiceMonitor precisa possuir um label específico (release: monitoring) para ser descoberto pelo Prometheus. Sem esse label, a coleta simplesmente não acontece e, pior, não existe uma mensagem de erro evidente indicando o motivo.
Com tudo conectado, o fluxo finalmente se fecha. Cada chamada ao endpoint incrementa o contador, o Prometheus coleta esse valor periodicamente e o Grafana o exibe em tempo real. Pela primeira vez, vi uma métrica escrita por mim, em Java, aparecer em um dashboard.
Houve ainda um detalhe interessante: o gráfico mostrava duas séries diferentes, uma para cada pod, com valores distintos. Não era um bug. Era o balanceamento de carga do Kubernetes se tornando visível através das métricas, mostrando que o tráfego estava sendo distribuído de forma desigual entre as instâncias da aplicação.
Os detalhes que só a prática ensina
Se eu tivesse que resumir o valor de construir tudo isso manualmente, em vez de apenas ler sobre o assunto, seria nos detalhes que dificilmente aparecem em diagramas.
Descobri que imagens carregadas localmente no kind exigem imagePullPolicy: IfNotPresent; caso contrário, o Kubernetes tentará buscá-las em um registry remoto e falhará.
Também percebi que separar o repositório de infraestrutura do repositório de manifestos evita acoplar mudanças na plataforma aos deploys das aplicações, permitindo que cada um siga seu próprio ciclo de vida.
E, talvez o hábito mais importante de todos, aprendi que ler cuidadosamente o resultado de um terraform plan antes do terraform apply é a diferença entre operar com confiança e simplesmente torcer para que tudo funcione.
Conclusão
No fim, a sensação é quase decepcionante de tão tranquila: você muda um número no Git, faz um git push e o cluster inteiro se ajusta sozinho.
Mas essa simplicidade aparente é justamente o objetivo. Uma boa plataforma esconde a complexidade atrás de um simples git push. Toda a engenharia — o cluster multi-nó, os componentes do ArgoCD conversando entre si, a reconciliação contínua e toda a cadeia de observabilidade — existe para que operar seja simples. Construir a transmissão automática é difícil; dirigir um carro automático é fácil. Eu quis construir a transmissão.
Se você também está atravessando a jornada de backend para plataforma, meu conselho é simples: não leia apenas. Construa. Conceitos como reconciliação, estado desejado e fonte da verdade deixam de ser abstratos no momento em que você vê o ArgoCD desfazer uma alteração manual e restaurar exatamente o que o Git determina.
O código completo, com instruções para executar tudo do zero, está disponível no repositório. E este é apenas o começo: os próximos capítulos incluem alertas, um pipeline de validação de manifestos e a migração para uma cloud gerenciada.
Até a próxima.
답글 남기기