Pular para o conteúdo
CloudIniciante

Kubernetes para quem vem do Docker Compose

Traduza seu docker-compose.yml para Kubernetes: Deployment, Service, ConfigMap, Secret, volumes e probes, e entenda por que depends_on não existe.

Por Equipe IAUAI Estudos · 22 de julho de 2026 · 10 min de leitura

Nesta página

Ao final deste artigo você vai conseguir pegar um docker-compose.yml simples e escrever os manifestos equivalentes para Kubernetes, sabendo o que cada peça faz e onde o modelo mental muda. Quem já usa Compose tem meio caminho andado, porque os conceitos se parecem mais do que os nomes sugerem.

O ponto de partida

Imagine uma API, um Postgres e um Redis rodando no seu laptop:

# compose.yaml
services:
  api:
    image: myapp/api:1.2.0
    ports:
      - "8000:8000"
    environment:
      LOG_LEVEL: INFO
      DATABASE_URL: postgres://user:pass@db:5432/app
      REDIS_URL: redis://cache:6379
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started

  db:
    image: postgres:17
    environment:
      POSTGRES_DB: app
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    volumes:
      - dbdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d app"]
      interval: 5s
      retries: 10

  cache:
    image: redis:7-alpine

volumes:
  dbdata:

Nota: o campo version: não é mais necessário no Compose v2 e gera aviso de obsoleto. O comando é docker compose up, sem hífen.

Em Kubernetes, quase cada trecho vira um objeto separado. A tabela resume o mapa:

Compose Kubernetes
services.api Deployment (os pods) + Service (o endereço estável)
ports Service dentro do cluster; Ingress ou Gateway para fora
environment ConfigMap (não sensível) e Secret (sensível)
volumes PersistentVolumeClaim (PVC)
healthcheck readinessProbe, livenessProbe, startupProbe
depends_on não existe; probes e retentativa na aplicação
deploy.resources resources.requests e limits

Deployment e Service: rodar e ser encontrado

O Deployment descreve quantas cópias do container você quer e como atualizá-las. O Service dá a esse conjunto de pods um nome DNS e um IP estáveis, já que os pods nascem e morrem o tempo todo.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: myapp/api:1.2.0
          ports:
            - containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
  name: api
spec:
  selector:
    app: api
  ports:
    - port: 80
      targetPort: 8000

O Service do tipo padrão (ClusterIP) só é acessível dentro do cluster. Outros pods chamam a API por http://api, do mesmo jeito que no Compose você chamava http://api:8000 pelo nome do serviço.

Para testar sem expor nada, use kubectl port-forward svc/api 8000:80 e acesse localhost:8000.

Expor para a internet: Ingress ou Gateway API

O equivalente ao ports: "8000:8000" apontando para o mundo externo é um Ingress ou, cada vez mais, um recurso da Gateway API. Um Ingress mínimo fica assim:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api
spec:
  ingressClassName: nginx
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api
                port:
                  number: 80

Atenção a um detalhe de atualidade. O controlador ingress-nginx, o mais usado em tutoriais, foi anunciado como aposentado pelo projeto Kubernetes, com manutenção apenas até março de 2026. A própria comunidade recomenda migrar para a Gateway API ou para outro controlador. Se você está começando agora, vale estudar a Gateway API em vez de decorar anotações de nginx. Em clusters gerenciados (EKS, GKE, AKS), costuma haver um controlador do próprio provedor.

ConfigMap e Secret: configuração fora da imagem

No Compose as variáveis ficam no YAML. No Kubernetes elas viram objetos próprios, e o Deployment só referencia:

apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
data:
  LOG_LEVEL: "INFO"
---
apiVersion: v1
kind: Secret
metadata:
  name: api-secret
type: Opaque
stringData:
  DATABASE_URL: "postgres://user:pass@db:5432/app"

E no container, envFrom injeta todas as chaves de uma vez:

      containers:
        - name: api
          image: myapp/api:1.2.0
          envFrom:
            - configMapRef:
                name: api-config
            - secretRef:
                name: api-secret

Um aviso importante sobre Secret. Ele guarda o valor em base64, que é só codificação, não criptografia. Quem tem permissão de leitura no namespace lê tudo. Para proteger de verdade, habilite criptografia em repouso no etcd, restrinja o RBAC e, em produção, busque os segredos de um cofre externo (por exemplo com o External Secrets Operator). E nunca versione o manifesto de Secret com valores reais.

Volumes e banco de dados

O volumes: dbdata: do Compose vira um PVC, um pedido de armazenamento que o cluster atende com um volume. Para dados que precisam de identidade estável, como um banco, o objeto adequado é o StatefulSet, que cria um PVC por réplica:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: db
spec:
  serviceName: db
  replicas: 1
  selector:
    matchLabels:
      app: db
  template:
    metadata:
      labels:
        app: db
    spec:
      containers:
        - name: postgres
          image: postgres:17
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_DB
              value: app
            - name: POSTGRES_USER
              value: user
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: password
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 10Gi

Na prática, muita equipe nem roda o banco dentro do cluster. Backup, failover e atualização de versão dão trabalho, e um serviço gerenciado (RDS, Cloud SQL) ou um operador maduro como o CloudNativePG resolvem isso melhor que um StatefulSet escrito à mão. Para estudar e para ambientes de teste, o exemplo acima serve bem.

Um cuidado se você trocar para o Postgres 18: a imagem oficial mudou o diretório de dados padrão, então confira a documentação da imagem antes de reaproveitar o mountPath.

depends_on não existe, e tudo bem

No Compose, depends_on ordena a subida. No Kubernetes não há ordem garantida entre Deployments, porque o cluster trabalha de forma declarativa: ele tenta levar o estado real ao desejado, o tempo todo, e pods podem ser recriados a qualquer momento. Se o banco reiniciar às 3 da manhã, nenhum depends_on salvaria a API.

A resposta é dupla. A aplicação precisa tolerar dependência indisponível (retentativa com espera crescente na conexão). E o Kubernetes usa probes para saber o estado de cada pod:

          readinessProbe:
            httpGet:
              path: /health/ready
              port: 8000
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /health/live
              port: 8000
            periodSeconds: 20
            failureThreshold: 3
          startupProbe:
            httpGet:
              path: /health/startup
              port: 8000
            periodSeconds: 10
            failureThreshold: 30

A readinessProbe decide se o pod recebe tráfego. A livenessProbe decide se ele deve ser reiniciado. A startupProbe dá tempo para aplicações lentas subirem sem que a liveness as mate. Um erro frequente é fazer a liveness checar o banco: se o banco cair, todos os pods da API reiniciam em cascata sem resolver nada. Deixe a liveness barata e local, e coloque as checagens de dependência na readiness.

Um exemplo mínimo em Flask:

from flask import Flask

app = Flask(__name__)

@app.get("/health/live")
def live():
    return {"status": "alive"}, 200

@app.get("/health/ready")
def ready():
    # aqui você pode testar o banco, por exemplo
    return {"status": "ready"}, 200

Pod, ReplicaSet e Deployment

O Pod é a menor unidade: um ou mais containers que compartilham rede e volumes. O ReplicaSet mantém N pods vivos. O Deployment cria e gerencia ReplicaSets para dar atualização gradual (rolling update) e rollback. Você quase sempre escreve o Deployment e deixa os outros dois por conta dele. Se apagar um pod na mão, o ReplicaSet cria outro em seguida, algo que o Compose só faz com restart: always e sem noção de réplicas.

Requests e limits

No Kubernetes, requests é o que o scheduler reserva para o pod ao escolher um nó, e limits é o teto que o container pode usar.

          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"

Sem requests, o scheduler enxerga o pod como "gratuito" e pode lotar um nó. Ao passar do limite de memória, o container é morto por OOM. Já o limite de CPU não mata, apenas limita (throttling), e por isso muitos times definem requests de CPU e omitem o limite. É uma escolha de projeto, não uma regra.

Armadilhas comuns

Tag latest ou imagem sem tag. O pod pode subir uma versão diferente a cada reinício. Fixe a versão, ou o digest.

Réplicas fixas em tudo. Para carga variável, use um HorizontalPodAutoscaler (autoscaling/v2) apontando para o Deployment. Ele precisa de requests de CPU definidas para calcular a utilização.

Aplicar YAML solto sem organização. Quando passar de meia dúzia de arquivos, considere Helm ou Kustomize, e use kubectl diff -f antes de kubectl apply -f.

Se quiser um atalho para ver como o Compose "viraria" Kubernetes, a ferramenta Kompose gera manifestos a partir do compose.yaml. Use como ponto de partida e revise, porque o resultado raramente está pronto para produção.

Quando não usar Kubernetes

Se tudo cabe em um ou dois containers num servidor, Compose ou um serviço gerenciado de containers (Cloud Run, ECS, App Runner) entrega mais com menos manutenção. Kubernetes compensa quando você tem muitos serviços, precisa de escala automática, rollouts sem queda e um time que aguente operar o cluster, ou quando usa a versão gerenciada para dividir esse peso.

Para praticar sem custo, instale o kind ou o minikube, rode kubectl apply -f nos arquivos acima e acompanhe com kubectl get pods -w.

Próximos passos

Para provisionar o cluster e o resto da infraestrutura como código, veja Terraform: primeiros passos. E para acompanhar a saúde dos pods em produção, siga com Monitoramento com Prometheus e Grafana.

Quer aplicar isso na sua empresa? Marque uma conversa de 45 minutos em https://iauaicloud.com.br/consultoria

Aprenda jogando

Salto de Container

Pule entre containers que morrem e reiniciam sem ser reagendado pelo orquestrador.

Jogar Salto de Container

Teste seu conhecimento

Teste o que você aprendeu sobre Kubernetes para quem vem do Compose

Pergunta 1 de 6

O que um serviço do Compose, como services.api, vira no Kubernetes?

Um conteúdo prático por quinzena

Deixe o seu e-mail para receber um conteúdo prático de IA e cloud a cada quinzena. Sem spam, e você pede a exclusão quando quiser.

Continue lendo