Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Kubernetes Observability Lab — Simulação de Incidente com Juice Shop, Nginx, Prometheus e Grafana

Laboratório local com Kubernetes (kind) simulando uma arquitetura real com reverse proxy, observabilidade completa e ciclo completo de incidente: detecção → investigação → causa raiz → correção → validação.


Sumário

  1. Descrição geral
  2. Objetivos do laboratório
  3. Arquitetura
  4. Tecnologias utilizadas
  5. Estrutura de pastas
  6. Fluxo de tráfego
  7. Namespaces
  8. Deploy da aplicação Juice Shop
  9. Nginx reverse proxy
  10. Observabilidade com Prometheus e Grafana
  11. Como executar o projeto
  12. Como validar o ambiente
  13. Simulação de incidente
  14. Detecção do problema
  15. Investigação
  16. Causa raiz
  17. Correção aplicada
  18. Validação pós-correção
  19. Mitigação com rate limit no Nginx
  20. Alertas recomendados
  21. Lições aprendidas
  22. Próximas melhorias
  23. Conclusão

1. Descrição geral

Este projeto é um laboratório local e controlado que simula práticas reais de operação em Kubernetes. O ambiente foi construído do zero usando kind (Kubernetes in Docker), sem Minikube e sem Helm, com o objetivo de desenvolver e demonstrar habilidades em:

  • provisionamento e configuração de clusters Kubernetes;
  • deploy de aplicações com múltiplas réplicas;
  • configuração manual de reverse proxy com Nginx;
  • instrumentação de observabilidade com Prometheus e Grafana;
  • simulação de incidente por saturação de recursos;
  • ciclo completo de troubleshooting e remediação.

A aplicação alvo é o OWASP Juice Shop, uma aplicação web intencionalmente vulnerável usada amplamente em laboratórios de segurança e DevOps. Aqui ela serve como carga de trabalho realista para o laboratório de observabilidade.

Aviso: Todos os testes de carga foram executados exclusivamente contra localhost:8080, em ambiente totalmente isolado, sem qualquer tráfego externo, sem brute force e sem acesso a rotas sensíveis. O projeto tem fins exclusivamente educacionais e de portfólio.


2. Objetivos do laboratório

  • Montar um cluster Kubernetes local funcional com kind;
  • Implantar uma aplicação real com múltiplas réplicas e resource limits;
  • Configurar um reverse proxy com Nginx sem Ingress Controller;
  • Implementar observabilidade completa com Prometheus, Grafana, kube-state-metrics, cAdvisor e Nginx Exporter;
  • Simular uma falha realista por saturação de recursos sob carga;
  • Detectar o problema usando métricas do Prometheus;
  • Investigar a causa raiz com queries PromQL;
  • Aplicar e validar uma correção objetiva;
  • Adicionar uma mitigação complementar com rate limit no Nginx;
  • Documentar todo o processo como material técnico de portfólio.

3. Arquitetura

┌─────────────────────────────────────────────────────────────────┐
│                        Host (Windows)                           │
│                                                                 │
│  Usuário / hey.exe                                              │
│       │                                                         │
│  localhost:8080                                                 │
│       │                                                         │
│  ┌────▼──────────────────────────────────────────────────────┐  │
│  │                   Cluster kind                            │  │
│  │                                                           │  │
│  │  Namespace: ingress-nginx                                 │  │
│  │  ┌───────────────────────────────────────────────────┐   │  │
│  │  │  Service NodePort (porta 8080)                    │   │  │
│  │  │       │                                           │   │  │
│  │  │  Pod Nginx                                        │   │  │
│  │  │  ├── container: nginx (porta 80)                  │   │  │
│  │  │  └── container: nginx-exporter (porta 9113)       │   │  │
│  │  └───────────────────────────────────────────────────┘   │  │
│  │                    │ proxy_pass                            │  │
│  │  Namespace: app    │                                       │  │
│  │  ┌─────────────────▼─────────────────────────────────┐   │  │
│  │  │  Service ClusterIP juice-shop (porta 3000)        │   │  │
│  │  │       │                                           │   │  │
│  │  │  Deployment juice-shop (3 réplicas)               │   │  │
│  │  │  ├── Pod juice-shop-xxx-1                         │   │  │
│  │  │  ├── Pod juice-shop-xxx-2                         │   │  │
│  │  │  └── Pod juice-shop-xxx-3                         │   │  │
│  │  └───────────────────────────────────────────────────┘   │  │
│  │                                                           │  │
│  │  Namespace: monitoring                                    │  │
│  │  ┌───────────────────────────────────────────────────┐   │  │
│  │  │  Prometheus  ←→  kube-state-metrics               │   │  │
│  │  │  Grafana     ←→  kubelet/cAdvisor                 │   │  │
│  │  │                  nginx-exporter                   │   │  │
│  │  └───────────────────────────────────────────────────┘   │  │
│  └───────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘

4. Tecnologias utilizadas

Componente Função
kind Criação do cluster Kubernetes local
OWASP Juice Shop Aplicação alvo do laboratório
Nginx Reverse proxy manual (sem Ingress Controller)
Prometheus Coleta e armazenamento de métricas
Grafana Visualização de dashboards
kube-state-metrics Métricas de estado dos objetos Kubernetes
Nginx Prometheus Exporter Expõe métricas do Nginx via /stub_status
cAdvisor / kubelet Métricas de CPU, memória e containers
hey Ferramenta de teste de carga HTTP
PowerShell / kubectl Gerenciamento do cluster e execução de comandos

5. Estrutura de pastas

.
├── infra/
│   └── kind/
│       └── kind-config.yaml          # Configuração do cluster kind
├── namespaces/
│   └── namespaces.yaml               # Definição dos namespaces
├── app/
│   ├── deployment.yaml               # Deployment do Juice Shop (3 réplicas)
│   └── service.yaml                  # Service ClusterIP do Juice Shop
├── nginx/
│   ├── configmap.yaml                # Configuração do Nginx (reverse proxy)
│   ├── deployment.yaml               # Deployment do Nginx + nginx-exporter
│   └── service.yaml                  # Service NodePort do Nginx
├── monitoring/
│   ├── prometheus/                   # Manifests do Prometheus
│   └── grafana/                      # Manifests do Grafana
└── tools/
    └── hey.exe                       # Ferramenta de teste de carga

6. Fluxo de tráfego

Usuário / hey.exe
      │
      │  HTTP GET http://localhost:8080/
      ▼
Service NodePort (ingress-nginx)
      │  porta 8080 → porta 80
      ▼
Pod Nginx (container: nginx)
      │  proxy_pass http://juice-shop.app.svc.cluster.local:3000
      ▼
Service ClusterIP (namespace: app)
      │  porta 3000
      ▼
Pods Juice Shop (3 réplicas)
      │
      ▼
Resposta HTTP 200 OK

O Nginx atua como ponto de entrada único, encaminhando todo o tráfego para o Service interno da Juice Shop via DNS interno do Kubernetes (juice-shop.app.svc.cluster.local). O balanceamento entre os 3 pods é feito automaticamente pelo kube-proxy.


7. Namespaces

O projeto usa três namespaces isolados para organização e clareza:

Namespace Conteúdo
app Deployment e Service do Juice Shop
ingress-nginx Deployment e Service do Nginx reverse proxy
monitoring Prometheus, Grafana, kube-state-metrics
# namespaces/namespaces.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: app
---
apiVersion: v1
kind: Namespace
metadata:
  name: ingress-nginx
---
apiVersion: v1
kind: Namespace
metadata:
  name: monitoring

8. Deploy da aplicação Juice Shop

A Juice Shop é implantada como um Deployment com 3 réplicas no namespace app.

Resource limits em estado saudável

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 512Mi

Os requests definem o mínimo garantido pelo scheduler ao alocar o pod em um nó. Os limits definem o teto máximo de uso — ao ultrapassar o limite de memória, o container é encerrado com OOMKilled; ao ultrapassar o limite de CPU, o container sofre throttling (é desacelerado, não encerrado).


9. Nginx reverse proxy

O Nginx é configurado manualmente via ConfigMap, sem uso de Ingress Controller. Essa abordagem demonstra controle direto sobre a camada de proxy.

Estrutura do pod

O pod Nginx contém dois containers:

Container Função
nginx Recebe requisições e faz proxy para a Juice Shop
nginx-exporter Expõe métricas do Nginx para o Prometheus

ConfigMap principal

# nginx-config — montado em /etc/nginx/conf.d/default.conf

server {
  listen 80;

  location / {
    proxy_pass http://juice-shop.app.svc.cluster.local:3000;

    proxy_http_version 1.1;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

server {
  listen 8080;

  location /stub_status {
    stub_status;

    allow 127.0.0.1;
    deny all;
  }
}

O endpoint /stub_status expõe métricas internas do Nginx (conexões ativas, requisições totais etc.) que são coletadas pelo nginx-exporter e enviadas ao Prometheus.


10. Observabilidade com Prometheus e Grafana

Fontes de dados coletadas pelo Prometheus

Fonte Métricas coletadas
kubelet / cAdvisor CPU, memória, rede e disco por container
kube-state-metrics Estado dos Deployments, pods, réplicas, restarts
nginx-exporter Conexões ativas, requisições, status do Nginx
prometheus (self) Saúde e performance do próprio Prometheus

Principais queries utilizadas

Uso de CPU por pod:

sum by (pod) (
  rate(container_cpu_usage_seconds_total{namespace="app", pod=~"juice-shop.*", container!="POD", container!=""}[2m])
)

Uso de memória por pod (MiB):

sum by (pod) (
  container_memory_working_set_bytes{namespace="app", pod=~"juice-shop.*", container!="POD", container!=""}
) / 1024 / 1024

Restarts por container:

kube_pod_container_status_restarts_total{namespace="app", pod=~"juice-shop.*"}

Réplicas disponíveis:

kube_deployment_status_replicas_available{namespace="app", deployment="juice-shop"}

Réplicas indisponíveis:

kube_deployment_status_replicas_unavailable{namespace="app", deployment="juice-shop"}

CPU throttling:

sum by (pod) (
  rate(container_cpu_cfs_throttled_periods_total{namespace="app", pod=~"juice-shop.*", container!="POD", container!=""}[2m])
)

Percentual de uso de memória em relação ao limit:

(
  sum by (pod) (
    container_memory_working_set_bytes{namespace="app", pod=~"juice-shop.*", container!="POD", container!=""}
  )
  /
  sum by (pod) (
    kube_pod_container_resource_limits{namespace="app", pod=~"juice-shop.*", container="juice-shop", resource="memory"}
  )
) * 100

11. Como executar o projeto

Pré-requisitos

  • Docker instalado e em execução
  • kind instalado
  • kubectl configurado
  • PowerShell (Windows) ou terminal equivalente

Criação do cluster

kind create cluster --config infra/kind/kind-config.yaml

Aplicação dos manifests

# Namespaces
kubectl apply -f namespaces/namespaces.yaml

# Aplicação
kubectl apply -f app/deployment.yaml
kubectl apply -f app/service.yaml

# Nginx
kubectl apply -f nginx/configmap.yaml
kubectl apply -f nginx/deployment.yaml
kubectl apply -f nginx/service.yaml

# Monitoramento
kubectl apply -f monitoring/prometheus/
kubectl apply -f monitoring/grafana/

Acessos

Serviço Comando URL
Juice Shop via Nginx — (NodePort exposto) http://localhost:8080
Prometheus kubectl port-forward -n monitoring svc/prometheus 9090:9090 http://localhost:9090
Grafana kubectl port-forward -n monitoring svc/grafana 3000:3000 http://localhost:3000

12. Como validar o ambiente

Verificar todos os pods

kubectl get pods -A

Saída esperada: todos os pods com STATUS Running e READY completo.

Verificar deployments

kubectl get deploy -A

Verificar services

kubectl get svc -A

Verificar pods da aplicação com detalhes

kubectl get pods -n app -o wide

Testar acesso à aplicação

curl.exe -I http://localhost:8080

Resposta esperada:

HTTP/1.1 200 OK

Verificar eventos do namespace app

kubectl get events -n app --sort-by=.lastTimestamp

Verificar targets no Prometheus

Acesse http://localhost:9090/targets e confirme que todos os targets estão com status UP.


13. Simulação de incidente

O objetivo desta fase foi simular uma falha realista de saturação de recursos, causada por um pico controlado de tráfego local. Nenhuma rota sensível foi acessada, nenhum tráfego externo foi gerado e nenhum sistema de terceiros foi envolvido.

Linha de base saudável

Antes de iniciar a simulação, o ambiente estava em estado saudável:

  • Juice Shop: 3 pods Running, sem restarts
  • Nginx: Running
  • Prometheus e Grafana: funcionando
  • Todos os targets do Prometheus: UP
  • Acesso direto: HTTP/1.1 200 OK

Passo 1 — Redução proposital dos resource limits

Os limites do Deployment da Juice Shop foram reduzidos para simular um ambiente subdimensionado:

resources:
  requests:
    cpu: 50m
    memory: 96Mi
  limits:
    cpu: 75m
    memory: 128Mi

Após kubectl apply, o rollout foi concluído e os pods continuaram Running — a degradação não é imediata, mas a aplicação agora está vulnerável a sobrecarga.

Passo 2 — Teste moderado com recursos reduzidos

.\tools\hey.exe -n 300 -c 30 http://localhost:8080/

Resultado:

Average:  0.7631s
P95:      2.0985s
Slowest:  2.3949s
Requests/sec: 34.83
Status code: [200] 300 responses

A aplicação não caiu e não houve restarts, mas a latência já foi significativamente maior do que o esperado em estado saudável — evidência de saturação de CPU.

Passo 3 — Teste forte com recursos reduzidos

.\tools\hey.exe -n 3000 -c 300 http://localhost:8080/

Resultado:

Average:  4.8497s
P95:      8.9186s
P99:     10.6406s
Slowest: 11.0405s
Requests/sec: 52.59

Status code distribution:
[200] 2980 responses
[502]   12 responses

Error distribution:
[8] context deadline exceeded

Evidências no Kubernetes durante e após o teste

Pods: 3/3 Running
Deployment: 3/3 disponível
curl após o teste: HTTP/1.1 200 OK

Eventos registrados:

Readiness probe failed: context deadline exceeded
Liveness probe failed: context deadline exceeded
Container juice-shop failed liveness probe, will be restarted

Em uma rodada de stress prolongado, um pod chegou a ser reiniciado por falha de liveness probe.

Interpretação

A aplicação não caiu de forma permanente, mas apresentou:

  • degradação severa de latência (P95 quase 9 segundos);
  • queda de throughput (~52 req/s, metade do esperado);
  • erros 502 gerados pelo Nginx ao tentar encaminhar para pods lentos;
  • timeouts no lado do cliente;
  • falhas temporárias de readiness e liveness probe;
  • reinício de pod por falha de liveness.

14. Detecção do problema

Os primeiros sinais foram observados nas métricas do Prometheus durante e após o teste de carga:

Memória por pod:

~100 MiB a 110 MiB por pod (limite: 128Mi)

Uso de memória em relação ao limite:

~78% a 86% do limite — valor criticamente alto para uma aplicação em produção

CPU throttling:

Valores acima de 0 — indicando que os pods estavam sendo desacelerados pelo kernel

Réplicas disponíveis:

3/3 — deployment manteve réplicas funcionais

Restarts:

0 inicialmente → 1 restart em stress prolongado

Os dashboards no Grafana mostraram claramente o aumento de CPU throttling e de consumo de memória durante o pico, confirmando a hipótese de saturação de recursos.


15. Investigação

Passos de investigação seguidos

1. Verificar estado dos pods:

kubectl get pods -n app -o wide

2. Descrever o deployment:

kubectl describe deployment juice-shop -n app

3. Verificar eventos recentes:

kubectl get events -n app --sort-by=.lastTimestamp

Aqui apareceram as evidências de falha de readiness/liveness probe.

4. Checar o YAML do deployment para confirmar os limits:

kubectl get deployment juice-shop -n app -o yaml

5. Analisar métricas no Prometheus:

Query de throttling:

sum by (pod) (
  rate(container_cpu_cfs_throttled_periods_total{namespace="app", pod=~"juice-shop.*", container!="POD", container!=""}[2m])
)

Query de uso de memória vs limite:

(
  sum by (pod) (
    container_memory_working_set_bytes{namespace="app", pod=~"juice-shop.*", container!="POD", container!=""}
  )
  /
  sum by (pod) (
    kube_pod_container_resource_limits{namespace="app", pod=~"juice-shop.*", container="juice-shop", resource="memory"}
  )
) * 100

6. Correlacionar os resultados dos testes de carga com as métricas:

O pico de latência, os erros 502 e os timeouts coincidiram exatamente com o período de maior throttling de CPU e maior uso de memória — confirmando a causa raiz.


16. Causa raiz

A falha parcial foi causada por saturação de recursos nos pods da Juice Shop após a
redução proposital dos limites de CPU e memória. Sob pico controlado de 300 conexões
concorrentes, os pods ficaram lentos para responder, gerando aumento severo de latência,
timeouts no cliente, erros 502 no Nginx e falhas temporárias de readiness/liveness.
A aplicação se recuperou após o pico, mas a experiência do usuário foi degradada.

Detalhamento técnico

Sintoma Causa técnica
Latência alta (P95 ~9s) CPU throttling — pods desacelerados pelo cgroup
Erros 502 no Nginx Pods demoraram mais que o timeout do proxy para responder
Timeouts no cliente Requisições excederam o deadline antes de receber resposta
Falha de readiness probe Pod com CPU throttled não respondeu ao probe a tempo
Falha de liveness probe Idem — o Kubernetes reiniciou o pod por entender que estava travado
Memória em 78–86% do limite Pouca margem de segurança; risco real de OOMKill em pico prolongado

Este não foi um ataque real, não houve brute force, não houve tráfego externo e nenhuma rota sensível foi acessada. Trata-se de uma simulação ética, executada inteiramente em localhost, com fins educacionais e de portfólio.


17. Correção aplicada

A correção consistiu em restaurar os resource limits do Deployment da Juice Shop para valores adequados à carga esperada:

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 512Mi

Aplicação da correção

kubectl apply -f .\app\deployment.yaml
kubectl rollout status deployment/juice-shop -n app

Resultado do rollout

Waiting for deployment "juice-shop" rollout to finish...
3 desired | 3 updated | 3 total | 3 available | 0 unavailable
deployment "juice-shop" successfully rolled out

Todos os pods novos ficaram com status:

READY   STATUS    RESTARTS
1/1     Running   0

18. Validação pós-correção

Teste moderado pós-correção

.\tools\hey.exe -n 300 -c 30 http://localhost:8080/

Teste forte pós-correção

.\tools\hey.exe -n 3000 -c 300 http://localhost:8080/

Comparação — Teste moderado (300 requisições, 30 conexões)

Métrica Antes da correção Depois da correção Melhoria
Average 0.7631s 0.1072s ~7× mais rápido
P95 2.0985s 0.2317s ~9× mais rápido
Slowest 2.3949s 0.2872s ~8× mais rápido
Requests/sec 34.83 242.31 ~7× mais throughput
Erros 0 0

Comparação — Teste forte (3000 requisições, 300 conexões)

Métrica Antes da correção Depois da correção Melhoria
Average 4.8497s 1.0076s ~5× mais rápido
P95 8.9186s 3.1530s ~3× mais rápido
P99 10.6406s 3.5567s ~3× mais rápido
Slowest 11.0405s 4.1683s ~3× mais rápido
Requests/sec 52.59 266.08 ~5× mais throughput
Status 200 2980 3000 100% de sucesso
Status 502 12 0 Eliminado
Timeouts 8 0 Eliminado

Conclusão

A correção foi efetiva. O aumento dos limites de CPU e memória eliminou o throttling, reduziu drasticamente a latência, aumentou o throughput, eliminou todos os erros 502 e timeouts, e manteve os pods estáveis sob carga intensa.


19. Mitigação com rate limit no Nginx

Além de corrigir os recursos da aplicação, foi planejada uma mitigação complementar no Nginx para limitar o volume de requisições por IP. Essa é uma camada de proteção independente: mesmo que os resources sejam adequados, picos abusivos podem ser controlados antes de chegar aos pods.

Diferença entre correção e mitigação

Ação Tipo Efeito
Aumentar CPU/memória da Juice Shop Correção Resolve a causa raiz do problema
Rate limit no Nginx Mitigação Reduz o volume de tráfego que chega aos pods

ConfigMap do Nginx com rate limit

limit_req_zone $binary_remote_addr zone=juice_limit:10m rate=20r/s;

server {
  listen 80;

  location / {
    limit_req zone=juice_limit burst=40 nodelay;
    limit_req_status 429;

    proxy_pass http://juice-shop.app.svc.cluster.local:3000;

    proxy_http_version 1.1;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

server {
  listen 8080;

  location /stub_status {
    stub_status;

    allow 127.0.0.1;
    deny all;
  }
}

Explicação das diretivas

Diretiva Significado
limit_req_zone $binary_remote_addr zone=juice_limit:10m rate=20r/s Cria uma zona de controle por IP com tamanho de 10MB e taxa média de 20 requisições/segundo
limit_req zone=juice_limit burst=40 nodelay Aplica o rate limit com tolerância de burst de 40 requisições sem enfileiramento
limit_req_status 429 Responde com 429 Too Many Requests quando o limite é excedido

Com essa configuração, um único IP que envie mais de 20 req/s (com burst até 40) receberá 429 imediatamente, sem que o excesso chegue aos pods da aplicação. Isso protege os pods de picos abusivos mesmo em situação de recursos apertados.

Para aplicar a mudança:

kubectl apply -f nginx/configmap.yaml
kubectl rollout restart deployment/nginx -n ingress-nginx

20. Alertas recomendados

Os alertas a seguir podem ser criados no Prometheus Alertmanager ou como anotações de alerta em painéis do Grafana.

Restart de container

Dispara se qualquer container no namespace app foi reiniciado nos últimos 5 minutos.

increase(kube_pod_container_status_restarts_total{namespace="app"}[5m]) > 0

Réplicas indisponíveis

Dispara se qualquer réplica do Juice Shop estiver indisponível.

kube_deployment_status_replicas_unavailable{namespace="app", deployment="juice-shop"} > 0

Memória acima de 85% do limite

Dispara quando o uso de memória de um pod supera 85% do limite configurado.

(
  sum by (pod) (
    container_memory_working_set_bytes{namespace="app", pod=~"juice-shop.*", container!="POD", container!=""}
  )
  /
  sum by (pod) (
    kube_pod_container_resource_limits{namespace="app", pod=~"juice-shop.*", container="juice-shop", resource="memory"}
  )
) * 100 > 85

CPU throttling detectado

Dispara quando qualquer pod da Juice Shop apresenta CPU throttling.

sum by (pod) (
  rate(container_cpu_cfs_throttled_periods_total{namespace="app", pod=~"juice-shop.*", container!="POD", container!=""}[5m])
) > 0

Taxa de erros 5xx no Nginx

O nginx-exporter baseado em stub_status não expõe status HTTP por código de resposta. Para alertar sobre erros 5xx, existem duas opções:

  • Opção A (melhoria futura): Configurar o Nginx para expor logs no formato Prometheus via nginx-vts-exporter ou similar, permitindo filtrar por status_code="5xx".
  • Opção B (melhoria futura): Processar os access logs do Nginx com uma ferramenta como mtail ou promtail + Loki para derivar métricas de status HTTP.

Enquanto essa evolução não é implementada, os erros 5xx podem ser inferidos indiretamente por aumento de latência ou por alertas de réplicas indisponíveis.


21. Lições aprendidas

1. Resource limits mal dimensionados são silenciosos — até não serem. A aplicação ficou Running com os limits reduzidos. Não houve erro visível até o momento do teste de carga. Em um ambiente real, esse tipo de subdimensionamento pode passar despercebido por semanas, até que um pico natural de tráfego revele o problema.

2. CPU throttling é mais insidioso que OOMKill. Quando a memória acaba, o Kubernetes reinicia o container e o problema aparece. Quando a CPU é throttled, o container continua rodando — apenas mais devagar. Isso torna o throttling mais difícil de perceber sem observabilidade adequada.

3. Readiness e liveness probes são a linha de defesa do cluster. O fato de o Kubernetes ter reiniciado automaticamente o pod com liveness probe falhando é exatamente o comportamento esperado. Sem probes configurados, um pod travado permaneceria servindo tráfego indefinidamente.

4. Observabilidade antecipa o diagnóstico. Sem as queries de throttling e de percentual de memória, a causa raiz poderia ter sido atribuída incorretamente ao Nginx ou à rede. As métricas do Prometheus permitiram identificar a origem do problema em minutos.

5. Correção e mitigação são complementares, não equivalentes. Aumentar os resources corrigiu o problema. Adicionar rate limit no Nginx adiciona uma camada de proteção que torna o sistema mais resiliente a picos futuros, independente do dimensionamento atual.


22. Próximas melhorias

Melhoria Descrição
Horizontal Pod Autoscaler (HPA) Escalar automaticamente o número de pods com base em CPU ou métricas customizadas
Prometheus Alertmanager Configurar alertas com notificações reais (Slack, e-mail, webhook)
Persistent Volumes para Grafana Preservar dashboards entre reinicializações
Loki + Promtail Adicionar agregação de logs para correlacionar logs e métricas
nginx-vts-exporter Substituir stub_status por exporter que expõe status HTTP por código de resposta
Network Policies Restringir comunicação entre namespaces para simular isolamento de rede
RBAC Configurar permissões por ServiceAccount para cada componente
CI/CD local Integrar o kubectl apply com um pipeline local (ex: Tekton ou GitHub Actions com self-hosted runner)
Testes de caos Usar ferramentas como chaos-mesh ou litmus para simular falhas de nó ou rede

23. Conclusão

Este laboratório demonstrou, do início ao fim, um ciclo completo de operação em Kubernetes:

  1. Provisionamento do cluster local com kind, sem dependências externas;
  2. Deploy de uma aplicação real com múltiplas réplicas, service e resource limits;
  3. Configuração de reverse proxy manual com Nginx, sem Ingress Controller;
  4. Instrumentação de observabilidade com Prometheus, Grafana, kube-state-metrics e Nginx Exporter;
  5. Simulação controlada de incidente por saturação de recursos;
  6. Detecção do problema via métricas de CPU throttling, memória e falhas de probe;
  7. Investigação com queries PromQL e comandos kubectl;
  8. Identificação da causa raiz: resource limits subdimensionados;
  9. Correção objetiva com rollout controlado;
  10. Validação da correção com testes de carga comparativos;
  11. Mitigação adicional com rate limit no Nginx.

O projeto não simula um ambiente de produção, mas aplica os mesmos princípios e ferramentas usados em ambientes reais. O objetivo é demonstrar compreensão técnica, capacidade de diagnóstico e raciocínio estruturado para resolução de problemas — habilidades centrais para uma carreira em DevOps, SRE e Engenharia de Plataforma.


Feito com Kubernetes, Prometheus, Grafana e muito kubectl describe.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors