CONTÊINER · DEVOPS-2026-BR · CARGA: CONHECIMENTO

DevOps, Docker &
Kubernetes

Do primeiro docker run à operação de clusters em produção: cultura, pipelines, containers, orquestração, GitOps, observabilidade e segurança — com foco constante no que o mercado cobra em vagas e entrevistas.

21 capítulos básico → muito avançado foco: mercado de trabalho Docker 27+ · Kubernetes 1.31+ pt-BR · julho/2026

Como usar esta apostila

  • Digite os comandos, não copie. DevOps é habilidade motora: quem digitou kubectl describe pod cinquenta vezes debuga mais rápido em entrevista prática.
  • Monte seu laboratório no capítulo 2 e use-o em todos os outros. Tudo aqui roda em uma máquina local com Docker Desktop ou Linux + kind/minikube.
  • Caixas 🎯 "cai em entrevista" marcam o que recrutadores técnicos realmente perguntam; caixas 💼 conectam o conteúdo a vagas e salários.
  • Trilha sugerida: iniciante — caps. 1–7 + 21; pleno — tudo até o 16; sênior/SRE — caps. 8, 14, 15, 17–20 são o seu diferencial.
  • Cada capítulo fecha com exercícios práticos e um checkpoint; só avance com todas as caixas marcadas de verdade.
MOD-01 · Parte I

O que é DevOps de verdade

Entender DevOps como cultura + práticas + ferramentas — e falar sobre isso do jeito que entrevistadores esperam ouvir.

Nível: básico

1.1 O problema que o DevOps resolve

No modelo tradicional, desenvolvimento queria entregar mudanças rápido e operações queria estabilidade — e cada lado era medido por metas opostas. Resultado: deploys raros, gigantes e traumáticos, "funciona na minha máquina", e um muro de tíquetes entre os times. DevOps nasceu (por volta de 2009) para derrubar esse muro: o mesmo time constrói, entrega e opera o software, com automação de ponta a ponta.

Cai em entrevista

"O que é DevOps para você?" — resposta forte: "É uma cultura de responsabilidade compartilhada pelo ciclo de vida completo do software, sustentada por automação (CI/CD, IaC), medição (métricas DORA, observabilidade) e feedback rápido. Ferramentas como Docker e Kubernetes são meios, não o fim." Fugir da resposta "DevOps é usar Jenkins/Docker" já diferencia você de metade dos candidatos.

1.2 CALMS: o modelo mental

PilarSignificadoExemplo prático
CultureResponsabilidade compartilhada, sem culpadosPostmortem sem apontar dedos (blameless)
AutomationAutomatizar tudo que é repetitivoPipeline que testa e faz deploy a cada merge
LeanLotes pequenos, fluxo contínuoPRs pequenos, feature flags, deploys diários
MeasurementDecidir com dadosMétricas DORA, SLOs, dashboards
SharingConhecimento circulaRunbooks, documentação, pair ops

1.3 Métricas DORA — o vocabulário de quem é sênior

A pesquisa State of DevOps (Google/DORA) consolidou 4 métricas que separam times de elite dos demais. Elas aparecem em entrevistas para vagas pleno/sênior:

  • Deployment Frequency — com que frequência vai código para produção (elite: sob demanda, várias vezes ao dia).
  • Lead Time for Changes — tempo do commit até produção (elite: < 1 dia).
  • Change Failure Rate — % de deploys que causam incidente (elite: < 15%).
  • MTTR / Failed Deployment Recovery Time — tempo para se recuperar de uma falha (elite: < 1 hora).

Repare no insight central: velocidade e estabilidade não são opostas — times de elite entregam mais rápido e quebram menos, porque lotes pequenos e automatizados são mais seguros que lotes grandes e manuais.

1.4 O ciclo de vida DevOps e onde cada ferramenta entra

FaseO que aconteceFerramentas típicas
PlanBacklog, histórias, épicosJira, Linear, GitHub Projects
CodeVersionamento e revisãoGit, GitHub/GitLab, PRs
BuildCompilar, empacotar, gerar imagemDocker, GitHub Actions, GitLab CI
TestTestes automatizados, análise estáticapytest/Jest, SonarQube, Trivy
Release/DeployEntregar em ambientesArgo CD, Helm, Flux, Spinnaker
OperateRodar e escalarKubernetes, AWS/GCP/Azure, Terraform
MonitorMétricas, logs, traces, alertasPrometheus, Grafana, Loki, OpenTelemetry, Datadog

1.5 DevOps, SRE e Platform Engineering — títulos que você verá nas vagas

  • DevOps Engineer: constrói pipelines, automação e infraestrutura; o título mais comum no Brasil.
  • SRE (Site Reliability Engineer): a implementação do Google para operar sistemas com engenharia de software — foco em confiabilidade, SLOs, error budgets e resposta a incidentes (cap. 19).
  • Platform Engineer: tendência forte desde 2023 — constrói a plataforma interna (IDP) que os devs usam em autosserviço (templates, pipelines prontos, clusters gerenciados). Backstage é a ferramenta-símbolo.
  • Cloud Engineer / Infra: mais peso em nuvem e IaC, menos em pipeline.
Leitura de mercado

Na prática as vagas misturam tudo: uma vaga "DevOps Pleno" no Brasil quase sempre pede Docker + Kubernetes + Terraform + uma nuvem (AWS lidera) + CI/CD + observabilidade. Esta apostila cobre exatamente esse conjunto. "AI/MLOps" e "FinOps" (otimização de custos) são os complementos que mais cresceram em 2025–2026 — cite-os como interesse de evolução.

Exercícios do capítulo 1
  1. Escreva em 5 linhas, com suas palavras, a diferença entre DevOps e SRE. Guarde — é pergunta clássica de triagem.
  2. Pesquise 3 vagas reais de DevOps no LinkedIn e liste as ferramentas pedidas. Compare com o sumário desta apostila e marque o que você já cobre.
  3. Estime as 4 métricas DORA de um projeto seu (mesmo pessoal). Qual seria a mais fácil de melhorar?
✔ checkpoint — capítulo 1

MOD-02 · Parte I

Linux e terminal para DevOps

O mínimo de Linux que sustenta todo o resto — e a montagem do laboratório usado na apostila inteira.

Nível: básico

Containers são Linux (cap. 4). Toda entrevista prática de DevOps começa num shell. Se você já domina, pule para 2.4 e monte o laboratório.

2.1 Sobrevivência no shell

terminal — navegação e arquivos
# onde estou / o que tem aqui / entrar e sair
pwd && ls -lah && cd /var/log && cd -
# ver conteúdo: cat (tudo), less (paginado), tail -f (acompanhar ao vivo — essencial p/ logs)
tail -f /var/log/syslog
# buscar texto e arquivos
grep -rn "ERROR" ./logs/
find / -name "*.conf" -mtime -1 2>/dev/null
# permissões: dono/grupo/outros — rwx = 421
chmod 640 app.env && chown app:app app.env

2.2 Processos, portas e recursos — o kit de diagnóstico

terminal — diagnóstico
# processos e consumo
ps aux --sort=-%mem | head
top   # ou htop; dentro: M ordena por memória, k mata processo
# quem está ouvindo em quais portas (pergunta clássica de entrevista prática)
ss -tulpn | grep 8080
# disco e memória
df -h && du -sh /var/lib/docker && free -h
# sinais: TERM (15) pede para encerrar; KILL (9) é o último recurso
kill -15 1234 || kill -9 1234
# serviços (systemd) e seus logs
systemctl status docker && journalctl -u docker --since "10 min ago"
Cai em entrevista (prática ao vivo)

"A aplicação não responde na porta 8080. Como você investiga?" — roteiro: ss -tulpn (algo está ouvindo?), ps aux | grep app (o processo vive?), journalctl -u app/logs do container (por que morreu?), curl -v localhost:8080 (responde localmente? então o problema é rede/firewall/proxy), df -h e free -h (disco cheio e OOM são vilões clássicos). Verbalizar um roteiro ordenado vale mais que acertar de primeira.

2.3 Pipes, variáveis e um script de verdade

terminal — bash essencial
# pipes encadeiam programas; awk/cut fatiam colunas
ps aux | grep nginx | awk '{print $2}'
# variáveis de ambiente — o mecanismo nº1 de configuração em containers
export APP_ENV=production && echo $APP_ENV
# script com o trio de segurança: para no erro, em var indefinida e em falha no pipe
cat > healthcheck.sh <<'SH'
#!/usr/bin/env bash
set -euo pipefail
URL="${1:?uso: healthcheck.sh <url>}"
if curl -fsS --max-time 5 "$URL" > /dev/null; then
  echo "OK  $URL"
else
  echo "FAIL $URL" >&2; exit 1
fi
SH
chmod +x healthcheck.sh && ./healthcheck.sh https://example.com

set -euo pipefail em todo script é marca de profissional — scripts que continuam após erro silencioso causam incidentes reais.

2.4 Montando o laboratório da apostila

Você precisa de: Docker, kubectl e um cluster local (kind — Kubernetes in Docker — é leve e é o que uso nos exemplos; minikube e k3d são alternativas válidas).

terminal — instalação (Linux/WSL2)
# Docker Engine (Ubuntu/Debian) — no Windows/macOS use Docker Desktop ou Rancher Desktop
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER && newgrp docker
docker run hello-world
# kubectl
curl -LO "https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install kubectl /usr/local/bin/ && kubectl version --client
# kind + cluster de laboratório
go install sigs.k8s.io/kind@latest 2>/dev/null || curl -Lo kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 && chmod +x kind && sudo mv kind /usr/local/bin/
kind create cluster --name lab
Creating cluster "lab" ... ✓  Set kubectl context to "kind-lab"
kubectl get nodes
💡 Windows: use WSL2

Todo o mercado assume shell Linux. No Windows, instale o WSL2 (wsl --install) e trabalhe dentro do Ubuntu — Docker Desktop integra com WSL2 nativamente. Dominar isso também elimina o clássico sofrimento de caminhos e finais de linha.

Exercícios do capítulo 2
  1. Monte o laboratório completo (Docker + kubectl + kind) e guarde um lab.md com os comandos que usou — será o embrião do seu repositório de estudos.
  2. Escreva um script diskwatch.sh que alerte (exit 1 + mensagem) se qualquer partição passar de 80% de uso. Dica: df -h --output=pcent,target.
  3. Sem olhar a apostila, execute o roteiro de diagnóstico da caixa de entrevista contra um container nginx que você mesmo parar de propósito.
✔ checkpoint — capítulo 2

MOD-03 · Parte I

CI/CD com GitHub Actions

Construir a esteira que testa, constrói e publica automaticamente — a habilidade DevOps mais pedida em vagas júnior/pleno.

Nível: intermediário

3.1 Conceitos: CI, CD e CD

  • Integração Contínua (CI): a cada push/PR, um robô roda build, testes e lint. Código quebrado não entra na main.
  • Entrega Contínua (Continuous Delivery): todo commit aprovado gera um artefato pronto para produção; o deploy final pode ter aprovação manual.
  • Deploy Contínuo (Continuous Deployment): sem aprovação manual — passou, foi para produção. Exige testes e observabilidade maduros.
Cai em entrevista

"Qual a diferença entre Continuous Delivery e Continuous Deployment?" — a resposta está no parágrafo acima; a maioria dos candidatos não sabe. Emende com: "e a fronteira do CD moderno é o GitOps, onde o deploy é reconciliado a partir do Git" (cap. 17) para sinalizar profundidade.

3.2 Anatomia de um workflow

GitHub Actions lê arquivos YAML em .github/workflows/. Estrutura: workflow → disparado por eventos (on) → contém jobs (rodam em paralelo por padrão, cada um numa VM runner) → compostos de steps (comandos ou actions reutilizáveis).

.github/workflows/ci.yml
name: CI
on:
  push: { branches: [main] }
  pull_request:

permissions:          # princípio do menor privilégio no token do job
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22, cache: npm }
      - run: npm ci
      - run: npm run lint
      - run: npm test -- --coverage
  • npm ci (e não install): instala exatamente o lockfile — builds reprodutíveis.
  • cache no setup-node corta minutos de pipeline; em pipelines grandes, cache é a otimização nº 1.
  • permissions mínimas evitam que um step comprometido faça push ou leia secrets além do necessário.

3.3 Build e push de imagem Docker com tag imutável

.github/workflows/release.yml
name: Build & Push
on:
  push: { branches: [main] }

permissions:
  contents: read
  packages: write     # publicar no GHCR sem secret manual

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v6
        with:
          push: true
          tags: |
            ghcr.io/${{ github.repository }}:${{ github.sha }}
            ghcr.io/${{ github.repository }}:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max
⚠️ Nunca faça deploy de :latest

Tag pelo SHA do commit (ou versão semântica). latest muda por baixo de você: impossibilita rollback confiável e auditoria. Em produção, tag imutável + (idealmente) digest pinning imagem@sha256:.... Isso é pergunta recorrente de entrevista pleno.

3.4 Secrets, ambientes e aprovações

  • Secrets (Settings → Secrets and variables): nunca comite credencial; use ${{ secrets.NOME }}. O runner mascara o valor nos logs.
  • Environments (ex.: staging, production): cada um com seus secrets, required reviewers (aprovação manual) e restrição de branch — assim você implementa Continuous Delivery com gate humano.
  • OIDC para nuvem (padrão sênior): em vez de guardar AWS_SECRET_ACCESS_KEY como secret estático, o workflow troca um token OIDC de curta duração por credenciais na AWS/GCP/Azure (aws-actions/configure-aws-credentials com role-to-assume). Zero credencial de longa duração — mencione isso em entrevista e você pontua.

3.5 Estratégias de deploy que você precisa saber explicar

EstratégiaComo funcionaTrade-off
RollingSubstitui instâncias aos poucos (padrão do K8s)Simples; versões antigas e novas convivem durante o rollout
Blue-GreenDois ambientes completos; vira o tráfego de uma vezRollback instantâneo; custa 2× infra durante a troca
CanárioNova versão recebe 1→5→25→100% do tráfego, guiada por métricasMenor risco; exige observabilidade e automação (Argo Rollouts, Flagger)
Feature flagsCódigo vai para produção desligado; liga por configuraçãoDesacopla deploy de release; flags viram dívida se não forem limpas
Exercícios do capítulo 3
  1. Crie um repositório com uma API mínima (qualquer linguagem) e o workflow de CI da seção 3.2 adaptado. Quebre um teste de propósito e veja o PR ser bloqueado.
  2. Adicione o workflow de build/push para o GHCR com tag por SHA. Rode docker pull da sua própria imagem.
  3. Crie o environment production com aprovação manual e um job de "deploy" (pode ser um echo) que só roda após aprovação.
  4. Desafio: adicione um job de matriz testando em Node 20 e 22 simultaneamente (strategy.matrix).
✔ checkpoint — capítulo 3

MOD-04 · Parte II DOCKER

Containers por dentro

Entender o que um container realmente é — o conhecimento que separa quem "usa Docker" de quem entende Docker.

Nível: intermediário

4.1 Container não é uma VM leve

Uma VM virtualiza hardware e roda um kernel próprio dentro de um hipervisor. Um container é apenas um processo Linux comum, isolado por recursos do kernel do hospedeiro — sem kernel próprio, sem boot, por isso sobe em milissegundos.

VMContainer
IsolamentoHardware virtual + kernel próprio (forte)Kernel compartilhado + namespaces (mais fraco)
TamanhoGBsMBs
BootMinutosMilissegundos
DensidadeDezenas por hostCentenas por host

4.2 As três tecnologias do kernel

  • Namespaces — limitam o que o processo enxerga. Cada container tem seu próprio: pid (vê só seus processos; seu app é o PID 1), net (interfaces e portas próprias), mnt (sistema de arquivos próprio), uts (hostname), ipc e user (mapeamento de UIDs — base do modo rootless).
  • cgroups (control groups v2) — limitam quanto o processo usa: CPU, memória, I/O, PIDs. É o cgroup que mata seu container com OOM quando ele estoura o limite de memória.
  • Union filesystem (OverlayFS) — a imagem é uma pilha de camadas somente-leitura; ao rodar, o Docker acrescenta uma fina camada de escrita por cima (copy-on-write). Por isso 50 containers da mesma imagem quase não ocupam disco extra — e por isso o que se escreve no container morre com ele.
terminal — vendo o isolamento com os próprios olhos
docker run -d --name web nginx:alpine
# dentro do container, o nginx é PID 1 e só vê a si mesmo
docker exec web ps aux
PID   USER   COMMAND
  1   root   nginx: master process
# no host, é um processo comum com PID normal
ps aux | grep "nginx: master"
root  41783  ...  nginx: master process
# limite de memória = cgroup; estourou, o kernel mata (OOMKilled)
docker run --rm -m 64m progrium/stress --vm 1 --vm-bytes 128M

4.3 A pilha de execução: Docker → containerd → runc (OCI)

"Docker" hoje é um conjunto de peças padronizadas pela OCI (Open Container Initiative):

  1. docker CLI fala com o dockerd (daemon, API REST);
  2. dockerd delega ao containerd (runtime de alto nível: gerencia imagens, ciclo de vida) —
  3. que invoca o runc (runtime de baixo nível: cria namespaces/cgroups e executa o processo, seguindo a OCI Runtime Spec).

Consequências práticas que caem em entrevista: o Kubernetes usa containerd diretamente, sem Docker (o "dockershim" foi removido no K8s 1.24 — suas imagens Docker continuam funcionando porque seguem a OCI Image Spec); e alternativas como Podman (sem daemon, rootless por padrão) e runtimes isolados (gVisor, Kata Containers) plugam na mesma especificação.

Cai em entrevista

"O Kubernetes abandonou o Docker?" — resposta precisa: o K8s removeu o dockershim e passou a falar com runtimes via CRI (containerd, CRI-O). As imagens construídas com Docker seguem o padrão OCI e rodam normalmente. Quem responde isso com clareza demonstra entender a arquitetura, não só decorar comandos.

Exercícios do capítulo 4
  1. Rode um container e compare ps aux dentro e fora dele. Identifique o PID do mesmo processo nos dois mundos.
  2. Use docker run -m 32m --rm alpine sh -c 'cat /sys/fs/cgroup/memory.max' e confirme o limite imposto pelo cgroup.
  3. Explique por escrito, em até 8 linhas, por que dados gravados dentro de um container somem quando ele é removido.
✔ checkpoint — capítulo 4

MOD-05 · Parte II DOCKER

Docker essencial

O ciclo completo imagem → container → registry, e os comandos que você usará todos os dias da carreira.

Nível: básico

5.1 Vocabulário mínimo

  • Imagem: pacote imutável com o sistema de arquivos e metadados (comando padrão, portas, env). É o "molde".
  • Container: uma instância em execução (ou parada) de uma imagem. É o "bolo".
  • Registry: repositório de imagens — Docker Hub, GHCR, ECR (AWS), Harbor (self-hosted).
  • Tag: apelido de versão (nginx:1.27-alpine). Sem tag = latest, que é só um apelido móvel, não "a mais recente" garantida.

5.2 O ciclo de vida na prática

terminal — o dia a dia
# rodar: -d (fundo), --name, -p host:container, -e env, --rm (autolimpeza)
docker run -d --name api -p 8080:80 -e TZ=America/Sao_Paulo nginx:alpine
# inspecionar
docker ps -a                       # todos, inclusive parados
docker logs -f --tail 100 api      # logs ao vivo
docker exec -it api sh             # shell dentro do container
docker inspect api | less          # JSON completo: IP, mounts, config
docker stats                       # CPU/mem em tempo real
# ciclo
docker stop api && docker start api && docker restart api
docker rm -f api
# imagens e limpeza (decore: prune salva o disco de todo time)
docker images && docker pull redis:7-alpine && docker rmi redis:7-alpine
docker system prune -af --volumes  # CUIDADO: remove tudo que não está em uso
⚠️ Os 3 erros de iniciante que entregam inexperiência
  • Confundir a ordem de -p: é sempre host:container. -p 8080:80 = "porta 8080 da minha máquina → 80 do container".
  • Container que "morre sozinho": container vive enquanto o processo principal (PID 1) vive. docker run ubuntu sai na hora porque o bash sem TTY termina. Use -it para interativo ou um processo de longa duração.
  • Guardar dados no container: a camada de escrita morre com ele. Dados persistentes → volumes (cap. 7).

5.3 Publicando no registry

terminal — build, tag, push
docker build -t minha-api:1.0.0 .
# o nome completo é registry/namespace/repo:tag
docker tag minha-api:1.0.0 ghcr.io/seu-usuario/minha-api:1.0.0
docker login ghcr.io
docker push ghcr.io/seu-usuario/minha-api:1.0.0
# em produção, o supremo em reprodutibilidade é referenciar o digest:
docker pull ghcr.io/seu-usuario/minha-api@sha256:9f8e...
Exercícios do capítulo 5
  1. Suba um postgres:16-alpine com senha via -e, entre com docker exec -it ... psql -U postgres e crie uma tabela. Remova o container, suba de novo e constate que a tabela sumiu (motivação para o cap. 7).
  2. Suba dois nginx em portas diferentes do host e acesse ambos no navegador.
  3. Use docker inspect para descobrir o IP interno de um container e dê ping nele a partir de outro container na mesma rede.
✔ checkpoint — capítulo 5

MOD-06 · Parte II DOCKER

Dockerfile a fundo

Escrever imagens pequenas, rápidas de construir e seguras — o artefato que mais denuncia (ou comprova) senioridade num teste técnico.

Nível: intermediário → avançado

6.1 Instruções essenciais

InstruçãoFaz o quêNota de profissional
FROMImagem baseFixe versão: node:22-alpine, nunca node solto
WORKDIRDefine diretório de trabalhoUse sempre; evita cd e caminhos relativos frágeis
COPYCopia arquivos do contextoPrefira a ADD (que também baixa URLs e extrai tar — comportamento surpresa)
RUNExecuta comando no build (cria camada)Encadeie com && e limpe caches na mesma camada
ENV / ARGVariável em runtime / só no buildSegredo nunca em ENV/ARG — fica gravado na imagem
EXPOSEDocumenta portaÉ só metadado; quem publica é o -p
USERUsuário de execuçãoRodar como não-root é requisito de segurança básico
CMD / ENTRYPOINTComando padrão / executável fixoVer 6.4 — pergunta clássica de entrevista
HEALTHCHECKTeste periódico de saúdeDocker marca unhealthy; no K8s, quem faz isso são as probes

6.2 Cache de camadas: a regra de ouro

Cada instrução gera uma camada; o Docker reaproveita camadas até a primeira que mudou — dali para baixo, tudo é reconstruído. Logo: o que muda pouco vai para cima; o que muda sempre (seu código) vai para baixo.

Dockerfile — ordem certa para cache
FROM node:22-alpine
WORKDIR /app
# 1º só os manifestos: camada de dependências só invalida se eles mudarem
COPY package*.json ./
RUN npm ci --omit=dev
# 2º o código — muda a cada commit, mas as deps acima ficam em cache
COPY . .
CMD ["node", "server.js"]

Complete com um .dockerignore (irmão do .gitignore): node_modules, .git, *.env, logs. Reduz o contexto enviado ao daemon, acelera o build e evita vazar segredos para dentro da imagem.

6.3 Multi-stage: a técnica que todo teste técnico espera

Compile num estágio gordo, copie só o artefato final para um estágio mínimo. A imagem final não carrega compilador, código-fonte nem dependências de build:

Dockerfile — multi-stage (Go: de ~800 MB para ~15 MB)
# ---- estágio de build ----
FROM golang:1.23-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /bin/app ./cmd/api

# ---- estágio final: só o binário ----
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /bin/app /app
USER nonroot
EXPOSE 8080
ENTRYPOINT ["/app"]
  • Bases mínimas: alpine (~5 MB, musl libc — atenção a binários que esperam glibc), distroless (Google: sem shell nem gerenciador de pacotes — superfície de ataque mínima), ubuntu/debian-slim (compatibilidade máxima). Para Java, use eclipse-temurin:21-jre-alpine (JRE, não JDK) ou jlink para runtime customizado.
  • Menos pacotes = menos CVEs no scanner = imagem que passa na esteira de segurança da empresa.

6.4 ENTRYPOINT vs CMD — de uma vez por todas

  • ENTRYPOINT = o executável fixo. CMD = argumentos padrão (substituíveis na linha de comando).
  • Juntos: ENTRYPOINT ["python", "app.py"] + CMD ["--port", "8000"]docker run img --port 9000 troca só os argumentos.
  • Use sempre a forma exec ["binário","arg"], não a forma shell binário arg: na forma shell o processo vira filho de /bin/sh, que é o PID 1 e não repassa SIGTERM — seu app morre com SIGKILL sem graceful shutdown. Esse detalhe derruba candidatos em entrevistas de nível pleno.

6.5 Boas práticas que scanners e revisores cobram

  1. Não rode como root: crie usuário (RUN adduser -D app && USER app) ou use base nonroot.
  2. Uma responsabilidade por container — app e banco em containers separados, sempre.
  3. Segredos via BuildKit, nunca em camadas: RUN --mount=type=secret,id=npmrc npm ci monta o segredo só durante o comando, sem gravá-lo na imagem.
  4. Cache de pacotes com BuildKit: RUN --mount=type=cache,target=/root/.npm npm ci persiste o cache entre builds.
  5. Rotule a imagem: LABEL org.opencontainers.image.source, .revision — rastreabilidade em produção.
  6. Lint: rode hadolint Dockerfile no CI.
Cai em entrevista

Teste técnico padrão: "Otimize este Dockerfile". Checklist mental: fixar tag da base → ordenar camadas p/ cache (manifestos antes do código) → multi-stage → base mínima → usuário não-root → forma exec no ENTRYPOINT → .dockerignore. Aplicar os sete em voz alta, explicando o porquê de cada um, é resposta de aprovação.

Exercícios do capítulo 6
  1. Containerize uma API sua com multi-stage. Compare docker images antes/depois e registre a redução no README (ótimo material de portfólio).
  2. Meça o efeito do cache: mude 1 linha de código e cronometre o rebuild com a ordem errada (COPY . . antes das deps) e com a ordem certa.
  3. Rode hadolint e docker run --rm aquasec/trivy image sua-imagem; corrija ao menos 3 apontamentos.
  4. Prove o problema do PID 1: crie uma imagem com forma shell no CMD, rode, docker stop e cronometre os 10 s até o SIGKILL; converta para forma exec e compare.
✔ checkpoint — capítulo 6

MOD-07 · Parte II DOCKER

Redes, volumes e Compose

Fazer containers conversarem, persistirem dados e subirem em conjunto com um único comando.

Nível: intermediário

7.1 Redes

  • bridge (padrão): rede NAT interna. Na bridge padrão containers só se falam por IP; em uma bridge criada por você, ganham DNS automático pelo nome — por isso a regra prática é: crie sempre sua rede.
  • host: sem isolamento de rede; o container usa as portas do host diretamente (útil para performance/ferramentas, só em Linux).
  • none: sem rede — para jobs totalmente isolados.
terminal — DNS entre containers
docker network create app-net
docker run -d --name db  --network app-net -e POSTGRES_PASSWORD=dev postgres:16-alpine
docker run -d --name api --network app-net -e DATABASE_URL=postgres://postgres:dev@db:5432/postgres minha-api:1.0.0
# "db" resolve por DNS interno — nenhuma porta do banco exposta ao host
docker exec api ping -c1 db

Repare no padrão de segurança: o banco não tem -p — só a API é alcançável de fora. Expor o mínimo é hábito que entrevistadores notam.

7.2 Volumes e bind mounts

TipoSintaxeQuando usar
Volume nomeado-v pgdata:/var/lib/postgresql/dataDados de produção/persistentes; gerenciado pelo Docker, sobrevive ao container
Bind mount-v $(pwd)/src:/app/srcDesenvolvimento: código do host refletido ao vivo no container
tmpfs--tmpfs /tmpDados sensíveis/temporários só em memória
terminal — persistência de verdade
docker volume create pgdata
docker run -d --name db -v pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=dev postgres:16-alpine
# destrua o container, suba outro apontando pro mesmo volume: os dados continuam lá
docker rm -f db && docker run -d --name db2 -v pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=dev postgres:16-alpine

7.3 Docker Compose: o ambiente inteiro em um arquivo

Compose descreve um conjunto de serviços, redes e volumes em YAML — o padrão universal para desenvolvimento local e o primeiro "infra as code" da maioria das carreiras:

compose.yaml
services:
  api:
    build: .
    ports: ["8080:8080"]
    environment:
      DATABASE_URL: postgres://postgres:${DB_PASS:-dev}@db:5432/app
    depends_on:
      db: { condition: service_healthy }   # espera o banco ficar SAUDÁVEL, não só "iniciado"
    develop:
      watch:                                  # hot reload moderno: docker compose watch
        - action: sync
          path: ./src
          target: /app/src
  db:
    image: postgres:16-alpine
    environment: { POSTGRES_PASSWORD: ${DB_PASS:-dev}, POSTGRES_DB: app }
    volumes: [pgdata:/var/lib/postgresql/data]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 5
volumes:
  pgdata:
terminal — operando o Compose
docker compose up -d --build
docker compose ps && docker compose logs -f api
docker compose exec db psql -U postgres app
docker compose down            # -v também apaga volumes (cuidado)
💡 Os detalhes que diferenciam seu Compose

depends_on com condition: service_healthy (e não a forma simples, que só espera o container iniciar), variáveis com default ${VAR:-valor}, healthchecks em todos os serviços, e nenhum segredo real comitado (use .env no .gitignore). Um compose.yaml assim no seu portfólio comunica maturidade imediata.

Exercícios do capítulo 7
  1. Monte o stack API + Postgres + Redis com Compose, healthchecks em tudo e o banco sem porta exposta. Prove a persistência destruindo e recriando o stack.
  2. Configure docker compose watch e veja uma mudança de código refletir sem rebuild.
  3. Desafio: adicione um serviço adminer apenas em um arquivo compose.override.yaml de desenvolvimento, mantendo o arquivo base limpo para produção.
✔ checkpoint — capítulo 7

MOD-08 · Parte II DOCKER

Docker avançado e segurança

Hardening, builds multi-arquitetura e o que separa "sei Docker" de "opero Docker em produção".

Nível: avançado

8.1 Modelo de ameaça do container

O risco central: containers compartilham o kernel do host. Um escape de container (via kernel bug ou má configuração) compromete o host inteiro. As defesas são camadas:

  • Não-root dentro do container (cap. 6) + rootless Docker/Podman: o próprio daemon roda sem root, então mesmo um escape aterrissa num usuário comum do host.
  • Capabilities: root no Linux é um pacote de ~40 capacidades. Derrube todas e devolva só o necessário: docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE ...
  • --security-opt no-new-privileges: impede escalada via binários setuid.
  • Filesystem somente-leitura: --read-only --tmpfs /tmp — o app não consegue gravar malware em si mesmo.
  • seccomp/AppArmor: o perfil seccomp padrão do Docker já bloqueia ~44 syscalls perigosas; nunca use --privileged (desliga TUDO isso) a menos que saiba exatamente por quê.
  • Runtimes isolados para cargas não confiáveis: gVisor (kernel em userspace) e Kata (microVM por container).
Cai em entrevista (sênior)

"O que --privileged faz e por que é perigoso?" — dá todas as capabilities, desabilita seccomp/AppArmor e expõe os devices do host: o container vira praticamente root na máquina. Alternativas: conceder capability específica, montar apenas o device necessário, ou usar runtime isolado. Citar que "montar /var/run/docker.sock dentro de um container equivale a dar root no host" também rende pontos.

8.2 Escaneando imagens: Trivy no fluxo

terminal — scanning e SBOM
# vulnerabilidades (CVEs) na imagem — rode local e no CI
trivy image --severity HIGH,CRITICAL minha-api:1.0.0
# segredos e misconfigs no repositório
trivy fs --scanners secret,misconfig .
# SBOM: inventário de tudo que existe na imagem (exigência crescente pós-ataques de supply chain)
trivy image --format cyclonedx --output sbom.json minha-api:1.0.0

No pipeline, falhe o build em CRITICAL. Base mínima (distroless) + rebuild periódico (base atualizada) mantêm o relatório limpo.

8.3 BuildKit e buildx: builds modernos

  • Multi-arquitetura — obrigatório na era dos servidores ARM (AWS Graviton) e Macs Apple Silicon:
    docker buildx build --platform linux/amd64,linux/arm64 -t img:1.0 --push .
  • Cache remoto compartilhado entre runners de CI: --cache-to type=registry,ref=img:cache,mode=max.
  • Attestations: --provenance=true --sbom=true anexa procedência e SBOM à imagem (supply chain, cap. 20).

8.4 Operação: logs, limites e restart

terminal — container pronto para produção standalone
docker run -d --name api \
  --restart unless-stopped \
  --memory 512m --cpus 1.5 \
  --read-only --tmpfs /tmp \
  --cap-drop ALL --security-opt no-new-privileges \
  --log-driver json-file --log-opt max-size=10m --log-opt max-file=3 \
  ghcr.io/voce/minha-api@sha256:9f8e...

Sem max-size nos logs, o json-file cresce até derrubar o disco do host — um dos incidentes mais comuns do mundo real. Logs devem ir para stdout/stderr (nunca arquivo interno) para serem coletados pelo driver — princípio 12-factor que o K8s também assume.

Exercícios do capítulo 8
  1. Pegue sua API do cap. 6 e rode com o comando "produção standalone" acima. Documente cada flag no README.
  2. Rode Trivy na sua imagem, troque a base por distroless e compare o número de CVEs (screenshot para o portfólio).
  3. Faça um build multi-arch amd64+arm64 e verifique o manifesto com docker buildx imagetools inspect.
  4. Desafio: demonstre (em VM descartável!) por que montar o docker.sock num container é perigoso, criando um container que lista os containers do host.
✔ checkpoint — capítulo 8

MOD-09 · Parte III K8S

Arquitetura do Kubernetes

Entender as peças do cluster e o modelo declarativo — a base para debugar qualquer coisa depois.

Nível: intermediário

9.1 Por que orquestração

Docker resolve um container em uma máquina. Produção exige: distribuir centenas de containers por dezenas de máquinas, reiniciar o que morre, escalar sob carga, balancear tráfego, fazer rollout sem downtime e voltar atrás quando dá errado. O Kubernetes (K8s, do projeto interno Borg do Google, aberto em 2014) automatiza tudo isso a partir de um princípio único:

💡 O modelo mental que explica TODO o Kubernetes

Você declara o estado desejado ("quero 3 réplicas desta imagem, com este limite de memória") em YAML. Controladores em loops de reconciliação comparam continuamente o estado atual com o desejado e agem para convergir. Pod morreu? O controlador vê 2 ≠ 3 e cria outro. Você nunca ordena "suba um container"; você descreve o mundo que quer e o cluster o persegue. Internalize isso e todo o resto da apostila vira consequência.

9.2 Control plane e worker nodes

ComponenteOndeFunção
kube-apiservercontrol planePorta de entrada única: valida e serve a API REST. Tudo (kubectl, controladores, kubelet) fala com ele
etcdcontrol planeBanco chave-valor distribuído (Raft) com TODO o estado do cluster. Perdeu o etcd sem backup = perdeu o cluster
kube-schedulercontrol planeDecide em qual node cada Pod novo roda (recursos, afinidades, taints)
controller-managercontrol planeRoda os loops de reconciliação (Deployment, ReplicaSet, Node, Job…)
kubeletcada nodeAgente que garante que os containers dos Pods designados estão rodando (via CRI → containerd)
kube-proxycada nodeImplementa Services na rede (iptables/IPVS); em CNIs modernos como Cilium, é substituído por eBPF

Interfaces plugáveis (vocabulário de entrevista): CRI (runtime: containerd, CRI-O), CNI (rede: Cilium, Calico, flannel), CSI (storage: EBS, Ceph…).

9.3 O caminho de um kubectl apply (pergunta clássica de entrevista)

  1. kubectl envia o YAML ao API server, que autentica, autoriza (RBAC), passa por admission controllers e grava no etcd;
  2. o controller-manager nota o Deployment novo → cria um ReplicaSet → que cria objetos Pod (ainda sem node);
  3. o scheduler vê Pods sem node e escolhe o melhor node para cada um;
  4. o kubelet do node escolhido vê o Pod atribuído a ele, puxa a imagem e inicia os containers via containerd;
  5. kubelet reporta status ao API server — e os loops seguem vigiando para sempre.

9.4 Distribuições e onde o mercado roda K8s

  • Gerenciados (o padrão): EKS (AWS — mais vagas no Brasil), GKE (Google — mais maduro), AKS (Azure). O provedor opera o control plane; você opera nodes e workloads.
  • Locais/edge: kind e minikube (estudo), k3s (leve, edge/IoT), Talos (SO imutável só-K8s).
  • On-prem corporativo: OpenShift (Red Hat), Rancher, kubeadm puro.
Exercícios do capítulo 9
  1. No seu cluster kind: kubectl get pods -n kube-system e identifique cada componente da tabela 9.2.
  2. Desenhe (papel mesmo) o fluxo do kubectl apply sem consultar. Refaça até sair fluido — é pergunta de entrevista.
  3. Rode kubectl get --raw /healthz e kubectl api-resources | head -30 para sentir a API por baixo do kubectl.
✔ checkpoint — capítulo 9

MOD-10 · Parte III K8S

Workloads: de Pods a Deployments

Os objetos que rodam sua aplicação, os comandos kubectl do dia a dia e rollouts sem downtime.

Nível: intermediário

10.1 Pod: a menor unidade

Um Pod é um grupo de 1+ containers que compartilham rede (mesmo IP, falam entre si por localhost) e volumes. Regra: 1 container principal por Pod; containers extras são sidecars (proxy, coletor de logs) ou init containers (preparação que roda antes do principal). Pods são descartáveis: têm IP efêmero e não se recriam sozinhos — por isso você quase nunca cria Pods diretamente.

10.2 Deployment: o objeto do dia a dia

O Deployment gerencia ReplicaSets (que mantêm N réplicas de um Pod) e orquestra atualizações entre versões:

deployment.yaml — o esqueleto que você escreverá mil vezes
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  labels: { app: api }
spec:
  replicas: 3
  selector:
    matchLabels: { app: api }      # liga o Deployment aos Pods — deve casar com o template
  template:                        # o "molde" do Pod
    metadata:
      labels: { app: api }
    spec:
      containers:
        - name: api
          image: ghcr.io/voce/minha-api:1.0.0
          ports: [{ containerPort: 8080 }]

Labels são o sistema circulatório do K8s: selectors de Deployments, Services e NetworkPolicies encontram Pods por labels, nunca por nome. Errar o matchLabelstemplate.labels é o erro de YAML mais clássico de iniciante.

10.3 kubectl que você usa o dia inteiro

terminal — kit de sobrevivência kubectl
kubectl apply -f deployment.yaml
kubectl get pods -o wide                 # -w acompanha ao vivo
kubectl describe pod api-7d9c...         # eventos no rodapé = ouro do debug
kubectl logs -f deploy/api               # --previous = logs do container que crashou
kubectl exec -it deploy/api -- sh
kubectl port-forward svc/api 8080:80     # acessar do seu notebook sem expor nada
kubectl get events --sort-by=.lastTimestamp
# gerar YAML sem decorar (técnica oficial de prova CKA/CKAD):
kubectl create deployment api --image=nginx --replicas=3 --dry-run=client -o yaml > deploy.yaml
kubectl explain deployment.spec.strategy # documentação embutida de qualquer campo

10.4 Rollout, rollback e a mecânica do zero-downtime

terminal — ciclo de release
kubectl set image deploy/api api=ghcr.io/voce/minha-api:1.1.0
kubectl rollout status deploy/api
Waiting for deployment "api" rollout to finish: 1 out of 3 new replicas...
kubectl rollout history deploy/api
kubectl rollout undo deploy/api          # --to-revision=2 para versão específica

Estratégia padrão RollingUpdate com maxSurge: 25% (quantos Pods a mais durante a troca) e maxUnavailable: 25% (quantos podem faltar). O rollout só avança conforme os Pods novos ficam Ready — o que depende das probes do próximo capítulo; sem readiness probe, "zero downtime" é ilusão: o K8s manda tráfego para Pods que ainda estão subindo.

Cai em entrevista

"Diferença entre Pod, ReplicaSet e Deployment?" — Pod roda containers; ReplicaSet mantém N cópias do Pod; Deployment gerencia ReplicaSets para permitir rollout/rollback declarativo (a cada versão nova ele cria um RS novo e escala o antigo a zero — por isso rollout undo é instantâneo). Emende: "na prática só crio Deployments; RS e Pods são gerenciados por ele".

Exercícios do capítulo 10
  1. Faça o deploy da sua API (imagem do cap. 6) com 3 réplicas no kind. Delete um Pod na mão e observe a reconciliação recriá-lo.
  2. Atualize a imagem para uma tag inexistente, observe o rollout travar (ImagePullBackOff) e faça rollout undo. Note que as réplicas antigas nunca saíram do ar.
  3. Use kubectl explain para descobrir o que faz spec.revisionHistoryLimit, sem Google.
✔ checkpoint — capítulo 10

MOD-11 · Parte III K8S

Configuração, probes e recursos

ConfigMaps, Secrets, health checks e requests/limits — o trio que define se sua aplicação é "cloud native" de verdade.

Nível: intermediário → avançado

11.1 ConfigMaps e Secrets

Configuração fora da imagem (12-factor): a mesma imagem roda em dev/staging/prod mudando só a config.

terminal + yaml — config e segredo
kubectl create configmap api-config --from-literal=LOG_LEVEL=info --from-file=app.properties
kubectl create secret generic api-secret --from-literal=DB_PASS='s3nh4'
# consumindo no Pod:
containers:
  - name: api
    envFrom:
      - configMapRef: { name: api-config }   # tudo vira env var
    env:
      - name: DB_PASS
        valueFrom:
          secretKeyRef: { name: api-secret, key: DB_PASS }
    volumeMounts:                             # ou como arquivo (atualiza sem restart)
      - { name: cfg, mountPath: /etc/app }
volumes:
  - name: cfg
    configMap: { name: api-config }
⚠️ Secret do K8s NÃO é criptografado por padrão

É apenas base64 (codificação, não criptografia) dentro do etcd. Em produção: habilite encryption at rest no etcd, restrinja leitura de Secrets via RBAC e — padrão de mercado — use um cofre externo (AWS Secrets Manager, Vault) integrado via External Secrets Operator ou CSI driver. Dizer "Secret é só base64" na entrevista é ponto garantido.

11.2 Probes: como o K8s sabe que seu app está vivo

ProbePergunta que respondeSe falha…
startup"Já terminou de subir?"Segura as outras probes (apps lentos, ex. Java); estourou o prazo → restart
readiness"Pode receber tráfego AGORA?"Pod sai dos endpoints do Service (sem restart) — controla rollout e picos
liveness"Travou sem esperança?"kubelet reinicia o container
yaml — probes bem configuradas
containers:
  - name: api
    startupProbe:
      httpGet: { path: /healthz, port: 8080 }
      failureThreshold: 30      # 30 × 2s = até 60s para subir
      periodSeconds: 2
    readinessProbe:
      httpGet: { path: /readyz, port: 8080 }   # checa dependências (banco ok?)
      periodSeconds: 5
    livenessProbe:
      httpGet: { path: /healthz, port: 8080 }  # leve e SEM checar dependências
      periodSeconds: 10
      failureThreshold: 3
⚠️ O erro clássico: liveness que checa o banco

Se a liveness testa a conexão com o banco e o banco cai, o K8s reinicia todos os seus Pods em loop — transformando um incidente de dependência em cascata total. Regra: liveness = só o processo ("estou deadlocked?"); readiness = dependências ("consigo servir?"). Essa distinção é pergunta recorrente em entrevistas pleno/sênior.

11.3 Requests, limits e QoS

  • requests = o que o Pod reserva (base do scheduler para escolher node e do HPA para calcular %).
  • limits = teto. CPU acima do limite → throttling (lentidão); memória acima → OOMKilled (morte).
yaml — recursos
    resources:
      requests: { cpu: 250m, memory: 256Mi }   # 250m = 0,25 vCPU
      limits:   { memory: 512Mi }               # limite de CPU é frequentemente omitido…

Prática de mercado atual: defina memória com request = limit (previsível; OOM é melhor que swap invisível) e CPU só com request, sem limit — limites de CPU causam throttling mesmo com CPU sobrando no node, uma das causas nº 1 de latência misteriosa. QoS resultante: Guaranteed (requests=limits em tudo), Burstable (o comum), BestEffort (sem nada — primeiro a morrer sob pressão do node).

11.4 Namespaces e organização

Namespaces particionam o cluster (por time/ambiente/produto): kubectl create ns staging, kubectl -n staging get pods. Com eles vêm ResourceQuota (teto de CPU/mem por namespace) e LimitRange (defaults por container) — governança básica de plataforma. Contextos: kubectl config use-context troca de cluster; kubens/kubectx são os utilitários queridinhos do dia a dia.

Exercícios do capítulo 11
  1. Adicione ao seu Deployment: ConfigMap (env + arquivo), Secret, as 3 probes e resources. Este YAML "completo" será seu template de carreira.
  2. Provoque um OOMKilled: limite de 32Mi numa app que aloca mais; diagnostique com describe (Last State: OOMKilled, Exit Code 137).
  3. Simule o desastre da liveness com dependência: crie um endpoint /healthz que retorna 500 e observe o CrashLoop do Deployment inteiro.
  4. Crie os namespaces dev e prod com ResourceQuota diferente em cada.
✔ checkpoint — capítulo 11

MOD-12 · Parte III K8S

Rede: Services, Ingress e policies

Como o tráfego chega aos Pods — de dentro do cluster, da internet, e como restringir quem fala com quem.

Nível: avançado

12.1 Services: IP estável para Pods efêmeros

Pods nascem e morrem com IPs novos; o Service dá um nome DNS e um IP virtual estáveis que balanceiam para os Pods selecionados por label:

TipoO que fazUso típico
ClusterIP (padrão)IP interno; DNS svc.namespace.svc.cluster.localComunicação interna serviço↔serviço
NodePortAbre porta 30000–32767 em todo nodeTestes/labs; raramente produção
LoadBalancerProvisiona LB da nuvem (um por Service)Expor 1 serviço L4; caro em escala
Headless (clusterIP: None)DNS retorna os IPs dos Pods diretamenteStatefulSets, clientes que descobrem peers (bancos)
service.yaml + debug de conectividade
apiVersion: v1
kind: Service
metadata: { name: api }
spec:
  selector: { app: api }        # casa com as labels dos PODS
  ports: [{ port: 80, targetPort: 8080 }]
# --- debug: o Service tem endpoints? (a pergunta nº1 quando "não conecta")
kubectl get endpointslices -l kubernetes.io/service-name=api
# vazio? → selector errado OU pods não-Ready (readiness!). Teste de dentro:
kubectl run tmp --rm -it --image=nicolaka/netshoot -- curl -s http://api.default/healthz

12.2 Ingress: HTTP(S) da internet para dentro

Um Ingress Controller (NGINX Ingress, Traefik, ALB Controller na AWS) é um proxy L7 que roda no cluster atrás de um LoadBalancer e roteia por host/caminho para os Services — TLS terminado ali, certificados automatizados pelo cert-manager + Let's Encrypt:

ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls: [{ hosts: [app.exemplo.com.br], secretName: web-tls }]
  rules:
    - host: app.exemplo.com.br
      http:
        paths:
          - { path: /api, pathType: Prefix, backend: { service: { name: api, port: { number: 80 }}}}
          - { path: /,    pathType: Prefix, backend: { service: { name: front, port: { number: 80 }}}}

Gateway API é o sucessor oficial do Ingress (GA desde 2023): separa papéis (infra define Gateway, dev define HTTPRoute), suporta TCP/gRPC e split de tráfego nativo. Migração é gradual; cite-o em entrevista como "para onde o ecossistema está indo".

12.3 NetworkPolicies: firewall entre Pods

Por padrão, todo Pod fala com todo Pod — inaceitável em ambientes regulados. NetworkPolicies (aplicadas pelo CNI; Cilium/Calico suportam, flannel não) definem allow-lists:

networkpolicy.yaml — só a API acessa o banco
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: db-allow-api, namespace: prod }
spec:
  podSelector: { matchLabels: { app: db } }   # a quem a regra se aplica
  policyTypes: [Ingress]
  ingress:
    - from: [{ podSelector: { matchLabels: { app: api } } }]
      ports: [{ port: 5432 }]

Padrão sênior: começar com default-deny por namespace e liberar fluxo a fluxo (zero trust). No dia a dia de debug de rede: nicolaka/netshoot é o canivete (curl, dig, tcpdump) e kubectl get networkpolicy é a primeira suspeita quando "parou de conectar do nada".

Cai em entrevista

"Service ClusterIP não responde — como debuga?" Roteiro: 1) kubectl get endpointslices (tem endpoints? não → selector errado ou Pods não-Ready); 2) Pod responde direto? (kubectl exec + curl no IP do Pod); 3) DNS resolve? (nslookup api.default.svc.cluster.local de um netshoot); 4) NetworkPolicy bloqueando?; 5) targetPort confere com a porta real do container? Decorar essa escada resolve 95% dos casos reais.

Exercícios do capítulo 12
  1. Exponha sua API com ClusterIP + Ingress NGINX no kind (o kind tem guia oficial de ingress; use extraPortMappings). Acesse via curl -H "Host: app.local" localhost.
  2. Quebre de propósito: mude o selector do Service e siga o roteiro de debug até o diagnóstico. Repita quebrando o targetPort.
  3. Instale o Cilium no kind e aplique default-deny + liberação seletiva. Prove com netshoot que o bloqueio funciona.
✔ checkpoint — capítulo 12

MOD-13 · Parte III K8S

Storage e workloads com estado

PV/PVC, StatefulSets e os outros tipos de workload — Jobs, CronJobs e DaemonSets.

Nível: avançado

13.1 A cadeia de storage: PV → PVC → Pod

  • PersistentVolume (PV): o disco em si (EBS, NFS, Ceph…), recurso do cluster.
  • PersistentVolumeClaim (PVC): o pedido do desenvolvedor ("quero 10Gi ReadWriteOnce"). O Pod monta o PVC, nunca o PV — desacoplamento entre app e infraestrutura.
  • StorageClass: perfis de provisionamento dinâmico via CSI (ex.: gp3 na AWS) — o PVC nasce e o disco é criado sozinho. É assim que 99% do mercado opera.
pvc.yaml + uso no Pod
apiVersion: v1
kind: PersistentVolumeClaim
metadata: { name: data }
spec:
  accessModes: [ReadWriteOnce]       # RWO: 1 node; RWX (NFS/EFS): vários nodes
  storageClassName: standard
  resources: { requests: { storage: 10Gi } }
# no Pod:
volumes: [{ name: data, persistentVolumeClaim: { claimName: data } }]

Conceitos que caem em prova/entrevista: reclaimPolicy (Delete apaga o disco junto com o PVC; Retain preserva — use Retain para dados críticos) e volumeBindingMode: WaitForFirstConsumer (cria o disco na zona certa só quando o Pod é agendado — evita Pod numa AZ e disco em outra).

13.2 StatefulSet: quando identidade importa

Deployment trata Pods como gado intercambiável. Bancos e sistemas de consenso precisam de identidade estável — é o que o StatefulSet dá:

  • Nomes previsíveis e ordenados: postgres-0, postgres-1… (criação/remoção em ordem);
  • DNS estável por Pod via Service headless: postgres-0.postgres.prod.svc — réplicas se encontram;
  • volumeClaimTemplates: cada réplica ganha seu próprio PVC, que a segue em restarts e sobrevive à remoção do Pod.
Banco dentro do K8s: a resposta madura

Pergunta de entrevista com pegadinha: "você rodaria Postgres no Kubernetes?" Resposta equilibrada: "Se há serviço gerenciado disponível (RDS, Cloud SQL), ele costuma vencer em custo operacional. Rodar no K8s é viável e cada vez mais comum, mas com um operator maduro (CloudNativePG, Zalando) cuidando de failover, backup e réplicas — nunca um StatefulSet artesanal." Mostra critério, não dogma.

13.3 DaemonSet, Job e CronJob

WorkloadGaranteCasos reais
DaemonSet1 Pod por node (inclusive nodes novos)Coletor de logs (Fluent Bit), agente de métricas (node-exporter), CNI
JobExecução até completar N vezes com sucessoMigração de banco, processamento em lote (backoffLimit, parallelism)
CronJobJob em agenda cronBackup noturno, relatórios (concurrencyPolicy: Forbid evita sobreposição)
cronjob.yaml — backup diário
apiVersion: batch/v1
kind: CronJob
metadata: { name: pg-backup }
spec:
  schedule: "0 3 * * *"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      backoffLimit: 2
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: dump
              image: postgres:16-alpine
              command: ["sh","-c","pg_dump $DATABASE_URL | gzip > /backup/$(date +%F).sql.gz"]
Exercícios do capítulo 13
  1. Suba um Postgres via StatefulSet com volumeClaimTemplates no kind, grave dados, delete o Pod e comprove que postgres-0 volta com os mesmos dados.
  2. Escreva um Job de migração que roda antes do deploy da API (padrão comum: Job + initContainer aguardando).
  3. Crie o CronJob de backup acima apontando para seu Postgres e verifique execuções com kubectl get jobs.
  4. Pesquisa dirigida: leia a página do CloudNativePG e liste 5 coisas que o operator automatiza que um StatefulSet puro não faz.
✔ checkpoint — capítulo 13

MOD-14 · Parte III K8S

Escalonamento e agendamento

HPA, autoscaling de nodes e o controle fino de onde cada Pod roda — o coração de custo e resiliência.

Nível: avançado

14.1 As três dimensões de autoscaling

CamadaFerramentaEscala o quê
Horizontal (Pods)HPANúmero de réplicas por métrica (CPU %, memória, métricas custom via Prometheus Adapter/KEDA)
Vertical (Pods)VPAAjusta requests/limits; ótimo em modo "recomendação" para dimensionar
NodesCluster Autoscaler / KarpenterCria/remove máquinas quando Pods ficam Pending ou nodes ociosos
hpa.yaml + teste de carga
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: api }
spec:
  scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: api }
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Resource
      resource: { name: cpu, target: { type: Utilization, averageUtilization: 70 } }
  behavior:                     # evita flapping: desce devagar
    scaleDown: { stabilizationWindowSeconds: 300 }
# requer metrics-server instalado; teste com gerador de carga:
kubectl run load --rm -it --image=busybox -- sh -c 'while true; do wget -qO- http://api; done'
kubectl get hpa -w

Pontos de senioridade: o HPA calcula sobre requests (requests errados = autoscaling errado); CPU é métrica ruim para apps I/O-bound — use KEDA para escalar por fila (SQS, Kafka lag) ou requisições, inclusive até zero. Karpenter (AWS, hoje CNCF) substituiu o Cluster Autoscaler como padrão: provisiona o node ideal em segundos direto da API, consolida nodes ociosos e casa com Spot — é assunto quente de FinOps em entrevistas.

14.2 Direcionando o scheduler

  • nodeSelector / nodeAffinity: "este Pod PRECISA/prefere rodar em nodes com tal label" (ex.: gpu=true, topology.kubernetes.io/zone).
  • Taints + tolerations: o inverso — o node repele Pods, salvo os que toleram. Uso clássico: reservar pool de GPU (kubectl taint nodes gpu1 gpu=true:NoSchedule), nodes Spot, control plane.
  • podAntiAffinity / topologySpreadConstraints: espalhar réplicas por nodes/zonas — sem isso, "3 réplicas" podem cair juntas com um único node. topologySpreadConstraints é a forma moderna e mais barata para o scheduler.
  • PriorityClass + preemption: cargas críticas despejam as de baixa prioridade quando falta recurso.

14.3 PodDisruptionBudget: sobrevivendo à manutenção

pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: { name: api }
spec:
  minAvailable: 2
  selector: { matchLabels: { app: api } }

Durante kubectl drain (upgrade de node, consolidação do Karpenter), o PDB impede que despejos voluntários derrubem mais réplicas do que o permitido. Sem PDB, upgrades de cluster causam downtime — e "por que meu app caiu no upgrade do EKS?" é história real de mercado. Complete o combo de resiliência com terminationGracePeriodSeconds + tratamento de SIGTERM no app (graceful shutdown, fechando conexões antes de morrer) e preStop hook com sleep curto para o LB parar de mandar tráfego antes do encerramento.

Cai em entrevista (sênior)

"Como você garante alta disponibilidade de um Deployment?" Resposta em camadas: réplicas ≥ 2 + topologySpreadConstraints por zona + PDB + probes corretas + HPA com comportamento estável + graceful shutdown + requests bem dimensionados. Quem responde só "aumento as réplicas" fica no básico.

Exercícios do capítulo 14
  1. Instale o metrics-server no kind, aplique o HPA acima e provoque o scale-up com carga; observe o scale-down lento pelo behavior.
  2. Crie um kind com 3 nodes, aplique taint num deles e prove que só Pods com toleration entram lá.
  3. Adicione topologySpreadConstraints ao seu Deployment e confirme com kubectl get pods -o wide a distribuição.
  4. Faça kubectl drain de um node com e sem PDB e compare o comportamento.
✔ checkpoint — capítulo 14

MOD-15 · Parte III K8S

Segurança no Kubernetes

RBAC, Pod Security e policy-as-code — os temas da certificação CKS e das vagas em bancos e fintechs.

Nível: avançado

15.1 Os 4 Cs

Modelo mental oficial: segurança em camadas — Cloud (conta, rede, IAM) → Cluster (API server, RBAC, etcd cifrado) → Container (imagem mínima, scanning, não-root) → Code (dependências, segredos). Cada camada assume que a de fora pode falhar.

15.2 RBAC: quem pode fazer o quê

rbac.yaml — dev só lê no namespace prod
apiVersion: rbac.authorization.k8s.io/v1
kind: Role                          # Role = 1 namespace; ClusterRole = cluster todo
metadata: { name: reader, namespace: prod }
rules:
  - apiGroups: ["", "apps"]
    resources: [pods, deployments, pods/log]
    verbs: [get, list, watch]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata: { name: devs-read, namespace: prod }
subjects: [{ kind: Group, name: devs, apiGroup: rbac.authorization.k8s.io }]
roleRef: { kind: Role, name: reader, apiGroup: rbac.authorization.k8s.io }
# testar permissões (comando de prova CKA/CKS):
kubectl auth can-i delete pods -n prod --as=maria
kubectl auth can-i --list -n prod
  • ServiceAccounts são as identidades dos Pods. Regra: cada workload com sua SA, automountServiceAccountToken: false quando a app não fala com a API — o token montado por padrão é vetor clássico de escalada.
  • Perigos a reconhecer de vista: ClusterRoleBinding a cluster-admin para SAs; permissão de criar Pods (permite montar o token de qualquer SA do namespace = escalada); verbo escalate/impersonate.
  • Na nuvem, associe SA a papéis de IAM (IRSA/Pod Identity no EKS, Workload Identity no GKE) em vez de chaves estáticas.

15.3 Pod Security e policy-as-code

  • Pod Security Standards (substituíram as PodSecurityPolicies, removidas na 1.25 — pegadinha de entrevista): três perfis — privileged, baseline, restricted — aplicados por label no namespace: kubectl label ns prod pod-security.kubernetes.io/enforce=restricted
  • O perfil restricted exige o securityContext canônico que você deve saber escrever de cor:
yaml — securityContext "restricted"
    securityContext:
      runAsNonRoot: true
      runAsUser: 10001
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities: { drop: ["ALL"] }
      seccompProfile: { type: RuntimeDefault }
  • Admission policy engines: para regras além do Pod Security ("toda imagem vem do nosso registry", "todo Deployment tem limits"), o mercado usa Kyverno (políticas em YAML, curva suave — o mais adotado) ou OPA Gatekeeper (linguagem Rego, mais poderoso). Desde o K8s 1.30, ValidatingAdmissionPolicy nativa (expressões CEL) cobre casos simples sem instalar nada.
  • Runtime security: Falco detecta comportamento anômalo ao vivo (shell aberto em container de produção, leitura de /etc/shadow) via eBPF.
  • Auditoria e etcd: habilite audit logs do API server e encryption at rest; backup do etcd (etcdctl snapshot save) é tópico garantido da CKA.
Cai em entrevista (segurança)

"Como você limitaria o raio de explosão de um Pod comprometido?" Resposta em camadas: imagem distroless não-root → securityContext restricted → SA dedicada sem token automontado e IAM mínimo → NetworkPolicy default-deny → namespace com quota → Falco alertando em runtime. Cada camada citada com o porquê = candidato de outro nível.

Exercícios do capítulo 15
  1. Crie um usuário fictício com Role somente-leitura e prove com kubectl auth can-i --as que ele não deleta Pods.
  2. Aplique enforce=restricted num namespace e tente subir um Pod sem securityContext — leia e entenda a rejeição.
  3. Instale o Kyverno e escreva uma policy que bloqueia imagens com tag latest. Teste.
  4. Desafio CKS: num cluster kind, faça snapshot do etcd e restaure-o.
✔ checkpoint — capítulo 15

MOD-16 · Parte III K8S

Helm e Kustomize

Empacotar e reutilizar YAML entre ambientes — ninguém mantém manifests duplicados na mão.

Nível: intermediário → avançado

16.1 Helm: o gerenciador de pacotes do K8s

Charts são pacotes de manifests com templates Go + valores parametrizáveis. Dois usos: consumir charts prontos (Prometheus, ingress-nginx, cert-manager — é assim que se instala quase tudo) e criar o chart da sua aplicação.

terminal — consumindo e criando charts
# consumir: repositório → install com valores próprios
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install monitor prometheus-community/kube-prometheus-stack -n monitoring --create-namespace -f my-values.yaml
helm upgrade monitor ... && helm rollback monitor 1 && helm uninstall monitor
# criar o seu
helm create minha-api
helm template minha-api ./minha-api | less     # renderiza local: SEMPRE revise antes
helm install api ./minha-api -f values-prod.yaml --atomic  # --atomic: falhou, reverte sozinho
trecho de template + values
# values.yaml
replicaCount: 3
image: { repository: ghcr.io/voce/minha-api, tag: "1.2.0" }
# templates/deployment.yaml (trecho)
replicas: {{ .Values.replicaCount }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"

16.2 Kustomize: patches sem templates

Embutido no kubectl (kubectl apply -k): uma base de YAML puro + overlays que aplicam patches por ambiente. Sem linguagem de template — só YAML válido, fácil de revisar:

estrutura + overlay de produção
app/
├── base/               (deployment.yaml, service.yaml, kustomization.yaml)
└── overlays/
    ├── dev/
    └── prod/kustomization.yaml:
resources: [../../base]
namespace: prod
replicas: [{ name: api, count: 5 }]
images: [{ name: minha-api, newTag: "1.2.0" }]
patches:
  - patch: |-
      - op: add
        path: /spec/template/spec/containers/0/resources/limits
        value: { memory: 1Gi }
    target: { kind: Deployment, name: api }

16.3 Qual usar? (pergunta certa de entrevista)

HelmKustomize
ModeloTemplates + valores; pacote versionado com lifecycle (install/upgrade/rollback)Base + patches; sem estado, sem templates
Brilha emDistribuir software (público/interno), muita parametrização, dependênciasVariação simples entre ambientes dos SEUS apps; legibilidade
Dói emTemplates ilegíveis quando crescem; depurar {{ }} aninhadoLógica condicional complexa; distribuição para terceiros

Resposta madura: "não é ou/ou — consumo software de terceiros via Helm, gerencio meus apps com Kustomize (ou um chart interno único), e em GitOps o Argo CD renderiza ambos". Isso reflete o mercado real.

Exercícios do capítulo 16
  1. Crie um chart Helm para sua API com replicas, imagem, resources e ingress parametrizados; instale com values de dev e de prod.
  2. Refaça a mesma separação com Kustomize base + overlays e compare a legibilidade dos diffs no Git.
  3. Instale o kube-prometheus-stack via Helm no kind (você usará no cap. 19).
✔ checkpoint — capítulo 16

MOD-17 · Parte III K8S

Muito avançado: Operators, GitOps e service mesh

Os três temas que definem o teto técnico das vagas sênior/staff em plataformas Kubernetes.

Nível: muito avançado

17.1 CRDs e Operators: estendendo o Kubernetes

Uma CustomResourceDefinition ensina à API do K8s um tipo novo (ex.: kind: PostgresCluster). Um Operator = CRD + controlador que reconcilia esse tipo, codificando conhecimento operacional humano: o operator do Postgres sabe fazer failover, backup e upgrade; o do cert-manager sabe renovar certificados. Todo o ecossistema moderno (Argo CD, Prometheus Operator, Istio, cert-manager) é feito de operators — entender o padrão é entender o ecossistema.

terminal — explorando CRDs
kubectl get crds
kubectl explain certificate.spec --recursive | head -30
# o loop de um controlador, em pseudocódigo:
for each evento em MeuRecurso:
    desejado = spec do recurso
    atual    = observar o mundo real
    if atual != desejado: agir()   # criar/ajustar recursos filhos
    atualizar status

Para construir operators: Kubebuilder/controller-runtime (Go, padrão da indústria) ou Operator SDK. Saber escrever um operator simples é diferencial raro e muito bem pago; para a maioria das carreiras, saber operar e depurar operators (ler status, conditions e logs do controlador) já coloca você no topo.

17.2 GitOps com Argo CD

GitOps leva o modelo declarativo até o fim: o Git é a única fonte de verdade; um agente no cluster reconcilia continuamente o cluster com o repositório. Ninguém roda kubectl apply em produção — muda-se um PR.

  • Pull, não push: o Argo CD (ou Flux) dentro do cluster puxa do Git — o CI não precisa de credenciais do cluster (ganho enorme de segurança).
  • Drift detection: alguém mexeu na mão? O Argo marca OutOfSync e (se configurado) reverte sozinho — selfHeal: true.
  • Auditoria e rollback grátis: o histórico de produção é git log; rollback é git revert.
application.yaml — Argo CD
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata: { name: api-prod, namespace: argocd }
spec:
  project: default
  source:
    repoURL: https://github.com/voce/gitops-repo
    path: apps/api/overlays/prod
    targetRevision: main
  destination: { server: https://kubernetes.default.svc, namespace: prod }
  syncPolicy:
    automated: { prune: true, selfHeal: true }

Fluxo completo de mercado: push no repo da app → CI testa e publica imagem por SHA → CI (ou Argo CD Image Updater) abre PR no repo de GitOps trocando a tag no overlay → merge → Argo sincroniza. Para canário/blue-green automatizados por métricas, o complemento é Argo Rollouts. Flux é a alternativa equivalente (mais unix-like, sem UI própria); saiba comparar os dois.

17.3 Service mesh

Um mesh injeta um proxy ao lado de cada Pod (ou no kernel, via eBPF) e move para a plataforma o que antes era código em cada app:

  • mTLS automático entre todos os serviços (identidade + criptografia — requisito em ambientes regulados);
  • Gerência de tráfego: retries, timeouts, circuit breaking, espelhamento, splits % (base de canário fino);
  • Observabilidade uniforme: métricas RED e traces de toda chamada serviço↔serviço sem tocar no código.

Opções: Istio (o mais completo e mais cobrado em vagas; modo ambient sem sidecar reduziu muito o custo), Linkerd (simplicidade e leveza) e Cilium Service Mesh (eBPF, sem sidecars). Resposta sênior sobre "devo usar mesh?": "só quando a escala de microsserviços justifica o custo operacional; para mTLS e retries em escala pequena, um bom ingress + bibliotecas resolvem" — critério vale mais que hype.

17.4 Troubleshooting profundo: a árvore de decisão

o mapa mental de plantão (decore)
Pod Pending      → describe: sem recursos? (requests × capacidade) | taint sem toleration?
                   | PVC não vinculado? | quota estourada?
ImagePullBackOff → nome/tag existem? registry privado sem imagePullSecret? rate limit do Hub?
CrashLoopBackOff → logs --previous! | exit 1: erro do app | 137: OOMKilled ou SIGKILL
                   | 143: SIGTERM | config/secret faltando? | comando errado?
Running não-Ready→ readiness falhando: endpoint certo? dependência fora? porta certa?
OOMKilled        → subir memory limit? vazamento? (compare métricas ao longo do tempo)
Service sem resp.→ escada do cap. 12 (endpoints → pod direto → DNS → netpol → targetPort)
Node NotReady    → describe node: disco? memória? kubelet? | drain + investigar

Ferramentas de gente grande: kubectl debug (containers efêmeros para inspecionar Pod distroless sem shell: kubectl debug -it pod/api --image=netshoot --target=api), k9s (TUI que multiplica sua velocidade), stern (logs agregados de múltiplos Pods), e kubectl get events -A --sort-by=.lastTimestamp como primeiro reflexo em qualquer incidente.

Exercícios do capítulo 17
  1. Projeto GitOps completo (o mais valioso da apostila): instale o Argo CD no kind, crie um repo gitops-repo com overlays dev/prod da sua API, configure a Application com selfHeal e demonstre: (a) deploy via PR; (b) drift revertido automaticamente após um kubectl scale manual. Grave um GIF para o README.
  2. Explore um operator de verdade: instale o CloudNativePG, crie um Cluster de 2 instâncias, mate a primária e observe o failover pelos eventos.
  3. Provoque e diagnostique, sem olhar a árvore: um Pending por requests impossíveis, um CrashLoop por env faltando e um ImagePullBackOff. Cronometre-se.
  4. Desafio staff: siga o tutorial do Kubebuilder e crie uma CRD Backup com um controlador que cria CronJobs a partir dela.
✔ checkpoint — capítulo 17

MOD-18 · Parte IV DEVOPS

Terraform e infraestrutura como código

Provisionar nuvem de forma versionada e reprodutível — o terceiro pilar (com Docker e K8s) do trio mais pedido em vagas.

Nível: avançado

18.1 Por que IaC

Infra clicada no console é irreproduzível, não auditável e diverge entre ambientes. Com IaC, a infraestrutura vira código: revisada em PR, testada, aplicada de forma idempotente. Terraform domina o mercado (HCL, multi-nuvem); OpenTofu é o fork open-source 1:1 (após a mudança de licença da HashiCorp em 2023 — bom assunto para mostrar que você acompanha o ecossistema); alternativas: Pulumi (linguagens reais), CloudFormation/CDK (só AWS), Ansible (gerência de configuração — complementar, não concorrente).

18.2 O núcleo: recursos, estado e o ciclo plan/apply

main.tf — VPC + EKS resumidos por módulos
terraform {
  required_version = ">= 1.9"
  backend "s3" {                       # estado remoto + lock: obrigatório em time
    bucket = "empresa-tfstate"
    key    = "prod/eks.tfstate"
    region = "us-east-1"
    use_lockfile = true
  }
}

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"
  name = "prod"
  cidr = "10.0.0.0/16"
  azs  = ["us-east-1a", "us-east-1b", "us-east-1c"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
}

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 20.0"
  cluster_name    = "prod"
  cluster_version = "1.31"
  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnets
}
terminal — o ciclo
terraform init      # baixa providers/módulos, configura backend
terraform plan -out plan.bin   # diff: o que será criado/alterado/DESTRUÍDO — leia sempre
terraform apply plan.bin
terraform state list && terraform destroy
  • O estado (tfstate) é sagrado: mapeia código ↔ recursos reais. Remoto (S3) com lock, cifrado, nunca no Git (contém segredos). Corromper/perder estado é o incidente clássico de Terraform — conheça terraform import e state mv para cirurgias.
  • Drift: mudou no console? O próximo plan mostra a divergência. Política madura: console somente-leitura em prod.
  • Módulos são as "funções" do Terraform: encapsulam padrões (a empresa inteira cria VPCs iguais). Fixe versões (~>).
  • Workspaces vs diretórios por ambiente: o mercado prefere diretórios (envs/dev, envs/prod) com backends separados — raio de explosão menor.
  • Pipeline de IaC: PR roda fmt + validate + plan comentado no PR (Atlantis ou GitHub Actions) + scanners (tfsec/checkov); apply só após aprovação, via OIDC.
Cai em entrevista

Trio garantido: "O que é o state e por que remoto com lock?" (mapeamento código↔real; lock evita dois applies simultâneos corrompendo tudo). "Diferença entre Terraform e Ansible?" (provisionar infra declarativa e imutável vs configurar máquinas existentes; hoje Ansible perde espaço para imagens imutáveis + user data). "Como você importa infra que já existe?" (terraform import + escrever o recurso correspondente; ou ferramentas de geração).

Exercícios do capítulo 18
  1. Sem nuvem paga: use o provider kind ou docker do Terraform para criar um cluster kind + namespaces + um Deployment inteiramente via terraform apply.
  2. Com free tier AWS: crie VPC + bucket S3 com estado remoto; provoque drift no console e observe o plan detectar.
  3. Rode checkov no seu código e corrija 3 apontamentos.
  4. Estruture envs/dev e envs/prod consumindo o mesmo módulo local com variáveis diferentes.
✔ checkpoint — capítulo 18

MOD-19 · Parte IV DEVOPS

Observabilidade e SRE

Métricas, logs, traces, SLOs e resposta a incidentes — como se opera produção de verdade.

Nível: avançado

19.1 Os três pilares (e o que cada um responde)

  • Métricas (números agregados no tempo): "o quê está anormal?" — baratas, base de alertas. Stack padrão: Prometheus (coleta por pull de endpoints /metrics, descobre alvos pela API do K8s) + Grafana (dashboards) + Alertmanager (roteia alertas p/ Slack/PagerDuty). Instala-se tudo com o chart kube-prometheus-stack.
  • Logs (eventos textuais): "o que exatamente aconteceu?" — app escreve JSON estruturado em stdout; um DaemonSet (Fluent Bit/Promtail/Alloy) coleta; Loki (barato, indexa só labels — casa com Grafana) ou ELK/OpenSearch armazenam.
  • Traces (a jornada de UMA requisição por N serviços): "onde a latência/erro nasceu?" — instrumentação via OpenTelemetry (o padrão universal: SDK + Collector), backend Tempo/Jaeger. Correlação métrica→trace→log no Grafana é o fluxo de debug moderno.
PromQL — as consultas que você vai escrever
# taxa de requisições por segundo (counter sempre com rate())
sum(rate(http_requests_total{job="api"}[5m]))
# % de erro (a métrica de SLO por excelência)
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))
# latência p95 a partir de histograma
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
# memória dos Pods vs limite (prevendo OOM)
container_memory_working_set_bytes / on(pod) kube_pod_container_resource_limits{resource="memory"}

Frameworks de "o que medir": RED por serviço (Rate, Errors, Duration) e USE por recurso (Utilization, Saturation, Errors). Golden signals do Google = RED + saturação.

19.2 SRE: SLI, SLO e error budget

  • SLI: a medida ("% de requisições com sucesso em <300 ms").
  • SLO: a meta interna ("99,9% em 30 dias"). SLA é o contrato externo com multa — sempre mais frouxo que o SLO.
  • Error budget: 100% − SLO = quanto pode falhar (99,9% ⇒ ~43 min/mês). É a ponte política entre velocidade e estabilidade: budget sobrando → acelere deploys; budget estourado → congela features e prioriza confiabilidade. Alertas maduros disparam por burn rate do budget (multiwindow), não por "CPU > 80%" — alerta bom exige ação humana e aponta impacto no usuário.

19.3 Incidentes e postmortems

  1. Papéis: Incident Commander coordena (não debuga), comunicador atualiza stakeholders, operadores investigam. Severidades (SEV1–3) definem quem acorda.
  2. Mitigar antes de entender: rollback/feature-flag primeiro; causa raiz depois. MTTR agradece.
  3. Postmortem blameless: linha do tempo, impacto, causas (técnica de "5 porquês"), ações com dono e prazo. Sem culpados: pessoas erram porque o sistema permitiu. Ter participado (ou simulado) de postmortems é história de ouro em entrevista comportamental de SRE.
Cai em entrevista (SRE)

"Latência p99 dobrou após um deploy, o que você faz?" Resposta estruturada: verificar impacto no SLO → rollback imediato (mitigação) → comparar janelas antes/depois nas métricas RED → traces das requisições lentas para achar o span vilão → logs correlacionados → hipótese: N+1? pool esgotado? CPU throttling por limit novo? GC? → corrigir, re-deploy canário observando burn rate → postmortem. A ordem (mitigar → diagnosticar) importa mais que a causa exata.

Exercícios do capítulo 19
  1. Com o kube-prometheus-stack instalado (cap. 16), exponha /metrics na sua API (lib Prometheus da sua linguagem), crie um ServiceMonitor e monte um dashboard RED no Grafana.
  2. Escreva as 4 queries PromQL acima para a SUA aplicação e crie um alerta de taxa de erro > 1% por 5 min chegando (Alertmanager → webhook de teste).
  3. Instale Loki + Alloy, converta os logs da sua API para JSON estruturado e faça uma busca por request_id.
  4. Defina SLI/SLO da sua API, calcule o error budget e escreva um mini-postmortem (1 página) do exercício de CrashLoop do cap. 17.
✔ checkpoint — capítulo 19

MOD-20 · Parte IV DEVOPS

DevSecOps e supply chain

Segurança embutida na esteira — a camada que virou requisito após os grandes ataques de cadeia de suprimentos.

Nível: muito avançado

20.1 Shift-left: segurança em cada estágio do pipeline

EstágioControleFerramentas
CommitVarredura de segredos (pre-commit + CI)gitleaks, trufflehog
CódigoSAST (análise estática) e SCA (dependências)Semgrep, CodeQL; Dependabot/Renovate, Trivy
BuildScan de imagem + IaC misconfigTrivy, Grype; checkov, tfsec
DeployAdmission: só imagens assinadas/aprovadasKyverno + cosign, Gatekeeper
RuntimeDetecção de anomalias, mTLS, netpolFalco, mesh, Cilium

Princípio de adoção realista: comece alertando, depois passe a bloquear — esteira que bloqueia tudo no dia 1 é esteira que os devs contornam.

20.2 Supply chain: assinar e provar a origem

Ataques como SolarWinds e pacotes maliciosos em registries deslocaram o foco para "como sei que este artefato é o que penso que é?":

  • SBOM (inventário de componentes, formatos SPDX/CycloneDX) gerado no build e anexado à imagem — exigido em contratos com governo e grandes empresas.
  • Assinatura com cosign (projeto Sigstore): cosign sign ghcr.io/voce/api@sha256:... — no modo keyless, a assinatura usa a identidade OIDC do pipeline, sem chave para vazar. No cluster, uma policy Kyverno verifyImages rejeita imagens não assinadas.
  • SLSA: níveis de garantia de procedência (provenance) do build — saber dizer "geramos provenance SLSA no CI" posiciona você na fronteira do tema.
  • Higiene contínua: Renovate/Dependabot atualizando dependências e bases de imagem automaticamente via PR — o controle com melhor custo-benefício de todos.
terminal — assinando e verificando
cosign sign --yes ghcr.io/voce/minha-api@sha256:9f8e...
cosign verify ghcr.io/voce/minha-api@sha256:9f8e... \
  --certificate-identity-regexp "github.com/voce/.*" \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
Exercícios do capítulo 20
  1. Adicione ao seu pipeline: gitleaks + Trivy (falhando em CRITICAL) + geração de SBOM como artefato.
  2. Assine sua imagem com cosign keyless no GitHub Actions e verifique localmente.
  3. Escreva a policy Kyverno que só admite imagens assinadas pelo seu repositório e demonstre a rejeição de uma imagem qualquer.
  4. Ative o Renovate no seu repositório e faça o merge do primeiro PR automático.
✔ checkpoint — capítulo 20

MOD-21 · Parte V CARREIRA

Mercado, certificações e entrevistas

Converter tudo o que você estudou em emprego: roadmap, portfólio, certificações e o banco de perguntas.

Nível: todos

21.1 O mapa das vagas no Brasil (2026)

NívelO que se esperaFaixa CLT/PJ típica (BR)*
Júnior / AnalistaLinux, Git, Docker, CI básico, uma nuvem no básico, vontade de plantãoR$ 4–8 mil
PlenoK8s no dia a dia, Terraform, pipelines completos, observabilidade, troubleshooting autônomoR$ 9–16 mil
Sênior / SREArquitetura de plataforma, GitOps, segurança, SLOs, incidentes, mentoria, custo (FinOps)R$ 16–28 mil+
Staff / SpecialistOperators, multi-cluster, decisões de longo prazo, influência entre timesR$ 25–40 mil+ · internacional US$ 90–160k

*Faixas indicativas — variam por região, setor (fintechs pagam mais) e formato; vagas remotas internacionais (contractor) multiplicam os valores. Pesquise faixas atuais em levantamentos como Glassdoor e pesquisas salariais da comunidade antes de negociar.

O combo que abre portas

A interseção pedida em praticamente toda vaga pleno no Brasil: AWS + Kubernetes (EKS) + Terraform + GitHub Actions/GitLab CI + Prometheus/Grafana + Python ou Go para automação. Diferenciais 2025–2026 que destacam currículo: GitOps (Argo CD), Karpenter/FinOps, plataforma interna (Backstage), segurança de supply chain e infra para IA (GPUs no K8s — MLOps).

21.2 Certificações: quais valem e em que ordem

CertificaçãoPara quemComentário de mercado
CKA (Certified Kubernetes Administrator)O alvo nº 1 desta apostilaPrática (terminal ao vivo, ~2h). A mais reconhecida em vagas DevOps/SRE. Caps. 9–17 cobrem o currículo; treine velocidade com killer.sh
CKADPerfil mais devFoco em workloads/config; mais fácil que a CKA. Boa primeira prova prática
CKSSênior/segurançaExige CKA válida; rara e muito valorizada em bancos/fintechs (cap. 15 + 20)
KCNA / KCSAIniciantesTeóricas e baratas; bom aquecimento, pouco peso sozinho
AWS SAA / DevOps ProTodos os níveisSAA (Solutions Architect Associate) é o filtro de RH mais comum no Brasil; DevOps Professional para sênior
Terraform AssociateComplementoBarata e objetiva; fecha o trio com CKA + AWS

Ordem sugerida: AWS SAA → CKA → Terraform Associate (→ CKS se seguir para segurança). Certificação abre a porta do RH; o que sustenta a entrevista técnica é o laboratório que você construiu nesta apostila.

21.3 Portfólio: 3 projetos que provam senioridade

  1. Plataforma completa de referência (o carro-chefe): API sua containerizada (multi-stage, distroless, não-root) → CI no GitHub Actions (testes, Trivy, SBOM, cosign, push por SHA) → deploy em K8s com Helm/Kustomize → GitOps com Argo CD (dev/prod) → observabilidade (Prometheus/Grafana/Loki) → tudo provisionado por Terraform. README com diagrama de arquitetura e GIFs de rollout, drift-heal e rollback. Este único repositório responde 80% de qualquer entrevista.
  2. Incidente documentado: injete falhas no projeto 1 (OOM, liveness errada, netpol bloqueando) e escreva postmortems blameless com gráficos das métricas. Ninguém faz isso — você será lembrado.
  3. Automação em Python/Go: uma CLI útil de operação (ex.: relatório de Pods sem limits por namespace, limpeza de imagens antigas no ECR) — prova que você programa, não só configura.

21.4 Banco de perguntas de entrevista (com a resposta-esqueleto)

  • "Explique o que acontece num kubectl apply" → fluxo do cap. 9.3, terminando em reconciliação contínua.
  • "Container vs VM?" → processo isolado por namespaces/cgroups vs kernel próprio; trade-off isolamento × densidade (cap. 4).
  • "Pod em CrashLoopBackOff, e agora?"logs --previous + describe + exit codes (137=OOM/KILL, 143=TERM) + árvore do cap. 17.4.
  • "Readiness vs liveness?" → tráfego vs restart; antipadrão da liveness com dependências (cap. 11).
  • "Como faz deploy sem downtime?" → RollingUpdate + readiness + PDB + graceful shutdown + preStop; canário com Argo Rollouts para risco menor (caps. 3, 10, 14).
  • "Requests vs limits?" → reserva vs teto; throttling vs OOMKilled; prática memória req=limit, CPU sem limit (cap. 11).
  • "O que é GitOps e por que pull?" → Git como fonte de verdade, reconciliação, credenciais fora do CI, drift/selfHeal (cap. 17).
  • "Secret do K8s é seguro?" → base64 ≠ criptografia; encryption at rest + RBAC + cofre externo (cap. 11).
  • "Por que o state do Terraform é crítico?" → mapeamento código↔real, remoto+lock, drift, import (cap. 18).
  • "Defina SLO e error budget" → meta + orçamento de falha como mecanismo de decisão velocidade×estabilidade (cap. 19).
  • Comportamentais: prepare 3 histórias no formato STAR — um incidente que você mitigou, uma automação que economizou tempo/dinheiro (quantifique!), um conflito técnico resolvido com dados.

21.5 Plano de estudo e próximos passos

SemanasMeta
1–2Caps. 1–3: laboratório montado + primeiro pipeline no ar
3–5Caps. 4–8: Docker completo; API própria com imagem otimizada e escaneada
6–10Caps. 9–14: K8s essencial; app rodando no kind com probes, HPA e Ingress
11–13Caps. 15–17: segurança, Helm, GitOps — projeto de portfólio nº 1 completo
14–16Caps. 18–20 + simulados killer.sh → agende a CKA
contínuoCap. 19 no projeto, postmortems, aplicar para vagas desde a semana 10 (não espere "estar pronto")

Fontes para seguir evoluindo: documentação oficial do Kubernetes (kubernetes.io — a melhor doc do ecossistema), Site Reliability Engineering e The SRE Workbook (Google, gratuitos online), The Phoenix Project e Accelerate (a base cultural/DORA), relatórios anuais da CNCF, e o simulador killer.sh para as provas Kubernetes.

🏁 Síntese final da apostila

Se três ideias ficaram, o trabalho está feito: (1) tudo é o loop de reconciliação — do Deployment ao Argo CD ao Terraform, você declara estado e sistemas convergem; (2) imutabilidade + automação — imagens fixas por digest, infra por código, deploy por PR: humanos decidem, robôs executam; (3) produção se opera com dados — probes, métricas, SLOs e postmortems transformam incidentes em aprendizado. Comandos mudam de versão; esses princípios sustentam a carreira inteira.