Índice

  1. O que é Kubernetes, afinal?
  2. Containers: a base de tudo
  3. Por que você precisa de orquestração
  4. Anatomia de um cluster
  5. Pods: a menor unidade
  6. Deployments: garantir o estado desejado
  7. Services: como encontrar seus pods
  8. Subindo sua primeira aplicação
  9. Auto-scaling: crescer sob demanda
  10. 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

AspectoMáquina Virtual (VM)Container
O que incluiSistema operacional completo + appSó a app e dependências
TamanhoGigabytesMegabytes
Tempo de inicializaçãoMinutosSegundos
IsolamentoTotal (cada VM tem seu SO)Compartilha o kernel do host
DensidadePoucas VMs por servidorCentenas de containers por servidor
Quando usarIsolamento máximo, SO diferenteAplicaçõ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:

  1. Em quais servidores colocar cada container?
  2. Se um servidor cair, como mover os containers para outro?
  3. Se o tráfego crescer às 14h, como adicionar containers automaticamente?
  4. Se um container travar, quem reinicia?
  5. 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çãoSem orquestraçãoCom Kubernetes
Container travaVocê reinicia manualmenteReinicia sozinho em segundos
Tráfego aumentaVocê adiciona servidores manualmenteAuto-scaling adiciona pods
Servidor caiContainers perdidos, app fora do arPods realocados para outro servidor
Deploy de nova versãoDerruba a app, sobe a novaAtualiza gradualmente, sem downtime
Descoberta de serviçosConfigura IPs manualmenteDNS 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

TipoAcessível porQuando usar
ClusterIPSó dentro do clusterComunicação interna entre serviços
NodePortDe fora do cluster (porta alta)Testes, acesso externo simples
LoadBalancerInternet (cria um LB da nuvem)Expor para a internet em produção
IngressInternet (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 API
  • app.minhaempresa.com → Service do frontend
  • admin.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

TipoO que escalaQuando
Horizontal Pod AutoscalerNúmero de podsDemanda variável de tráfego
Vertical Pod AutoscalerCPU/memória de cada podApp precisa de mais recursos
Cluster AutoscalerNúmero de worker nodesPods 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.