A maioria dos tutoriais de Kubernetes ensina o suficiente para criar um Deployment que funciona no laptop. Produção é diferente: o que não está configurado vai falhar de formas que um tutorial nunca cobre. Este conceito não é introdução ao Kubernetes — é o que falta depois que você já sabe criar Deployments, Services, e ConfigMaps.
Resources e limites — a base de tudo
Kubernetes gerencia recursos de dois tipos: CPU (compressível) e memória (não compressível). Essa distinção é fundamental:
- CPU é compressível: se um container tenta usar mais CPU do que o seu limit, o kernel throttle o processo — ele roda mais devagar, mas não morre.
- Memória não é compressível: se um container ultrapassa o memory limit, o kernel envia SIGKILL (OOMKilled) — o container morre imediatamente.
Resources em Kubernetes tem duas dimensões: request (o que o scheduler garante que estará disponível) e limit (o máximo que o container pode usar):
resources:
requests:
memory: "256Mi"
cpu: "250m" # 250 milicores = 0.25 core
limits:
memory: "512Mi"
cpu: "500m"
O scheduler do Kubernetes usa os requests para decidir em qual nó colocar um Pod — ele garante que o nó tem ao menos aquela quantidade disponível. Os limits são aplicados pelo cgroup do container. O erro clássico é não definir limites: um container sem limits.memory pode consumir toda a memória do nó e causar OOM em outros containers.
Containers sem limits são a causa mais comum de cascata de falhas em clusters Kubernetes de produção. Um leak de memória em uma aplicação consome toda a RAM do nó, o nó remove pods por OOM, os pods vão para outros nós, os outros nós ficam sobrecarregados — e o cluster fica indisponível em cascata. Sempre defina limits. Use LimitRange no namespace para forçar um default se os times esquecerem.
A escolha de valores corretos para requests/limits requer métricas históricas: meça o consumo real em produção por alguns dias, defina requests em ~75% do percentil 95, e limits em 2x o request. Ajuste baseado em alertas de throttling de CPU ou OOMKills.
HPA — Horizontal Pod Autoscaler
HPA escala o número de réplicas de um Deployment (ou StatefulSet) baseado em métricas. A configuração mais simples usa CPU:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: meu-servico-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: meu-servico
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # escala quando CPU média > 70% do request
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
HPA pode também usar métricas customizadas (via Prometheus Adapter) — escalar por número de mensagens na fila, latência p99, ou qualquer métrica que você expõe. O padrão de "escalar por tamanho de fila" é particularmente útil para workloads de processamento assíncrono.
O problema mais sutil do HPA é o período de cooldown: por padrão, Kubernetes não reduz o número de réplicas por 5 minutos após um scale-down. Se seu tráfego tem picos muito curtos (bursts de 30 segundos), o HPA pode não responder rápido o suficiente. Ajuste behavior.scaleDown.stabilizationWindowSeconds.
VPA — Vertical Pod Autoscaler
Enquanto HPA escala horizontalmente (mais réplicas), VPA ajusta requests de um Pod individualmente. Útil para workloads que não escalam horizontalmente (bancos de dados com estado) ou para calibrar requests de forma contínua:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: meu-servico-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: meu-servico
updatePolicy:
updateMode: "Off" # Off = apenas recomenda, não aplica automaticamente
O modo Off (ou Initial) é o mais seguro em produção: o VPA analisa o consumo histórico e recomenda requests ideais, mas não reinicia pods automaticamente. Use kubectl get vpa meu-servico-vpa para ver as recomendações e ajuste manualmente. VPA com updateMode: Auto reinicia pods quando as recomendações mudam — pode causar instabilidade em serviços com estado ou tráfego constante.
Usar HPA e VPA simultaneamente no mesmo Deployment é possível mas requer cuidado: não use VPA em modo Auto com HPA baseado em CPU — eles competem. O padrão seguro: HPA em CPU/custom metrics, VPA em modo Off para calibrar os requests que o HPA usa como base de cálculo.
PodDisruptionBudget — disponibilidade durante manutenções
PDB (PodDisruptionBudget) define quantos pods de um Deployment podem ser indisponíveis simultaneamente durante interrupções voluntárias: node drain (manutenção de nó), rolling update de nó, ou operações do cluster como upgrades de versão do Kubernetes.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: meu-servico-pdb
spec:
minAvailable: 2 # ou maxUnavailable: 1 — mas não os dois
selector:
matchLabels:
app: meu-servico
Sem PDB, um kubectl drain node-1 pode remover todos os pods de um Deployment de um nó se todas as réplicas estiverem no mesmo nó. Com minAvailable: 2, o drain aguarda que pelo menos 2 réplicas estejam disponíveis antes de remover cada pod — evitando downtime durante manutenção planejada.
PDB protege apenas contra interrupções voluntárias. Se um nó morrer abruptamente (falha de hardware), o PDB não se aplica — os pods são terminados sem respeito ao budget. Para proteção real, distribua réplicas por múltiplos nós com PodAntiAffinity.
Probes — semântica correta é crítica
Kubernetes tem três tipos de probe, cada um com semântica distinta:
Liveness probe: "o container está vivo?" — se falhar, Kubernetes reinicia o container. Use para detectar deadlocks ou estados irrecuperáveis. Nunca teste dependências externas na liveness probe: se seu banco de dados estiver down e todos os containers falharem na liveness probe, o Kubernetes vai reiniciar todos eles simultaneamente — piorando o problema.
Readiness probe: "o container está pronto para receber tráfego?" — se falhar, o pod é removido do Service Endpoint (não recebe tráfego novo), mas não é reiniciado. Use para sinalizar que o container está aquecendo (loading caches, aguardando conexão ao banco), ou que está temporariamente sobrecarregado. Dependências externas podem e devem ser testadas aqui.
Startup probe: "o container completou a inicialização?" — enquanto falhar, as liveness e readiness probes são desabilitadas. Use para containers com inicialização lenta (JVM, aplicações com warmup de cache). Evita que a liveness probe mate um container que ainda está inicializando.
livenessProbe:
httpGet:
path: /healthz # responde 200 se o processo está funcional
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 3 # reinicia após 3 falhas consecutivas
readinessProbe:
httpGet:
path: /ready # responde 200 se pode receber tráfego (inclui deps)
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 3
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30 # até 30 * 10s = 5 min para inicializar
periodSeconds: 10
O endpoint /healthz deve responder apenas "o processo está vivo" — sem verificar banco, cache, ou qualquer coisa externa. O endpoint /ready pode verificar dependências essenciais, mas com timeout curto para não bloquear o probe.
NetworkPolicies — segmentação de rede no cluster
Por padrão, todos os pods em um cluster Kubernetes podem se comunicar livremente entre si — incluindo entre namespaces diferentes. NetworkPolicy é o recurso que define quem pode falar com quem, como firewalls dentro do cluster.
O baseline recomendado é deny-all + allow explícito:
# 1. Negar todo tráfego de entrada e saída no namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: producao
spec:
podSelector: {} # aplica a todos os pods
policyTypes:
- Ingress
- Egress
---
# 2. Permitir explicitamente o que é necessário
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api-to-db
namespace: producao
spec:
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: api-server
ports:
- protocol: TCP
port: 5432
NetworkPolicies requerem um CNI plugin que as implemente: Calico, Cilium, ou Weave. O CNI padrão do EKS (AWS VPC CNI) não implementa NetworkPolicy — você precisa instalar Calico ou migrar para Cilium. GKE e AKS têm suporte nativo quando ativado explicitamente.
RBAC de cluster — princípio do menor privilégio
O RBAC do Kubernetes controla quais usuários e service accounts podem fazer o quê no cluster. Os conceitos são:
- Role / ClusterRole: define permissões (verbs em resources). Role é namespace-scoped; ClusterRole é cluster-wide.
- RoleBinding / ClusterRoleBinding: associa um Role a um sujeito (user, group, ou service account).
# Role para um serviço que só precisa ler Secrets no próprio namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-reader
namespace: meu-servico
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
resourceNames: ["db-credentials", "api-key"] # restringir a secrets específicos
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: meu-servico-reads-secrets
namespace: meu-servico
subjects:
- kind: ServiceAccount
name: meu-servico-sa
namespace: meu-servico
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: secret-reader
Nunca use cluster-admin para service accounts de aplicação. Prefira roles namespace-scoped com o mínimo de permissões necessárias. Valide periodicamente com kubectl auth can-i --list --as=system:serviceaccount:meu-servico:meu-servico-sa.
NodeAffinity e PodAntiAffinity
NodeAffinity controla em quais nós um pod pode ser agendado. PodAntiAffinity garante que pods de um Deployment sejam distribuídos por nós ou zonas diferentes:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/arch
operator: In
values: [amd64] # só nós x86-64 (não ARM)
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
topologyKey: topology.kubernetes.io/zone # prefere nós em zonas diferentes
labelSelector:
matchLabels:
app: meu-servico
A distinção entre required e preferred é crítica: required faz o pod ficar pendente se não houver nó compatível (pode causar indisponibilidade). preferred é uma preferência que o scheduler tenta satisfazer mas ignora se não for possível. Para anti-afinidade de zona, use preferred exceto quando a distribuição por zona é um requisito hard de disponibilidade.
Por que Kubernetes falha em produção — checklist
Os modos de falha mais comuns em clusters Kubernetes em produção:
- Sem resource limits: um container sem
limits.memoryconsome memória do nó → OOM → cascata. Mitigação: LimitRange no namespace com defaults. - Probes mal configuradas: liveness probe dependendo de banco → reinício em cascata quando banco fica lento. Mitigação: separar liveness de readiness.
- Sem PDB: upgrade do cluster drena nó e derruba todas as réplicas. Mitigação: PDB para todo Deployment com mais de 1 réplica.
- Réplicas no mesmo nó: 3 réplicas no mesmo nó perdem todas quando o nó falha. Mitigação: PodAntiAffinity por nó e zona.
- Sem NetworkPolicy: comprometimento de um pod permite movimento lateral para todos os outros. Mitigação: default-deny-all + allow explícito.
- Image pull sem digest: a tag
:latestpode apontar para imagens diferentes em diferentes nodes. Mitigação: sempre use SHA digest em produção (image: minha-app@sha256:abc123). - Sem requests definidos: o scheduler não tem informação para fazer bin-packing eficiente → nós subdimensionados ou superdimensionados.
- RBAC muito permissivo: service account com
cluster-admincomprometido dá controle total do cluster. Mitigação: principle of least privilege.
EKS, GKE, e AKS delegam a operação do control plane ao provider: você não opera etcd, não faz upgrade do API server, não se preocupa com HA do scheduler. O custo é de ~$70–150/mês por cluster e alguma perda de controle na configuração do control plane. Para a maioria das equipes com menos de 10 clusters, managed Kubernetes paga o investimento. A exceção: requisitos de air-gap, compliance que exige hardware específico, ou clusters em edge computing.
Decisões de engenharia
HPA (mais réplicas) é a escolha padrão para serviços stateless que respondem bem à escala horizontal — APIs, workers assíncronos. Escala rápido (segundos) e não causa interrupção de tráfego. VPA (mais recursos por pod) é adequado para workloads que não escalam horizontalmente (um pod de banco, jobs batch de ML com uso de memória crescente) ou para calibrar requests periodicamente com updateMode: Off. Nunca use HPA em CPU simultaneamente com VPA em Auto — eles competem: VPA sobe o request de CPU, HPA acha que CPU está alta e cria mais réplicas, VPA recalibra, loop infinito. O padrão seguro é HPA para escalabilidade + VPA em Off para recomendações de rightsizing.
LimitRange define defaults e máximos por container — se um Deployment não especifica resources, o LimitRange injeta os defaults automaticamente. É proteção contra containers sem limits que podem consumir o nó inteiro. ResourceQuota controla o consumo agregado do namespace — "esse namespace pode usar no máximo 20 cores e 40Gi de memória no total". Use ambos juntos: LimitRange garante que cada workload tem limites definidos; ResourceQuota garante que a soma dos workloads não explode o cluster. Em clusters multi-tenant (múltiplos times compartilhando o mesmo cluster), ResourceQuota por namespace é o mecanismo de isolamento de cotas entre times.
Liveness deve testar apenas que o processo está vivo e capaz de processar — sem dependências externas. Um endpoint /healthz que retorna 200 se o event loop (Node.js), thread pool (Java), ou runtime (Go) está responsivo. Se a liveness depende do banco e o banco fica lento, Kubernetes reinicia todos os pods simultaneamente — amplificando o incidente. Readiness deve testar que o pod está pronto para receber tráfego — inclui dependências críticas (conexão ao banco estabelecida, cache aquecido, feature flags carregadas). Se a readiness falha, o pod sai do load balancer mas não morre — pode se recuperar quando o banco voltar, sem restart. A separação semântica é: liveness = matar e recriar vai ajudar? readiness = devo enviar tráfego agora?
Managed é a escolha correta para a maioria dos casos: control plane altamente disponível sem operação (etcd, API server, scheduler gerenciados pelo provider), upgrades de versão com um comando, integração nativa com IAM, load balancers, e storage do provider. Custo: $70–150/mês por cluster + custo dos nós. Self-managed (kubeadm, K3s, RKE2) faz sentido para: ambientes air-gapped sem acesso à internet, edge computing em hardware específico, clusters de CI temporários onde o custo do managed cluster soma, ou requisitos de compliance que exigem controle total da versão de todos os componentes. K3s especificamente é adequado para clusters pequenos (IoT, edge) por ter footprint menor. Para equipes sem especialista dedicado em Kubernetes, managed elimina a categoria inteira de incidentes de control plane.
Perguntas de entrevista
Por que liveness probe nunca deve testar dependências externas como banco de dados?
A semântica da liveness probe é "reiniciar este container vai resolver o problema?". Se a liveness falha, Kubernetes mata e recria o container. Isso faz sentido para deadlocks e estados internos irrecuperáveis — reiniciar resolve. Mas se a liveness testa o banco de dados e o banco fica temporariamente lento ou indisponível, todos os pods do Deployment começam a falhar na liveness probe simultaneamente. O Kubernetes reinicia todos eles ao mesmo tempo — o que piora o problema por três razões: (1) o reinício não resolve a indisponibilidade do banco; (2) pods em reinício não recebem tráfego, amplificando o impacto; (3) o padrão de "restart em loop" consume recursos e pode colocar o node em pressão.
A solução é separar semântica: liveness testa apenas o processo interno (event loop, goroutines, thread pool). Readiness testa dependências — quando a readiness falha por indisponibilidade do banco, o pod sai do load balancer (não recebe tráfego novo) mas permanece vivo. Quando o banco volta, a readiness passa, o pod volta ao load balancer. Sem restart, sem amplificação do incidente.
O que acontece sem PodDisruptionBudget durante um node drain e por que isso causa downtime?
Sem PDB, kubectl drain node-1 remove imediatamente todos os pods do nó — sem verificar quantas réplicas estão disponíveis em outros nós. Se todas as 3 réplicas de um Deployment estiverem no node-1 (o que acontece quando o scheduler não tem PodAntiAffinity configurado), o drain remove todas as 3 simultaneamente. O Deployment fica com 0 réplicas durante o tempo que o scheduler recria os pods em outros nós — tipicamente 30–60 segundos de downtime completo.
Com minAvailable: 2 no PDB, o drain torna-se um processo respeitoso: antes de remover cada pod, o Kubernetes verifica que pelo menos 2 réplicas estão disponíveis e healthy em outros nós. Se apenas 2 réplicas estão disponíveis, o drain aguarda antes de remover a terceira. O resultado é zero downtime durante manutenção planejada — que inclui upgrades de versão do Kubernetes (que drain os nós um por um). PDB é obrigatório para qualquer serviço que precisa de disponibilidade contínua e passa por upgrades regulares do cluster.
Qual a diferença entre required e preferred em affinity rules, e quando cada uma pode causar problemas?
requiredDuringSchedulingIgnoredDuringExecution: o scheduler só coloca o pod em nós que satisfazem a regra. Se nenhum nó satisfaz, o pod fica no estado Pending indefinidamente. "IgnoredDuringExecution" significa que se um nó já rodando o pod deixar de satisfazer a regra (ex: uma label foi removida do nó), o pod não é evictado — a regra só se aplica no momento do scheduling.
preferredDuringSchedulingIgnoredDuringExecution: o scheduler tenta satisfazer a regra mas coloca o pod em qualquer nó disponível se a preferência não puder ser satisfeita. Tem um weight (1–100) que permite priorizar entre múltiplas preferências.
O problema com required: PodAntiAffinity required por nó em um cluster com 3 nós e 4 réplicas faz o 4° pod ficar pendente para sempre — não há nó que não tenha já um pod da mesma app. Use preferred para anti-afinidade de zona (se só houver 2 AZs para 3 réplicas, você quer que 2 fiquem numa AZ em vez de nenhuma ser criada). Use required apenas quando a regra de scheduling é realmente um requisito hard — como "só nós com GPU" para workloads de ML.
Por que o networking padrão do Kubernetes é inseguro e o que NetworkPolicy resolve?
Por padrão, Kubernetes implementa um modelo de rede flat: qualquer pod pode se comunicar com qualquer outro pod em qualquer namespace usando o IP interno do pod. Um pod comprometido no namespace frontend pode fazer requests diretos ao pod de banco no namespace database, ao serviço de pagamentos no namespace billing, e a qualquer outro serviço — sem nenhuma restrição de rede. Isso viola o princípio de defense in depth: comprometer um componente de baixo privilégio dá acesso à rede interna inteira.
NetworkPolicy resolve criando firewalls declarativos no nível de pod/namespace. O padrão de segurança é deny-all como baseline: uma NetworkPolicy com podSelector: {} e sem regras de ingress/egress bloqueia todo tráfego por padrão. Então você adiciona políticas explícitas de allow para os fluxos necessários: "frontend pode falar com api na porta 8080", "api pode falar com postgres na porta 5432". Qualquer movimento lateral não explicitamente autorizado é bloqueado. O custo operacional é manter as políticas atualizadas conforme a topologia de serviços evolui — motivo pelo qual Cilium com visualização de network flow é útil: você vê quais conexões estão sendo feitas e quais estão sendo bloqueadas.
Como o scheduler do Kubernetes usa requests (e não limits) para tomar decisões de placement?
O scheduler do Kubernetes não conhece o consumo real de um pod — ele usa os requests declarados como representação do que o pod vai usar. O algoritmo de scheduling funciona assim: para cada pod pendente, o scheduler lista os nós viáveis (aqueles com recursos disponíveis suficientes para os requests do pod), aplica predicates (NodeAffinity, Taints/Tolerations, PodAntiAffinity), e então scores os nós viáveis para escolher o melhor (geralmente o que tem mais recursos disponíveis, para espalhar a carga).
"Recursos disponíveis" no nó = capacidade do nó − soma dos requests de todos os pods rodando nele. Os limits são irrelevantes para o scheduler — eles são aplicados pelos cgroups em runtime. Isso tem implicações importantes: (1) se requests são muito menores que o uso real, o scheduler vai empilhar pods em nós que parecem ter capacidade mas não têm — resultando em OOM; (2) se requests são muito maiores que o uso real, os nós aparecem "cheios" mesmo com CPU/memória ociosa — desperdício de recursos. A calibração correta de requests (via VPA em modo Off ou análise de Prometheus) é o que torna o cluster eficiente.
Como praticar
-
Configurar resources, probes e PDB para um Deployment existente. Pegue um Deployment em qualquer cluster (Kind local ou Minikube) sem resources e probes configurados. Adicione requests/limits baseados no consumo observado (
kubectl top pod), implemente os três tipos de probe com endpoints corretos na aplicação, e adicione um PDB comminAvailable: 1.
Critério:kubectl describe poddeve mostrar Requests e Limits definidos. Simular falha da dependência externa (parar o banco) deve fazer a readiness probe falhar sem que o pod seja reiniciado (liveness não deve ser afetada).kubectl drainde um nó com o PDB configurado deve respeitar o budget — aguardando antes de remover o pod seminAvailablenão estiver satisfeito. -
Implementar NetworkPolicy de deny-all + allow explícito em um namespace. Em um cluster Kind com Calico ou Cilium como CNI, crie dois Deployments (api e db) no mesmo namespace. Verifique que eles se comunicam. Adicione a NetworkPolicy de deny-all e verifique que a comunicação é bloqueada. Adicione a política de allow explícito e verifique que apenas o fluxo autorizado funciona.
Critério: Antes da NetworkPolicy,kubectl exec api-pod -- curl db-service:5432deve funcionar. Após deny-all, deve retornar connection refused ou timeout. Após allow explícito, a comunicação api→db deve funcionar mas db→api (fluxo reverso não autorizado) deve continuar bloqueado. -
Configurar HPA com métricas de CPU e observar comportamento de scale. Crie um Deployment com HPA configurado para escalar entre 2 e 10 réplicas com target de 50% de CPU. Use uma ferramenta de stress (como
stress-ngem um container de teste) para aumentar a carga e observe o HPA escalar. Monitore comkubectl get hpa -wekubectl get pods -w.
Critério: Sob carga, o HPA deve aumentar o número de réplicas até reduzir a CPU média para próximo do target (50%). Após remover a carga, aguardar o período de cooldown (padrão 5 min) e verificar que o HPA escala down. Documentar o tempo de resposta do scale-up: quantos segundos entre a carga aumentar e os novos pods estarem ready. -
Configurar PodAntiAffinity por zona e verificar distribuição. Em um cluster com nós em múltiplas zonas (ou simular com labels em Kind), configure um Deployment com
preferredDuringSchedulingPodAntiAffinity usandotopologyKey: topology.kubernetes.io/zone. Escale para 6 réplicas e verifique a distribuição por zona.
Critério:kubectl get pods -o widedeve mostrar réplicas distribuídas entre as zonas disponíveis, não concentradas em uma única zona. Adicionar uma réplica quando já existe uma distribuição balanceada deve colocá-la na zona com menor contagem. Verificar comkubectl describe podque a affinity rule está sendo aplicada. -
Auditar o RBAC de um namespace e aplicar least privilege. Em um cluster existente (ou Kind), liste os ClusterRoleBindings e RoleBindings do namespace com
kubectl get rolebindings,clusterrolebindings -A. Identifique service accounts comcluster-adminou roles excessivamente permissivos. Para um service account específico, crie um Role mínimo com apenas os verbs e resources necessários e valide comkubectl auth can-i.
Critério:kubectl auth can-i --list --as=system:serviceaccount:<ns>:<sa>deve mostrar apenas as permissões explicitamente concedidas no novo Role. Tentar uma operação não autorizada (ex:kubectl get secrets --as=system:serviceaccount:<ns>:<sa>para um secret não incluído no Role) deve retornar "forbidden".
Referências para aprofundar
- book Kubernetes: Up and Running (3rd ed.) — Brendan Burns et al. (O'Reilly, 2022).
- book Kubernetes Patterns (2nd ed.) — Bilgin Ibryam & Roland Huß (O'Reilly, 2023).
- book Container Security — Liz Rice (O'Reilly, 2020).
- docs Kubernetes Docs — Configure Liveness, Readiness and Startup Probes.
- article A Visual Guide on Troubleshooting Kubernetes Deployments — Learnk8s.
- docs EKS Best Practices Guide — AWS.
- docs Cilium — NetworkPolicy Editor e Documentação.
- article Production Best Practices Checklist — Learnk8s.
- article Kubernetes the Hard Way — Kelsey Hightower.
- article CNCF Security Whitepaper — Cloud Native Security — CNCF TAG Security.
- article Vertical Pod Autoscaler — How It Works — Kubernetes Blog.
- book Production Kubernetes — Josh Rosso et al. (O'Reilly, 2021).