aula-04: NGINX Ingress com Keep Request (Lua)

Quando todos os pods estão indisponíveis, o Ingress mantém
a requisição por até 99s aguardando o backend voltar,
alcançando zero falhas visíveis ao usuário.
This commit is contained in:
Allyson de Paula
2025-12-25 13:38:46 -03:00
parent a479bf3696
commit 9e834de48d
6 changed files with 245 additions and 0 deletions

43
aula-04/configmap.yaml Normal file
View File

@@ -0,0 +1,43 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
MAX_REQUESTS: "3"
app.js: |
const http = require("http");
const MAX_REQUESTS = Number(process.env.MAX_REQUESTS || 3);
let requestCount = 0;
console.log("MAX_REQUESTS =", MAX_REQUESTS);
const server = http.createServer((req, res) => {
if (req.url === "/health") {
if (requestCount > MAX_REQUESTS) {
console.log('❌ O health check falhou');
return;
}
res.writeHead(200);
res.end(`ok`);
return;
}
requestCount++;
console.log("request", requestCount);
if (requestCount > MAX_REQUESTS) {
console.log(`🔥 App travado após ${MAX_REQUESTS} requests`);
return;
}
res.writeHead(200);
res.end(`Req -> ${requestCount}/${MAX_REQUESTS}`);
});
server.listen(3000, () => {
console.log("App rodando na porta 3000");
});

47
aula-04/deployment.yaml Normal file
View File

@@ -0,0 +1,47 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-bugado
spec:
replicas: 5 # 5 pods - retry encontra pods saudáveis quando alguns falham
selector:
matchLabels:
app: node-bugado
template:
metadata:
labels:
app: node-bugado
spec:
terminationGracePeriodSeconds: 1
containers:
- name: app
image: node:24-alpine
command: ["node", "/app/app.js"]
env:
- name: MAX_REQUESTS
valueFrom:
configMapKeyRef:
name: app-config
key: MAX_REQUESTS
ports:
- containerPort: 3000
readinessProbe:
httpGet:
path: /health
port: 3000
periodSeconds: 1 # Verifica a cada 1 segundo
failureThreshold: 1 # 1 falha = remove do Service
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 2 # Espera para começar a testar
periodSeconds: 1 # A cada 1 seg faz o teste novamente
failureThreshold: 2 # 2 falhas = reinicia o container
volumeMounts:
- name: app-volume
mountPath: /app
volumes:
- name: app-volume
configMap:
name: app-config

View File

@@ -0,0 +1,60 @@
# NGINX Ingress com "Keep Request" - Mantém request até backend disponível
# Quando todos os pods estão indisponíveis, aguarda até 99s antes de retornar 503
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: node-bugado-nginx
annotations:
# Location interno que aguarda o backend ficar disponível
nginx.ingress.kubernetes.io/server-snippet: |
error_page 502 503 504 = @wait_for_backend;
location @wait_for_backend {
internal;
content_by_lua_block {
local max_wait = 99
local interval = 1
local start = ngx.now()
local backend_host = "node-bugado.default.svc.cluster.local"
local backend_port = 3000
ngx.log(ngx.INFO, "[KeepRequest] Aguardando backend por ate ", max_wait, "s...")
while (ngx.now() - start) < max_wait do
local sock = ngx.socket.tcp()
sock:settimeout(1000)
local ok, err = sock:connect(backend_host, backend_port)
if ok then
sock:close()
local elapsed = math.floor(ngx.now() - start)
ngx.log(ngx.INFO, "[KeepRequest] Backend ready apos ", elapsed, "s")
-- Redirect interno (não retorna ao cliente)
return ngx.exec(ngx.var.request_uri)
end
ngx.log(ngx.WARN, "[KeepRequest] Backend indisponivel: ", err)
ngx.sleep(interval)
end
ngx.status = 503
ngx.header["Content-Type"] = "text/plain"
ngx.say("Servico indisponivel apos ", max_wait, "s de espera")
ngx.exit(503)
}
}
# Timeouts curtos para falhar rápido e ir para o handler Lua
nginx.ingress.kubernetes.io/proxy-connect-timeout: "2"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3"
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: node-bugado
port:
number: 3000

26
aula-04/ingress.yaml- Normal file
View File

@@ -0,0 +1,26 @@
# Middleware Retry - tenta outros pods quando um falha
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: retry-middleware
spec:
retry:
attempts: 5 # 5 tentativas
initialInterval: 500ms # 500ms entre ciclos
---
# IngressRoute
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: node-bugado
spec:
entryPoints:
- web
routes:
- match: PathPrefix(`/`)
kind: Rule
middlewares:
- name: retry-middleware
services:
- name: node-bugado
port: 3000

12
aula-04/service.yaml Normal file
View File

@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: node-bugado
spec:
type: LoadBalancer # Acesso direto na porta 3000 + Traefik na porta 80
selector:
app: node-bugado
ports:
- port: 3000
targetPort: 3000
protocol: TCP

57
aula-04/teste-stress.sh Executable file
View File

@@ -0,0 +1,57 @@
#!/bin/bash
# Teste de Stress para verificar resiliência do Ingress
# Uso: ./teste-stress.sh [URL] [NUM_REQUESTS]
URL="${1:-http://localhost}"
TOTAL="${2:-50}"
TIMEOUT=120
echo "============================================"
echo " Teste de Stress - Alta Disponibilidade"
echo "============================================"
echo "URL: $URL"
echo "Requisições: $TOTAL"
echo "Timeout: ${TIMEOUT}s"
echo "============================================"
echo ""
SUCCESS=0
FAIL=0
for i in $(seq 1 $TOTAL); do
RESULT=$(curl -s -m $TIMEOUT "$URL" 2>&1)
if echo "$RESULT" | grep -q "Req ->"; then
SUCCESS=$((SUCCESS + 1))
echo -e "[$i/$TOTAL] \033[32mOK\033[0m - $RESULT"
else
FAIL=$((FAIL + 1))
# Extrai apenas o título do erro se for HTML
if echo "$RESULT" | grep -q "<title>"; then
ERROR=$(echo "$RESULT" | grep -o "<title>[^<]*</title>" | sed 's/<[^>]*>//g')
else
ERROR="$RESULT"
fi
echo -e "[$i/$TOTAL] \033[31mFALHA\033[0m - $ERROR"
fi
sleep 0.2
done
echo ""
echo "============================================"
echo " Resultado Final"
echo "============================================"
PERCENT=$((SUCCESS * 100 / TOTAL))
echo -e "Sucesso: \033[32m$SUCCESS\033[0m / $TOTAL ($PERCENT%)"
echo -e "Falhas: \033[31m$FAIL\033[0m / $TOTAL"
echo "============================================"
if [ $FAIL -eq 0 ]; then
echo -e "\033[32m*** ZERO FALHAS! ***\033[0m"
elif [ $PERCENT -ge 95 ]; then
echo -e "\033[33mBom resultado (>= 95%)\033[0m"
else
echo -e "\033[31mResultado abaixo do esperado\033[0m"
fi