feat(aula-14): adicionar Istio Traffic Splitting com canary deployment

- Instala Istio (base + istiod + ingressgateway)
- Configura Kiali e Jaeger para observabilidade
- Deploy de app-backend v1 e v2 com traffic splitting 90/10
- Integra com Victoria Metrics da aula-12
- Inclui teste-stress.sh para validar distribuição de tráfego
- Tráfego externo passa pelo Istio Gateway via NGINX Ingress
This commit is contained in:
ArgoCD Setup
2026-01-24 07:40:51 -03:00
parent 3e53328214
commit 3860809e5c
22 changed files with 1545 additions and 0 deletions

View File

@@ -24,6 +24,7 @@ App de demonstração: `node-bugado` - trava após N requests para demonstrar he
| 11 | ArgoCD + GitLab Runner | Hetzner | | 11 | ArgoCD + GitLab Runner | Hetzner |
| 12 | Victoria Metrics (Observabilidade) | Hetzner | | 12 | Victoria Metrics (Observabilidade) | Hetzner |
| 13 | Container Factory (eStargz) | Hetzner | | 13 | Container Factory (eStargz) | Hetzner |
| 14 | Istio Traffic Splitting | Hetzner |
## Comandos Rápidos ## Comandos Rápidos
@@ -38,6 +39,7 @@ cd aula-10 && ./setup.sh # GitLab
cd aula-11 && ./setup.sh # ArgoCD cd aula-11 && ./setup.sh # ArgoCD
cd aula-12 && ./setup.sh # Victoria Metrics cd aula-12 && ./setup.sh # Victoria Metrics
cd aula-13 && ./setup.sh # Container Factory cd aula-13 && ./setup.sh # Container Factory
cd aula-14 && ./setup.sh # Istio Traffic Splitting
``` ```
## App node-bugado ## App node-bugado

230
aula-14/README.md Normal file
View File

@@ -0,0 +1,230 @@
# Aula 14 - Istio Traffic Splitting
Demonstração de **app-backend deployment** usando Istio para dividir tráfego entre duas versões da aplicação.
## Motivação
Em produção, lançar uma nova versão diretamente para 100% dos usuários é arriscado. Com traffic splitting podemos:
- Enviar apenas 10% do tráfego para a nova versão inicialmente
- Monitorar erros e latência antes de aumentar o percentual
- Fazer rollback instantâneo sem redeploy
- Visualizar o fluxo de tráfego em tempo real com Kiali
## Arquitetura
```
┌─────────────────────────────────────────────┐
│ Istio Service Mesh │
│ │
Requisições │ ┌─────────────────┐ │
──────────────► │ │ VirtualService │ │
│ │ (90/10 split) │ │
│ └────────┬────────┘ │
│ │ │
│ ┌─────┴─────┐ │
│ ▼ ▼ │
│ ┌──────┐ ┌──────┐ │
│ │ v1 │ │ v2 │ │
│ │ bug │ │ ok │ │
│ └──────┘ └──────┘ │
│ │
│ ┌─────────┐ ┌────────┐ ┌─────────────────┐│
│ │ Kiali │ │ Jaeger │ │ Victoria Metrics││
│ └─────────┘ └────────┘ └─────────────────┘│
└─────────────────────────────────────────────┘
```
## Conceitos
| Conceito | Descrição |
|----------|-----------|
| Service Mesh | Camada de infraestrutura que gerencia comunicação entre serviços |
| Sidecar Injection | Proxy Envoy injetado automaticamente em cada pod |
| VirtualService | Define regras de roteamento de tráfego |
| DestinationRule | Define subsets (versões) do serviço |
| Traffic Splitting | Divisão percentual do tráfego entre versões |
## Pré-requisitos
- Cluster Kubernetes na Hetzner (aula-08)
- GitLab com Registry (aula-10)
- Victoria Metrics (aula-12) para métricas
- kubectl, helm e docker instalados
## Estrutura
```
aula-14/
├── README.md
├── setup.sh
├── cleanup.sh
├── app-backend/
│ ├── v1/
│ │ ├── app.js # Versão com bug (trava após N requests)
│ │ └── Dockerfile
│ └── v2/
│ ├── app.js # Versão corrigida (estável)
│ └── Dockerfile
├── k8s/
│ ├── namespace.yaml # Namespace com istio-injection
│ ├── deployment-v1.yaml
│ ├── deployment-v2.yaml
│ ├── service.yaml
│ ├── destination-rule.yaml # Define subsets v1, v2
│ └── virtual-service.yaml # Traffic splitting (90/10)
└── istio/
├── kiali-values.yaml
├── jaeger-values.yaml
└── gateway.yaml
```
## Instalação
```bash
cd aula-14
./setup.sh
```
O script irá:
1. Verificar pré-requisitos (incluindo Victoria Metrics da aula-12)
2. Coletar configuração (domínio, registry, TLS)
3. Instalar Istio (istio-base + istiod)
4. Instalar Kiali e Jaeger (métricas via Victoria Metrics)
5. Configurar Ingress para dashboards
6. Build e push das imagens v1 e v2
7. Deploy da aplicação com traffic splitting 90/10
## Verificação
### Verificar pods
```bash
kubectl get pods -n istio
kubectl get pods -n istio-system
```
### Verificar sidecar injection
Cada pod deve ter 2 containers (app + istio-proxy):
```bash
kubectl get pods -n istio -o jsonpath='{range .items[*]}{.metadata.name}{": "}{range .spec.containers[*]}{.name}{" "}{end}{"\n"}{end}'
```
### Testar distribuição de tráfego
```bash
kubectl exec -n istio curl-test -- sh -c \
'for i in $(seq 1 100); do curl -s http://app-backend/; done' | sort | uniq -c
```
Resultado esperado (aproximado):
```
90 v1 - Request X
10 v2 - Request Y
```
## Exercício Prático
### 1. Observar distribuição inicial (90/10)
```bash
kubectl exec -n istio curl-test -- sh -c \
'for i in $(seq 1 100); do curl -s http://app-backend/; done' | sort | uniq -c
```
### 2. Acessar Kiali e visualizar tráfego
Abra o Kiali no navegador e observe o gráfico de tráfego em tempo real.
Gere tráfego contínuo em outro terminal:
```bash
kubectl exec -n istio curl-test -- sh -c \
'while true; do curl -s http://app-backend/ > /dev/null; sleep 0.1; done'
```
### 3. Alterar para 50/50
```bash
kubectl patch virtualservice app-backend -n istio --type='json' \
-p='[{"op":"replace","path":"/spec/http/0/route/0/weight","value":50},
{"op":"replace","path":"/spec/http/0/route/1/weight","value":50}]'
```
### 4. Verificar traces no Jaeger
Abra o Jaeger e observe os traces das requisições passando pelo mesh.
### 5. Rollout completo para v2
```bash
kubectl patch virtualservice app-backend -n istio --type='json' \
-p='[{"op":"replace","path":"/spec/http/0/route/0/weight","value":0},
{"op":"replace","path":"/spec/http/0/route/1/weight","value":100}]'
```
### 6. Confirmar que app não trava mais
Com 100% do tráfego na v2, a aplicação permanece estável independente da quantidade de requests.
## Comandos Úteis
```bash
# Ver distribuição atual
kubectl get virtualservice app-backend -n istio -o yaml
# Ver pods com labels de versão
kubectl get pods -n istio --show-labels
# Logs da v1
kubectl logs -n istio -l app=app-backend,version=v1 -f
# Logs da v2
kubectl logs -n istio -l app=app-backend,version=v2 -f
# Port-forward para Kiali (alternativa ao Ingress)
kubectl port-forward svc/kiali -n istio-system 20001:20001
# Port-forward para Jaeger
kubectl port-forward svc/tracing -n istio-system 16686:80
```
## Cleanup
```bash
./cleanup.sh
```
Remove Istio, addons e namespace da aplicação.
## Troubleshooting
### Pod não tem sidecar
Verificar se o namespace tem o label correto:
```bash
kubectl get ns istio --show-labels
```
Deve ter `istio-injection=enabled`. Se não tiver:
```bash
kubectl label ns istio istio-injection=enabled
kubectl rollout restart deployment -n istio
```
### Kiali não mostra tráfego
1. Verificar se Victoria Metrics está coletando métricas (aula-12)
2. Gerar tráfego suficiente (pelo menos 10 requests)
3. Aguardar 30 segundos para métricas aparecerem
### VirtualService não aplicando weights
Verificar se DestinationRule existe:
```bash
kubectl get destinationrule app-backend -n istio -o yaml
```

View File

@@ -0,0 +1,6 @@
FROM node:24-alpine
WORKDIR /app
COPY app.js .
EXPOSE 3000
USER node
CMD ["node", "app.js"]

View File

@@ -0,0 +1,32 @@
const http = require("http");
const MAX_REQUESTS = parseInt(process.env.MAX_REQUESTS) || 5;
let requestCount = 0;
let frozen = false;
const server = http.createServer((req, res) => {
if (frozen) {
return;
}
requestCount++;
console.log(`[v1] request ${requestCount}/${MAX_REQUESTS}`);
if (requestCount >= MAX_REQUESTS) {
frozen = true;
console.log("[v1] Aplicação travou");
}
if (req.url === "/health") {
res.writeHead(200);
res.end("ok");
return;
}
res.writeHead(200);
res.end(`v1 - Request ${requestCount}\n`);
});
server.listen(3000, () => {
console.log(`[v1] Porta 3000 (trava após ${MAX_REQUESTS} requests)`);
});

View File

@@ -0,0 +1,6 @@
FROM node:24-alpine
WORKDIR /app
COPY app.js .
EXPOSE 3000
USER node
CMD ["node", "app.js"]

View File

@@ -0,0 +1,21 @@
const http = require("http");
let requestCount = 0;
const server = http.createServer((req, res) => {
if (req.url === "/health") {
res.writeHead(200);
res.end("ok");
return;
}
requestCount++;
console.log(`[v2] request ${requestCount}`);
res.writeHead(200);
res.end(`v2 - Request ${requestCount}\n`);
});
server.listen(3000, () => {
console.log("[v2] Porta 3000 (estável)");
});

116
aula-14/cleanup.sh Executable file
View File

@@ -0,0 +1,116 @@
#!/bin/bash
# ============================================================================
# Aula 14 - Cleanup do Istio Traffic Splitting
# ============================================================================
# Remove todos os componentes instalados pelo setup.sh:
# - Namespace istio (aplicação)
# - Kiali, Jaeger (addons)
# - Istio (istiod + base)
# - Ingress resources
#
# Mantém:
# - Cluster Kubernetes
# - GitLab e Registry (aula-10)
# - Outros namespaces
# ============================================================================
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERRO]${NC} $1"; }
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo ""
echo -e "${CYAN}╔═══════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ Cleanup - Aula 14 Istio Traffic Splitting ║${NC}"
echo -e "${CYAN}╚═══════════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e "${YELLOW}Este script irá remover:${NC}"
echo " - Namespace 'istio' com aplicação"
echo " - Addons: Kiali, Jaeger"
echo " - Istio: istiod e istio-base"
echo " - Ingress do Kiali e Jaeger"
echo ""
read -p "Continuar? (digite 'sim' para confirmar): " confirm
if [[ "$confirm" != "sim" ]]; then
log_info "Operação cancelada"
exit 0
fi
echo ""
# Remover pod de teste
log_info "Removendo pod de teste..."
kubectl delete pod curl-test -n istio --ignore-not-found=true 2>/dev/null || true
# Remover namespace istio (aplicação demo)
if kubectl get namespace istio &> /dev/null; then
log_info "Removendo namespace istio..."
kubectl delete namespace istio --wait=false 2>/dev/null || true
log_success "Namespace istio marcado para remoção"
else
log_info "Namespace istio não encontrado"
fi
# Remover Ingress
log_info "Removendo Ingress..."
kubectl delete ingress kiali jaeger -n istio-system --ignore-not-found=true 2>/dev/null || true
log_success "Ingress removidos"
# Remover addons (Kiali, Jaeger)
log_info "Removendo Kiali..."
kubectl delete -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/addons/kiali.yaml 2>/dev/null || true
log_info "Removendo Jaeger..."
kubectl delete -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/addons/jaeger.yaml 2>/dev/null || true
log_success "Addons removidos"
# Remover istiod
if helm status istiod -n istio-system &> /dev/null; then
log_info "Removendo istiod..."
helm uninstall istiod -n istio-system --wait
log_success "istiod removido"
else
log_info "istiod não encontrado"
fi
# Remover istio-base
if helm status istio-base -n istio-system &> /dev/null; then
log_info "Removendo istio-base..."
helm uninstall istio-base -n istio-system --wait
log_success "istio-base removido"
else
log_info "istio-base não encontrado"
fi
# Remover namespace istio-system
if kubectl get namespace istio-system &> /dev/null; then
log_info "Removendo namespace istio-system..."
kubectl delete namespace istio-system --wait=false 2>/dev/null || true
log_success "Namespace istio-system marcado para remoção"
fi
# Remover arquivo .env
echo ""
read -p "Remover arquivo .env? [s/N]: " remove_env
if [[ "$remove_env" == "s" || "$remove_env" == "S" ]]; then
rm -f "${SCRIPT_DIR}/.env"
log_success "Arquivo .env removido"
fi
echo ""
echo -e "${GREEN}Cleanup concluído.${NC}"
echo ""
echo "Aguarde alguns minutos para os namespaces serem removidos completamente."
echo "Verificar: kubectl get ns"
echo ""

View File

@@ -0,0 +1,36 @@
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: app-backend-gateway
namespace: istio
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "${APP_HOST}"
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: app-backend-external
namespace: istio
spec:
hosts:
- "${APP_HOST}"
gateways:
- app-backend-gateway
http:
- route:
- destination:
host: app-backend
subset: v1
weight: 90
- destination:
host: app-backend
subset: v2
weight: 10

View File

@@ -0,0 +1,29 @@
# Jaeger - Distributed Tracing
# Tracing para visualizar requisições através do mesh
provisionDataStore:
cassandra: false
allInOne:
enabled: true
image: jaegertracing/all-in-one
tag: "1.62"
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
storage:
type: memory
collector:
enabled: false
query:
enabled: false
agent:
enabled: false

View File

@@ -0,0 +1,24 @@
# Kiali - Service Mesh Observability
# Dashboard para visualizar tráfego do Istio
server:
web_root: /kiali
auth:
strategy: anonymous
deployment:
accessible_namespaces:
- "**"
ingress:
enabled: false
external_services:
prometheus:
url: http://vmsingle-victoria-metrics-k8s-stack.monitoring:8429
tracing:
enabled: true
in_cluster_url: http://tracing.istio-system:16685/jaeger
use_grpc: true
grafana:
enabled: false

View File

@@ -0,0 +1,53 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-backend-v1
namespace: istio
labels:
app: app-backend
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: app-backend
version: v1
template:
metadata:
labels:
app: app-backend
version: v1
spec:
imagePullSecrets:
- name: regcred
containers:
- name: app-backend
image: ${REGISTRY_HOST}/${REGISTRY_PROJECT}/app-backend:v1
imagePullPolicy: Always
ports:
- containerPort: 3000
env:
- name: MAX_REQUESTS
value: "100"
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 3
periodSeconds: 3
failureThreshold: 2
terminationGracePeriodSeconds: 10

View File

@@ -0,0 +1,53 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-backend-v2
namespace: istio
labels:
app: app-backend
version: v2
spec:
replicas: 1
selector:
matchLabels:
app: app-backend
version: v2
template:
metadata:
labels:
app: app-backend
version: v2
spec:
imagePullSecrets:
- name: regcred
containers:
- name: app-backend
image: ${REGISTRY_HOST}/${REGISTRY_PROJECT}/app-backend:v2
imagePullPolicy: Always
ports:
- containerPort: 3000
env:
- name: MAX_REQUESTS
value: "100"
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 3
periodSeconds: 3
failureThreshold: 2
terminationGracePeriodSeconds: 10

View File

@@ -0,0 +1,14 @@
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: app-backend
namespace: istio
spec:
host: app-backend
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2

15
aula-14/k8s/gateway.yaml Normal file
View File

@@ -0,0 +1,15 @@
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: app-gateway
namespace: istio
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "${APP_HOST}"

View File

@@ -0,0 +1,23 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-backend
namespace: istio-system
annotations:
nginx.ingress.kubernetes.io/backend-protocol: HTTP
nginx.ingress.kubernetes.io/upstream-vhost: "${APP_HOST}"
${TLS_ANNOTATION}
spec:
ingressClassName: nginx
${TLS_CONFIG}
rules:
- host: ${APP_HOST}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: istio-ingressgateway
port:
number: 80

View File

@@ -0,0 +1,25 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jaeger
namespace: istio-system
annotations:
nginx.ingress.kubernetes.io/backend-protocol: HTTP
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
${TLS_ANNOTATION}
spec:
ingressClassName: nginx
${TLS_CONFIG}
rules:
- host: ${JAEGER_HOST}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: tracing
port:
number: 80

View File

@@ -0,0 +1,25 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kiali
namespace: istio-system
annotations:
nginx.ingress.kubernetes.io/backend-protocol: HTTP
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
${TLS_ANNOTATION}
spec:
ingressClassName: nginx
${TLS_CONFIG}
rules:
- host: ${KIALI_HOST}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kiali
port:
number: 20001

View File

@@ -0,0 +1,9 @@
apiVersion: v1
kind: Namespace
metadata:
name: istio
labels:
istio-injection: enabled
# Istio sidecar requer NET_ADMIN e NET_RAW capabilities
pod-security.kubernetes.io/enforce: privileged
pod-security.kubernetes.io/warn: privileged

14
aula-14/k8s/service.yaml Normal file
View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: app-backend
namespace: istio
labels:
app: app-backend
spec:
ports:
- port: 80
targetPort: 3000
name: http
selector:
app: app-backend

View File

@@ -0,0 +1,22 @@
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: app-backend
namespace: istio
spec:
hosts:
- app-backend
- "${APP_HOST}"
gateways:
- app-gateway
- mesh
http:
- route:
- destination:
host: app-backend
subset: v1
weight: 90
- destination:
host: app-backend
subset: v2
weight: 10

592
aula-14/setup.sh Executable file
View File

@@ -0,0 +1,592 @@
#!/bin/bash
# ============================================================================
# Aula 14 - Istio Traffic Splitting
# ============================================================================
# Instala Istio com Kiali e Jaeger para demonstrar canary deployment
# usando traffic splitting entre duas versões da aplicação.
#
# Componentes:
# - Istio (istio-base + istiod)
# - Kiali (visualização do service mesh)
# - Jaeger (tracing distribuído)
# - Aplicação app-backend v1 e v2
#
# Observabilidade:
# - Usa Victoria Metrics da aula-12 para métricas
# - Jaeger para tracing distribuído
#
# Pré-requisitos:
# - Cluster Kubernetes da aula-08
# - Victoria Metrics da aula-12
# - GitLab com Registry da aula-10
# - kubectl, helm e docker instalados
# ============================================================================
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERRO]${NC} $1"; }
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${SCRIPT_DIR}/.env"
AULA10_ENV="${SCRIPT_DIR}/../aula-10/.env"
# ============================================================================
# Gerenciamento de Configuração
# ============================================================================
load_config() {
if [[ -f "$ENV_FILE" ]]; then
source "$ENV_FILE"
return 0
fi
return 1
}
save_config() {
cat > "$ENV_FILE" << EOF
# Configuração da Aula 14 - Istio Traffic Splitting
# Gerado em: $(date)
REGISTRY_HOST=${REGISTRY_HOST}
REGISTRY_PROJECT=${REGISTRY_PROJECT}
DOMAIN=${DOMAIN}
APP_HOST=${APP_HOST}
KIALI_HOST=${KIALI_HOST}
JAEGER_HOST=${JAEGER_HOST}
USE_LETSENCRYPT=${USE_LETSENCRYPT}
LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL}
BASIC_AUTH_USER=${BASIC_AUTH_USER}
BASIC_AUTH_PASS=${BASIC_AUTH_PASS}
EOF
log_success "Configuração salva em .env"
}
collect_user_input() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Configuração do Istio Traffic Splitting${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo ""
if load_config; then
echo -e "Configuração existente encontrada:"
echo -e " Registry: ${GREEN}${REGISTRY_HOST}${NC}"
echo -e " App: ${GREEN}${APP_HOST}${NC}"
echo -e " Kiali: ${GREEN}${KIALI_HOST}${NC}"
echo -e " Jaeger: ${GREEN}${JAEGER_HOST}${NC}"
echo ""
echo -e "[1] Usar configuração existente"
echo -e "[2] Inserir nova configuração"
read -p "Escolha [1/2]: " choice
if [[ "$choice" == "1" ]]; then
return 0
fi
fi
if [[ -f "$AULA10_ENV" ]]; then
source "$AULA10_ENV"
log_info "Configuração herdada da aula-10"
fi
if [[ -z "$DOMAIN" ]]; then
read -p "Domínio base (ex: kube.quest): " DOMAIN
else
echo -e "Domínio: ${GREEN}${DOMAIN}${NC}"
read -p "Enter para confirmar ou digite novo valor: " new_domain
[[ -n "$new_domain" ]] && DOMAIN="$new_domain"
fi
if [[ -z "$REGISTRY_HOST" ]]; then
REGISTRY_HOST="reg.${DOMAIN}"
fi
echo -e "Registry: ${GREEN}${REGISTRY_HOST}${NC}"
read -p "Enter para confirmar ou digite novo valor: " new_reg
[[ -n "$new_reg" ]] && REGISTRY_HOST="$new_reg"
# Grupo/projeto no GitLab para o registry (ex: root, demo, factory)
if [[ -z "$REGISTRY_PROJECT" ]]; then
REGISTRY_PROJECT="root"
fi
echo ""
echo -e "Grupo/projeto no GitLab: ${GREEN}${REGISTRY_PROJECT}${NC}"
echo -e " Imagens: ${CYAN}${REGISTRY_HOST}/${REGISTRY_PROJECT}/app-backend:v1${NC}"
echo -e " ${CYAN}${REGISTRY_HOST}/${REGISTRY_PROJECT}/app-backend:v2${NC}"
read -p "Grupo/projeto [${REGISTRY_PROJECT}]: " new_project
[[ -n "$new_project" ]] && REGISTRY_PROJECT="$new_project"
APP_HOST="${APP_HOST:-app.${DOMAIN}}"
KIALI_HOST="${KIALI_HOST:-kiali.${DOMAIN}}"
JAEGER_HOST="${JAEGER_HOST:-jaeger.${DOMAIN}}"
echo ""
echo -e "Hosts para serviços:"
echo -e " App: ${GREEN}${APP_HOST}${NC}"
echo -e " Kiali: ${GREEN}${KIALI_HOST}${NC}"
echo -e " Jaeger: ${GREEN}${JAEGER_HOST}${NC}"
read -p "Enter para confirmar ou 'n' para personalizar: " confirm
if [[ "$confirm" == "n" ]]; then
read -p "Host do App: " APP_HOST
read -p "Host do Kiali: " KIALI_HOST
read -p "Host do Jaeger: " JAEGER_HOST
fi
echo ""
echo -e "[1] Usar Let's Encrypt (HTTPS)"
echo -e "[2] Sem TLS (HTTP)"
read -p "Escolha [1/2]: " tls_choice
if [[ "$tls_choice" == "1" ]]; then
USE_LETSENCRYPT=true
if [[ -z "$LETSENCRYPT_EMAIL" ]]; then
read -p "Email para Let's Encrypt: " LETSENCRYPT_EMAIL
fi
else
USE_LETSENCRYPT=false
fi
# Basic Auth para Kiali e Jaeger
echo ""
echo -e "${CYAN}Autenticação para Kiali e Jaeger:${NC}"
if [[ -z "$BASIC_AUTH_USER" ]]; then
BASIC_AUTH_USER="admin"
fi
echo -e "Usuário: ${GREEN}${BASIC_AUTH_USER}${NC}"
read -p "Enter para confirmar ou digite novo valor: " new_user
[[ -n "$new_user" ]] && BASIC_AUTH_USER="$new_user"
if [[ -z "$BASIC_AUTH_PASS" ]]; then
BASIC_AUTH_PASS=$(openssl rand -base64 12 | tr -d '/+=' | head -c 16)
echo -e "Senha gerada: ${GREEN}${BASIC_AUTH_PASS}${NC}"
else
echo -e "Senha: ${GREEN}${BASIC_AUTH_PASS}${NC}"
fi
read -p "Enter para confirmar ou digite nova senha: " new_pass
[[ -n "$new_pass" ]] && BASIC_AUTH_PASS="$new_pass"
save_config
}
# ============================================================================
# Verificação de Pré-requisitos
# ============================================================================
check_prerequisites() {
echo ""
log_info "Verificando pré-requisitos..."
if ! command -v kubectl &> /dev/null; then
log_error "kubectl não encontrado"
exit 1
fi
log_success "kubectl encontrado"
if ! command -v helm &> /dev/null; then
log_error "helm não encontrado"
exit 1
fi
log_success "helm encontrado"
if ! command -v docker &> /dev/null; then
log_error "docker não encontrado"
exit 1
fi
log_success "docker encontrado"
if ! kubectl cluster-info &> /dev/null; then
log_error "Cluster Kubernetes não acessível"
exit 1
fi
log_success "Cluster Kubernetes acessível"
# Verificar Victoria Metrics (aula-12)
if kubectl get svc -n monitoring vmsingle-victoria-metrics-k8s-stack &> /dev/null; then
log_success "Victoria Metrics encontrado (aula-12)"
VICTORIA_METRICS_URL="http://vmsingle-victoria-metrics-k8s-stack.monitoring:8429"
else
log_warn "Victoria Metrics não encontrado. Instale a aula-12 primeiro."
log_warn "Kiali funcionará sem métricas até Victoria Metrics estar disponível."
VICTORIA_METRICS_URL=""
fi
}
# ============================================================================
# Instalação do Istio
# ============================================================================
install_istio() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Instalando Istio${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
helm repo add istio https://istio-release.storage.googleapis.com/charts 2>/dev/null || true
helm repo update istio
kubectl create namespace istio-system 2>/dev/null || true
log_info "Instalando istio-base..."
if helm status istio-base -n istio-system &> /dev/null; then
helm upgrade istio-base istio/base -n istio-system --wait
else
helm install istio-base istio/base -n istio-system --wait
fi
log_success "istio-base instalado"
log_info "Instalando istiod..."
if helm status istiod -n istio-system &> /dev/null; then
helm upgrade istiod istio/istiod -n istio-system --wait
else
helm install istiod istio/istiod -n istio-system --wait
fi
log_success "istiod instalado"
log_info "Aguardando istiod..."
kubectl wait --for=condition=available deployment/istiod -n istio-system --timeout=300s
log_success "istiod pronto"
log_info "Instalando istio-ingressgateway..."
if helm status istio-ingressgateway -n istio-system &> /dev/null; then
helm upgrade istio-ingressgateway istio/gateway -n istio-system --wait
else
helm install istio-ingressgateway istio/gateway -n istio-system --wait
fi
log_success "istio-ingressgateway instalado"
log_info "Aguardando ingressgateway..."
kubectl wait --for=condition=available deployment/istio-ingressgateway -n istio-system --timeout=300s
log_success "ingressgateway pronto"
}
# ============================================================================
# Instalação de Observabilidade
# ============================================================================
install_observability() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Instalando Kiali e Jaeger${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
log_info "Instalando Jaeger..."
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/addons/jaeger.yaml 2>/dev/null || true
log_success "Jaeger instalado"
# Instalar Kiali com configuração para Victoria Metrics
log_info "Instalando Kiali..."
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/addons/kiali.yaml 2>/dev/null || true
# Configurar Kiali para usar Victoria Metrics
if [[ -n "$VICTORIA_METRICS_URL" ]]; then
log_info "Configurando Kiali para usar Victoria Metrics..."
kubectl patch configmap kiali -n istio-system --type merge -p "{
\"data\": {
\"config.yaml\": \"external_services:\\n prometheus:\\n url: ${VICTORIA_METRICS_URL}\\n tracing:\\n enabled: true\\n in_cluster_url: http://tracing.istio-system:16685/jaeger\\n use_grpc: true\\n\"
}
}" 2>/dev/null || true
# Reiniciar Kiali para aplicar configuração
kubectl rollout restart deployment/kiali -n istio-system 2>/dev/null || true
fi
log_success "Kiali instalado"
log_info "Aguardando pods de observabilidade..."
kubectl wait --for=condition=available deployment/kiali -n istio-system --timeout=300s 2>/dev/null || true
kubectl wait --for=condition=available deployment/jaeger -n istio-system --timeout=300s 2>/dev/null || true
log_success "Observabilidade pronta"
}
# ============================================================================
# Configuração de Basic Auth
# ============================================================================
setup_basic_auth() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Configurando Basic Auth para Kiali e Jaeger${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
log_info "Criando secret basic-auth..."
# Verificar se htpasswd está disponível
if ! command -v htpasswd &> /dev/null; then
log_info "htpasswd não encontrado, usando openssl..."
# Gerar hash com openssl (compatível com Apache htpasswd)
local HASH=$(openssl passwd -apr1 "${BASIC_AUTH_PASS}")
echo "${BASIC_AUTH_USER}:${HASH}" > /tmp/auth
else
htpasswd -cb /tmp/auth "${BASIC_AUTH_USER}" "${BASIC_AUTH_PASS}"
fi
kubectl delete secret basic-auth -n istio-system 2>/dev/null || true
kubectl create secret generic basic-auth -n istio-system --from-file=auth=/tmp/auth
rm -f /tmp/auth
log_success "Secret basic-auth criado"
}
# ============================================================================
# Configuração de Ingress
# ============================================================================
setup_ingress() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Configurando Ingress para Kiali e Jaeger${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
local TLS_ANNOTATION=""
local TLS_CONFIG=""
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
TLS_ANNOTATION="cert-manager.io/cluster-issuer: letsencrypt-prod"
fi
log_info "Criando Ingress para Kiali..."
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
TLS_CONFIG="tls:
- hosts:
- ${KIALI_HOST}
secretName: kiali-tls"
fi
export KIALI_HOST TLS_ANNOTATION TLS_CONFIG
envsubst '${KIALI_HOST} ${TLS_ANNOTATION} ${TLS_CONFIG}' < "${SCRIPT_DIR}/k8s/ingress-kiali.yaml" | kubectl apply -f -
log_success "Ingress do Kiali criado"
log_info "Criando Ingress para Jaeger..."
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
TLS_CONFIG="tls:
- hosts:
- ${JAEGER_HOST}
secretName: jaeger-tls"
else
TLS_CONFIG=""
fi
export JAEGER_HOST TLS_ANNOTATION TLS_CONFIG
envsubst '${JAEGER_HOST} ${TLS_ANNOTATION} ${TLS_CONFIG}' < "${SCRIPT_DIR}/k8s/ingress-jaeger.yaml" | kubectl apply -f -
log_success "Ingress do Jaeger criado"
}
setup_app_ingress() {
echo ""
log_info "Criando Ingress para App..."
local TLS_ANNOTATION=""
local TLS_CONFIG=""
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
TLS_ANNOTATION="cert-manager.io/cluster-issuer: letsencrypt-prod"
TLS_CONFIG="tls:
- hosts:
- ${APP_HOST}
secretName: app-tls"
fi
export APP_HOST TLS_ANNOTATION TLS_CONFIG
envsubst '${APP_HOST} ${TLS_ANNOTATION} ${TLS_CONFIG}' < "${SCRIPT_DIR}/k8s/ingress-app.yaml" | kubectl apply -f -
log_success "Ingress do App criado"
}
# ============================================================================
# Build e Push das Imagens
# ============================================================================
build_and_push_images() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Build e Push das Imagens (ARM64)${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
local IMAGE_BASE="${REGISTRY_HOST}/${REGISTRY_PROJECT}/app-backend"
log_info "Build app-backend:v1 (linux/arm64)..."
docker buildx build --platform linux/arm64 -t "${IMAGE_BASE}:v1" --push "${SCRIPT_DIR}/app-backend/v1"
log_success "app-backend:v1 construída e enviada"
log_info "Build app-backend:v2 (linux/arm64)..."
docker buildx build --platform linux/arm64 -t "${IMAGE_BASE}:v2" --push "${SCRIPT_DIR}/app-backend/v2"
log_success "app-backend:v2 construída e enviada"
}
# ============================================================================
# Deploy da Aplicação
# ============================================================================
create_registry_secret() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Criando Secret do Registry${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
log_info "Criando namespace istio..."
kubectl apply -f "${SCRIPT_DIR}/k8s/namespace.yaml"
log_success "Namespace criado"
log_info "Obtendo credenciais do registry..."
local CREDS
if command -v docker-credential-osxkeychain &> /dev/null; then
CREDS=$(docker-credential-osxkeychain get <<< "${REGISTRY_HOST}" 2>/dev/null || true)
elif command -v docker-credential-secretservice &> /dev/null; then
CREDS=$(docker-credential-secretservice get <<< "${REGISTRY_HOST}" 2>/dev/null || true)
fi
if [[ -n "$CREDS" ]]; then
local USERNAME=$(echo "$CREDS" | jq -r '.Username')
local PASSWORD=$(echo "$CREDS" | jq -r '.Secret')
kubectl delete secret regcred -n istio 2>/dev/null || true
kubectl create secret docker-registry regcred -n istio \
--docker-server="${REGISTRY_HOST}" \
--docker-username="${USERNAME}" \
--docker-password="${PASSWORD}"
log_success "Secret regcred criado"
else
log_warn "Credenciais não encontradas no credential helper"
log_info "Tentando criar secret a partir do Docker config..."
kubectl delete secret regcred -n istio 2>/dev/null || true
kubectl create secret generic regcred -n istio \
--from-file=.dockerconfigjson="${HOME}/.docker/config.json" \
--type=kubernetes.io/dockerconfigjson 2>/dev/null || \
log_warn "Falha ao criar secret. Faça login no registry: docker login ${REGISTRY_HOST}"
fi
}
deploy_application() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Deploy da Aplicação${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
log_info "Deploy app-backend v1..."
export REGISTRY_HOST REGISTRY_PROJECT
envsubst < "${SCRIPT_DIR}/k8s/deployment-v1.yaml" | kubectl apply -f -
log_success "v1 implantada"
log_info "Deploy app-backend v2..."
envsubst < "${SCRIPT_DIR}/k8s/deployment-v2.yaml" | kubectl apply -f -
log_success "v2 implantada"
log_info "Criando Service..."
kubectl apply -f "${SCRIPT_DIR}/k8s/service.yaml"
log_success "Service criado"
log_info "Aplicando DestinationRule..."
kubectl apply -f "${SCRIPT_DIR}/k8s/destination-rule.yaml"
log_success "DestinationRule aplicada"
log_info "Aplicando Gateway..."
export APP_HOST
envsubst < "${SCRIPT_DIR}/k8s/gateway.yaml" | kubectl apply -f -
log_success "Gateway aplicado"
log_info "Aplicando VirtualService (90% v1, 10% v2)..."
envsubst < "${SCRIPT_DIR}/k8s/virtual-service.yaml" | kubectl apply -f -
log_success "VirtualService aplicado"
log_info "Aguardando pods..."
kubectl wait --for=condition=available deployment/app-backend-v1 -n istio --timeout=120s
kubectl wait --for=condition=available deployment/app-backend-v2 -n istio --timeout=120s
log_success "Pods prontos"
}
create_test_pod() {
log_info "Criando pod de teste..."
kubectl run curl-test -n istio --image=curlimages/curl:latest \
--restart=Never --command -- sleep infinity 2>/dev/null || true
kubectl wait --for=condition=ready pod/curl-test -n istio --timeout=60s 2>/dev/null || true
log_success "Pod de teste disponível"
}
# ============================================================================
# Resumo
# ============================================================================
show_summary() {
local protocol="http"
[[ "$USE_LETSENCRYPT" == "true" ]] && protocol="https"
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Instalação Concluída${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo ""
echo -e "${GREEN}Componentes instalados:${NC}"
echo " - Istio (istiod + base)"
echo " - Kiali (dashboard do service mesh)"
echo " - Jaeger (tracing distribuído)"
echo " - app-backend v1 e v2"
echo ""
if [[ -n "$VICTORIA_METRICS_URL" ]]; then
echo -e "${GREEN}Métricas:${NC} Victoria Metrics (aula-12)"
else
echo -e "${YELLOW}Métricas:${NC} Victoria Metrics não disponível. Instale aula-12."
fi
echo ""
echo -e "${GREEN}URLs:${NC}"
echo -e " App: ${CYAN}${protocol}://${APP_HOST}${NC}"
echo -e " Kiali: ${CYAN}${protocol}://${KIALI_HOST}${NC}"
echo -e " Jaeger: ${CYAN}${protocol}://${JAEGER_HOST}${NC}"
echo ""
echo -e "${GREEN}Credenciais (Basic Auth para Kiali/Jaeger):${NC}"
echo -e " Usuário: ${CYAN}${BASIC_AUTH_USER}${NC}"
echo -e " Senha: ${CYAN}${BASIC_AUTH_PASS}${NC}"
echo ""
echo -e "${GREEN}Fluxo de Uso:${NC}"
echo ""
echo " # 1. Setup"
echo " ./setup.sh # Deploy com 90% v1, 10% v2"
echo ""
echo " # 2. Testar distribuição"
echo " ./teste-stress.sh # Veja ~90 v1, ~10 v2"
echo ""
echo " # 3. Mudar para 50/50 e re-testar"
echo " kubectl patch virtualservice app-backend -n istio --type='json' \\"
echo " -p='[{\"op\":\"replace\",\"path\":\"/spec/http/0/route/0/weight\",\"value\":50},"
echo " {\"op\":\"replace\",\"path\":\"/spec/http/0/route/1/weight\",\"value\":50}]'"
echo " ./teste-stress.sh"
echo ""
echo " # 4. Rollout completo para v2"
echo " kubectl patch virtualservice app-backend -n istio --type='json' \\"
echo " -p='[{\"op\":\"replace\",\"path\":\"/spec/http/0/route/0/weight\",\"value\":0},"
echo " {\"op\":\"replace\",\"path\":\"/spec/http/0/route/1/weight\",\"value\":100}]'"
echo " ./teste-stress.sh"
echo ""
echo -e "${YELLOW}Configure DNS para:${NC}"
echo " ${APP_HOST} -> IP do Ingress"
echo " ${KIALI_HOST} -> IP do Ingress"
echo " ${JAEGER_HOST} -> IP do Ingress"
echo ""
}
# ============================================================================
# Execução
# ============================================================================
main() {
echo ""
echo -e "${CYAN}╔═══════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ Aula 14 - Istio Traffic Splitting ║${NC}"
echo -e "${CYAN}╚═══════════════════════════════════════════════════════════╝${NC}"
check_prerequisites
collect_user_input
install_istio
install_observability
setup_basic_auth
setup_ingress
build_and_push_images
create_registry_secret
deploy_application
setup_app_ingress
create_test_pod
show_summary
}
main "$@"

198
aula-14/teste-stress.sh Executable file
View File

@@ -0,0 +1,198 @@
#!/bin/bash
# ============================================================================
# Teste de Stress - Demonstração de Traffic Splitting do Istio
# ============================================================================
# Script interativo para visualizar a distribuição de tráfego entre v1 e v2.
#
# Uso:
# ./teste-stress.sh [URL] [NUM_REQUESTS]
#
# Exemplos:
# ./teste-stress.sh # Usa APP_HOST do .env, 100 requests
# ./teste-stress.sh https://app.kube.quest # URL específica, 100 requests
# ./teste-stress.sh https://app.kube.quest 50 # 50 requests
# ============================================================================
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
NC='\033[0m'
BOLD='\033[1m'
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${SCRIPT_DIR}/.env"
# ============================================================================
# Configuração
# ============================================================================
# Carregar .env se existir
if [[ -f "$ENV_FILE" ]]; then
source "$ENV_FILE"
fi
# Parâmetros
URL="${1:-}"
NUM_REQUESTS="${2:-100}"
# Se URL não foi passada, tentar construir do .env
if [[ -z "$URL" ]]; then
if [[ -n "$APP_HOST" ]]; then
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
URL="https://${APP_HOST}"
else
URL="http://${APP_HOST}"
fi
else
echo -e "${RED}[ERRO]${NC} URL não especificada e APP_HOST não encontrado no .env"
echo ""
echo "Uso: $0 [URL] [NUM_REQUESTS]"
echo ""
echo "Exemplos:"
echo " $0 https://app.kube.quest"
echo " $0 https://app.kube.quest 50"
echo ""
echo "Ou configure APP_HOST no .env executando setup.sh novamente."
exit 1
fi
fi
# ============================================================================
# Funções
# ============================================================================
get_current_weights() {
local weights
weights=$(kubectl get virtualservice app-backend -n istio -o jsonpath='{.spec.http[0].route[*].weight}' 2>/dev/null || echo "90 10")
echo "$weights"
}
draw_bar() {
local value=$1
local max=$2
local width=40
local filled=$((value * width / max))
local empty=$((width - filled))
printf "["
for ((i=0; i<filled; i++)); do printf "${GREEN}#${NC}"; done
for ((i=0; i<empty; i++)); do printf "."; done
printf "]"
}
show_header() {
local weights
weights=$(get_current_weights)
local v1_weight=$(echo "$weights" | awk '{print $1}')
local v2_weight=$(echo "$weights" | awk '{print $2}')
echo ""
echo -e "${CYAN}${BOLD}╔═══════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}${BOLD}║ Teste de Traffic Splitting - Istio ║${NC}"
echo -e "${CYAN}${BOLD}╚═══════════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e "${YELLOW}Distribuição configurada:${NC} v1=${v1_weight}% v2=${v2_weight}%"
echo -e "${YELLOW}URL:${NC} ${URL}"
echo -e "${YELLOW}Requests:${NC} ${NUM_REQUESTS}"
echo ""
echo -e "${CYAN}───────────────────────────────────────────────────────────${NC}"
}
run_test() {
local count_v1=0
local count_v2=0
local count_other=0
for ((i=1; i<=NUM_REQUESTS; i++)); do
# Fazer request e capturar resposta
local response
response=$(curl -sk --connect-timeout 5 --max-time 10 "$URL" 2>/dev/null || echo "ERROR")
# Identificar versão baseado na resposta
if echo "$response" | grep -q "v1\|node-bugado v1"; then
((count_v1++))
elif echo "$response" | grep -q "v2\|stable\|node-bugado v2"; then
((count_v2++))
else
((count_other++))
fi
# Exibir progresso
printf "\r${NC}[%3d/%d] ${BLUE}v1:%-3d${NC} ${GREEN}v2:%-3d${NC}" "$i" "$NUM_REQUESTS" "$count_v1" "$count_v2"
# Pequena pausa para não sobrecarregar
sleep 0.05
done
echo ""
echo ""
# Exibir resultados
show_results "$count_v1" "$count_v2" "$count_other"
}
show_results() {
local count_v1=$1
local count_v2=$2
local count_other=$3
local total=$((count_v1 + count_v2 + count_other))
local pct_v1=0
local pct_v2=0
[[ $total -gt 0 ]] && pct_v1=$((count_v1 * 100 / total))
[[ $total -gt 0 ]] && pct_v2=$((count_v2 * 100 / total))
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${BOLD}Resultados:${NC}"
echo ""
printf " ${BLUE}v1${NC}: %3d (%2d%%) " "$count_v1" "$pct_v1"
draw_bar "$count_v1" "$total"
echo ""
printf " ${GREEN}v2${NC}: %3d (%2d%%) " "$count_v2" "$pct_v2"
draw_bar "$count_v2" "$total"
echo ""
if [[ $count_other -gt 0 ]]; then
echo -e " ${RED}Erros${NC}: $count_other"
fi
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo ""
# Comandos para alterar distribuição
show_commands
}
show_commands() {
echo -e "${BOLD}Comandos para alterar distribuição:${NC}"
echo ""
echo -e "${YELLOW}# Aumentar tráfego para v2 (50/50)${NC}"
echo "kubectl patch virtualservice app-backend -n istio --type='json' \\"
echo " -p='[{\"op\":\"replace\",\"path\":\"/spec/http/0/route/0/weight\",\"value\":50},"
echo " {\"op\":\"replace\",\"path\":\"/spec/http/0/route/1/weight\",\"value\":50}]'"
echo ""
echo -e "${YELLOW}# Virada completa para v2 (0/100)${NC}"
echo "kubectl patch virtualservice app-backend -n istio --type='json' \\"
echo " -p='[{\"op\":\"replace\",\"path\":\"/spec/http/0/route/0/weight\",\"value\":0},"
echo " {\"op\":\"replace\",\"path\":\"/spec/http/0/route/1/weight\",\"value\":100}]'"
echo ""
echo -e "${YELLOW}# Rollback para v1 se necessário (100/0)${NC}"
echo "kubectl patch virtualservice app-backend -n istio --type='json' \\"
echo " -p='[{\"op\":\"replace\",\"path\":\"/spec/http/0/route/0/weight\",\"value\":100},"
echo " {\"op\":\"replace\",\"path\":\"/spec/http/0/route/1/weight\",\"value\":0}]'"
echo ""
echo -e "${CYAN}Após alterar, re-execute: ${NC}${BOLD}./teste-stress.sh${NC}"
echo ""
}
# ============================================================================
# Execução
# ============================================================================
show_header
run_test