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.
- Descrição geral
- Objetivos do laboratório
- Arquitetura
- Tecnologias utilizadas
- Estrutura de pastas
- Fluxo de tráfego
- Namespaces
- Deploy da aplicação Juice Shop
- Nginx reverse proxy
- Observabilidade com Prometheus e Grafana
- Como executar o projeto
- Como validar o ambiente
- Simulação de incidente
- Detecção do problema
- Investigação
- Causa raiz
- Correção aplicada
- Validação pós-correção
- Mitigação com rate limit no Nginx
- Alertas recomendados
- Lições aprendidas
- Próximas melhorias
- Conclusão
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.
- 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.
┌─────────────────────────────────────────────────────────────────┐
│ 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 │ │ │
│ │ └───────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
| 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 |
.
├── 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
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.
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: monitoringA Juice Shop é implantada como um Deployment com 3 réplicas no namespace app.
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512MiOs 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).
O Nginx é configurado manualmente via ConfigMap, sem uso de Ingress Controller. Essa abordagem demonstra controle direto sobre a camada de proxy.
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 |
# 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.
| 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 |
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
- Docker instalado e em execução
kindinstaladokubectlconfigurado- PowerShell (Windows) ou terminal equivalente
kind create cluster --config infra/kind/kind-config.yaml# 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/| 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 |
kubectl get pods -ASaída esperada: todos os pods com STATUS Running e READY completo.
kubectl get deploy -Akubectl get svc -Akubectl get pods -n app -o widecurl.exe -I http://localhost:8080Resposta esperada:
HTTP/1.1 200 OK
kubectl get events -n app --sort-by=.lastTimestampAcesse http://localhost:9090/targets e confirme que todos os targets estão com status UP.
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.
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
Os limites do Deployment da Juice Shop foram reduzidos para simular um ambiente subdimensionado:
resources:
requests:
cpu: 50m
memory: 96Mi
limits:
cpu: 75m
memory: 128MiApó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.
.\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.
.\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
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.
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.
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.
1. Verificar estado dos pods:
kubectl get pods -n app -o wide2. Descrever o deployment:
kubectl describe deployment juice-shop -n app3. Verificar eventos recentes:
kubectl get events -n app --sort-by=.lastTimestampAqui 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 yaml5. 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.
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.
| 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.
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: 512Mikubectl apply -f .\app\deployment.yaml
kubectl rollout status deployment/juice-shop -n appWaiting 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
.\tools\hey.exe -n 300 -c 30 http://localhost:8080/.\tools\hey.exe -n 3000 -c 300 http://localhost:8080/| 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 | — |
| 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 |
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.
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.
| 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 |
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;
}
}| 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-nginxOs alertas a seguir podem ser criados no Prometheus Alertmanager ou como anotações de alerta em painéis do Grafana.
Dispara se qualquer container no namespace app foi reiniciado nos últimos 5 minutos.
increase(kube_pod_container_status_restarts_total{namespace="app"}[5m]) > 0
Dispara se qualquer réplica do Juice Shop estiver indisponível.
kube_deployment_status_replicas_unavailable{namespace="app", deployment="juice-shop"} > 0
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
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
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-exporterou similar, permitindo filtrar porstatus_code="5xx". - Opção B (melhoria futura): Processar os access logs do Nginx com uma ferramenta como
mtailoupromtail+ 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.
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.
| 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 |
Este laboratório demonstrou, do início ao fim, um ciclo completo de operação em Kubernetes:
- Provisionamento do cluster local com
kind, sem dependências externas; - Deploy de uma aplicação real com múltiplas réplicas, service e resource limits;
- Configuração de reverse proxy manual com Nginx, sem Ingress Controller;
- Instrumentação de observabilidade com Prometheus, Grafana, kube-state-metrics e Nginx Exporter;
- Simulação controlada de incidente por saturação de recursos;
- Detecção do problema via métricas de CPU throttling, memória e falhas de probe;
- Investigação com queries PromQL e comandos
kubectl; - Identificação da causa raiz: resource limits subdimensionados;
- Correção objetiva com rollout controlado;
- Validação da correção com testes de carga comparativos;
- 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.