Introdução
Por que orquestrar, e o que o Docker Compose não sabe fazer.
Introdução ao Kubernetes
Você sabe construir uma imagem e executar um contêiner. Na sua máquina, docker compose up sobe uma API, um banco de dados e um cache em poucos segundos, e tudo funciona. Então surge a pergunta que muda tudo: o que acontece quando essa aplicação precisa rodar continuamente, em várias máquinas, sem que ninguém precise acordar de madrugada para reiniciá-la?
O Compose descreve um estado desejado, mas nada o mantém ao longo do tempo. Se o processo falha, restart: always o reinicia. Se a máquina cai, nada mais reinicia coisa alguma. Se o tráfego triplica, você sobe réplicas manualmente, os endereços delas mudam e é preciso reconfigurar o reverse proxy. Se você implanta uma nova versão, há uma interrupção entre a parada da antiga e a inicialização da nova. Cada um desses problemas tem uma resposta manual óbvia. O que se torna insustentável é o acúmulo deles.
A resposta do Kubernetes não é “mais comandos”. É uma inversão de responsabilidade: você descreve o estado que deseja, de forma permanente, e um conjunto de loops de controle trabalha continuamente para que a realidade corresponda a essa descrição. Esse é o único conceito realmente estruturante do sistema: todo o resto — Pods, Deployments, Services, volumes — é apenas uma variação dessa ideia aplicada a um objeto específico.
Esta lição apresenta esse modelo e depois descreve a arquitetura de um cluster. Não por gosto de nomenclatura: saber qual componente decide o quê é o que permitirá diagnosticar problemas. Um Pod bloqueado porque o scheduler não encontra nenhum nó disponível e um Pod bloqueado porque o kubelet não consegue baixar a imagem são duas falhas que não têm a mesma causa nem o mesmo lugar onde investigar.
Imperativo versus declarativo
Com o Docker, você dá ordens. Cada comando descreve uma ação a ser executada agora:
# Impératif : je décris les actions, une par une, dans l'ordre.
docker build -t mon-api:1.2.0 .
docker run -d --name api-1 -p 8080:8080 mon-api:1.2.0
docker run -d --name api-2 -p 8081:8080 mon-api:1.2.0
docker stop api-1 # pour mettre à jour : coupure assumée
docker rm api-1
docker run -d --name api-1 -p 8080:8080 mon-api:1.3.0
Esse script funciona uma vez. Ele não diz nada sobre o que deve continuar sendo verdade. Ao lê-lo, ninguém sabe se deve haver duas réplicas ou três, nem qual versão deveria estar em execução.
Com o Kubernetes, você descreve um alvo. O arquivo abaixo não diz “inicie três contêineres”, ele diz “deve haver três réplicas desta versão, permanentemente”:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mon-api
spec:
replicas: 3 # état voulu, pas une action ponctuelle
selector:
matchLabels:
app: mon-api
template:
metadata:
labels:
app: mon-api
spec:
containers:
- name: api
image: mon-api:1.3.0
ports:
- containerPort: 8080
A diferença prática é imediata. Mate um dos três contêineres: um quarto nasce alguns segundos depois, porque a contagem não corresponde mais ao alvo. Desligue um nó: as réplicas que ele hospedava são recriadas em outro lugar. Ninguém deu nenhuma ordem, um controlador apenas constatou uma divergência.
O loop de reconciliação
Este é o mecanismo central, e ele é simples:
- Um controlador lê o estado desejado (o que você declarou).
- Ele observa o estado real do cluster.
- Se os dois diferem, ele age para reduzir a divergência.
- Ele recomeça. Indefinidamente.
Existe um controlador para cada tipo de responsabilidade, e eles são propositalmente míopes: aquele que gerencia os ReplicaSets não sabe nada sobre a rede, aquele que gerencia os Services não sabe nada sobre os volumes. Cada um vigia a sua própria divergência. A orquestração global emerge desses loops independentes, não é conduzida por um único maestro.
Essa arquitetura tem uma consequência que convém aceitar cedo: o Kubernetes é assíncrono. Quando kubectl apply responde, ele não implantou nada, apenas registrou a sua intenção. O trabalho real acontece depois, e pode falhar depois. É por isso que nunca se verifica uma implantação pelo retorno do apply, mas sempre pelo estado observado:
# apply enregistre l'intention : le retour ne prouve rien sur l'exécution.
kubectl apply -f deployment.yaml
# La vérification se fait sur l'état réel, ensuite.
kubectl get deployment mon-api
kubectl get pods -l app=mon-api
# Et si un Pod ne démarre pas, la raison est dans les événements de fin de description.
kubectl describe pod mon-api-7d4b8c9f5-xk2mp
A arquitetura de um cluster
Um cluster se divide em dois papéis: o plano de controle (control plane), que decide, e os nós de trabalho (worker nodes), que executam.
O plano de controle
| Componente | Papel | O que isso implica em caso de falha |
|---|---|---|
kube-apiserver | Única porta de entrada. Valida e persiste todas as requisições. | Nenhuma modificação é mais possível; as cargas de trabalho já iniciadas continuam rodando. |
etcd | Banco chave-valor que armazena o estado desejado e o estado observado. | Perda do etcd sem backup = perda do cluster lógico. |
kube-scheduler | Escolhe em qual nó posicionar cada novo Pod. | Os novos Pods permanecem em Pending. |
kube-controller-manager | Executa os loops de reconciliação. | Sem mais autorrecuperação: os Pods mortos não são substituídos. |
O ponto notável: kubectl só fala com o API server. Ele não abre nenhuma conexão com um nó e não controla nenhum contêiner. Toda a segurança e toda a rastreabilidade passam, portanto, por um único ponto.
Os nós de trabalho
Em cada nó rodam três elementos:
- kubelet: o agente que recebe a lista de Pods a executar no seu nó, pede ao runtime que os inicie, executa as sondas de saúde e reporta o estado.
- runtime de contêineres:
containerdouCRI-O, na maioria das vezes. É ele que baixa as imagens e inicia os processos. Suas imagens Docker funcionam sem modificação: elas seguem o formato OCI, que esses runtimes sabem ler. - kube-proxy: programa as regras de rede do nó para que o endereço estável de um Service alcance os Pods corretos.
# Voir les nœuds et leur état vu du plan de contrôle
kubectl get nodes
# Détail d'un nœud : capacité, ressources déjà réservées, conditions
kubectl describe node minikube
# Les composants du plan de contrôle sont eux-mêmes des Pods, dans kube-system
kubectl get pods -n kube-system
Este último comando merece uma pausa: o plano de controle é administrado pelo próprio Kubernetes. Não é um capricho, é o que torna seus componentes observáveis e reparáveis com as mesmas ferramentas das suas aplicações.
O Pod, e por que ele não é um contêiner
O Kubernetes não implanta contêineres. Ele implanta Pods. Um Pod é um grupo de um ou mais contêineres que compartilham o mesmo espaço de rede e podem compartilhar volumes. Os contêineres de um mesmo Pod se comunicam via localhost e são sempre alocados no mesmo nó.
apiVersion: v1
kind: Pod
metadata:
name: demo-pod
labels:
app: demo
spec:
containers:
- name: web
image: nginx:1.27-alpine
ports:
- containerPort: 80
Por que essa camada intermediária? Porque algumas necessidades são inseparáveis do processo principal: um coletor de logs que lê o mesmo diretório, um proxy que termina o TLS antes da aplicação. Tratá-los como contêineres independentes obrigaria a garantir a colocalização deles por meios indiretos. O Pod torna essa garantia explícita.
Segundo ponto, mais importante na prática: um Pod é descartável. Ele não tem um nome estável, seu endereço IP muda a cada recriação e nada sobrevive ao seu desaparecimento. Por isso, quase nunca se cria um Pod diretamente — o manifesto acima é um exemplo pedagógico, não um modelo de produção. Cria-se um Deployment, que cria e substitui os Pods para você. A lição 3 detalha essa cadeia.
O que você ganha, o que você paga
O Kubernetes oferece, sem que você precise escrever:
- a autorrecuperação: Pods e nós com falha são substituídos automaticamente;
- a descoberta de serviços: um nome DNS estável na frente de um conjunto de Pods que mudam o tempo todo;
- as atualizações progressivas com rollback, sem interrupção;
- a escalabilidade manual ou automática conforme a carga medida;
- a distribuição sob restrições: o scheduler posiciona os Pods de acordo com a CPU e a memória realmente disponíveis.
A autorrecuperação se verifica com uma única manipulação, e é a demonstração mais clara do modelo declarativo:
# On supprime une réplique à la main…
kubectl delete pod mon-api-7d4b8c9f5-xk2mp
# …et le compte est déjà rétabli, avec un Pod au suffixe différent.
kubectl get pods -l app=mon-api
O custo é real e precisa ser dito. O Kubernetes adiciona uma camada de abstração com sua própria complexidade: mais conceitos a conhecer, mais configuração em YAML, um cluster a manter ou a pagar e um conjunto de ferramentas de diagnóstico a aprender. Para um site institucional em uma única máquina, docker compose continua sendo a escolha certa, e afirmar o contrário seria desonesto. O Kubernetes se torna vantajoso quando você tem vários serviços, uma exigência de disponibilidade ou várias equipes que fazem deploy na mesma infraestrutura.
O vocabulário comum a todos os manifestos
Todo objeto do Kubernetes, qualquer que seja o seu tipo, é escrito com os mesmos quatro campos de primeiro nível. Reconhecê-los torna qualquer manifesto legível:
apiVersion: apps/v1 # groupe et version de l'API qui définit ce type
kind: Deployment # le type d'objet
metadata: # identité : nom, namespace, labels, annotations
name: mon-api
labels:
app: mon-api
spec: # l'état voulu ; son contenu dépend du kind
replicas: 3
Há uma única regra de apiVersion a reter para este curso, e ela evita o erro mais frequente dos iniciantes:
v1para Pod, Service, ConfigMap, Secret, PersistentVolume, PersistentVolumeClaim;apps/v1para Deployment, ReplicaSet, StatefulSet, DaemonSet.
Um apiVersion: v1 em um Deployment é rejeitado pelo API server com uma mensagem pouco explícita. A lista exata para um determinado tipo pode ser lida diretamente do cluster:
# Quel apiVersion pour ce type ? La réponse vient du cluster, pas de la mémoire.
kubectl api-resources | grep -i deployment
# Documentation d'un champ, sans quitter le terminal
kubectl explain deployment.spec.strategy
kubectl explain é a ferramenta mais subutilizada do ecossistema. Ele descreve os campos da versão da API realmente instalada no seu cluster, algo que nenhum artigo de blog pode garantir.
O que o restante do curso aborda
- Instalação e configuração — um cluster local com minikube ou kind,
kubectl, contextos e namespaces. - Pods, Services e Deployments — os recursos do dia a dia, os seletores de labels, as atualizações progressivas e as sondas.
- ConfigMaps e Secrets — tirar a configuração das imagens, e o que “Secret” não significa.
- Armazenamento persistente — volumes, claims, StorageClass e StatefulSet.
- Implantação de aplicações complexas — multisserviços, Ingress, recursos, autoscaling, visão geral do Helm.
Todos esses objetos já são conhecidos pelo seu futuro cluster. Este comando os lista com o nome abreviado, aquele que você digitará no dia a dia:
kubectl api-resources | grep -E 'deployments|services|configmaps|secrets|persistentvolumeclaims'
# deployments deploy apps/v1 true Deployment
# services svc v1 true Service
# configmaps cm v1 true ConfigMap
Pontos-chave
- O Kubernetes é declarativo: você descreve um estado desejado, e loops de reconciliação o mantêm.
applyregistra uma intenção, não garante um resultado — a verificação é sempre feita sobre o estado observado. - O plano de controle decide (API server, etcd, scheduler, controller-manager), os nós executam (kubelet, runtime, kube-proxy). Saber quem faz o quê é a base do diagnóstico.
- A unidade implantada é o Pod, não o contêiner: um grupo colocalizado que compartilha rede e volumes e, sobretudo, descartável. Ele é criado por meio de um controlador, nunca manualmente em produção.
- Todos os manifestos compartilham
apiVersion,kind,metadata,spec. Lembre-se dev1para Pod/Service/ConfigMap/Secret/PVC eapps/v1para Deployment/StatefulSet. - A orquestração tem um custo de complexidade assumido. Ela se justifica pelo número de serviços, pela exigência de disponibilidade e pelo número de equipes, não pela moda.
Referência: a documentação oficial está em francês para boa parte dos conceitos. Mantenha kubernetes.io/fr/docs/concepts/ aberto durante este curso.
Comentários
Os comentários são alimentados pelo GitHub Discussions
Conecte-se com o GitHub para participar da discussão