Índice
- O que é Kubernetes, afinal?
- Containers: a base de tudo
- Por que você precisa de orquestração
- Anatomia de um cluster
- Pods: a menor unidade
- Deployments: garantir o estado desejado
- Services: como encontrar seus pods
- Subindo sua primeira aplicação
- Auto-scaling: crescer sob demanda
- Erros comuns
O que é Kubernetes, afinal? {#o-que-e}
Imagine um porto de navios. Chegam milhares de contêineres por dia — cada um com um conteúdo diferente, um destino diferente, um peso diferente. Sem alguém coordenando, vira caos: contêineres empilhados no lugar errado, guindastes ociosos enquanto outros estão sobrecarregados, cargas perecíveis estragando enquanto esperam.
O gerente do porto é quem organiza tudo: decide qual contêiner vai para qual navio, redireciona quando um guindaste quebra, escala mais caminhões no horário de pico. Ele não carrega os contêineres — ele orquestra quem carrega.
Kubernetes é esse gerente de porto, mas para software.
Kubernetes (pronuncia-se koo-ber-NET-ees) é uma plataforma de código aberto que automatiza a implantação, o escalonamento e a operação de aplicações em containers. Ele foi criado pelo Google em 2014, baseado em um sistema interno chamado Borg que rodava os serviços do Google há mais de uma década. Hoje é mantido pela CNCF (Cloud Native Computing Foundation — uma fundação que cuida de projetos de software "cloud native", ou seja, pensados para rodar na nuvem desde o início).
A palavra Kubernetes vem do grego e significa "timoneiro" — quem governa o leme do navio. Por isso o logo é uma roda de navio.
A abreviação k8s é comum: o "8" representa as oito letras entre o "k" inicial e o "s" final (k-ubernete-s).
O que Kubernetes NÃO é
É importante alinhar expectativas desde já:
- Não é um sistema de containers: ele gerencia containers, mas quem os cria e executa é um "container runtime" (motor de execução) como containerd ou Docker. Kubernetes é a camada acima.
- Não é uma nuvem: Kubernetes roda sobre uma nuvem (AWS, Google Cloud, Azure) ou até em seus próprios servidores. Ele não substitui a infraestrutura — ele a orquestra.
- Não é mágica: ele automatiza muito, mas exige configuração, conhecimento e manutenção. Não é "instalar e esquecer".
Containers: a base de tudo {#containers}
Antes de entender Kubernetes, você precisa entender containers.
Container é uma forma de empacotar uma aplicação junto com tudo o que ela precisa para rodar — código, bibliotecas, configurações, variáveis de ambiente — em uma unidade portátil e isolada.
Analogia: pense em um container de navio. Não importa o que tem dentro (eletrodomésticos, roupas, comida), o container tem um formato padrão. O navio não precisa saber o que tem dentro — ele só precisa saber as dimensões. Qualquer navio carrega qualquer container.
Da mesma forma, um container de software roda em qualquer servidor que tenha um container runtime. O servidor não precisa ter as bibliotecas da sua aplicação instaladas — tudo vem dentro do container.
Container vs máquina virtual
| Aspecto | Máquina Virtual (VM) | Container |
|---|---|---|
| O que inclui | Sistema operacional completo + app | Só a app e dependências |
| Tamanho | Gigabytes | Megabytes |
| Tempo de inicialização | Minutos | Segundos |
| Isolamento | Total (cada VM tem seu SO) | Compartilha o kernel do host |
| Densidade | Poucas VMs por servidor | Centenas de containers por servidor |
| Quando usar | Isolamento máximo, SO diferente | Aplicações na mesma linguagem/SO |
A diferença fundamental: uma VM virtualiza o hardware (cada VM roda um sistema operacional completo), enquanto um container virtualiza o sistema operacional (vários containers compartilham o mesmo kernel, o núcleo do SO).
Docker é o container runtime mais conhecido. Você escreve um
Dockerfile (receita de como montar o container), executa docker build e
obtém uma imagem — um pacote imutável. Ao executar essa imagem, você tem
um container rodando.
# Dockerfile: receita para criar a imagem da aplicação
FROM node:20-alpine # Começa com uma imagem base (Node.js leve)
WORKDIR /app # Define o diretório de trabalho dentro do container
COPY package.json . # Copia o arquivo de dependências
RUN npm install # Instala as dependências (roda só no build)
COPY . . # Copia o resto do código
EXPOSE 3000 # Documenta que a app usa a porta 3000
CMD ["node", "server.js"] # Comando executado quando o container inicia
Por que você precisa de orquestração {#por-que-orquestracao}
Imagine que sua aplicação faz sucesso. Você tinha 1 container rodando, agora precisa de 10. Sem orquestração, você faz tudo manualmente:
- Em quais servidores colocar cada container?
- Se um servidor cair, como mover os containers para outro?
- Se o tráfego crescer às 14h, como adicionar containers automaticamente?
- Se um container travar, quem reinicia?
- Como atualizar a versão sem derrubar o serviço?
Fazer isso manualmente com 10 containers já é trabalhoso. Com 100, é impossível. Com 1.000, é uma pesadelo.
É aqui que entra o orquestrador. Kubernetes resolve todos esses problemas automaticamente:
- Escalonamento: adicionar ou remover containers conforme a demanda
- Self-healing (auto-cura): reiniciar containers que travaram
- Balanceamento de carga: distribuir tráfego entre os containers
- Rolling updates: atualizar a versão sem downtime (tempo fora do ar)
- Service discovery: containers se encontram automaticamente, sem IPs fixos
Sem Kubernetes vs com Kubernetes
| Situação | Sem orquestração | Com Kubernetes |
|---|---|---|
| Container trava | Você reinicia manualmente | Reinicia sozinho em segundos |
| Tráfego aumenta | Você adiciona servidores manualmente | Auto-scaling adiciona pods |
| Servidor cai | Containers perdidos, app fora do ar | Pods realocados para outro servidor |
| Deploy de nova versão | Derruba a app, sobe a nova | Atualiza gradualmente, sem downtime |
| Descoberta de serviços | Configura IPs manualmente | DNS interno resolve automaticamente |
Anatomia de um cluster {#anatomia}
Um cluster Kubernetes é um conjunto de máquinas que rodam containers gerenciados pelo Kubernetes. Ele tem duas partes principais:
┌─────────────────────────────────────────────────────┐
│ CLUSTER K8S │
│ │
│ ┌─────────────────┐ ┌──────────────────────┐ │
│ │ CONTROL PLANE │ │ WORKER NODES │ │
│ │ (o cérebro) │ │ (os músculos) │ │
│ │ │ │ │ │
│ │ • API Server │ │ Node 1 Node 2 │ │
│ │ • Scheduler │ │ [pods] [pods] │ │
│ │ • etcd │ │ Node 3 │ │
│ │ • Controller │ │ [pods] │ │
│ └────────┬────────┘ └──────────▲───────────┘ │
│ │ │ │
│ └─────── controla ──────┘ │
└─────────────────────────────────────────────────────┘
Control Plane (plano de controle) — o cérebro
É quem toma as decisões. Não roda suas aplicações — apenas gerencia o cluster.
- API Server: a porta de entrada. Tudo o que você faz (criar pods, escalar, atualizar) passa pelo API Server. É como a recepção de um prédio.
- Scheduler (agendador): decide em qual worker node cada pod vai rodar, baseado em recursos disponíveis (CPU, memória).
- etcd: um banco de dados chave-valor que guarda todo o estado do cluster. É a "memória" do Kubernetes — sem ele, o cluster esquece tudo.
- Controller Manager: garante que o estado real corresponde ao estado desejado. Se você pediu 3 pods e só tem 2, ele cria o terceiro.
Worker Nodes (nós de trabalho) — os músculos
São as máquinas que realmente rodam suas aplicações. Cada worker node tem:
- kubelet: o agente que recebe ordens do control plane e garante que os pods estão rodando conforme o esperado.
- kube-proxy: responsável pelo roredamento de rede entre os pods.
- Container runtime: o motor que executa os containers (containerd, Docker, CRI-O).
Analogia final: o control plane é o gerente do porto (decide, planeja, monitora). Os worker nodes são os guindastes e caminhões (executam o trabalho pesado).
Pods: a menor unidade {#pods}
Pod é a menor unidade gerenciável no Kubernetes. E é o conceito mais importante para entender.
Um pod pode conter um ou mais containers, mas o caso mais comum é um pod = um container. Os containers dentro do mesmo pod compartilham rede e armazenamento — são "amigos íntimos" que precisam estar sempre juntos.
Por que pods e não containers direto? Porque Kubernetes precisa gerenciar algo maior que um container. Um pod pode ter, além do container principal, um "sidecar" — um container auxiliar. Por exemplo: um container com sua aplicação + um container que coleta logs e envia para um servidor central.
Analogia: se o container é um peixe, o pod é o aquário. Você pode ter vários peixes no mesmo aquário (containers no mesmo pod), compartilhando a mesma água (rede) e o mesmo fundo (armazenamento). Kubernetes gerencia aquários, não peixes diretamente.
Características dos pods
- Efêmeros: pods nascem e morrem. Se um pod morre, o Kubernetes cria outro — mas com um IP novo. Por isso você nunca deve depender do IP de um pod.
- Não se atualizam: para atualizar a versão da sua app, o Kubernetes mata o pod antigo e cria um novo com a nova imagem.
- Escalam em número: para suportar mais tráfego, você cria mais réplicas do mesmo pod.
# pod-simples.yaml — exemplo de um pod básico
apiVersion: v1
kind: Pod
metadata:
name: minha-app # Nome do pod
labels:
app: minha-app # Label (etiqueta) para identificar o pod depois
spec:
containers:
- name: minha-app # Nome do container dentro do pod
image: nginx:latest # Imagem que vai rodar (nginx neste exemplo)
ports:
- containerPort: 80 # Porta que o container expõe
Atenção: na prática, você quase nunca cria pods diretamente. Você cria Deployments (próxima seção), que gerenciam os pods para você. Criar pods "soltos" é como construir uma casa sem alicerce — funciona, mas não sobrevive a uma tempestade.
Deployments: garantir o estado desejado {#deployments}
Deployment é o objeto do Kubernetes que garante que um número de réplicas de um pod esteja sempre rodando. É a forma correta de rodar aplicações no Kubernetes.
Como funciona: você diz ao Kubernetes "quero 3 pods da minha aplicação rodando". O Deployment cria esses 3 pods. Se um morrer, ele cria outro. Se você quiser atualizar a versão, ele substitui gradualmente os pods antigos por pods novos. Se você quiser escalar para 10, ele cria mais 7.
Analogia: imagine um gerente de uma fábrica que recebe a ordem "mantenha sempre 3 máquinas de embalagem funcionando". Se uma quebra, ele liga uma reserva. Se você pede para aumentar para 10, ele liga mais máquinas. Ele não embala nada — ele garante que as máquinas estejam operando.
# deployment.yaml — a forma correta de rodar sua aplicação
apiVersion: apps/v1
kind: Deployment
metadata:
name: minha-app
spec:
replicas: 3 # Quantos pods queremos rodando simultaneamente
selector:
matchLabels:
app: minha-app # Quais pods este Deployment gerencia (por label)
template: # O "molde" para criar cada pod
metadata:
labels:
app: minha-app # Label que casa com o selector acima
spec:
containers:
- name: minha-app
image: minha-app:v1.0.0 # Versão da imagem (use tags, não :latest)
ports:
- containerPort: 3000
resources: # Importante: defina limites de recursos!
requests: # O mínimo que o pod precisa para ser agendado
memory: "128Mi"
cpu: "250m" # 250m = 250 milicores = 0.25 de um núcleo
limits: # O máximo que o pod pode consumir
memory: "256Mi"
cpu: "500m"
livenessProbe: # Verifica se a app está viva (senão, reinicia)
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10 # Espera 10s antes de começar a checar
readinessProbe: # Verifica se a app está pronta para receber tráfego
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
Liveness vs Readiness: a diferença importa
Esses dois termos confundem iniciantes, mas a diferença é crucial:
- Liveness probe (sonda de vitalidade): "A aplicação está viva?" Se falhar, o Kubernetes reinicia o pod. Como um médico checando os sinais vitais — se o paciente parou de respirar, chama o reanimador.
- Readiness probe (sonda de prontidão): "A aplicação está pronta para receber tráfego?" Se falhar, o Kubernetes remove o pod do balanceamento de carga — não reinicia, só para de enviar requisições. Como um garçom que não anota pedidos de uma mesa onde o chef ainda não chegou.
Cenário: sua app demora 30 segundos para iniciar (precisa carregar dados em memória). Sem readiness probe, o Kubernetes já envia tráfego aos 5 segundos — a app quebra, o liveness falha, o pod reinicia, e entra em um loop infinito. Com readiness probe configurada corretamente, o Kubernetes espera a app ficar pronta antes de enviar tráfego.
Services: como encontrar seus pods {#services}
Lembra que pods são efêmeros e mudam de IP? Então como outras aplicações encontram o seu pod se o IP muda toda hora?
Service é a resposta. Um Service é um endereço fixo (IP + DNS) que funciona como uma porta de entrada para um conjunto de pods. Ele faz load balancing (distribuição de carga) entre os pods que casam com seu seletor.
Analogia: pense no Service como o número do telefone da recepção de um hospital. Os médicos (pods) entram e saem, trocam de plantão, mas o número da recepção (Service) é sempre o mesmo. Você liga para a recepção e ela direciona para um médico disponível.
Tipos de Service
| Tipo | Acessível por | Quando usar |
|---|---|---|
| ClusterIP | Só dentro do cluster | Comunicação interna entre serviços |
| NodePort | De fora do cluster (porta alta) | Testes, acesso externo simples |
| LoadBalancer | Internet (cria um LB da nuvem) | Expor para a internet em produção |
| Ingress | Internet (HTTP/HTTPS com domínio) | Múltiplas apps no mesmo domínio |
# service.yaml — expõe a aplicação dentro do cluster
apiVersion: v1
kind: Service
metadata:
name: minha-app-service
spec:
type: ClusterIP # Endereço acessível só dentro do cluster
selector:
app: minha-app # Roteia tráfego para pods com este label
ports:
- port: 80 # Porta que o Service escuta
targetPort: 3000 # Porta do pod para onde o tráfego vai
Ingress: o porteiro do prédio
Quando você tem múltiplas aplicações (API, frontend, admin) e quer expor todas na internet com um único IP e domínios diferentes, usa Ingress. É como um porteiro que recebe todos os visitantes e direciona cada um para o andar certo baseado no nome que procuram:
api.minhaempresa.com→ Service da APIapp.minhaempresa.com→ Service do frontendadmin.minhaempresa.com→ Service do painel admin
Subindo sua primeira aplicação {#primeira-app}
Vamos juntar tudo. Aqui está o passo a passo para subir uma aplicação Node.js no Kubernetes, do zero.
Passo 1: criar a imagem Docker
# Constrói a imagem e envia para um registro (registry)
# Registry = um "Docker Hub" privado onde imagens ficam armazenadas
docker build -t meu-registro/minha-app:v1.0.0 .
docker push meu-registro/minha-app:v1.0.0
Passo 2: criar o Deployment
Salve o arquivo deployment.yaml (use o exemplo da seção Deployments acima,
trocando a imagem por meu-registro/minha-app:v1.0.0) e aplique:
# kubectl = a ferramenta de linha de comando que fala com o Kubernetes
# apply = aplica a configuração do arquivo no cluster
kubectl apply -f deployment.yaml
# Veja os pods sendo criados
kubectl get pods
# Saída esperada:
# NAME READY STATUS RESTARTS AGE
# minha-app-7d4f5b6c8-x2k9j 1/1 Running 0 30s
# minha-app-7d4f5b6c8-m8p3q 1/1 Running 0 30s
# minha-app-7d4f5b6c8-r1n5t 1/1 Running 0 30s
Passo 3: criar o Service
kubectl apply -f service.yaml
# Veja o Service criado com seu IP interno
kubectl get services
# Saída:
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
# minha-app-service ClusterIP 10.96.34.72 <none> 80/TCP
Passo 4: expor para a internet (opcional)
Para testar localmente, você pode fazer port-forward (encaminhamento de porta) — cria um túnel da sua máquina para o Service dentro do cluster:
# Encaminha a porta 8080 da sua máquina para a porta 80 do Service
kubectl port-forward service/minha-app-service 8080:80
# Agora acesse http://localhost:8080 no navegador
Passo 5: atualizar a versão
Quando você lançar a v1.1.0, basta mudar a imagem no Deployment:
# Muda a imagem do deployment para a nova versão
kubectl set image deployment/minha-app minha-app=meu-registro/minha-app:v1.1.0
# O Kubernetes faz um rolling update: cria pods novos, remove os antigos,
# um de cada vez, sem derrubar a aplicação
kubectl rollout status deployment/minha-app
# Saída: "deployment minah-app successfully rolled out"
Se algo der errado, você pode reverter (rollback) em segundos:
# Volta para a versão anterior
kubectl rollout undo deployment/minha-app
Auto-scaling: crescer sob demanda {#auto-scaling}
O Horizontal Pod Autoscaler (HPA) aumenta ou diminui o número de pods automaticamente baseado em métricas (uso de CPU, memória, ou métricas customizadas).
Analogia: é como um restaurante que abre mais caixas quando a fila fica grande. Quando o movimento cai, fecha os caixas extras para economizar.
# hpa.yaml — auto-scaling horizontal de pods
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: minha-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: minha-app # Qual Deployment escalar
minReplicas: 2 # Mínimo de pods (mesmo sem tráfego)
maxReplicas: 20 # Máximo de pods (limite de custo)
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # Se CPU passar de 70%, adiciona pods
Com essa configuração, se a CPU média dos pods passar de 70%, o Kubernetes adiciona mais pods. Se cair, remove. Tudo automático.
Importante: para o HPA funcionar com CPU/memória, você precisa do Metrics Server instalado no cluster. Ele coleta as métricas de consumo de cada pod.
Tipos de auto-scaling no Kubernetes
| Tipo | O que escala | Quando |
|---|---|---|
| Horizontal Pod Autoscaler | Número de pods | Demanda variável de tráfego |
| Vertical Pod Autoscaler | CPU/memória de cada pod | App precisa de mais recursos |
| Cluster Autoscaler | Número de worker nodes | Pods não cabem nos nodes atuais |
O ideal é combinar HPA (mais pods) com Cluster Autoscaler (mais servidores quando os pods não cabem nos servidores existentes).
Erros comuns {#erros-comuns}
1. Usar :latest como tag de imagem
Problema: :latest é mutável — hoje é a v1.0, amanhã é a v2.0. O
Kubernetes pode não perceber que a imagem mudou e continuar usando uma versão
antiga em cache.
Solução: sempre use tags versionadas (v1.0.0, v1.2.3) ou o hash
SHA da imagem.
2. Não definir resource requests e limits
Problema: sem limites, um pod pode consumir toda a CPU/memória do node e derrubar outros pods. Sem requests, o scheduler não sabe onde colocar o pod.
Solução: sempre defina resources.requests (mínimo) e resources.limits
(máximo) para cada container.
3. Confundir liveness e readiness
Problema: usar liveness probe para checar dependências externas (banco de dados, API de terceiros). Se o banco cai, o liveness falha, o pod reinicia — mas o banco continua caído, e o pod reinicia de novo em loop.
Solução: liveness checa se a própria aplicação está viva. Readiness checa se ela está pronta para servir, incluindo dependências.
4. Não configurar PodDisruptionBudget
Problema: durante manutenção do cluster, o Kubernetes pode derrubar todos os seus pods de uma vez, causando downtime.
Solução: configure um PodDisruptionBudget (orçamento de disrupção) garantindo que sempre um número mínimo de pods continue rodando:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: minha-app-pdb
spec:
minAvailable: 2 # Sempre mantém pelo menos 2 pods rodando
selector:
matchLabels:
app: minha-app
5. Ignorar a segurança de containers
Problema: rodar containers como root, sem limites de privilégio, abre brechas de segurança.
Solução: adicione securityContext nos seus manifests:
spec:
containers:
- name: minha-app
image: minha-app:v1.0.0
securityContext:
runAsNonRoot: true # Não roda como usuário root
runAsUser: 1000 # Roda como usuário específico
readOnlyRootFilesystem: true # Sistema de arquivos só-leitura
Precisa de ajuda com Kubernetes? A Inicialize Tec implementa clusters Kubernetes, migra aplicações para containers e configura auto-scaling e observabilidade do zero. Conversamos sobre seu caso.