Istio : application pratique

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.