Kubernetes Completo: do Pod ao cluster de produção
- ⬜🐳 Docker Completo: do zero ao production-ready(DevOps & Containers)
Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.
Kubernetes é um orquestrador de containers. Tradução: ele pega centenas de containers, distribui em dezenas de servidores, garante que o número certo esteja rodando, reinicia os que quebram, escala por carga, faz deploy sem downtime, roteia tráfego e cuida de estado persistente. É gigante porque os problemas que ele resolve são gigantes. Este guia cobre arquitetura do control plane, todos os objetos que você realmente usa (Pod, Deployment, Service, Ingress, ConfigMap, Secret, PVC, Namespace), RBAC, autoscaling, Helm, observabilidade e os comandos de kubectl que resolvem 95% dos casos. Pré-requisito: entender Docker (se não entende, volte um módulo).
Por que K8s existe: o problema que ele resolve
Com Docker você sobe um container numa máquina. Mas produção real tem perguntas chatas:
K8s responde todas essas. O custo é complexidade e uma curva de aprendizado real. Para apps pequenos um único container num servidor basta; K8s começa a valer a pena quando você tem múltiplos serviços, SLA de uptime, ou precisa de escalabilidade elástica.
A arquitetura: Control Plane + Data Plane
Pod — a unidade atômica
Pod é o menor objeto que o K8s agenda. Um Pod é um ou mais containersque compartilham rede (mesmo IP, localhost entre eles), volumes e ciclo de vida. Na prática, 95% dos Pods têm 1 container. Multi-container Pod é o padrão “sidecar” (ex.: app + proxy Envoy, app + log shipper).
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80
resources:
requests: { cpu: 100m, memory: 64Mi }
limits: { cpu: 500m, memory: 256Mi }| Coisa | Entre containers DO mesmo Pod | Entre Pods |
|---|---|---|
| Rede | localhost, mesma IP | IP próprio, via Service |
| Volume | Podem montar o mesmo | Não compartilham (use PVC) |
| Ciclo de vida | Pod cai, todos caem juntos | Independente |
kubectl run --rm -it debug --image=alpine -- sh.Deployment + ReplicaSet — como apps stateless rodam
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
selector:
matchLabels: { app: api }
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 1 a mais pode existir durante o rollout
maxUnavailable: 0 # nenhum pode estar indisponível
template:
metadata:
labels: { app: api }
spec:
containers:
- name: api
image: ghcr.io/me/api:1.2.0
ports: [{ containerPort: 3000 }]
env:
- name: DATABASE_URL
valueFrom: { secretKeyRef: { name: api-secrets, key: db-url } }
readinessProbe:
httpGet: { path: /health, port: 3000 }
initialDelaySeconds: 3
periodSeconds: 5
livenessProbe:
httpGet: { path: /health, port: 3000 }
initialDelaySeconds: 15
periodSeconds: 20
resources:
requests: { cpu: 100m, memory: 128Mi }
limits: { cpu: 500m, memory: 512Mi }kubectl rollout status deploy/api, kubectl rollout history deploy/api, kubectl rollout undo deploy/api.StatefulSet, DaemonSet, Job — quando usar cada um
| Controller | Propósito | Exemplos |
|---|---|---|
| Deployment | Apps stateless com N réplicas intercambiáveis | API HTTP, worker de fila, frontend |
| StatefulSet | Pods com identidade estável (pod-0, pod-1) e PVC por Pod | Postgres, Kafka, Elasticsearch, Redis cluster |
| DaemonSet | Um Pod por node (ou subset via nodeSelector) | Log collector (fluentd), node exporter, CNI |
| Job | Roda até completar N vezes com sucesso | Migração de schema, export de dados |
| CronJob | Job em schedule cron | Backup noturno, cleanup de lixo, relatório diário |
db-0, db-1) e volume persistente por Pod (volumeClaimTemplates). Se db-0 morre e volta, reengancha no mesmo PVC. Essencial pra bancos e sistemas distribuídos que precisam saber quem é o líder/follower.Services — 4 tipos, 4 propósitos
Service é o objeto que dá endpoint estável pra um conjunto de Pods. Pods morrem e nascem com IPs diferentes; Service tem um IP virtual (ClusterIP) e um nome DNS (api.default.svc.cluster.local) que persistem.
| Tipo | Escopo | Quando usar |
|---|---|---|
| ClusterIP | Só dentro do cluster | Default. Comunicação entre services. |
| NodePort | Expõe em uma porta 30000-32767 de cada node | Dev, on-prem sem LoadBalancer, debug |
| LoadBalancer | Pede LB externo à cloud (ELB, GLB, Azure LB) | Produção cloud — expõe um service ao mundo |
| ExternalName | DNS CNAME pra fora do cluster | Apontar pra RDS externo, API parceira |
| Headless (clusterIP: None) | Sem IP virtual, só DNS de Pods | StatefulSet — cada Pod precisa ser endereçado |
apiVersion: v1
kind: Service
metadata:
name: api
spec:
type: ClusterIP
selector: { app: api }
ports:
- port: 80 # a porta do Service
targetPort: 3000 # a porta do container nos PodsIngress — o roteamento HTTP do cluster
Service opera em L4 (TCP/UDP). Pra roteamento HTTP/S por host ou path com TLS, você quer Ingress. Mas Ingress por si só é só uma regra — precisa de um Ingress Controller(pod que efetivamente faz o roteamento). Os mais comuns: nginx-ingress, Traefik, HAProxy, AWS ALB Controller.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts: [api.meusite.com]
secretName: api-tls
rules:
- host: api.meusite.com
http:
paths:
- path: /v1
pathType: Prefix
backend:
service: { name: api-v1, port: { number: 80 } }
- path: /v2
pathType: Prefix
backend:
service: { name: api-v2, port: { number: 80 } }ConfigMap e Secret — separar código de config
| Objeto | Conteúdo | Como injetar |
|---|---|---|
| ConfigMap | Config não-sensível (feature flags, URLs, tunings) | env var, volume de arquivo, argv |
| Secret | Segredo (senha de db, API key, TLS cert) | env var ou volume (preferível volume) |
apiVersion: v1
kind: ConfigMap
metadata: { name: app-config }
data:
LOG_LEVEL: info
FEATURE_NEW_DASHBOARD: "true"
---
apiVersion: v1
kind: Secret
metadata: { name: app-secrets }
type: Opaque
stringData:
db-url: postgres://user:pass@db:5432/app
jwt-key: "super-secret-change-me"
---
# no Pod spec
spec:
containers:
- name: app
image: me/app:1.0
envFrom:
- configMapRef: { name: app-config }
env:
- name: DB_URL
valueFrom: { secretKeyRef: { name: app-secrets, key: db-url } }External Secrets Operator + Vault / AWS Secrets Manager / Azure Key Vault — o Secret no cluster fica sincronizado do cofre real, nunca em plaintext no git.envFrom, o valor é congelado no start. Soluções: (1) mount como volume (atualiza sozinho) e o app faz re-load; (2) adicionar annotation com hash do ConfigMap no PodTemplate (Reloader é um operator que faz isso automaticamente).Storage — PV, PVC, StorageClass
Volumes em K8s são uma abstração em 3 camadas:
# PVC dinâmico: o StorageClass provisiona o PV sozinho
apiVersion: v1
kind: PersistentVolumeClaim
metadata: { name: pg-data }
spec:
accessModes: [ReadWriteOnce]
storageClassName: gp3
resources:
requests: { storage: 20Gi }
---
apiVersion: v1
kind: Pod
metadata: { name: postgres }
spec:
containers:
- name: pg
image: postgres:16-alpine
volumeMounts:
- { name: data, mountPath: /var/lib/postgresql/data }
volumes:
- name: data
persistentVolumeClaim: { claimName: pg-data }| accessMode | Significado | Backends típicos |
|---|---|---|
| ReadWriteOnce (RWO) | Montado R/W em um node por vez | EBS, disk volumes |
| ReadOnlyMany (ROX) | Vários Pods, só leitura | Config/assets em NFS |
| ReadWriteMany (RWX) | Vários Pods, todos R/W | EFS, CephFS, GlusterFS |
| ReadWriteOncePod (RWOP) | Um único Pod R/W (mais forte que RWO) | PV para Pods únicos |
Namespaces — multi-tenancy dentro do cluster
Namespace é um agrupamento lógico. Recursos com o mesmo nome podem coexistir em namespaces diferentes. Default quando você não define: default. Do sistema: kube-system (control plane), kube-public.
kubectl create namespace staging kubectl apply -f deployment.yaml -n staging kubectl get pods -n staging kubectl config set-context --current --namespace=staging # fica no ns
ResourceQuota), e anexar políticas (RBAC, NetworkPolicy). Pra isolamento forte (tenants hostis), use clusters separados.RBAC — controle de acesso
4 objetos:
# Permite que o SA "deployer" faça qualquer coisa com Deployments em prod
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployer
namespace: prod
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: deployer-bind
namespace: prod
subjects:
- kind: ServiceAccount
name: ci-bot
namespace: ci
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: deployerAutoscaling — HPA, VPA, Cluster Autoscaler
| Autoscaler | O que escala | Baseado em |
|---|---|---|
| HorizontalPodAutoscaler (HPA) | Número de réplicas do Deployment | CPU, memória, custom metrics (Prometheus) |
| VerticalPodAutoscaler (VPA) | Requests/limits do Pod | Histórico de uso — recomenda ou ajusta |
| Cluster Autoscaler | Número de nodes | Pods pendentes que não cabem nos nodes atuais |
| KEDA | Réplicas, baseado em eventos (fila SQS, Kafka lag) | Event-driven (0 → N) |
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: api-hpa }
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target: { type: Utilization, averageUtilization: 70 }
- type: Resource
resource:
name: memory
target: { type: Utilization, averageUtilization: 80 }metrics-server instalado no cluster — não vem por default em clusters DIY. Em EKS/GKE/AKS geralmente já vem. Sem metrics-server, HPA reporta <unknown> nos targets e não escala nada.Helm — package manager do K8s
Aplicar 15 YAMLs à mão, com valores diferentes por ambiente, vira pesadelo. Helm empacota isso em um Chart: templates Go com valores parametrizáveis + um values.yaml por ambiente.
# instalar um chart público (Postgres oficial do Bitnami) helm repo add bitnami https://charts.bitnami.com/bitnami helm install my-pg bitnami/postgresql \ --namespace data \ --set auth.postgresPassword=secret \ --set primary.persistence.size=20Gi # seu próprio chart helm create minhaapp # scaffolds templates/ helm install meuapp ./minhaapp -f values.prod.yaml helm upgrade meuapp ./minhaapp -f values.prod.yaml helm rollback meuapp 3 # volta pra revisão 3 helm uninstall meuapp
Kustomize (embutido no kubectl — overlays em YAML puro, sem templating); Argo CD (GitOps pull-based). Em produção profissional, o padrão hoje é Helm + Argo CD: Helm empacota, Argo CD reconcilia do git pro cluster.Observabilidade — o que você precisa pra dormir à noite
kubectl — o CLI que resolve 95% do dia-a-dia
# Contexto e namespace kubectl config get-contexts kubectl config use-context prod kubectl config set-context --current --namespace=default # Inspeção kubectl get pods # lista pods no ns atual kubectl get pods -A # todos os namespaces kubectl get pods -o wide # + node, IP kubectl get pods -w # watch (live) kubectl describe pod api-xyz # tudo sobre o pod (eventos!) kubectl get events --sort-by=.lastTimestamp # Logs e exec kubectl logs -f deploy/api # segue logs do Deployment kubectl logs -p pod/api-xyz # logs do container que morreu (--previous) kubectl exec -it pod/api-xyz -- sh # shell no container # Debug de rede kubectl run -it --rm curl --image=curlimages/curl -- sh kubectl port-forward svc/api 8080:80 # acessa service local em http://localhost:8080 # Mudanças rápidas kubectl scale deploy/api --replicas=5 kubectl set image deploy/api api=me/api:1.3.0 kubectl rollout status deploy/api kubectl rollout undo deploy/api # Apply, diff, explain kubectl apply -f manifests/ kubectl diff -f manifests/ # o que vai mudar kubectl explain deployment.spec.strategy # docs do schema direto do cluster # Copy e debug effêmero kubectl cp api-xyz:/app/log.txt ./log.txt kubectl debug pod/api-xyz --image=nicolaka/netshoot # sidecar de debug
Troubleshooting — o checklist quando algo quebra
kubectl describe antes de kubectl logs. A seção Events no final do describe mostra o que aconteceu, em ordem, com timestamp. 90% dos problemas aparecem ali.Cenários de decisão
📋 Equipe de 6 devs, 3 microservices, SLA baixo. Precisa de K8s?
K8s introduz complexidade enorme (RBAC, ingress, helm, observabilidade). Pra 3 apps stateless com tráfego moderado, compose + systemd resolve. Mude pra K8s quando tiver 10+ services e time de ops.
Alt: K3s (K8s leve) — se a equipe já sabe K8s e quer uniformidade.
Alt: ECS/Fargate — orquestração gerenciada AWS sem a curva do K8s.
📋 20 microservices, 3 ambientes, vários times. Expor cada um?
Um LB só cloud (caro) → Ingress → 20 services ClusterIP. cert-manager renova TLS Let's Encrypt automaticamente. Roteamento L7 por host/path. Custo previsível, governança centralizada.
Alt: LoadBalancer por service — 20 × US$16/mês + complexidade de DNS. Evite.
📋 Preciso rodar Postgres HA dentro do K8s — é boa ideia?
StatefulSet cru não faz HA de Postgres — backup, failover, PITR são não-triviais. Operators encapsulam esse know-how. RDS/Aurora terceirizam o problema inteiro. Só 'Postgres artesanal' se o time entende muito bem.
Alt: RDS/Cloud SQL — menos controle, muito menos dor operacional.
Perguntas típicas
❓ Posso começar a aprender K8s sem ter um cluster pago?
kind (Kubernetes-in-Docker) ou minikube sobem um cluster inteiro na sua máquina em 30 segundos. Depois, clusters gratuitos na cloud com free tier (EKS tem custo da control plane; GKE Autopilot e AKS têm camadas gratuitas). Pra treino puro, kind é o melhor.❓ Qual a diferença entre EKS, GKE e AKS?
❓ Preciso escrever YAML todo dia?
cdk8s (YAML via TypeScript/Python) geram. YAML continua sendo o formato de cabo final, mas quase nunca você digita do zero depois dos primeiros meses.❓ K8s faz CI/CD?
Argo CD (GitOps pull-based — manifestos no git, Argo sincroniza), Flux, ou pipelines tradicionais (GitHub Actions, GitLab, Jenkins) que rodam kubectl apply ou helm upgrade.❓ O que é um Operator?
kind: PostgresCluster— e o operator watcheia esses CRDs e faz a mágica (provisionar réplicas, failover, backup). É como “transformar know-how humano em controller”.kubectl describe antes de kubectl logs. (10) K8s resolve problemas grandes e cobra complexidade — só adote quando o retorno compensar.Quiz rápido
4 perguntas · Acerte tudo e ganhe o badge 🎯 Gabarito