Entendendo a Arquitetura de Containers no Kubernetes
Antes de mergulharmos em Pods, Deployments e ReplicaSets, é fundamental entender que Kubernetes não trabalha diretamente com containers Docker. Em vez disso, ele abstrai a complexidade através de objetos que gerenciam containers em escala. O Kubernetes foi projetado para orquestrar containers, garantir alta disponibilidade, balanceamento de carga e escalabilidade automática. Esses três componentes (Pods, Deployments e ReplicaSets) são os pilares dessa orquestração e trabalham em conjunto de forma hierárquica.
A beleza do Kubernetes está justamente em camadas de abstração que facilitam o trabalho do desenvolvedor e do operador. Você não precisa pensar em máquinas individuais ou em como replicar manualmente um container quando ele falha. O Kubernetes faz isso por você, e compreender como funciona essa orquestração é a chave para dominar a plataforma.
Pods: O Menor Recurso Computável do Kubernetes
O que é um Pod?
Um Pod é a menor unidade de deployment no Kubernetes. Diferente do que muitos iniciantes pensam, um Pod não é um container — é um wrapper ao redor de um ou mais containers que compartilham recursos de rede e armazenamento. Na maioria dos casos, você terá um container por Pod, mas existem cenários onde múltiplos containers em um mesmo Pod faz sentido (padrão sidecar, por exemplo).
Todos os containers dentro de um Pod compartilham o mesmo namespace de rede, o que significa que eles têm o mesmo endereço IP e podem se comunicar via localhost usando portas diferentes. Além disso, podem compartilhar volumes de armazenamento, facilitando a troca de dados. Um Pod é efêmero — quando ele morre, desaparece. Você nunca deve criar Pods manualmente em produção; sempre os gerencie através de controllers como Deployments e ReplicaSets.
Criando um Pod Simples
Vejamos um exemplo prático de um Pod rodando uma aplicação Nginx:
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
namespace: default
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Para criar esse Pod no seu cluster, você salvaria esse arquivo como nginx-pod.yaml e executaria:
kubectl apply -f nginx-pod.yaml
Para verificar se o Pod foi criado com sucesso:
kubectl get pods
kubectl describe pod nginx-pod
Um Pod com Múltiplos Containers (Padrão Sidecar)
Às vezes, você pode precisar de um container auxiliar que trabalhe junto com o principal. Neste exemplo, temos uma aplicação que precisa fazer logging centralizado:
apiVersion: v1
kind: Pod
metadata:
name: app-with-sidecar
spec:
containers:
- name: app
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: shared-logs
mountPath: /var/log/nginx
- name: log-collector
image: busybox:latest
command: ['sh', '-c', 'tail -f /var/log/nginx/access.log']
volumeMounts:
- name: shared-logs
mountPath: /var/log/nginx
volumes:
- name: shared-logs
emptyDir: {}
Nesse padrão, o container log-collector monitora logs gerados pelo Nginx, ambos compartilhando o volume shared-logs. Essa é uma forma elegante de separar responsabilidades sem criar overhead de comunicação por rede.
ReplicaSets: Garantindo Disponibilidade
Introdução aos ReplicaSets
Um ReplicaSet é um controller que garante que um número específico de réplicas de um Pod estejam sempre rodando. Se um Pod falha, o ReplicaSet automaticamente cria um novo para manter o número desejado. Isso garante alta disponibilidade e tolerância a falhas. Um ReplicaSet utiliza um seletor de labels para identificar quais Pods ele gerencia, tornando a gestão dinâmica e flexível.
Embora você possa usar ReplicaSets diretamente, a maioria das aplicações modernas usam Deployments, que são um nível a mais de abstração sobre ReplicaSets e fornecem atualizações controladas (rolling updates). Porém, entender ReplicaSets é essencial para compreender como o Kubernetes mantém suas aplicações funcionando.
Criando um ReplicaSet
Aqui está um ReplicaSet prático que mantém 3 réplicas de um servidor web:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-replicaset
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: web-server
template:
metadata:
labels:
app: web-server
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Aplicando esse ReplicaSet:
kubectl apply -f nginx-replicaset.yaml
Verificando o status:
kubectl get replicasets
kubectl get pods -l app=web-server
Você verá 3 Pods sendo gerenciados automaticamente. Se você deletar um Pod manualmente:
kubectl delete pod <nome-do-pod>
O ReplicaSet imediatamente criará um novo Pod para manter as 3 réplicas ativas.
Escalando um ReplicaSet
Uma das grandes vantagens é a escalabilidade dinâmica. Para aumentar para 5 réplicas:
kubectl scale replicaset nginx-replicaset --replicas=5
Ou edite o arquivo YAML diretamente e reaplique:
kubectl edit replicaset nginx-replicaset
Mude o campo replicas para 5 e salve. O Kubernetes ajustará automaticamente.
Deployments: Orquestração Inteligente e Atualizações
O Poder dos Deployments
Um Deployment é a abstração mais alta e mais usada no Kubernetes. Ele gerencia um ReplicaSet, que por sua vez gerencia os Pods. A grande vantagem é que Deployments facilitam atualizações de aplicações de forma segura e controlada. Você pode fazer rolling updates, onde as novas versões são implantadas gradualmente enquanto as antigas continuam servindo tráfego. Se algo der errado, você pode fazer rollback instantaneamente.
Deployments também fornecem histórico de revisões, permitindo voltar para qualquer versão anterior da sua aplicação. Além disso, garantem que o número desejado de réplicas esteja sempre disponível, mesmo durante atualizações. Na prática, 99% do tempo você usará Deployments em vez de ReplicaSets diretamente.
Criando seu Primeiro Deployment
Vejamos um Deployment funcional com uma aplicação real:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
namespace: default
labels:
app: myapp
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
version: v1
spec:
containers:
- name: myapp
image: nginx:1.21
ports:
- containerPort: 80
name: http
env:
- name: ENVIRONMENT
value: "production"
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Criando o Deployment:
kubectl apply -f app-deployment.yaml
Verificando o status:
kubectl get deployments
kubectl get replicasets
kubectl get pods
Você verá a hierarquia: 1 Deployment gerenciando 1 ReplicaSet que gerencia 3 Pods.
Rolling Update: Atualizando sua Aplicação
Agora vem a magia. Você quer atualizar a imagem do Nginx para a versão 1.22. Em vez de derrubar tudo e reiniciar, o Kubernetes faz isso graciosamente:
kubectl set image deployment/app-deployment myapp=nginx:1.22 --record
O comando --record registra essa mudança no histórico de revisões. Observe o que acontece:
kubectl rollout status deployment/app-deployment
Você verá os Pods sendo atualizados gradualmente. Nunca há downtime. Se você quiser ver mais detalhes:
kubectl describe deployment app-deployment
Rollback: Voltando para a Versão Anterior
Se algo deu errado com a versão 1.22, você pode voltar instantaneamente:
kubectl rollout undo deployment/app-deployment
Para ver o histórico de revisões:
kubectl rollout history deployment/app-deployment
Para voltar para uma revisão específica:
kubectl rollout undo deployment/app-deployment --to-revision=1
Configurando a Estratégia de Atualização
No exemplo anterior, usamos RollingUpdate com maxSurge: 1 e maxUnavailable: 1. Isso significa: crie no máximo 1 Pod novo além das 3 replicas, e mantenha no máximo 1 Pod indisponível. Você pode ajustar isso conforme sua necessidade:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2 # até 2 Pods além do desejado
maxUnavailable: 0 # nunca deixe Pods indisponíveis
Ou use a estratégia Recreate para ambientes de teste:
strategy:
type: Recreate
Isso derruba todos os Pods antigos e cria novos, causando downtime breve mas garantindo que apenas uma versão rode de cada vez.
Probes e Healthchecks: Mantendo sua Aplicação Saudável
Liveness e Readiness Probes
Para que o Kubernetes gerencie adequadamente seus Pods, você precisa dizer a ele quando um Pod está saudável e pronto para receber tráfego. Existem dois tipos principais de probes:
Liveness Probe: Detecta quando um Pod travou e precisa ser reiniciado. Se a probe falhar, o Kubernetes derruba o Pod e cria um novo.
Readiness Probe: Determina se um Pod está pronto para receber tráfego. Pods não prontos são temporariamente removidos do load balancer.
Aqui está um exemplo completo com ambas as probes:
apiVersion: apps/v1
kind: Deployment
metadata:
name: health-check-app
spec:
replicas: 2
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: node:16
command: ["node", "app.js"]
ports:
- containerPort: 3000
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 2
Neste exemplo, a aplicação Node.js precisa responder com sucesso no endpoint /health a cada 10 segundos. Se falhar 3 vezes consecutivas, o Pod é reiniciado. Além disso, /ready indica se a aplicação está inicializada e pronta para receber requisições.
Casos de Uso Práticos: Quando Usar Cada Um
Pods Isolados: Raramente em Produção
Você criaria um Pod diretamente apenas em cenários de teste, debug ou em jobs únicos que não precisam de replicação. Na maioria das vezes, evite. O exemplo que mostramos no início foi apenas para fins educacionais.
ReplicaSets: Quando Você Quer Controle Fino
Use ReplicaSets quando precisar apenas de replicação simples, sem atualizações controladas. Cenários raros em produção, pois Deployments cobrem 99% dos casos. Você poderia usar ReplicaSets se tiver uma aplicação stateful que não pode ter rolling updates complexas, mas mesmo assim, considere usar StatefulSets (que é outro tópico).
Deployments: A Escolha Padrão
Para 99% das aplicações, use Deployments. Eles fornecem replicação, high availability, rolling updates, rollback automático e histórico. Quer fazer deploy de uma nova versão? Deployments. Quer escalar sua aplicação? Deployments. Quer garantir que sempre haja 3 instâncias rodando? Deployments.
Aqui está um exemplo realista completo que você poderia usar em produção:
apiVersion: apps/v1
kind: Deployment
metadata:
name: production-api
namespace: production
labels:
app: api
environment: prod
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
environment: prod
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- api
topologyKey: kubernetes.io/hostname
containers:
- name: api
image: myregistry.azurecr.io/myapp:v1.2.3
imagePullPolicy: Always
ports:
- containerPort: 8080
name: http
env:
- name: LOG_LEVEL
value: "info"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: connection-string
livenessProbe:
httpGet:
path: /api/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /api/ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 2
resources:
requests:
memory: "256Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "1000m"
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
Neste exemplo, note que:
- Usamos
affinitypara distribuir os Pods em diferentes nós - Injetamos secrets (credenciais de banco de dados) de forma segura
- Definimos limites de recursos para evitar que a aplicação consuma todos os recursos do nó
- Configuramos probes apropriadas para detectar falhas rapidamente
- Usamos imagens de um registry privado com
imagePullPolicy: Always
Debugging e Monitoramento
Comandos Essenciais
Quando algo dá errado, você precisa saber como investigar. Aqui estão os comandos mais úteis:
# Ver logs da aplicação
kubectl logs <nome-do-pod>
kubectl logs deployment/app-deployment -c container-name
# Acompanhar logs em tempo real
kubectl logs -f pod/app-deployment-xxxxx
# Execar comando dentro do container
kubectl exec -it <nome-do-pod> -- /bin/bash
# Ver eventos do cluster
kubectl describe pod <nome-do-pod>
kubectl describe deployment app-deployment
# Verificar por quais nós os Pods estão distribuídos
kubectl get pods -o wide
# Debug completo de um Deployment
kubectl get all -l app=myapp
Conclusão
Dominar Pods, ReplicaSets e Deployments é fundamental para trabalhar efetivamente com Kubernetes. A chave é entender a hierarquia: Deployments gerenciam ReplicaSets, que gerenciam Pods, que contêm containers. Na prática, você quase nunca criará Pods ou ReplicaSets manualmente em produção — use sempre Deployments, que fornecem atualizações seguras, rollback automático e escalabilidade sem downtime. Por fim, sempre configure liveness e readiness probes para permitir que o Kubernetes gerencie sua aplicação de forma inteligente, detectando e recuperando-se automaticamente de falhas.