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:
@@ -24,6 +24,7 @@ App de demonstração: `node-bugado` - trava após N requests para demonstrar he
|
||||
| 11 | ArgoCD + GitLab Runner | Hetzner |
|
||||
| 12 | Victoria Metrics (Observabilidade) | Hetzner |
|
||||
| 13 | Container Factory (eStargz) | Hetzner |
|
||||
| 14 | Istio Traffic Splitting | Hetzner |
|
||||
|
||||
## Comandos Rápidos
|
||||
|
||||
@@ -38,6 +39,7 @@ cd aula-10 && ./setup.sh # GitLab
|
||||
cd aula-11 && ./setup.sh # ArgoCD
|
||||
cd aula-12 && ./setup.sh # Victoria Metrics
|
||||
cd aula-13 && ./setup.sh # Container Factory
|
||||
cd aula-14 && ./setup.sh # Istio Traffic Splitting
|
||||
```
|
||||
|
||||
## App node-bugado
|
||||
|
||||
230
aula-14/README.md
Normal file
230
aula-14/README.md
Normal 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
|
||||
```
|
||||
6
aula-14/app-backend/v1/Dockerfile
Normal file
6
aula-14/app-backend/v1/Dockerfile
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM node:24-alpine
|
||||
WORKDIR /app
|
||||
COPY app.js .
|
||||
EXPOSE 3000
|
||||
USER node
|
||||
CMD ["node", "app.js"]
|
||||
32
aula-14/app-backend/v1/app.js
Normal file
32
aula-14/app-backend/v1/app.js
Normal 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)`);
|
||||
});
|
||||
6
aula-14/app-backend/v2/Dockerfile
Normal file
6
aula-14/app-backend/v2/Dockerfile
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM node:24-alpine
|
||||
WORKDIR /app
|
||||
COPY app.js .
|
||||
EXPOSE 3000
|
||||
USER node
|
||||
CMD ["node", "app.js"]
|
||||
21
aula-14/app-backend/v2/app.js
Normal file
21
aula-14/app-backend/v2/app.js
Normal 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
116
aula-14/cleanup.sh
Executable 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 ""
|
||||
36
aula-14/istio/gateway.yaml
Normal file
36
aula-14/istio/gateway.yaml
Normal 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
|
||||
29
aula-14/istio/jaeger-values.yaml
Normal file
29
aula-14/istio/jaeger-values.yaml
Normal 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
|
||||
24
aula-14/istio/kiali-values.yaml
Normal file
24
aula-14/istio/kiali-values.yaml
Normal 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
|
||||
53
aula-14/k8s/deployment-v1.yaml
Normal file
53
aula-14/k8s/deployment-v1.yaml
Normal 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
|
||||
53
aula-14/k8s/deployment-v2.yaml
Normal file
53
aula-14/k8s/deployment-v2.yaml
Normal 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
|
||||
14
aula-14/k8s/destination-rule.yaml
Normal file
14
aula-14/k8s/destination-rule.yaml
Normal 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
15
aula-14/k8s/gateway.yaml
Normal 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}"
|
||||
23
aula-14/k8s/ingress-app.yaml
Normal file
23
aula-14/k8s/ingress-app.yaml
Normal 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
|
||||
25
aula-14/k8s/ingress-jaeger.yaml
Normal file
25
aula-14/k8s/ingress-jaeger.yaml
Normal 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
|
||||
25
aula-14/k8s/ingress-kiali.yaml
Normal file
25
aula-14/k8s/ingress-kiali.yaml
Normal 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
|
||||
9
aula-14/k8s/namespace.yaml
Normal file
9
aula-14/k8s/namespace.yaml
Normal 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
14
aula-14/k8s/service.yaml
Normal 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
|
||||
22
aula-14/k8s/virtual-service.yaml
Normal file
22
aula-14/k8s/virtual-service.yaml
Normal 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
592
aula-14/setup.sh
Executable 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
198
aula-14/teste-stress.sh
Executable 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
|
||||
Reference in New Issue
Block a user