Dans un précédent article, nous vous avons présenté le concept de service Mesh, et plus précisément Istio et son fonctionnement.

Nous avons pu voir ensemble les composants du contrôle plane et du data plane. Aujourd’hui, nous vous proposons de nous pencher sur quelques cas pratiques de routing.

Pré-requis :

  • Helm : choco install kubernetes-helm
  • Kubectl : choco install kubernetes-cli
  • az cli : choco install azure-cli
  • Lens : Kubernetes IDE (lien vers Github)

 

 Nous allons aborder deux sujets :

  1. La mise en place d’Istio ;
  2. Comment mettre en place une stratégie de routing pour votre application au travers d’un service Mesh ?

 

Mise en place d’Istio

 

Création d’un cluster AKS

Commençons par la création d’un cluster AKS via ce script. Ce cluster AKS que nous allons déployer n’est pas ready to prod. Cette configuration minimaliste est destinée uniquement à cette démo.

 

$resourcegroup="DV-AKS-RG"
$clusterAks="akscluster"
az login
az group create --name $resourcegroup --location westeurope
az aks create --resource-group $resourcegroup --name $clusterAks --node-count 3 --enable-addons monitoring --generate-ssh-keys
az aks get-credentials -n $akscluster -g $resourcegroup

 

Bilan : nous avons créé un cluster dans un ressource group et injecté la configuration de notre cluster sur notre machine. Désormais, nous pouvons faire des appels kubectl sur notre nouveau cluster.

 

Installation d’Istio

Cette étape consiste à effectuer l’installation d’Istio sur notre cluster. Nous allons commencer par télécharger le CLI IstioCtl et l’ajouter au path de notre environnent :

 

$ISTIO_VERSION="1.7.3"

[Net.ServicePointManager]::SecurityProtocol = "tls12"
$ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -URI "https://github.com/istio/istio/releases/download/$ISTIO_VERSION/istioctl-$ISTIO_VERSION-win.zip" -OutFile "istioctl-$ISTIO_VERSION.zip"
Expand-Archive -Path "istioctl-$ISTIO_VERSION.zip" -DestinationPath .

New-Item -ItemType Directory -Force -Path "C:\Istio"
Move-Item -Path .\istioctl.exe -Destination "C:\Istio\"

$USER_PATH = [environment]::GetEnvironmentVariable("PATH", "User") + ";C:\Istio\"
[environment]::SetEnvironmentVariable("PATH", $USER_PATH, "User")
$env:PATH += ";C:\Istio\"

 

Istio fournit un opérateur pour gérer ses installations et ses mises à jour dans le cluster. Nous l’installons via cette commande :

 

istioctl operator init

 

Nous devons ajouter un namespace afin de faire les choses proprement et installer Istio dans son propre namespace :

 

kubectl create ns istio-system

 

Nous allons ensuite créer le fichier istio.aks.yaml qui décrit les spécifications de l’opérateur Istio afin d’ajouter des composants utiles (Grafana, Prometheus, Kiali) :

 

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  namespace: istio-system
  name: istio-control-plane
spec:
  # Use the default profile as the base
  # More details at: https://istio.io/docs/setup/additional-setup/config-profiles/
  profile: default
  # Enable the addons that we will want to use
  addonComponents:
    grafana:
      enabled: true
    prometheus:
      enabled: true
    tracing:
      enabled: true
    kiali:
      enabled: true
  values:
    global:
      # Ensure that the Istio pods are only scheduled to run on Linux nodes
      defaultNodeSelector:
        beta.kubernetes.io/os: linux
    kiali:
      dashboard:
        auth:
          strategy: anonymous

 

Enfin, nous déployons la configuration de l’opérateur Istio sur le cluster :

 

kubectl apply -f istio.aks.yaml

 

Mettre en place une stratégie de routing

 

Istio : vocabulaire et définitions

Avant de rentrer dans le vif du sujet, voici les principales définitions de concepts Istio à retenir :

  • Gateway: La Gateway est la passerelle de notre service Mesh. Elle permet d’en contrôler le trafic entrant et sortant.
  • Virtual Service : un Virtual Service se compose d’un ensemble de règles de routage évaluées dans l’ordre et permettant à Istio de faire correspondre chaque demande donnée au service virtuel à une destination réelle spécifique dans le maillage.

Destination Rule : une Destination Rule définit les stratégies qui s’appliquent au trafic d’un service après le routage. Ces règles spécifient la configuration de l’équilibrage de charge, la taille du pool de connexions à partir du sidecar, etc

 

Schéma Istio Dev User Service Ingress Controller Control Plane API

Suite à l’installation d’Istio, l’Ingress Controller sera notre unique point d’entrée vers nos applications hébergées dans le cluster. Pour le connaître, nous allons utiliser l’IDE Lens afin de retrouver l’IP public de l’Ingress Controller d’Istio dans la section Network > Service.

 

image de Lens

Pour en savoir plus, je vous invite à consulter la documentation Lens.

 

Use Case : application pratique d’Istio

Je vous propose de travailler sur une application simple : une application React qui appelle 4 webapi net core.

Pour continuer sur ce tutoriel, je vous invite à récupérer les sources sur github et à cloner ce repository.

Istio application WebAPI react

image de l'application

 

Configuration préalable : création du namespace

 

Nous allons créer un namespace pour notre nouvelle application :

 

kubectl create namespace monapplication

 

Afin de profiter du mécanisme d’injection d’Istio de façon implicite, nous allons ajouter un label au namespace :

 

kubectl label namespace monapplication istio-injection=enabled

 

Pour simplifier : à chaque déploiement dans ce namespace, Istio se chargera de mettre en place un pod (envoy) en mode side-car en amont de votre application.

 

Scénario 1 : mise à disposition de features web API

 

Nous avons développé une nouvelle application front disposant de 4 features. Chaque feature fait appel à une nouvelle web API. Nous souhaitons mettre à disposition la version 1.0 de ces nouvelles features (web API). Nous sommes donc dans un cas simple de routage.

Nous allons exposer nos services via une gateway puis router simplement nos services sur la version 1.0.

Voici le fichier yaml de l’application front :

 

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: monapplication
  name: front-v1
  labels:
    app: front
    version: v1
spec:
  selector:
    matchLabels:
      app: front
      version: v1
  replicas: 1
  template:
    metadata:
      labels:
        app: front
        version: v1
    spec:
      serviceAccountName: front
      containers:
      - name: front
        image: moustafarai/front:1.0.0
        stdin: true
        tty: true
        resources:
          requests:
            cpu: "200m"
            memory : "500Mi"
          limits:
            cpu: "0.4"
            memory : "701Mi"
        ports:
        - containerPort: 3000
          protocol: TCP
---
apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: monapplication
  name: front
  labels:
    account: front
---
apiVersion: v1
kind: Service
metadata:
  namespace: monapplication
  name: front
  labels:
    app: front
    service: front
spec:
  ports:
  - name: http
    port: 3000
  selector:
    app: front

 

Nous avons défini dans ce fichier yaml les composants suivants :

  • Le service Account ;
  • Le service ;
  • Le déploiement du pod.

Le service ne doit pas être exposé publiquement : il est de type Clusterip Istio a besoin de connaître votre version dans les métadata pour fonctionner : version: v1

Nous allons déployer les 4 applications sur le cluster.

Les fichiers yaml sont disponible dans le répertoire : /Deployments/Scenario-Starter/k8s

Nous devons exécuter la commande suivante pour les déployer dans le cluster :

 

kubectl create -f front-v1.0.0.yml -f webapia-v1.0.0.yml -f webapib-v1.0.0.yml -f webapic-v1.0.0.yml -f webapid-v1.0.0.yml

 

Nous pouvons observer le déploiement depuis Lens.

image de lens

Suite au déploiement, votre application n’est pas joignable depuis Internet. Il est donc nécessaire de configurer la route. Pour cela, nous devons dans un premier temps définir la configuration de la gateway :

 

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  namespace: monapplication
  name: demo-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"

 

Nous allons effectuer son déploiement :

 

kubectl apply -f gateway.yaml

Nous avons ouvert le port 80.

La deuxièmes étape consiste à définir les subsets dans les destination rules de toutes nos applications :

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  namespace: monapplication
  name: front-destination
spec:
  host: front
  subsets:
  - name: v1
    labels:
      version: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  namespace: monapplication
  name: webapia-destination
spec:
  host: webapia
  subsets:
  - name: v1
    labels:
      version: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  namespace: monapplication
  name: webapib-destination
spec:
  host: webapib
  subsets:
  - name: v1
    labels:
      version: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  namespace: monapplication
  name: webapic-destination
spec:
  host: webapic
  subsets:
  - name: v1
    labels:
      version: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  namespace: monapplication
  name: webapid-destination
spec:
  host: webapid
  subsets:
  - name: v1
    labels:
      version: v1

 

Dans une destination rule, un subset permet de définir plusieurs versions pour une même destination. Un subset est un sous-ensemble d’une destination rule : il pointera vers l’application correspondant à son label.

Dans ce premier scénario, un seul subset sera défini. Le subset de la web API A pointera sur la web API ayant le label « version v1 » :

 

labels:
    app: webapia
    version: v1  

 

Effectuons maintenant son déploiement :

 

kubectl apply -f destinationrules.yaml

Pour le moment, nous n’avons qu’une seule version : la v1. Les subsets sont maintenant liés à la version v1.

La dernière étape du routing consiste à faire le lien entre la gateway et les destinations rules.

Voici le fichier virtualservice.yaml :

 

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  namespace: monapplication
  name: front
spec:
  hosts:
  - "*"
  gateways:
  - demo-gateway
  http:
  - match:
    - uri:
        prefix: "/demo"
    - uri:
        prefix: /static
    route:
    - destination:
        subset: v1
        host: front
        port:
          number: 3000
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  namespace: monapplication
  name: webapia
spec:
  hosts:
  - "*"
  gateways:
  - demo-gateway
  http:
  - match:
    - uri:
        prefix: "/ServiceA"
    route:
    - destination:
        subset: v1
        host: webapia
        port:
          number: 80
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  namespace: monapplication
  name: webapib
spec:
  hosts:
  - "*"
  gateways:
  - demo-gateway
  http:
  - match:
    - uri:
        prefix: "/ServiceB"
    route:
    - destination:
        subset: v1
        host: webapib
        port:
          number: 80
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  namespace: monapplication
  name: webapic
spec:
  hosts:
  - "*"
  gateways:
  - demo-gateway
  http:
  - match:
    - uri:
        prefix: "/ServiceC"
    route:
    - destination:
        subset: v1
        host: webapic
        port:
          number: 80
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  namespace: monapplication
  name: webapid
spec:
  hosts:
  - "*"
  gateways:
  - demo-gateway
  http:
  - match:
    - uri:
        prefix: "/ServiceD"
    route:
    - destination:
        subset: v1
        host: webapid
        port:
          number: 80

 

Nous matchons les différents préfixes de route vers les destinations rules, et plus précisément sur le subset v1 de chaque application.

Notre site est maintenant disponible sur le web à l’adresse suivante : http://< ip-public-de-l-istio-ingress>/demo

 

Istio Sample affichage features

Monitoring du service Mesh

Nous allons maintenant utiliser l’add-on Kiali, qui permet de configurer et monitorer notre service Mesh. Il est accessible directement depuis L’IDE Lens, dans la rubrique Network > Service, en cliquant sur le détail du service Kiali.

 

Kiali Lens Kubernetes IDE

 

Sur le dashboard de Kiali, nous pouvons ainsi consulter le graph de notre service Mesh dans le namespace « monapplication ».

Je vous invite à cliquer ici pour en savoir plus sur Kiali.

 

Kiali dashboard graph du service Mesh

Scénario 2 : tester la nouvelle features des APIs 

Une nouvelle feature de nos web APIs est disponible : la version 2. Nous souhaitons que les internautes puissent la tester. Pour cela, nous allons équilibrer le trafic entre l’ancienne version (v1) et la nouvelle (v2).

Nous allons rediriger 50 % du trafic sur la version 1 et 50 % du trafic sur la version 2, et  déployer la nouvelle version des APIs dans le cluster.

Voici le yaml de la web API A version 2 :

 

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: monapplication
  name: webapia-v2
  labels:
    app: webapia
    version: v2
spec:
  selector:
    matchLabels:
      version: v2
      app: webapia
  replicas: 1
  template:
    metadata:
      labels:
        version: v2
        app: webapia
    spec:
      serviceAccountName: webapia
      containers:
      - name: webapia
        image: moustafarai/webapia:2.0.0
        resources:
          requests:
            cpu: "200m"
            memory : "200Mi"
          limits:
            cpu: "0.4"
            memory : "201Mi"
        ports:
        - containerPort: 80
          protocol: TCP

 

Nous n’allons pas visualiser toutes les APIs : le seul élément changeant est la « version : v2 » dans les labels.

Déployons toutes les web APIs via cette commande dans le répertoire Deployments/2.Scenario-Routing1/k8s :

 

kubectl create -f webapia-v2.0.0.yml -f webapib-v2.0.0.yml -f webapic-v2.0.0.yml -f webapid-v2.0.0.yml

 

Après avoir vérifié le bon déroulement sur Lens, nous pouvons passer à la suite :

 

Lens Kubernetes IDE WebAPI deployement

 

Nous pouvons constater qu’il y a deux versions de subsets dans les destination rules de notre fichier Deployments/2.Scenario-Routing1/istio/destinationrules.yaml.

 

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  namespace: monapplication
  name: front-destination
spec:
  host: front
  subsets:
  - name: v1
    labels:
      version: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  namespace: monapplication
  name: webapia-destination
spec:
  host: webapia
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  namespace: monapplication
  name: webapib-destination
spec:
  host: webapib
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  namespace: monapplication
  name: webapic-destination
spec:
  host: webapic
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: webapid-destination
spec:
  host: webapid
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2

 

Déployons cette nouvelle version :

 

kubectl apply -f Deployments/2.Scenario-Routing1/istio/destinationrules.yaml

 

Nous allons tester le routing par poids avec la répartition suivante :

  • 50 % du trafic sur la version 1 des web APIs ;
  • 50 % du trafic sur la version 2 des web APIs.

Voici la définition des Virtual Services de nos web APIs :

 

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  namespace: monapplication
  name: front
spec:
  hosts:
  - "*"
  gateways:
  - demo-gateway
  http:
  - match:
    - uri:
        prefix: "/demo"
    - uri:
        prefix: /static
    route:
    - destination:
        subset: v1
        host: front        
        port:
          number: 3000
      
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  namespace: monapplication
  name: webapia
spec:
  hosts:
  - "*"
  gateways:
  - demo-gateway
  http:
  - match:
    - uri:
        prefix: "/ServiceA"
    route:
    - destination:
        subset: v1
        host: webapia
        port:
          number: 80
      weight: 50
    - destination:
        subset: v2
        host: webapia
        port:
          number: 80
      weight: 50
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  namespace: monapplication
  name: webapib
spec:
  hosts:
  - "*"
  gateways:
  - demo-gateway
  http:
  - match:
    - uri:
        prefix: "/ServiceB"
    route:
    - destination:
        subset: v1
        host: webapib
        port:
          number: 80
      weight: 50
    - destination:
        subset: v2
        host: webapib
        port:
          number: 80
      weight: 50
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  namespace: monapplication
  name: webapic
spec:
  hosts:
  - "*"
  gateways:
  - demo-gateway
  http:
  - match:
    - uri:
        prefix: "/ServiceC"
    route:
    - destination:
        subset: v1
        host: webapic
        port:
          number: 80
      weight: 50
    - destination:
        subset: v2
        host: webapic
        port:
          number: 80
      weight: 50
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  namespace: monapplication
  name: webapid
spec:
  hosts:
  - "*"
  gateways:
  - demo-gateway
  http:
  - match:
    - uri:
        prefix: "/ServiceD"
    route:
    - destination:
        subset: v1
        host: webapid
        port:
          number: 80
      weight: 50
    - destination:
        subset: v2
        host: webapid
        port:
          number: 80
      weight: 50

 

Passons à l’exécution de la configuration :

 

kubectl apply -f Deployments/2.Scenario-Routing1/istio/virtualservice.yaml

 

Nous pouvons de nouveau tester l’application afin de vérifier son comportement.

 

Test application Istio features Lens

 

Notons qu’à chaque rafraîchissement (refresh) de la page, nous pouvons passer de la version 1 à la version 2 de l’application.

Dans cette démo, nous n’avons pas configuré d’affinité mais il est possible de le faire pour rester sur une version afin de garder une cohérence de votre application.

Passons maintenant sur Kiali afin d’observer le service Mesh.

Nous pouvons dores et déjà constater que le graphique a changé :

 

Observation du service Mesh sur Kiali

 

Nous avons maintenant un routing alétoire 50/50 sur nos webAPIs.

 

Istio, une solution complexe mais aboutie

 

Nous avons abordé dans cet article la mise en place d’Istio dans un contexte de routing. Bien que nous soyons restés très simplistes sur le concept de trafic d’Istio, il est toutefois possible d’aller encore plus loin et d’utiliser d’autres options pour rediriger le trafic (cookies, header, affinités, etc.). Istio est un outil très complet qui permet de répondre aux attentes et contraintes du monde de l’IT.

Parmi toutes les solutions de services Mesh existantes (Linkerd, Consul…), Istio reste le plus avancé. Il peut parfois sembler un peu complexe à mettre à l’échelle mais reste néanmoins le plus abouti.

Pour aller plus loin sur le traffic management d’Istio, n’hésitez pas à consulter la documentation Istio.