From 9e834de48dfe5229f75d4ba7cc2778393572238e Mon Sep 17 00:00:00 2001 From: Allyson de Paula Date: Thu, 25 Dec 2025 13:38:46 -0300 Subject: [PATCH] aula-04: NGINX Ingress com Keep Request (Lua) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- aula-04/configmap.yaml | 43 +++++++++++++++++++++++++++ aula-04/deployment.yaml | 47 +++++++++++++++++++++++++++++ aula-04/ingress-nginx.yaml | 60 ++++++++++++++++++++++++++++++++++++++ aula-04/ingress.yaml- | 26 +++++++++++++++++ aula-04/service.yaml | 12 ++++++++ aula-04/teste-stress.sh | 57 ++++++++++++++++++++++++++++++++++++ 6 files changed, 245 insertions(+) create mode 100644 aula-04/configmap.yaml create mode 100644 aula-04/deployment.yaml create mode 100644 aula-04/ingress-nginx.yaml create mode 100644 aula-04/ingress.yaml- create mode 100644 aula-04/service.yaml create mode 100755 aula-04/teste-stress.sh diff --git a/aula-04/configmap.yaml b/aula-04/configmap.yaml new file mode 100644 index 0000000..1f600c8 --- /dev/null +++ b/aula-04/configmap.yaml @@ -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"); + }); diff --git a/aula-04/deployment.yaml b/aula-04/deployment.yaml new file mode 100644 index 0000000..8ca40cf --- /dev/null +++ b/aula-04/deployment.yaml @@ -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 diff --git a/aula-04/ingress-nginx.yaml b/aula-04/ingress-nginx.yaml new file mode 100644 index 0000000..0836e24 --- /dev/null +++ b/aula-04/ingress-nginx.yaml @@ -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 diff --git a/aula-04/ingress.yaml- b/aula-04/ingress.yaml- new file mode 100644 index 0000000..645a230 --- /dev/null +++ b/aula-04/ingress.yaml- @@ -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 diff --git a/aula-04/service.yaml b/aula-04/service.yaml new file mode 100644 index 0000000..2e7034d --- /dev/null +++ b/aula-04/service.yaml @@ -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 diff --git a/aula-04/teste-stress.sh b/aula-04/teste-stress.sh new file mode 100755 index 0000000..8da00e1 --- /dev/null +++ b/aula-04/teste-stress.sh @@ -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 ""; then + ERROR=$(echo "$RESULT" | grep -o "<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