Alisson Machado
31 March 2020

Istio e Minikube: Teste A/B

Eai gente, Estive bastante tempo sumido, pois estou trabalhando bastante, tenho escrito um livro sobre DevOps para uma editora da Índia, então acabou não sobrando muito tempo pro blog, mas hoje vamos com um assunto novo. Istio é um assunto que se fala muito, mas ainda é meio misterioso, que muita gente pensa, poxa, já existe o Ingress, pra que eu vou usar isso? Uma das grandes vantagens em trocar o tradicional Ingress pelo Istio é sistema de gerenciamento de rotas que nos permite fazer redirecionamentos mais elaborados, então nesse post eu vou mostrar um pouco disso fazendo um teste A/B, então vou fazer o deploy de duas versoes de uma aplicação no Kubernetes, e quando as requisições vierem do Firefox vão abrir uma versão da aplicação e caso venham de qualquer outro navegador vão abrir outra versão da aplicação. Nesse cenário vou utilizar o Minikube instalar o Istio e fazer o deploy da aplicação. Então Vamos lá!
PS C:\Users\1511 MXTI> minikube start --vm-driver virtualbox                                                                                                               * minikube v1.9.0 on Microsoft Windows 10 Pro 10.0.18363 Build 18363
* Using the virtualbox driver based on user configuration
* Creating virtualbox VM (CPUs=2, Memory=6000MB, Disk=20000MB) ...
* Preparing Kubernetes v1.18.0 on Docker 19.03.8 ...
* Enabling addons: default-storageclass, storage-provisioner
* Done! kubectl is now configured to use "minikube"
Agora vamos fazer o Download do Istio: https://github.com/istio/istio/releases No meu caso, como eu estou escrevendo esse post de uma maquina Windows, eu baixei a versao para Windows e descompactei em C:/istio .
PS C:\Users\1511 MXTI> ls C:\istio\                                                                                                                                        

    Directory: C:\istio


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        3/23/2020   9:57 PM       95280640 istioctl.exe
E configurei na variavel PATH do windows conforme a imagem abaixo: Nessa imagem e mostrado o caminho C:/istio incluido dentro da variable PATH do windows Agora configurado, no Windows e necessario abrir um novo terminal para que ele consiga carregar os novos comandos da variavel PATH. Para fazer a instalação do Istio dentro do seu Kubernetes você pode executar o seguinte comando:
PS C:\Users\1511 MXTI> istioctl.exe manifest apply --set profile=default
Detected that your cluster does not support third party JWT authentication. Falling back to less secure first party JWT. See https://istio.io/docs/ops/best-practices/security/#configure-third-party-service-account-tokens for details.
- Applying manifest for component Base...
✔ Finished applying manifest for component Base.
- Applying manifest for component Pilot...
✔ Finished applying manifest for component Pilot.
  Waiting for resources to become ready...
✔ Installation complete
Quando é feita a instalação do Istio, existem varios profiles que podem ser selecionados e você pode também criar o seu próprio custom profile. No meu caso eu escolhi o perfil default, pois ele já tem os recursos que precisamos fazer uma aplicação funcionar. Para fazer o deploy de uma aplicação, você pode simplesmente copiar o arquivo abaixo e rodar em seu kubernetes.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: blog
  labels:
    app: blog
spec:
  replicas: 1
  selector:
    matchLabels:
      app: blog
  template:
    metadata:
      labels:
        app: blog
    spec:
      containers:
      - name: blog
        image: alissonmenezes/alisson:latest
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: blog
  labels:
      app: blog
      service: blog
spec:
  selector:
      app: blog
  ports:
    - port: 8080
      targetPort: 8080  
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: blog-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: blog
spec:
  hosts:
  - "*"
  gateways:
  - blog-gateway
  http:
  - route:
    - destination:
        host: blog
        port:
          number: 8080 
Isso aqui já é o suficiente para fazer o deploy de uma aplicação utilizando o Istio, note que não temos mais a presença do ingress, ao invés disso utilizamos os componentes Gateway, que é o pod com o proxy rodando que vai fazer o redirecionamento para o nosso Service e o VirtualService, que é o responsável por fazer as regras de roteamento. Salve o arquivo e execute o comando:
PS C:\Users\1511 MXTI\Documents\Blog\istio> kubectl apply -f .\deployment.yaml
deployment.apps/blog created
service/blog created
gateway.networking.istio.io/blog-gateway created
virtualservice.networking.istio.io/blog created
Para testar a aplicação agora, você precisa do IP do seu Minikube e da porta em que o Istio Gateway está rodando, para isso execute os seguintes comandos:
PS C:\Users\1511 MXTI\Documents\Blog\istio> minikube ip
192.168.99.100
PS C:\Users\1511 MXTI\Documents\Blog\istio> kubectl -n istio-system get service istio-ingressgateway -o yaml  | Select-String -Pattern http2 -Context 0,1
>   - name: http2
      nodePort: 31896
Okay, então agora eu devo acessar: http://192.168.99.100:31896 e a tela a seguir deve ser essa: Nessa imagem e mostrada a aplicacao funcionando, com a mensagem, Fazendo teste no minishift Então o que podemos concluir que: Todas as requisições chegam através do Gateway, que foi declarado como blog-gateway, esse gateway tem as rotas configuradas no VirtualService, que por sua vez redireciona para o Service que esta respondendo pela porta 8080 e redireciona para a porta 8080 do Deployment, que esta rodando a imagem alissonmenezes/alisson:latest . Faça o deploy de uma nova aplicaçào de acordo com o arquivo abaixo:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: blog2
  labels:
    app: blog2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: blog2
  template:
    metadata:
      labels:
        app: blog2
    spec:
      containers:
      - name: blog2
        image: alissonmenezes/python_app:v1
        ports:
        - containerPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: blog
  labels:
    app: blog
spec:
  replicas: 1
  selector:
    matchLabels:
      app: blog
  template:
    metadata:
      labels:
        app: blog
    spec:
      containers:
      - name: blog
        image: alissonmenezes/alisson:latest
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: blog
  labels:
      app: blog
      service: blog
spec:
  selector:
      app: blog
  ports:
    - port: 8080
      targetPort: 8080  
---
apiVersion: v1
kind: Service
metadata:
  name: blog2
  labels:
      app: blog2
      service: blog2
spec:
  selector:
      app: blog2
  ports:
    - port: 8080
      targetPort: 8080 
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: blog-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: blog
spec:
  hosts:
  - "*"
  gateways:
  - blog-gateway
  http:
  - route:
    - destination:
        host: blog
        port:
          number: 8080 
Okay, agora se você quiser trocar entre as duas aplicações, você pode simplesmente editar essa parte:
  - route:
    - destination:
        host: blog
        port:
          number: 8080 
Se eu editar para host: para blog2, que é o Service correspondente a nova aplicação e aplicar a configuração deverá aparecer essa pagina: Nessa  imagem e mostrada uma pagina com a foto do ricky and morty Agora o desafio é, fazer uma regra para quando o User-Agent, que é a parte do cabeçalho HTTP responsável por informar quem é o Browser ( Navegador ) que está fazendo a requisição, devemos redirecionar para uma versão diferente da aplicação. Para fazer isso precisamos alterar somente o VirtualService:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: blog
spec:
  hosts:
  - "*"
  gateways:
  - blog-gateway
  http:
  - match:
    - headers:
        User-Agent:
          exact: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0"
    route:
    - destination:
        host: blog2
        port:
          number: 8080 
  - route:
      - destination:
          host: blog
          port:
            number: 8080
Ao analizar o objeto, é possível perceber que eu adicionei uma Matching Rule, para quando no cabeçalho o User-Agent, for igual ao valor: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0" , que eu peguei do meu browser, ele irá abrir a aplicação blog2, caso contrário ele irá abrir a aplicação blog. Em geral o arquivo ficou assim:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: blog2
  labels:
    app: blog2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: blog2
  template:
    metadata:
      labels:
        app: blog2
    spec:
      containers:
      - name: blog2
        image: alissonmenezes/python_app:v1
        ports:
        - containerPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: blog
  labels:
    app: blog
spec:
  replicas: 1
  selector:
    matchLabels:
      app: blog
  template:
    metadata:
      labels:
        app: blog
    spec:
      containers:
      - name: blog
        image: alissonmenezes/alisson:latest
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: blog
  labels:
      app: blog
      service: blog
spec:
  selector:
      app: blog
  ports:
    - port: 8080
      targetPort: 8080  
---
apiVersion: v1
kind: Service
metadata:
  name: blog2
  labels:
      app: blog2
      service: blog2
spec:
  selector:
      app: blog2
  ports:
    - port: 8080
      targetPort: 8080 
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: blog-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: blog
spec:
  hosts:
  - "*"
  gateways:
  - blog-gateway
  http:
  - match:
    - headers:
        User-Agent:
          exact: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0"
    route:
    - destination:
        host: blog2
        port:
          number: 8080 
  - route:
      - destination:
          host: blog
          port:
            number: 8080 
Para concluir, isso foi só um exemplo simples do que pode ser feito utilizando o poderoso sistema de rotas do Istio, dessa maneira podemos fazer Canary Deployments, os Testes AB podem ser mais refinados, sendo executados por usuários e a mesma lógica pode ser aplicada para quando você quiser testar por exemplo usuário de IPhone e de Android, cada um pode ter uma versão diferente do site. Valeu Galera!