O Istio é a principal ferramenta de service mesh para o Kubernetes, ele disponibiliza diversas features para você gerenciar a sua malha de serviços, dentre essas features temos a parte de gerenciamento de tráfego onde uma das possibilidade é criar um 'mirror'. Mas o que é um mirror? um mirror permite você 'espelhar' o tráfego de requisições de um serviço para outro. E porque isso é útil? podemos encaixar essa funcionalidade em algumas situações, dentre elas vou destacar uma:
Vamos supor que você tem uma aplicação que usa o banco de dados mysql e você precisa migra-la para postgresql, porém, antes de tornar a versão migrada produtiva você quer testar o comportamento dela para ver se os dados serão salvos corretamente e se a aplicação não apresentará erros. Para isso, você pode espelhar as requisições que chegam na aplicação cuja versão utiliza o mysql para a versão que utiliza o postgresql. O tráfego continuará sendo enviado para a versão com mysql e a versão com postgresql receberá uma cópia desse tráfego, o retorno da aplicação com postgresql não é enviado ao cliente, garantindo assim que o cliente receba apenas o retorno da aplicação com mysql que já funciona, evitanto assim receber possíveis erros da versão nova com postgresql.
Interessante, não? 🔥
Vamos ver como funciona na prática? Para isso vamos precisar das seguintes ferramentas instaladas na nossa máquina:
⚠️ Os comandos citados abaixo para criar os componentes começam com a instrução istioctl kube-inject -f, esta instrução injeta no pod a ser criado o container de proxy do istio, para que o istio possa controlar este pod. Caso você não queira usar esta instrução confira aqui como injetar o proxy do istio de outras maneiras.
Mãos a obra 🤝🎓
1 - Para começar, vamos subir nosso cluster minikube, primeiramente inicie o docker abrindo o docker desktop (para Windows), em seguida abra um terminal git bash e execute o seguinte comando para subir o minikube:
minikube start
2 - Instale o istio
istioctl install --set profile=demo -y
3 - Copie o código abaixo e execute no git bash para criar o deployment principal
cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: istioapp-v1
labels:
app: istioapp
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: istioapp
version: v1
template:
metadata:
labels:
app: istioapp
version: v1
spec:
containers:
- name: istioapp
image: wandpsilva/istioapp:v1.0
ports:
- containerPort: 8080
EOF
4 - Agora faça o mesmo para o código abaixo para a versão 2 que receberá o tráfego espelhado
cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: istioapp-v2
labels:
app: istioapp
version: v2
spec:
replicas: 1
selector:
matchLabels:
app: istioapp
version: v2
template:
metadata:
labels:
app: istioapp
version: v2
spec:
containers:
- name: istioapp
image: wandpsilva/istioapp:v1.0
ports:
- containerPort: 8080
EOF
5 - Vamos criar o service do kubernetes
kubectl create -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: istioapp-service
spec:
selector:
app: istioapp
ports:
- protocol: TCP
port: 8080
targetPort: 8080
type: NodePort
EOF
6 - Criaremos agora nosso virtualservice e destinationrule, estes são componentes do istio utilizados para gerenciamento de tráfego e neste momento possuem uma configuração básica, apenas enviar a requisição recebida para o pod v1
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: istioapp-virtualservice
spec:
hosts:
- istioapp-service.default.svc.cluster.local
http:
- route:
- destination:
host: istioapp-service.default.svc.cluster.local
subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: istioapp-destinationrule
spec:
host: istioapp-service.default.svc.cluster.local
subsets:
- name: v1
labels:
app: istioapp
version: v1
EOF
7 - Com todos os componentes criados, vamos criar um deployment do httpbin para chamarmos a nossa aplicação e testa-la
cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: sleep
spec:
replicas: 1
selector:
matchLabels:
app: sleep
template:
metadata:
labels:
app: sleep
spec:
containers:
- name: sleep
image: curlimages/curl
command: ["/bin/sleep","3650d"]
imagePullPolicy: IfNotPresent
EOF
8 - Com o httpbin criado podemos chamar o endpoint da aplicação que subimos no minikube e verificarmos o response dela pelo log do pod. Vamos então chamar a aplicação com o seguinte comando
export SLEEP_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
kubectl exec "${SLEEP_POD}" -c sleep -- curl -sS http://istioapp-service:8080/v1/istioapp/hello-word/ping
Verifique as logs dos dois pods
pod v1
export V1_POD=$(kubectl get pod -l app=istioapp,version=v1 -o jsonpath={.items..metadata.name})
kubectl logs "$V1_POD" -c istioapp
pod v2
export V2_POD=$(kubectl get pod -l app=istioapp,version=v2 -o jsonpath={.items..metadata.name})
kubectl logs "$V2_POD" -c istioapp
você deverá ver a mensagem 'PONG' na log do pod v1 e nada na log do pod v2.
9 - Vamos fazer agora o mirror funcionar, fazendo com que a chamada feita no passo 8 chegue tanto para o pod v1 quanto para o pod v2, para isso vamos configurar novamente nosso virtualservice e destinationrule, aplique as configurações abaixo
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: istioapp-virtualservice
spec:
hosts:
- istioapp-service.default.svc.cluster.local
http:
- route:
- destination:
host: istioapp-service.default.svc.cluster.local
subset: v1
weight: 100
mirror:
host: istioapp-service.default.svc.cluster.local
subset: v2
mirrorPercentage:
value: 100
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: istioapp-destinationrule
spec:
host: istioapp-service.default.svc.cluster.local
subsets:
- name: v1
labels:
app: istioapp
version: v1
- name: v2
labels:
app: istioapp
version: v2
EOF
10 - Chame novamente a aplicação
export SLEEP_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
kubectl exec "${SLEEP_POD}" -c sleep -- curl -sS http://istioapp-service:8080/v1/istioapp/hello-word/ping
Verifique a log dos pods novamente
pod v1
export V1_POD=$(kubectl get pod -l app=istioapp,version=v1 -o jsonpath={.items..metadata.name})
kubectl logs "$V1_POD" -c istioapp
pod v2
export V2_POD=$(kubectl get pod -l app=istioapp,version=v2 -o jsonpath={.items..metadata.name})
kubectl logs "$V2_POD" -c istioapp
O que aconteceu? 🤔
Como você pode notar, ambos os pods exibiram nas logs a mensagem PONG, o que significa que ambos receberam a requisição feita, porém, o pod v2 recebe apenas uma cópia do tráfego, o seu retorno seja sucesso ou falha não retorna ao cliente, o que nos dá segurança de testarmos qualquer alteração e em qualquer ambiente.
Como aconteceu? 😃
Toda a lógica do mirror fica entre os componentes virtualservice e destinationrule, analisando os arquivos acima note que o virtualservice através do atributo mirror consegue replicar o tráfego para um outro subset (v2), este subset é configurado no destinationrule e lá dizemos através de labels para qual pod a requisição deverá ser enviada. Também é possível dizer o percentual de tráfego que deverá ser espelhado com o atributo mirrorPercentage, se este for omitido, 100% do tráfego será espelhado.
Este artigo foi baseado na documentação oficial do istio. Recomendo navegar pela documentação e explorar mais e mais do istio 🚀
Espero que tenham gostado, até a próxima. 😉
Top comments (1)
Muito bom a demo bem clara e objetiva.