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:
43
aula-04/configmap.yaml
Normal file
43
aula-04/configmap.yaml
Normal 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
47
aula-04/deployment.yaml
Normal 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
|
||||
60
aula-04/ingress-nginx.yaml
Normal file
60
aula-04/ingress-nginx.yaml
Normal 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
26
aula-04/ingress.yaml-
Normal 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
12
aula-04/service.yaml
Normal 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
57
aula-04/teste-stress.sh
Executable 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
|
||||
Reference in New Issue
Block a user