O que acontece quando rodamos kubectl apply -f deployment.yaml?

Entenda o fluxo interno do Kubernetes desde o envio do manifesto até a criação do Deployment, ReplicaSet e Pods.

Capa com o fluxo do deployment.yaml pelo API Server, Deployment, ReplicaSet e Pods até a aplicação em execução

Executamos o comando:

kubectl apply -f deployment.yaml

e, poucos segundos depois, nossos Pods aparecem em execução.

Mas o que realmente aconteceu dentro do Kubernetes entre esses dois momentos?

O kubectl não cria diretamente um container e o Deployment também não cria os Pods sozinho. Existe uma sequência de componentes trabalhando para transformar o estado declarado no YAML no estado real do cluster.

Neste artigo, vamos acompanhar esse caminho.

Terminal mostrando a criação do namespace apply-lab, do Deployment e do Service webapp-demo com kubectl apply

Tudo começa pelo estado desejado

Um manifesto Kubernetes descreve como queremos que determinado recurso exista.

Um exemplo simples:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webapp-demo
  template:
    metadata:
      labels:
        app: webapp-demo
    spec:
      containers:
        - name: webapp
          image: nginx:alpine

Nesse caso, estamos declarando, entre outras coisas, que queremos uma aplicação com duas réplicas (Pods).

Ao executar:

kubectl apply -f deployment.yaml

o kubectl lê essa definição e envia a requisição para a API do Kubernetes.

A partir desse momento, começa o processo de reconciliação.

1. kubectl envia a requisição ao API Server

O API Server é a principal interface de comunicação com o control plane.

Antes de aceitar o recurso, a requisição passa por etapas como autenticação, autorização, controle de admissão e validação.

Se a definição for aceita, o novo estado desejado passa a fazer parte do estado persistido do cluster.

De forma simplificada:

deployment.yaml

kubectl

API Server

2. O estado do cluster é persistido

O Kubernetes utiliza o etcd como armazenamento chave-valor do estado do cluster.

Isso inclui informações sobre objetos como:

  • Deployments;
  • ReplicaSets;
  • Pods;
  • Services;
  • ConfigMaps;
  • outros recursos da API.

Um detalhe importante: os componentes do Kubernetes normalmente interagem com esse estado através do API Server. O diagrama conceitual API Server → etcd não significa que cada controlador acessa o etcd diretamente.

3. O controlador percebe o novo Deployment

Depois que o objeto Deployment existe no cluster, o Deployment Controller identifica que há um novo estado desejado a ser atendido.

Se declaramos:

replicas: 2

o objetivo passa a ser manter duas réplicas da aplicação.

Mas o Deployment não cria diretamente esses Pods.

Ele cria e gerencia um ReplicaSet.

Deployment

ReplicaSet

Terminal listando o Deployment webapp-demo, seu ReplicaSet e dois Pods em execução no namespace apply-lab

4. O ReplicaSet mantém a quantidade de Pods

O ReplicaSet recebe a responsabilidade de manter a quantidade desejada de Pods.

Com duas réplicas:

Deployment

ReplicaSet
   ↙     ↘
 Pod     Pod

Se um desses Pods desaparecer, o ReplicaSet identifica que o estado real ficou diferente do desejado e cria outro.

É por isso que o modelo de controladores do Kubernetes é tão importante: eles trabalham continuamente para reduzir a diferença entre estado desejado (DESIRED) e estado atual (CURRENT).

5. Os Pods precisam de um node

Criar o objeto Pod ainda não significa que o container já está executando.

O Pod precisa ser associado a um node.

O kube-scheduler observa Pods que ainda não possuem um node definido e escolhe onde eles deverão executar considerando fatores como:

  • recursos disponíveis;
  • requests;
  • affinity e anti-affinity;
  • taints e tolerations;
  • outras restrições de scheduling.

Depois dessa decisão, o Pod fica associado ao node selecionado.

6. Kubelet e runtime iniciam os containers

No node escolhido, o kubelet acompanha a especificação do Pod e trabalha com o runtime de containers para materializar aquele workload.

Nesse processo, podem ocorrer eventos como:

Scheduled
Pulling
Pulled
Created
Started

Esses eventos são facilmente observados com:

kubectl describe pod <pod> -n <namespace>

Saída de kubectl describe mostrando o Pod webapp-demo em execução, pronto e com os eventos Scheduled, Pulled, Created e Started

É nesse momento que a definição que começou no YAML resulta efetivamente em containers executando no node.

Como enxergar essa relação no cluster

Um comando simples permite visualizar boa parte dessa cadeia:

kubectl get deployment,rs,pods -n <namespace>

Também podemos confirmar a relação de propriedade entre os recursos.

O ReplicaSet possui uma referência ao Deployment:

kubectl get rs <replicaset> \
  -n <namespace> \
  -o jsonpath='{.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}{"\n"}'

Consulta JSONPath mostrando que o ReplicaSet pertence ao Deployment webapp-demo

E o Pod possui uma referência ao ReplicaSet:

kubectl get pod <pod> \
  -n <namespace> \
  -o jsonpath='{.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}{"\n"}'

Consulta JSONPath mostrando que o Pod pertence ao ReplicaSet webapp-demo

Na prática:

Deployment

ReplicaSet

Pod

Essa é uma forma interessante de confirmar no próprio cluster a cadeia que normalmente vemos apenas em diagramas.

E o que acontece quando executamos apply novamente?

O kubectl apply não serve apenas para a criação inicial.

Imagine que nosso manifesto tenha:

replicas: 2

e alteremos para:

replicas: 3

Manifesto do Deployment webapp-demo com o número de réplicas alterado para três

Ao executar novamente:

kubectl apply -f deployment.yaml

podemos encontrar uma saída semelhante a:

deployment.apps/webapp-demo configured

O estado desejado agora é diferente.

O Kubernetes então trabalha para fazer o estado real convergir para ele:

Desejado: 3 Pods
Atual:    2 Pods

Reconciliação

Atual:    3 Pods

Terminal mostrando o Deployment webapp-demo configurado novamente após executar kubectl apply

Depois:

kubectl get pods -n <namespace>

deverá mostrar três Pods.

Terminal listando três Pods webapp-demo em execução, prontos e sem reinicializações

Esse é um dos conceitos fundamentais do Kubernetes: declaramos o resultado esperado e os controladores trabalham continuamente para alcançá-lo e mantê-lo.

O fluxo completo

De forma simplificada:

deployment.yaml

kubectl

API Server

estado persistido

Deployment Controller

ReplicaSet

Pods

Scheduler

Node

Kubelet + Runtime

containers em execução

Esse fluxo é propositalmente simplificado.

Na arquitetura real, os componentes do control plane se comunicam por meio da API do Kubernetes e executam loops de controle independentes. O importante é compreender a responsabilidade de cada componente, e não interpretar o desenho como uma chamada síncrona e linear entre todos eles.

Infográfico com dez etapas do kubectl apply, do envio do manifesto ao API Server até o Pod em execução

O principal aprendizado

Quando executamos:

kubectl apply -f deployment.yaml

não estamos simplesmente pedindo ao Kubernetes para “subir um container”.

Estamos declarando um estado desejado.

O API Server recebe essa intenção, os controladores observam os objetos correspondentes, o Deployment gerencia um ReplicaSet, o ReplicaSet mantém os Pods e os componentes do node transformam essas definições em containers efetivamente executando.

Em resumo:

YAML

estado desejado

controladores

reconciliação

workload em execução

Entender essa cadeia facilita não apenas o uso do Kubernetes, mas também troubleshooting, análise de rollouts e compreensão do que realmente acontece quando algo entre o YAML e o Pod não funciona como esperado.

Referências