DEV Community

Pedro Oliveira
Pedro Oliveira

Posted on

Reconciliation Loop: Como o Kubernetes implementa infraestrutura declarativa

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:

  • Infraestrutura Imperativa: Nessa abordagem, é necessário especificar as etapas e comandos exatos para provisionar e orquestrar recursos
  • Infraestrutura Declarativa: Ao contrário da forma imperativa, aqui o foco não está em como provisionar e orquestrar mas sim onde o ponto final da infraestrutura

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):

  • Modelo Baseado em Eventos (Edge-Triggered / Polling): O sistema depende da entrega perfeita de notificações de eventos ou de consultas periódicas (polling). Se uma notificação de "falha de Pod" for perdida devido a uma oscilação na rede, a plataforma perde o contexto e a infraestrutura se mantém em um estado inconsistente.
  • Modelo Baseado no Estado Atual (Level-Triggered): Em vez de se focar no evento que passou, o Kubernetes se baseia no estado presente. Ele utiliza uma conexão contínua (Watch) para capturar alterações, mas a decisão do que fazer é tomada comparando a foto atual da infraestrutura com o estado desejado.

É 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

O motor por trás do Loop: Informers, Cache e WorkQue

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.

1. Informer: Escutando a API em Tempo Real

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{})
Enter fullscreen mode Exit fullscreen mode

Sempre que um evento ocorre em um Pod no cluster, o apiserver notifica o
informer em tempo real.

2. Cache Local: Performance e Escalabilidade

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)
Enter fullscreen mode Exit fullscreen mode

Essa consulta não faz uma request ao Kubernetes; ela lê instantaneamente
do cache local mantido pelo Informer.

3. WorkQueue: Garantindo Resiliência e Desduplicação

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:

  1. Deduplicação: Se múltiplos eventos do mesmo Pod chegam em um curto espaço de tempo, a chave é inserida uma vez na fila, evitando execuções redundantes.
  2. Rate Limiting e Backoff: Se a reconciliação falhar, a fila reagenda a tentativa com tempo de espera exponencial (exponential backoff)
  3. Desacoplamento: O recebimento de eventos e processamento deles rodam de forma assíncrona.

Desmistificando o Reconcile(): O Código na Prática

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)
Enter fullscreen mode Exit fullscreen mode

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

1. Leitura via Cache In-Memory e Tratamento de Exclusão

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
    }
Enter fullscreen mode Exit fullscreen mode
  • Interação com o Cache: A chamada r.Get() consulta diretamente o Cache In-Memory mantido pelo Informer
  • Caso de Pod Deletado: Se o Pod foi removido do cluster, o Informer atualiza o cache local. Quando o r.Get() tenta buscar a chave req.NamespacedName, ele retorna um erro do tipo NotFound
  • Resposta ao WorkQueue: Ao usar 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"}
Enter fullscreen mode Exit fullscreen mode

2. Avaliação de Estado (Level-Triggered in Action)

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)
    }
Enter fullscreen mode Exit fullscreen mode
  • Lógica de Inspeção: Como o controller não recebe flags imperativas (como isCreate), ele deve deduzir o cenário inspecionando os metadados e o Spec/Status do Pod carregado da RAM
  • Uso de Finalizers: Inspecionar o array de Finalizers permite ao controller identificar se o objeto está em processo de graceful deletion ou se é uma reconciliação comum de criação/autorização

Podemos verificar esse comportamento, rodando o comando abaixo para criação
de um Pod:

kubectl run --image nginx nginx
Enter fullscreen mode Exit fullscreen mode

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"}
Enter fullscreen mode Exit fullscreen mode

Se colocarmos uma label nesse Pod recém criado:

kubectl label pod nginx env=prod
Enter fullscreen mode Exit fullscreen mode

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"}
Enter fullscreen mode Exit fullscreen mode

3. Sinalizando o Fim do Loop para a WorkQueue

A instrução de encerramento do método define como a WorkQueue deve gerenciar aquela chave:

    return ctrl.Result, nil
}
Enter fullscreen mode Exit fullscreen mode

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 WorkQueue
  • ctrl.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.

Conclusão: A "Mágica" do Kubernetes Desmistificada

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:

  1. O Informer reage a eventos sem sobrecarregar a rede com polling.
  2. O Cache Local In-Memory garante leituras de altíssima performance.
  3. A WorkQueue desacopla o recebimento de eventos da execução, garantindo resiliência, deduplicação e rate-limiting.
  4. A função 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.

Top comments (0)