O básico de mirror do Istio
Tags: istio, kubernetes, devops, sre
Published: 2024-06-23 12:57:03

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:

- [Docker](https://www.docker.com/products/docker-desktop/)
- [Minikube](https://storage.googleapis.com/minikube/releases/latest/minikube-installer.exe)
- [Istioctl](https://github.com/istio/istio/releases)
- [Git bash](https://git-scm.com/downloads)

> ⚠️ 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](https://istio.io/latest/docs/setup/additional-setup/sidecar-injection/) 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](https://httpbin.org/) 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](https://istio.io/latest/docs/tasks/traffic-management/mirroring/). Recomendo navegar pela documentação e explorar mais e mais do istio 🚀

Espero que tenham gostado, até a próxima. 😉