Pular para conteúdo

Kubernetes

O Docker Compose inicia o BoxOffice em uma máquina. A produção precisa de mais: quarenta containers espalhados por várias máquinas, um substituto iniciado em segundos quando um deles morre, uma nova versão publicada sem indisponibilidade e capacidade adicionada automaticamente quando a venda começa. Fazer isso à mão — SSH em um nó, docker run, atualizar o load balancer — não sobrevive à primeira noite de plantão. Um orquestrador faz isso por você, e o Kubernetes é o padrão de fato5.

O Kubernetes descende dos gerenciadores de cluster internos do Google, Borg e Omega; seus projetistas resumiram a lição de uma década executando containers em escala como estado declarativo reconciliado por laços de controle independentes1.


O laço de controle: estado desejado vs estado real

Você nunca diz ao Kubernetes "inicie um container". Você diz "deve haver cinco réplicas de catalog, versão 1.4.0, cada uma com 1 CPU" — o estado desejado — e o armazena no API server. Os controladores então rodam para sempre:

loop:
    desired = read spec from the API server
    actual  = observe the cluster
    if actual != desired:
        act to reduce the difference (create, delete, update)
sequenceDiagram
    participant U as kubectl / Argo CD
    participant API as API server (+ etcd)
    participant RS as Controlador do ReplicaSet
    participant S as Scheduler
    participant K as kubelet (nó 2)
    U->>API: aplica Deployment catalog, replicas = 5
    API->>RS: watch: desejado 5, real 4 (um nó morreu)
    RS->>API: cria o Pod catalog-7f9c
    API->>S: watch: pod sem nó
    S->>API: associa catalog-7f9c ao nó 2 (CPU livre suficiente)
    API->>K: watch: novo pod para este nó
    K->>K: baixa a imagem, inicia o container, executa as probes
    K->>API: status: Running, Ready
    Note over RS,API: real = desejado → nada a fazer

É por isso que o Kubernetes se autorrecupera: um processo que caiu, um pod apagado ou um nó morto aparecem todos como "real ≠ desejado", e o mesmo laço os corrige. É também por isso que a API é declarativa e idempotente: aplicar o mesmo manifest duas vezes não muda nada.

As peças de um cluster

Componente Onde Papel
API server Control plane O único ponto de entrada; valida e armazena objetos; todo o resto observa (watch) o API server
etcd Control plane Armazenamento chave-valor fortemente consistente (consenso Raft) com o estado do cluster
Scheduler Control plane Escolhe um nó para cada novo pod (recursos livres, afinidade, restrições de espalhamento)
Controller manager Control plane Executa os controladores (Deployment, ReplicaSet, Job, Node, HPA…)
kubelet Todo nó Inicia os containers do pod via runtime de containers (containerd), executa as probes, reporta o status
kube-proxy / plugin CNI Todo nó Implementa os IPs virtuais dos Services e a rede dos pods (iptables, IPVS ou eBPF, ex.: Cilium)

Com um serviço gerenciado (EKS, GKE, AKS), o provedor executa e atualiza o control plane; você gerencia os nós — ou, com GKE Autopilot ou EKS no Fargate, nem eles.


Os objetos centrais

Objeto O que é Uso no BoxOffice
Pod Um ou mais containers que compartilham rede e volumes; a unidade de escalonamento. Efêmero Uma instância de catalog (às vezes com um sidecar)
Deployment Mantém N pods idênticos rodando e publica novas versões (via ReplicaSets) catalog, inventory, order, gateway
Service Um IP virtual e um nome DNS estáveis na frente de um conjunto mutável de pods, selecionados por labels http://inventory:8080 sempre funciona, quaisquer que sejam os pods existentes
Ingress / Gateway API Roteamento HTTP de fora do cluster para Services (host, caminho, TLS) api.boxoffice.example/catalog/** → catalog
ConfigMap / Secret Configuração e credenciais injetadas como variáveis de ambiente ou arquivos URL do banco; senha do banco (de um gerenciador de segredos)
StatefulSet Pods com identidade estável e volume persistente próprio Kafka ou PostgreSQL se auto-hospedados
Job / CronJob Trabalho que roda até terminar, uma vez ou em um agendamento Liberar reservas expiradas a cada minuto; reconciliação noturna com o provedor de pagamento
HorizontalPodAutoscaler Ajusta o número de réplicas de um Deployment a partir de métricas catalog de 4 → 40 réplicas durante a venda
PodDisruptionBudget Limita quantos pods disrupções voluntárias (drenagem de nós, upgrades) podem derrubar de uma vez Nunca menos de 3 pods de inventory durante um upgrade de nó

O catalog do BoxOffice no Kubernetes

Os manifests abaixo mostram o que um catalog de produção precisa. Leia-os de cima para baixo; cada bloco responde a uma pergunta operacional. O Hands-on 10 escreve uma versão menor, para um cluster de três nós num notebook (quadro depois dos manifests).

k8s/catalog/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: catalog
  labels: { app: catalog }
spec:
  replicas: 4
  selector:
    matchLabels: { app: catalog }
  strategy:
    type: RollingUpdate
    rollingUpdate: { maxSurge: 25%, maxUnavailable: 0 }   # never below 4 ready pods
  template:
    metadata:
      labels: { app: catalog }
    spec:
      terminationGracePeriodSeconds: 30
      topologySpreadConstraints:                           # spread replicas across zones
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: ScheduleAnyway
          labelSelector: { matchLabels: { app: catalog } }
      containers:
        - name: catalog
          image: ghcr.io/boxoffice/catalog@sha256:9f2c0d7e1b4a   # digest, not a tag
          ports: [ { containerPort: 8080 } ]
          envFrom:
            - configMapRef: { name: catalog-config }
            - secretRef:    { name: catalog-db }
          resources:
            requests: { cpu: "500m", memory: "768Mi" }      # what the scheduler reserves
            limits:   { memory: "768Mi" }                   # hard ceiling (OOMKilled above)
          startupProbe:                                     # JVM warm-up: up to 60 s
            httpGet: { path: /actuator/health/liveness, port: 8080 }
            periodSeconds: 5
            failureThreshold: 12
          readinessProbe:                                   # receive traffic?
            httpGet: { path: /actuator/health/readiness, port: 8080 }
            periodSeconds: 5
            failureThreshold: 3
          livenessProbe:                                    # restart me?
            httpGet: { path: /actuator/health/liveness, port: 8080 }
            periodSeconds: 10
            failureThreshold: 3
          lifecycle:
            preStop: { sleep: { seconds: 5 } }              # let endpoints update before SIGTERM
k8s/catalog/service-hpa-pdb.yaml
apiVersion: v1
kind: Service
metadata: { name: catalog }
spec:
  selector: { app: catalog }
  ports: [ { port: 8080, targetPort: 8080 } ]
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: catalog }
spec:
  scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: catalog }
  minReplicas: 4
  maxReplicas: 40
  metrics:
    - type: Resource
      resource:
        name: cpu
        target: { type: Utilization, averageUtilization: 60 }   # % of the CPU request
  behavior:
    scaleUp:   { stabilizationWindowSeconds: 0,   policies: [ { type: Percent, value: 100, periodSeconds: 15 } ] }
    scaleDown: { stabilizationWindowSeconds: 300, policies: [ { type: Pods,    value: 2,   periodSeconds: 60 } ] }
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: { name: catalog }
spec:
  minAvailable: 3
  selector: { matchLabels: { app: catalog } }
k8s/edge/httproute.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata: { name: boxoffice-api }
spec:
  parentRefs: [ { name: public-gateway } ]
  hostnames: [ "api.boxoffice.example" ]
  rules:
    - matches: [ { path: { type: PathPrefix, value: /catalog } } ]
      backendRefs: [ { name: catalog, port: 8080 } ]
    - matches: [ { path: { type: PathPrefix, value: /inventory } } ]
      backendRefs: [ { name: inventory, port: 8080 } ]

A Gateway API (Gateway + HTTPRoute) é a sucessora do antigo objeto Ingress: ela separa o dono da infraestrutura (quem opera o Gateway) dos times de aplicação (donos das rotas) e suporta nativamente backends com pesos3 — que é como os canaries dividem o tráfego (veja Entrega). A aula de Borda trata do componente Ingress.

No código do BoxOffice: o catalog num cluster kind (lab 10)

O Hands-on 10 — Implantar o BoxOffice usa as mesmas peças com números de notebook: 2 réplicas (o HPA assume depois), um request de 250m de CPU e 512 Mi, e um Ingress do Traefik (o HTTPRoute da Gateway API está em catalog-route.yaml, como alternativa). Observe três escolhas. A startup probe permite até 5 minutos, porque a primeira partida também roda as migrações do Flyway, e o Kubernetes não tem depends_on: o catalog reinicia até o PostgreSQL responder. A liveness verifica só a JVM, então uma oscilação do banco não reinicia todos os pods. E o lab mantém um limite de 1 CPU, o mesmo "servidor" do Compose no lab 1, para que os números continuem comparáveis aos dos labs anteriores — ao preço do throttling descrito abaixo.

k8s/base/catalog.yaml (lab 10: resources, probes, graceful shutdown)
env:
  - name: SERVER_SHUTDOWN
    value: graceful        # finish in-flight requests on SIGTERM
resources:
  requests:                # what the scheduler reserves; HPA percentages are relative to this
    cpu: 250m
    memory: 512Mi
  limits:
    cpu: "1"
    memory: 768Mi
startupProbe:              # the JVM needs time: no liveness kills before it has started
  httpGet: { path: /actuator/health/liveness, port: http }
  periodSeconds: 5
  failureThreshold: 60     # up to 5 min: the first start also runs the Flyway migrations
livenessProbe:             # "restart me": only for a process that is stuck
  httpGet: { path: /actuator/health/liveness, port: http }
  periodSeconds: 10
  failureThreshold: 3
readinessProbe:            # "send me traffic": removed from the Service while not ready
  httpGet: { path: /actuator/health/readiness, port: http }
  periodSeconds: 5
  failureThreshold: 2
lifecycle:
  preStop:
    sleep:
      seconds: 5           # let the endpoints update before the JVM stops accepting

Probes: três perguntas diferentes

Probe Pergunta Em caso de falha Erro comum
Startup A aplicação terminou de iniciar? Mantém as outras probes desativadas; após failureThreshold, reinicia o container Sem ela → uma JVM lenta é morta pela liveness antes de terminar de iniciar
Readiness Este pod deve receber tráfego agora? Removido dos endpoints do Service (sem reinício) Verificar dependências: quando o PostgreSQL pisca, todos os pods ficam não prontos e o serviço inteiro some
Liveness O processo travou sem conserto? Container reiniciado Fazê-la depender do banco: uma queda do banco dispara tempestades de reinício que atrasam a recuperação

Mantenha a liveness local (a JVM está respondendo?) e faça a readiness refletir apenas o que o próprio pod precisa para atender. O Spring Boot Actuator expõe /actuator/health/liveness e /actuator/health/readiness exatamente com essa semântica. A aula de Observabilidade trata health checks como um componente da paleta (Health Check).


Requests, limits e qualidade de serviço

  • Request — o que o scheduler reserva em um nó. Decide onde um pod cabe e é a base da porcentagem de utilização do HPA.
  • Limit — o teto. Acima do limite de memória, o container é morto (OOMKilled); acima do limite de CPU, ele é estrangulado (throttled) — simplesmente espera, o que aparece como latência, não como erros.
Classe de QoS Regra Despejado sob pressão no nó
Guaranteed requests = limits para CPU e memória Por último
Burstable alguns requests definidos, menores que os limits No meio
BestEffort nada definido Primeiro

Limites de CPU e latência

Um serviço Java com limite de 1 CPU que mantém 4 threads (GC e requisições) ocupadas em paralelo num nó com vários núcleos pode consumir sua cota de 100 ms nos primeiros 25 ms de cada período de 100 ms e então ficar parado por 75 ms. O p99 cresce enquanto a CPU média parece normal. Observe a métrica do cgroup nr_throttled (exposta pelo cAdvisor como container_cpu_cfs_throttled_periods_total). Muitos times definem um request de CPU mas nenhum limit de CPU para serviços sensíveis à latência, e sempre definem um limite de memória. A aula de Desempenho mostra como identificar throttling em um teste de carga.


Perdendo um nó

O laço de controle substitui os pods perdidos, mas a autocura precisa de duas coisas que o laço não fornece. Os pods substitutos precisam de espaço: o scheduler só coloca um pod num nó cuja capacidade livre cubra seus requests, então num cluster lotado a 100% os pods de um nó morto ficam Pending. E quem chama precisa parar de enviar aos pods mortos: até o nó ser declarado NotReady (dezenas de segundos, por padrão) e os endpoints do Service serem atualizados, parte do tráfego ainda vai para pods que não existem mais. São as duas regras de redundância e failover da aula de Confiabilidade, no nível dos nós: capacidade N + 1 e detecção rápida de falhas.

Experimente: em k8s-node-failure, catalog e pricing rodam 6 pods cada em 3 nós × 4 pods — um cluster lotado — e em t = 60 s um nó morre.

  1. Rode como está. Por cerca de 40 s, perto de 90% das requisições falham: quem chama ainda envia aos pods mortos. Depois os erros caem, e voltam a 7–10% a partir de t ≈ 150 s: os 2 pods perdidos de cada serviço não têm para onde ir, e 4 sobreviventes carregam a carga de 6. No total: erros ≈ 27%, p99 ≈ 4,6 s.
  2. Dê espaço ao cluster: ajuste podsPerNode para 8 no quadro do cluster. Os erros tardios desaparecem (os pods perdidos são realocados), mas a janela de 40 s continua: erros ≈ 25%.
  3. Conecte um Service Registry a catalog e pricing — o equivalente, no playground, a endpoints atualizados rapidamente. Quem chama descarta os pods mortos em 3 s em vez de 30–40 s: erros ≈ 4%.
O que você deveria ter observado, e o que ainda falta?

Depois do passo 3, os erros restantes (5–9%) vêm dos sobreviventes: 4 pods carregam a carga de 6 até as substitutas ficarem prontas. Suba os dois serviços para 8 réplicas, para que perder um terço ainda deixe capacidade suficiente: erros ≈ 1,7%. Depois acrescente um timeout de 300 ms com 2 retries em catalog → pricing: erros ≈ 0,8%, p99 ≈ 43 ms, por cerca de 1.370 USD/mês em vez de 1.330. A ordem importa: o mesmo timeout e os mesmos retries com só 6 réplicas levam os erros a ≈ 48% — retries contra sobreviventes sobrecarregados são uma tempestade de retries.


Autoscaling em três níveis

flowchart LR
    load["k6 / fãs<br/>requisições por segundo ↑"] --> hpa["HPA<br/>mais pods"]
    hpa --> pend["Pods Pending<br/>(sem espaço nos nós)"]
    pend --> ca["Cluster Autoscaler / Karpenter<br/>mais nós"]
    ca --> run["Pods escalonados<br/>imagem baixada, JVM aquecida"]
    run --> ready["Ready → recebem tráfego"]

1. Pods — Horizontal Pod Autoscaler (HPA). A cada 15 s o HPA calcula2

\[ \text{desejado} = \left\lceil \text{atual} \times \frac{\text{métrica atual}}{\text{métrica alvo}} \right\rceil \]

Com 4 pods de catalog a 90 % de CPU (do request) e meta de 60 %: ⌈4 × 90/60⌉ = 6 pods. Escalar por CPU funciona para serviços limitados por CPU; para serviços limitados por I/O, escale por requisições por segundo por pod ou pelo atraso da fila por meio de métricas customizadas/externas.

2. Nós — Cluster Autoscaler ou Karpenter. Quando pods ficam Pending porque nenhum nó tem espaço, um autoscaler de nós provisiona novas máquinas (o Karpenter escolhe tipos de instância por carga de trabalho e pode usar capacidade spot). Isso leva minutos: boot da VM, entrada do nó no cluster, download da imagem.

3. Eventos — KEDA. O Kubernetes Event-Driven Autoscaler escala a partir de fontes externas (atraso de consumidores Kafka, tamanho de filas, janelas de cron) e consegue escalar até zero — natural para os workers de notification4.

No código do BoxOffice: o HPA do catalog (lab 10)

O Hands-on 10 — Autoscaling escala o catalog de 2 a 8 pods a 60% do seu request de 250m de CPU — a fórmula acima, com uma subida de "dobrar ou +2 pods a cada 15 s" e uma descida lenta (janela de 2 minutos, 1 pod a cada 30 s), para que uma queda curta não remova capacidade que será necessária de novo um minuto depois. O lab mede o atraso sobre o qual esta seção alerta: o HPA pediu mais pods cerca de dois minutos depois do início da rampa, mas o oitavo pod só ficou pronto em 7:31, quando o patamar já tinha acabado, com um p95 de 5–7 s na execução (Meça).

k8s/base/catalog-hpa.yaml (lab 10)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: catalog
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: catalog
  minReplicas: 2
  maxReplicas: 8
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60     # % of the CPU *request* (250m): scale when pods average > 150m
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0  # react at once to load
      selectPolicy: Max
      policies:
        - type: Percent
          value: 100                 # at most double...
          periodSeconds: 15
        - type: Pods
          value: 2                   # ...or +2 pods, whichever is more, every 15 s
          periodSeconds: 15
    scaleDown:
      stabilizationWindowSeconds: 120  # wait 2 min of lower load before removing pods
      policies:
        - type: Pods
          value: 1
          periodSeconds: 30

BoxOffice: por que só o HPA não salva a venda

O HPA reage depois que a CPU sobe; o autoscaler de nós precisa de minutos; cada JVM precisa de ~20–40 s para iniciar e aquecer. Uma venda relâmpago vai de 5 % a 100 % do pico em segundos. Por isso o time pré-escala antes das 10:00 — um CronJob ou o scaler de cron do KEDA ajusta minReplicas para o pico esperado às 09:45 —, mantém uma reserva de nós livres (pods "balão" de baixa prioridade, despejados quando pods reais precisam de espaço) e coloca uma sala de espera na frente, para que a admissão, e não o autoscaling, controle a taxa. Os números de capacidade vêm da página de Planejamento de capacidade.

Experimente

O HPA começa com o catalog-hpa.yaml do lab 10: alvo de 60% de CPU, de 2 a 8 pods, sobe dobrando ou +2 pods a cada 15 s, desce 1 pod a cada 30 s depois de uma janela de 120 s. Cada pod pede 250m e pode ir até o limite de 1 CPU, então 2 pods absorvem até 400 requisições/s. O tempo de início (40 s) é o da JVM, do quadro acima; o lab mediu bem mais num laptop.

  1. Rode a abertura às 10:00: o HPA quer 8 pods em 30 s, mas eles só ficam prontos 40 s depois — cerca de 8.900 requisições (1,7%) perdidas ou lentas em 45 s acima da capacidade. Suba o início do pod para 120 s: cerca de 25.000 (4,9%).
  2. Pré-escale: ponha Mín. pods em 4 e nada se perde, por 193 réplica-minutos em vez de 181; mínimo 8 custa 241.
  3. Escolha a rampa diária e observe a queda às 10:10: com a janela de 120 s o HPA ainda tem 8 pods às 10:11:30 e 6 quando o tráfego volta, às 10:13 (a CPU chega a 76%); ponha a janela em 0 e ele está com 4, e a CPU chega a 113% — 167 réplica-minutos em vez de 185, pagos com esse pico.

Experimente: no cenário abaixo, rode o perfil spike com 2 réplicas e depois com 6. Observe a latência p99 e os erros enquanto as réplicas estão ocupadas; depois ative o autoscaling (se disponível) e observe o atraso entre o início do pico e o momento em que as novas réplicas ficam saudáveis.


Onde rodar Kubernetes

Amazon EKS — control plane gerenciado (cobrado por hora de cluster), nós como node groups gerenciados de EC2, Karpenter ou Fargate. AWS Load Balancer Controller para Ingress/Gateway; IAM Roles for Service Accounts para credenciais dos pods.

GKE Standard (você gerencia os node pools) ou Autopilot (cobrado pelos requests de recursos dos pods; o Google gerencia os nós). A Gateway API é suportada nativamente.

AKS — control plane gerenciado, node pools sobre VM scale sets, KEDA disponível como add-on, Azure Container Apps como alternativa serverless mais simples construída sobre Kubernetes.

kind (Kubernetes in Docker) e minikube para os labs; k3s para clusters pequenos de borda ou de uma única VM. Mesma API — os manifests acima rodam sem alterações.


Trade-offs

Pergunta Kubernetes Alternativa mais simples (ECS, Cloud Run, PaaS)
Tamanho do time e número de serviços Compensa com muitos serviços e times compartilhando ferramentas Melhor para poucos serviços e um time pequeno
Portabilidade Mesma API em qualquer nuvem e on-premises Preso a um provedor
Ecossistema Enorme: Argo CD, KEDA, operators, service mesh, motores de política Menor, mas integrado
Carga operacional Upgrades a cada poucos meses, add-ons, rede, políticas de segurança A maior parte fica com o provedor
Custo fixo Control plane + pods de sistema + capacidade ociosa nos nós Perto de zero quando ocioso (escala a zero no Cloud Run)

Na entrevista

Você raramente desenha Kubernetes em uma entrevista de system design — você desenha réplicas de serviços sem estado atrás de um load balancer com autoscaling. O Kubernetes é o detalhe de implementação que você menciona quando perguntam "como você faz o deploy e escala isso?". Bons sinais: distinguir readiness de liveness, saber que o autoscaling tem atraso (e pré-escalar para picos conhecidos), manter os bancos de dados fora do cluster ou em operators com cuidado e espalhar réplicas entre zonas de disponibilidade.

Boas práticas

  • Dê à startup probe o pior caso de partida, não o típico — periodSeconds × failureThreshold precisa cobrir uma partida a frio com migrações. No BoxOffice: 5 s × 60 = 5 min no lab 10.
  • Faça rollouts com maxUnavailable: 0 e um surge pequeno — a capacidade nunca cai abaixo do número desejado durante um deploy, ao preço de um pod extra. No BoxOffice: maxSurge: 1 em k8s/base/catalog.yaml (lab 10).
  • Pause alguns segundos no preStop antes de o processo parar de aceitar — a remoção dos endpoints chega a cada nó com atraso, e as requisições continuam chegando nesse meio-tempo. No BoxOffice: um sleep de 5 s no preStop, medido no lab 10 — Measure it.
  • Escale para cima rápido e para baixo devagar — nenhuma janela de estabilização na subida, minutos na descida, para que uma queda curta não remova capacidade necessária de novo um minuto depois. No BoxOffice: 0 s na subida, 120 s na descida em catalog-hpa.yaml (lab 10).
  • Mantenha espaço para os pods de um nó (N + 1 nós) — senão os substitutos de um nó morto ficam Pending. No BoxOffice: Perdendo um nó.
  • Defina um PodDisruptionBudget para todo serviço com mais de uma réplica — atualizações de nós passam a drenar um pod de cada vez. No BoxOffice: minAvailable: 3 nos manifests do catalog acima.

Armadilhas

  • Sem requests/limits: o scheduler empacota pods às cegas e um pod barulhento sufoca os vizinhos.
  • Liveness dependendo de serviços downstream: uma piscada do banco reinicia a frota inteira.
  • Sem PodDisruptionBudget: um upgrade rotineiro de nó drena todos os pods de inventory de uma vez.
  • Todas as réplicas em uma zona ou em um nó: "redundância" que falha junto.
  • HPA por CPU em um serviço limitado por I/O: a CPU fica baixa enquanto a latência explode; escale por RPS ou atraso.
  • kubectl apply a partir de notebooks em produção: ninguém sabe o que está implantado — use GitOps (Entrega).

Como se conecta

flowchart LR
    ETCD["etcd: Raft<br/>→ Consenso (aula 3)"] --> DS["estado desejado<br/>no API server"]
    DS --> CL["laço de controle:<br/>real → desejado"]
    CL --> DEP["Deployment → pods"]
    DEP --> SVC["Service, Ingress, Gateway API<br/>→ Balanceamento de carga"]
    DEP --> PR["probes de startup,<br/>readiness, liveness"]
    PR --> HC["endpoints de saúde<br/>→ Confiabilidade, Observabilidade"]
    DEP --> RL["requests e limits,<br/>QoS, throttling de CPU"]
    RL --> HPA["HPA, autoscaler de nós,<br/>KEDA por lag"]
    HPA --> PRE["pré-escalar antes das 10:00<br/>→ Autoscaling em Compute"]
    RL --> COST["requests movem a fatura<br/>→ FinOps"]
    DEP --> PDB["PDB, espalhar por zonas<br/>→ redundância"]

Componentes da paleta: app · worker (KEDA escala o notification pelo lag) · scheduler (CronJob libera reservas expiradas) · lb (um Service é um balanceador L4) · ingress · healthcheck · metrics (o que o HPA lê). Cenários: scale-out, k8s-node-failure (N + 1 nós), flash-sale (pré-escala). Continua em: Tráfego e borda — balanceamento no Kubernetes, Distribuídos — consenso (etcd), Compute — atraso do autoscaling, Confiabilidade — health checks, FinOps, Exercício — autoscaling no Kubernetes.



  1. BURNS, B.; GRANT, B.; OPPENHEIMER, D.; BREWER, E.; WILKES, J. Borg, Omega, and Kubernetes. ACM Queue, v. 14, n. 1, 2016. ↩

  2. Documentação do Kubernetes. Concepts; Horizontal Pod Autoscaling; Configure liveness, readiness and startup probes. ↩

  3. Kubernetes SIG Network. Gateway API. ↩

  4. KEDA. Kubernetes Event-driven Autoscaling; Karpenter. Documentation. ↩

  5. BURNS, B.; BEDA, J.; HIGHTOWER, K.; EVENSON, L. Kubernetes: Up and Running. 3rd ed. O'Reilly, 2022. ↩