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).
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
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 } }
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.
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.
- 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.
- Dê espaço ao cluster: ajuste
podsPerNodepara 8 no quadro do cluster. Os erros tardios desaparecem (os pods perdidos são realocados), mas a janela de 40 s continua: erros ≈ 25%. - Conecte um Service Registry a
catalogepricing— 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
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).
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.
- 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%).
- 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.
- 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 × failureThresholdprecisa cobrir uma partida a frio com migrações. No BoxOffice: 5 s × 60 = 5 min no lab 10. - Faça rollouts com
maxUnavailable: 0e 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: 1emk8s/base/catalog.yaml(lab 10). - Pause alguns segundos no
preStopantes 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: umsleepde 5 s nopreStop, 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: 3nos manifests docatalogacima.
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
inventoryde 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 applya 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.
-
BURNS, B.; GRANT, B.; OPPENHEIMER, D.; BREWER, E.; WILKES, J. Borg, Omega, and Kubernetes. ACM Queue, v. 14, n. 1, 2016. ↩
-
Documentação do Kubernetes. Concepts; Horizontal Pod Autoscaling; Configure liveness, readiness and startup probes. ↩
-
Kubernetes SIG Network. Gateway API. ↩
-
KEDA. Kubernetes Event-driven Autoscaling; Karpenter. Documentation. ↩
-
BURNS, B.; BEDA, J.; HIGHTOWER, K.; EVENSON, L. Kubernetes: Up and Running. 3rd ed. O'Reilly, 2022. ↩