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 :
- La mise en place d’Istio ;
- 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
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.

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.

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.

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

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.

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.

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 :

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.

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é :

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.