devto 2026-08-20 원문 보기 ↗
Ao meu ver, uma das maiores qualidades é o que faz o Kubernetes ser
a ferramenta padrão quando se fala de operar infraestrutura em larga escala,
seja ela em um cloud provider ou rodando on premisse é a sua abordagem
na implementação de infraestrutura declarativa. O que faz com que seja usada por organizações
de inúmeros tamanhos e áreas de atuação que desejam entregar uma plataforma de tecnologia
que seja confiável e atenda inúmeros casos de uso
Hoje, no mundo das ferramentas de infraestrutura como código (IaC) existem
duas abordagens principais para a implementação do conceito:
Além disso, para garantir que o estado declarado no arquivo YAML,
ou outra linguagem de configuração, seja mantido ao longo do tempo,
os sistemas de orquestração historicamente adotavam abordagens baseadas em
Polling (varreduras periódicas) ou Edge-Triggered (reação a eventos pontuais):
É exatamente a implementação desse modelo Level-Triggered que chamamos de
Reconciliation Loop.
Durante esse artigo, tentarei explicar o funcionamento dessa abordagem através
de um controller feito com kubebuilder (um framework Go para operator Kubernetes)
que apresenta uma mensagem sempre que um Pod é criado ou deletado
Para implementar a abordagem Level-Triggered na prática sem sobrecarregar
o kube-apiserver, a biblioteca controller-runtime (base do Kubebuilder)
utiliza uma arquitetura baseada em três pilares fundamentais:
Informer, Cache Local e WorkQueue.
Em vez de fazer requisições periódicas (polling) para saber se algo mudou,
o controller utiliza o Informer. Ele estabelece uma conexão HTTP de
streaming de longa duração (Watch) com o apiserver.
No código Go de exemplo, esse registro é feito de forma declarativa dentro
do SetupWithManager:
Watches(&corev1.Pod{}, &handler.EnqueueRequestForObject{})
Sempre que um evento ocorre em um Pod no cluster, o apiserver notifica o
informer em tempo real.
Se em cada reconciliação, fosse preciso buscar os dados completos direto no
etcd, o cluster colapsaria em larga escala. Por isso, ao receber uma
notificação, o Informer atualiza um Cache In-Memory mantendo localmente
dentro do processo do controller.
Quando é executada a leitura do recurso dentro da função de reconciliação:
pod := &corev1.Pod{}
err := r.Get(ctx, req.NamespacedName, pod)
Essa consulta não faz uma request ao Kubernetes; ela lê instantaneamente
do cache local mantido pelo Informer.
O Informer não chama a função Reconcile() diretamente. Em vez disso, o
utilitário handler.EnqueueRequestForObject{} extrai apenas os metadados
do recurso - a chave Namespace/Name - e a insere em uma fila de trabalho
(WorkQueue)
A WorkQueue resolve três grandes problemas de engenharia:
Com Informer, Cache Local e WorkQueue implementadas no
SetupWithManager, a execução chega no coração de tudo: A função Reconcile().
Diferente de arquiteturas orientadas a eventos tradicionais, o parâmetro
recebido por essa função não contém o Pod inteiro e nem o tipo de evento
que ocorreu (como Create, Update ou Delete). O controller recebe apenas
a chave do recurso através do parâmetro ctrl.Request:
func (r *ExampleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := logf.FromContext(ctx)
A seguir, vamos analisar como cada bloco da função interage com os componentes
internos e onde inspecionar os logs para validar o comportamento em tempo
real
O ciclo começa obtendo o estado atual do recurso
pod := &corev1.Pod{}
if err := r.Get(ctx, req.NamespacedName, pod); err != nil {
if client.IgnoreNotFound(err) != nil {
return ctrl.Result{}, err
}
log.Info("The pod was deleted", "name", req.Name, "namespace", req.Namespace)
return ctrl.Result{}, nil
}
r.Get() consulta diretamente o
Cache In-Memory mantido pelo Informerr.Get() tenta buscar a chave
req.NamespacedName, ele retorna um erro do tipo NotFound
client.IgnoreNotFound(err), o código
reconhece que a ausência do recurso é um estado válido final. Após isso,
imprime o log e retorna ctrl.Result{}, nil, sinalizando para a WorkQueue
que o item foi processado com sucesso e pode ser removido da fila.
2026-08-19T17:09:45-03:00 INFO The pod was deleted {"controller": "example", "namespace": "default", "name": "nginx", "reconcileID": "69af3331-bdd4-45c6-98dd-88eaf32f2439", "name": "nginx", "namespace": "default"}
Se o recurso foi encontrado no cache, o código prossegue para avaliar o seu
estado atual e tomar decisões
if controllerutil.ContainsFinalizer(pod, "my.domain/finalizer") {
log.Info("Pod was deleted", "name", pod.Name, "namespace", pod.Namespace)
} else {
log.Info("Pod created", "name", pod.Name, "namespace", pod.Namespace)
}
isCreate), ele deve deduzir o cenário inspecionando os metadados
e o Spec/Status do Pod carregado da RAMPodemos verificar esse comportamento, rodando o comando abaixo para criação
de um Pod:
kubectl run --image nginx nginx
Que vai gerar a seguinte ocorrência no log do controller
2026-08-19T19:33:07-03:00 INFO Pod created {"controller": "example", "namespace": "default", "name": "nginx", "reconcileID": "88f9cba6-502e-481e-b5a5-c9a01b72d880", "name": "nginx", "namespace": "default"}
Se colocarmos uma label nesse Pod recém criado:
kubectl label pod nginx env=prod
A WorkQueue dispara o Reconcile() novamente
2026-08-19T19:35:53-03:00 INFO Pod created {"controller": "example", "namespace": "default", "name": "nginx", "reconcileID": "55f7aa09-fd82-4905-b169-37b35bd1f908", "name": "nginx", "namespace": "default"}
A instrução de encerramento do método define como a WorkQueue deve gerenciar aquela chave:
return ctrl.Result, nil
}
O retorno da função Reconcile dita o fluxo da fila de trabalho:
ctrl.Result{}, nil: O estado desejado é atingido
(ou o recurso não existe mais). A chave é removida da WorkQueuectrl.Result{}, err: Ocorreu uma falha. A WorkQueue retém a chave
e reagenda a execução aplicando Exponential Backoff para evitar
thundering herd.ctrl.Result{RequeueAfter: 10 * time.Second}, nil: Força um re-agendamento
periódico (Polling/Health-check), útil quando o controller depende de externos
ao Kubernetes.A capacidade do Kubernetes de manter clusters massivos operando de forma
resiliente não se deve a um passe de mágica, mas sim a elegância do
Reconciliation Loop e do modelo Level-Triggered.
Ao longo desse artigo, vimos que o modelo declarativo é sustentado por
componentes cirurgicamente projetados:
Reconcile() assume a responsabilidade de comparar o estado
atual (Status) com o estado desejado (Spec), agindo até que ambos convirjam.Entender essa engrenagem é o diferencial entre encarar o Kubernetes como
um "buraco negro de arquivos YAML" e utilizá-lo como a plataforma extensível que
ele realmente é. Quando você domina o funcionamento interno do
controller-runtime, criar seus próprios CRDs e Operators para automatizar
infraestrutura torna-se uma evolução natural na sua jornada de engenharia.