Service Mesh, Envoy Proxy, Linkerd, Istio… Ces noms vous parlent ? Ils affolent le buzzomètre dans le monde du DevOps depuis plus d’un an. Et ce n’est certainement pas près de s’arrêter en 2019.
C’est sur Istio que je souhaite m’attarder avec vous. En novembre dernier, Nicolas Muller, Software Engineer chez Treeptik, et moi-même présentions une démo d’Istio et Kiali au Devops D-Day dans le stade Vélodrome de Marseille. La réception par l’auditoire fut très positive, témoignant de la curiosité sinon de l’intérêt que suscite actuellement Istio et m’a encouragé à écrire cet article dans GNU/Linux Mag’. L’idée est de présenter Istio principalement par le biais de quelques exemples, portés par une démo que vous pouvez rejouer chez vous.
Le code présenté dans cet article a été testé sur Fedora 29, avec Kubernetes 1.10.0, Minikube 0.31.0 et Istio 1.0.5.
1. Mais avant tout, Istio, qu’est-ce donc ?
Istio est un logiciel open source développé principalement par Google, IBM et Lyft [1]. Dans la majorité des cas, on le verra tourner sur Kubernetes. Il se compose de plusieurs services et proxies, dont le but principal est de gérer, via un ensemble de règles et de définitions, les communications entre les services de l’application cliente. Typiquement, cela s’adresse aux architectures en microservices, mais pas uniquement : Istio peut notamment se révéler très utile pour une migration progressive d’un monolithe en plusieurs services.
Plus généralement, Istio fournit un Service Mesh : littéralement un maillage de services. Comme on peut le voir en figure 1, le concept repose sur l’utilisation d’un proxy associé à chaque service d’une application, qui sera capable d’appliquer les règles que l’on a définies sur chaque requête entrante ou sortante. Parmi toute la panoplie de règles disponibles, citons la possibilité de sécuriser les échanges (Mutual TLS), de définir des règles de routage (par exemple, pour mettre en place un Canary Deployment ou de l’A/B Testing), de définir des stratégies de résilience (Retries, Circuit Breaking), etc.
Fig. 1 : Avec Istio, ce sont des proxies Envoy qui captent tout le trafic entrant et sortant de nos services, et appliquent les règles que l’on aura définies ; le tout orchestré par un « control plane » qui propage les configurations, collecte les métriques, authentifie les services, etc. (source : https://istio.io/).
L’un des principes fondamentaux d’Istio est de ne nécessiter aucun changement dans votre code source : toutes les règles ne sont que configurations, ou plus exactement des Custom Resources de Kubernetes. Comme toute ressource de ce type, on les manipule sous forme d’objets JSON ou YAML que l’on peut créer, modifier ou supprimer à tout moment, sans provoquer de coupure de service. Istio définit de nombreux types de ressources : les Gateways sont une sorte de porte d’entrée au Service Mesh, les Service Entries permettent d’accéder à des services externes au mesh, et d’autres moins connues existent, mais nous nous focaliserons ici sur les deux probablement les plus importantes : les Virtual Services et les Destination Rules. Ce sont elles qui, combinées, permettent de définir les règles de routage entre les services du mesh.
2. Voir ou ne pas voir
Ces différents objets permettent donc d’interférer sur les communications entre les services. Mais Istio ne se limite pas à cela, il amène avec lui un élément crucial pour les microservices : une bonne observabilité.
Puisque chaque requête au sein du Service Mesh est interceptée par un proxy, pourquoi ne pas alimenter quelques métriques bien utiles sur la volumétrie, les temps de réponse, la taille des requêtes et des réponses ? Istio ne s’en prive pas. Et Envoy ajoute en prime des métriques sur la santé des services. Elles sont stockées dans Prometheus [2], le Timeseries Database (TSDB) très en vogue dans l’écosystème Kubernetes, entre autres.
Cependant, lorsqu’on parle de microservices, on comprend aisément que le monitoring peut représenter certains challenges. Les microservices peuvent être très nombreux, chaque service étant potentiellement mis à l’échelle en plusieurs instances, chacune produisant son lot de métriques. Ajoutons à cela le principe du Pets versus Cattle qui veut qu’un conteneur soit considéré comme une ressource jetable, potentiellement à courte durée de vie et que l’on redémarrera au moindre pépin… On comprend vite que le système de monitoring devra jongler avec une multiplicité de sources. Cela pourrait promettre quelques crises de nerfs à nos ops.
Heureusement, Prometheus va nous simplifier la vie. D’abord, parce que contrairement à d’autres comme Influx, Prometheus fonctionne en pull et non en push, c’est-à-dire que le système monitoré se contentera d’exposer des métriques sans avoir à se soucier de la présence ou non du TSDB, sans avoir à en connaître l’adresse ou les détails de connexion. Et c’est avec le Kubernetes service discovery de Prometheus que la magie opèrera, puisque via l’API de Kubernetes, Prometheus sera non seulement capable d’identifier et interroger l’ensemble des pods susceptibles d’exposer des métriques, mais aussi d’indexer celles-ci selon leur source. Cette indexation permettra ensuite d’effectuer des filtrages et des agrégations pour un ensemble de pods, un workload, un groupe arbitraire de workloads, un namespace… On parle là de la dimensionnalité d’une métrique. Ce principe vaut aussi bien pour les métriques d’Istio, que pour vos propres métriques applicatives qui pourront bénéficier des mêmes possibilités d’agrégation, si vous choisissez de les exposer au format Prometheus.
Istio propose par défaut quelques dashboards Grafana pour visualiser ces métriques. Mais aussi Kiali [3], logiciel développé chez Red Hat et auquel je contribue, qui tire entièrement partie des métriques d’Istio pour afficher un graphe animé du Service Mesh, des informations détaillées sur les objets le composant, sur leur santé, ainsi que d’autres dashboards de métriques. En outre, Kiali est aussi capable d’effectuer une validation des ressources Istio (par exemple : l’hôte déclaré dans un Virtual Service existe-t-il bel et bien ? La somme des poids attribués à des routes vaut-elle bien 100 ? etc.).
3. Ouvrez les terminaux
Passons aux choses sérieuses. Nous allons déployer quelques microservices et jouer avec Istio pour en explorer certaines possibilités.
Pour cela, il faut bien sûr avoir un cluster Kubernetes à disposition, ce qui peut se faire sans trop de difficulté sur une machine personnelle avec Minikube, pour peu que cette machine soit assez puissante. Oui, Istio et Kubernetes sont gourmands ; Istio recommande d’allouer au minimum 4 cœurs et 8 GB de mémoire, mais j’utilise sans difficulté un peu moins pour cette démo : 3 cœurs (Intel i5-6500) et 6 GB – il est certainement possible de diminuer encore les exigences. Plus d’informations sur l’installation de Minikube : https://kubernetes.io/docs/setup/minikube/.
3.1 Installer Istio et Kiali
Actuellement, la méthode la plus simple pour installer Istio est certainement via Helm [4], je vous propose donc cette option (pour les utilisateurs d’OpenShift, il existe une solution pré-packagée avec Istio et Kiali, nommée Maistra [5]).
Il faut commencer par télécharger une release d’Istio, par exemple la 1.0.5 (https://github.com/istio/istio/releases), dézipper l’archive et installer :
$ tar -zxvf istio-1.0.5-linux.tar.gz
$ cd istio-1.0.5/
$ helm template install/kubernetes/helm/istio --name istio --namespace istio-system --set kiali.enabled=true > $HOME/istio.yaml
$ kubectl apply -f install/kubernetes/helm/istio/templates/crds.yaml
$ kubectl create namespace istio-system
$ kubectl apply -f $HOME/istio.yaml
$ export PATH=$PATH:`pwd`/bin
Il faudra attendre un peu que l’ensemble des images Docker se télécharge et que les pods soient prêts. Pour vérifier la progression, exécutez :
$ kubectl get pods -n istio-system
Ou alors :
$ kubectl get pods -n istio-system --watch)
Lorsque tous les pods sont en statut Running ou Completed, nous pouvons poursuivre.
Pour accéder à Kiali, le plus simple est de faire un port-forwarding sur localhost (ouvrons un nouveau terminal pour cela) :
$ kubectl port-forward svc/kiali 20001:20001 -n istio-system
Kiali est alors accessible à l’adresse http://localhost:20001 (login : admin/admin).
3.2 Cloner demo-mesh-arena
Il s’agit du dépôt contenant les exemples qui vont suivre, hébergé sur GitHub. Il contient également le code source des microservices (en Java / Vert.x). Il n’est pas nécessaire de le compiler pour la démo, celle-ci se basant sur des images Docker publiquement accessibles (mais rien ne vous empêche de les reconstruire vous-même, les Dockerfiles sont également fournis). Seuls quelques fichiers YAML seront utilisés ici.
$ git clone https://github.com/jotak/demo-mesh-arena.git
$ cd demo-mesh-arena
Il s’agit d’une mini « simulation » de football aux règles très élastiques, non jouable, malheureusement, mais vous pourrez voir quelques « intelligences » artificielles (à l’intelligence toute relative, vous pourrez le constater) s’activer sur le terrain et marquer quelques buts. Au-delà de l’aspect ludique, cette démo présente plusieurs avantages pour montrer les possibilités d’Istio :
- Elle est architecturée en microservices : l’interface graphique en est un, de même que le terrain, le ballon, le joueur visiteur et le joueur local (voir figure 2). Ces services peuvent être répliqués en plusieurs instances, particulièrement les joueurs (pour former une équipe) et le ballon… ce qui ne manquera pas de causer quelques soucis de routage qu’il nous faudra régler, vous l’avez deviné, avec Istio.
Fig. 2 : Diagramme des principales interactions entre les services : connaissance des dimensions du terrain, collisions balle-terrain, interactions des joueurs avec le ballon, et requêtes d’affichage auprès de l’interface graphique.
- L’application est vivante, elle génère d’elle-même du trafic, ce qui nous permettra de constater à chaud et presque instantanément les effets de telle ou telle règle Istio injectée, sans devoir recourir à un générateur de trafic externe.
3.3 Lancer l’interface graphique
$ kubectl apply -f <(istioctl kube-inject -f ./services/ui/Deployment.yml)
$ kubectl create -f ./services/ui/Service.yml
Remarquons ici l’utilisation du binaire istioctl que nous avons exporté dans notre PATH précédemment. Avec la commande kube-inject, Istio va intégrer le proxy Envoy en tant que sidecar container (voir encadré) dans le déploiement de notre service, et donc dans chaque pod issu de ce déploiement. Il est à noter que notre fichier de déploiement ne présente en lui-même aucune spécificité propre à Istio. Il aurait pu être utilisé tel quel, via un kubectl apply -f ./services/ui/Deployment.yml, ce qui aurait été tout à fait valide. Mais il n’aurait alors pas reçu son injection de proxy, et serait donc exclu du Service Mesh.
Un pod peut contenir plusieurs conteneurs. Dans le jargon de Kubernetes, un sidecar container est un conteneur secondaire, souvent ne faisant pas partie de l’application au sens métier du terme, mais apportant une plus-value technique, comme ici un Proxy. Il fait partie intégrante du pod, garantissant une exécution sur le même nœud.
La seconde ligne concerne la création d’un Service Kubernetes associé au déploiement, tout ce qu’il y a de plus classique, afin d’exposer un port et une API.
Dans Kiali, cliquons sur le menu Graph et sélectionnons le namespace default. Nous voyons notre service ui, pour le moment isolé (figure 3).
Fig. 3 : Le graphe de Kiali encore quasiment vide.
Comme nous l’avons fait précédemment pour Kiali, ouvrons un port local relié à ce service :
$ kubectl port-forward svc/ui 8080:8080
Ceci nous permet d’ouvrir un navigateur à l’adresse http://localhost:8080 pour voir la page presque blanche de l’interface graphique.
3.4 Un stade, un ballon, des joueurs
Étoffons un peu le graphe :
$ kubectl apply -f <(istioctl kube-inject -f ./services/stadium/Deployment-Smaller.yml)
$ kubectl create -f ./services/stadium/Service.yml
$ kubectl apply -f <(istioctl kube-inject -f ./services/ball/Deployment.yml)
$ kubectl create -f ./services/ball/Service.yml
Dès que Kiali se rafraîchit, les deux nouveaux nœuds apparaissent dans le graphe. Et si l’on patiente encore quelques instants, le temps que Prometheus récupère les métriques, on pourra voir le trafic apparaître entre le Stade et l’UI, ainsi qu’entre le Ballon et l’UI, correspondant aux requêtes d’affichage (figure 4).
Fig. 4 : Le graphe incluant Stade et Ballon.
L’écran de jeu voit également apparaître ces éléments (figure 5).
Fig. 5 : Le stade est prêt !
Pour mieux visualiser les requêtes dans Kiali, nous pouvons activer l’animation du trafic depuis le menu Display.
Passons aux joueurs :
$ kubectl apply -f <(istioctl kube-inject -f ./services/ai/Deployment-2-locals.yml)
$ kubectl create -f ./services/ai/Service-locals.yml
$ kubectl apply -f <(istioctl kube-inject -f ./services/ai/Deployment-2-visitors.yml)
$ kubectl create -f ./services/ai/Service-visitors.yml
Nous déployons ici deux joueurs pour chaque équipe. Après quelques instants, vous devriez les voir surgir sur le terrain et s’emparer du ballon. Cliquer sur Start game positionnera le ballon au centre et mettra les compteurs à zéro.
Côté Kiali, les interactions se matérialisent entre les joueurs et le ballon. Chaque joueur cherche à connaître la position du ballon et, si possible, taper dedans – le ballon lui-même décidera s’il y parvient ou non. Si vous explorez les autres écrans de Kiali, vous pourrez voir l’inventaire des Services et Workloads Kubernetes, la santé de ces éléments, ou encore les métriques.
Le graphe, quant à lui, prend de l’ampleur. Plus on ajoute des services, plus il risque de devenir confus. Pour pallier à cela, nous pouvons double-cliquer sur un nœud pour restreindre la vue à ses relations directes, obtenant ainsi un sous-ensemble du graphe global. Faisons cela sur ai-visitors-basic (figure 6).
Fig. 6 : Vue partielle du graphe.
À ce stade nous n’avons tiré parti, indirectement, que d’une seule fonctionnalité d’Istio : la télémétrie, par le biais de Kiali. Mais tout est en place pour aller plus loin.
3.5 Deux ballons ? Arbitre, qu’est-ce qu’on fait ?
Introduisons un second ballon :
$ kubectl apply -f <(istioctl kube-inject -f ./services/ball/Deployment-v2.yml)
Nul besoin ici de créer un service, puisque nous avons déjà un service ball qui couvre notre nouveau pod via le label Kubernetes app:ball.
Pour pouvoir le distinguer sur le terrain, celui-ci sera rose.
Dans la réalité, il y a fort à parier que les joueurs sauront d’eux-mêmes vers quel ballon se tourner, même s’il y a parfois quelques ratés [6]. Malheureusement, nos IA n’ont pas toute l’intelligence d’un joueur de foot, et nous pouvons déjà anticiper ce qui va se passer.
Fig. 7 : Load-balancing classique, sans Istio.
Lorsqu’ils souhaiteront rejoindre un ballon, les joueurs interrogeront le service ball pour se déplacer. Comme deux pods sont susceptibles de recevoir la requête, elle sera load-balancée par Kubernetes, en round-robin : une fois l’un, une fois l’autre (figure 7).
Et de fait, sur le terrain, nous voyons que l’indécision gagne tous les joueurs : ils ne savent plus vers quel ballon se tourner (figure 8).
Fig. 8 : Les IA restent vaguement plantées entre les deux ballons.
Kiali matérialise également cela : dans le graphe, nous pouvons activer le label Requests percent of total (menu Edge Labels) pour afficher sur chaque liaison le pourcentage que représente son trafic par rapport au trafic total sortant du nœud source. On constate que le trafic se partage équitablement vers chacun des ballons (figure 9).
Fig. 9 : Répartition équitable, 24 % pour chacun des ballons.
3.6 Istio à la rescousse
Une première expérience peut se faire en appliquant une pondération pour chaque ballon. Pour que cela soit possible, nous devons être capables de discriminer les deux ballons. Si vous examinez les fichiers de déploiement que nous avons utilisés précédemment, vous verrez que chacun est labellisé différemment : nous avons un ballon version:v1 et un autre version:v2. Voilà notre discriminant – nous aurions pu en choisir un autre, mais les configurations par défaut d’Istio et de Kiali exploitent celui-ci en particulier pour la télémétrie. D’une manière générale, l’utilisation des labels app et version, sans être indispensable, est recommandée.
Nous pouvons dès lors utiliser une Destination Rule qui va déclarer ces labels comme subsets dans le fichier destrule.yml :
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: ball-dr
spec:
host: ball
subsets:
- name: ball-v1
labels:
version: v1
- name: ball-v2
labels:
version: v2
Puis un Virtual Service qui applique une pondération pour les requêtes à destination du service ball, de 75 % vers la version v1 et 25 % vers la v2, dans virtualservice-75-25.yml :
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ball-vs
spec:
hosts:
- ball
http:
- route:
- destination:
host: ball
subset: ball-v1
weight: 75
- destination:
host: ball
subset: ball-v2
weight: 25
Créons ces ressources :
$ istioctl create -f ./services/ball/destrule.yml
$ istioctl create -f ./services/ball/virtualservice-75-25.yml
Presque immédiatement, nous allons constater le changement de comportement des joueurs : ils parviennent à jouer essentiellement avec le premier ballon, quoiqu’encore un peu hésitants et reculant parfois vers le second.
Dans Kiali, comme le montre la figure 10, la répartition du trafic entre les ballons tend vers 1/4 – 3/4 (le changement est progressif, car Kiali base ses calculs sur la télémétrie agrégée de la minute passée, par défaut).
Fig. 10 : Trois fois plus de trafic vers v1 que vers v2.
La figure 11 montre l’impact du Virtual Service sur le traitement d’une requête.
Fig. 11 : Par l’utilisation d’un Virtual Service, Istio et Envoy se substituent au routeur de Kubernetes et son load-balancer : le sidecar client établit les routes sortantes et fait jouer son propre load-balancer, ici nommé « weighted least request » pour les routes pondérées.
3.7 Un match dans le match
Puisque nous avons deux ballons, pourquoi ne pas faire deux matchs ? Oui, sur le même terrain.
Faisons déjà entrer deux nouveaux joueurs, Messi et Mbappé :
$ kubectl apply -f <(istioctl kube-inject -f ./services/ai/Deployment-Messi.yml)
$ kubectl apply -f <(istioctl kube-inject -f ./services/ai/Deployment-Mbappe.yml)
Ceux-là ressemblent à peu près aux joueurs existants, si ce n’est qu’ils sont labellisés avec une version différente… et sont accessoirement bien meilleurs, ils ne vont d’ailleurs pas tarder à monopoliser le ballon.
Tout en conservant la Destination Rule existante, nous allons remplacer le précédent Virtual Service par celui-ci, contenu dans virtualservice-by-label.yml :
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ball-vs
spec:
hosts:
- ball
http:
- match:
- sourceLabels:
version: basic
route:
- destination:
host: ball
subset: ball-v1
- route:
- destination:
host: ball
subset: ball-v2
Ici, nous redirigeons chaque requête vers une version du ballon en fonction des labels de la source (des joueurs donc, mais également du terrain ou tout autre microservice qui interagirait avec le ballon).
$ istioctl replace -f ./services/ball/virtualservice-by-label.yml
Quasiment instantanément nous voyons le changement de comportement : Messi et Mbappé jouant l’un contre l’autre avec le ballon rose, et les autres joueurs avec le blanc.
3.8 Ballon crevé et Traffic Shadowing
Le Traffic Shadowing (ou mirroring) est un pattern très utile pour tester une nouvelle version d’un service en situation de production tout en évitant d’altérer celle-ci. Le principe est de dupliquer chaque requête à destination du service existant pour la rejouer vers la version à tester. Seule la réponse de la « vraie » requête sera perçue par le client. Bien entendu, cela suppose de ne pas brancher la version de test, en aval, sur des services tels que la base de données de prod [7].
Mais tout d’abord, faisons un peu de nettoyage…
$ kubectl delete -f ./services/ai/Deployment-Messi.yml
$ kubectl delete -f ./services/ai/Deployment-Mbappe.yml
$ istioctl delete -f ./services/ball/virtualservice-by-label.yml
Nous allons déployer un nouveau ballon « crevé », remplaçant notre ballon rose : celui-ci est programmé pour renvoyer un certain pourcentage d’erreurs sur les requêtes entrantes. Par ailleurs, des effets graphiques inclus dans la démo permettent de mieux visualiser les requêtes reçues par les ballons :
- lorsqu’un ballon ne reçoit aucune requête, il s’assombrit ;
- lorsqu’un ballon renvoie des erreurs, il rougit.
Nous créons aussi pour le moment un Virtual Service dont le rôle est d’envoyer tout le trafic des ballons vers la v1, afin de garder notre ballon crevé inactif.
$ istioctl create -f ./services/ball/virtualservice-all-to-v1.yml
$ kubectl apply -f <(istioctl kube-inject -f ./services/ball/Deployment-burst.yml)
Comme prévu, les joueurs ne jouent qu’avec la v1. L’autre ballon, ne recevant pas de requête, s’est assombri. Passons au shadowing. Istio permet d’activer ce mode de façon extrêmement simple sur un service (fichier virtualservice-mirrored.yml) :
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ball-vs
spec:
hosts:
- ball
http:
- route:
- destination:
host: ball
subset: ball-v1
mirror:
host: ball
subset: ball-v2
Puis :
$ istioctl replace -f ./services/ball/virtualservice-mirrored.yml
À ce moment, nous voyons que le second ballon sur le terrain vire du gris sombre au rouge (figure 12), ce qui signifie qu’il reçoit des requêtes et provoque des erreurs. Pour autant, les joueurs continuent de jouer exclusivement avec la v1 : preuve qu’ils ne reçoivent pas les réponses de la v2, grâce au shadowing. Il peut arriver parfois, si un joueur passe par hasard près du second ballon, qu’il tape vraiment dedans. C’est logique : il reçoit réellement les requêtes des joueurs même si ceux-ci n’en savent rien, et peut alors considérer qu’un joueur est parvenu à tirer.
Fig. 12 : Le ballon crevé est inactif, délaissé par les joueurs, mais reçoit tout de même des requêtes et renvoie des erreurs, preuve en est sa couleur rouge.
Côté Kiali, nous pouvons également voir les implications du shadowing, bien que ce soit un peu délicat. Lorsque nous avons affiché le sous-graphe du nœud ai-visitors-basic, nous nous sommes focalisés sur le point de vue de ce workload. De ce fait, nous ne voyons aucune requête en direction du ballon v2 : de la perspective du joueur, seule la v1 est contactée (figure 13).
Fig. 13 : Le ballon v1 capte l’ensemble du trafic.
En revanche, nous pouvons voir que la santé du ballon v2 n’est pas bonne (le contour orange signifiant « santé dégradée »). En passant le curseur sur l’icône de santé, le détail apparaît (figure 14) : le taux d’erreurs dépasse les 10 %. Cela correspond aux erreurs que le microservice génère intentionnellement. Mais surtout, cela signifie qu’il traite bel et bien des requêtes entrantes. Kiali nous informe donc correctement de la santé d’un déploiement en mode miroir, ce qui tombe bien puisque c’est exactement ce que l’on cherche à vérifier avec ce pattern.
Fig. 14 : L’infobulle de santé affiche plus de détails.
En complément de cela, nous pouvons double-cliquer sur le nœud v2 pour en adopter le point de vue. De là, les liaisons entrantes s’affichent en rouge, témoignant du trafic et des erreurs (figure 15).
Fig. 15 : Sous-graphe centré sur ball-v2.
La représentation du shadowing dans Kiali n’est peut-être pas des plus évidentes, mais avec un peu d’entraînement tout finit par prendre son sens.
3.9 Circuit breaking
Une dernière pour la route ? Istio / Envoy implémentent deux types de circuit-breaking, l’un basé sur une limite maximale de connexions ou requêtes concurrentes autorisées, l’autre basé sur le nombre d’erreurs HTTP consécutives émises. C’est ce dernier que nous abordons ici. Nommé outlier detection, il permet d’exclure temporairement un pod du pool de load-balancing d’Envoy lorsqu’il émet trop d’erreurs. Périodiquement, suivant un temps configurable, il sera remis dans le pool, lui donnant une nouvelle chance au cas où il se serait stabilisé. L’outlier detection se configure non pas dans un Virtual Service, mais dans la Destination Rule (destrule-outlier.yml) :
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: ball-dr
spec:
host: ball
subsets:
- name: ball-v1
labels:
version: v1
- name: ball-v2
labels:
version: v2
trafficPolicy:
outlierDetection:
consecutiveErrors: 1
baseEjectionTime: 10s
maxEjectionPercent: 50
Avant tout, supprimons le Virtual Service existant :
$ istioctl delete -f ./services/ball/virtualservice-mirrored.yml
En son absence, nous retrouvons la situation initiale où chaque joueur hésite entre les deux ballons.
Puis configurons le circuit-breaker :
$ istioctl replace -f ./services/ball/destrule-outlier.yml
Rapidement, le ballon v2 va s’assombrir : générant trop d’erreur, il a été évincé du pool de load-balancing et ne reçoit donc plus de requête (figure 16). Périodiquement, il redevient rouge : Envoy l’a replacé dans le pool… avant de le rééjecter presque aussitôt.
Fig. 16 : Le ballon gris est évincé du load-balancing.
Kiali montre également ces erreurs sur les requêtes parvenant au ballon v2. Lorsqu’on compare le volume de requête avec la v1, on constate qu’il est bien inférieur, comme attendu (figure 17).
Fig. 17 : Seulement 1 % des requêtes parviennent à la v2.
Conclusion
J’espère que cette introduction à Istio vous aura éclairé sur les possibilités offertes. Bien entendu, nous n’en avons gratté que la surface et de nombreux autres sujets demandent à être explorés : quid du mTLS, du tracing ? Istio couvre également ces domaines dont le tracing par le biais de Jaeger.
Et qu’en est-il des performances ? Les proxies Envoy ont beau être plébiscités dans le monde des conteneurs, le principe même de Service Mesh induit forcément un overhead sur la latence, la mémoire, la charge CPU – et ce proportionnellement au nombre de services dans le mesh. C’est un compromis demandé au regard des fonctionnalités offertes. Il est certain que le sujet est pris très au sérieux dans les équipes de Google, Lyft et IBM. Si cela vous intéresse, certains rapports et guides sont disponibles [8]. L’outil Fortio [9], en outre, a été spécifiquement conçu pour les tests en charge sur Istio.
Références
[1] Site officiel d’Istio : https://istio.io/ - GitHub : https://github.com/istio/istio
[2] Prometheus, Timeseries Database : https://prometheus.io/
[3] Site officiel de Kiali : https://kiali.io/
[4] Helm, Kubernetes package manager : https://helm.sh/ et S. BEURET, « Highway to Helm ! », GNU/Linux Magazine n°221, décembre 2018 : https://connect.ed-diamond.com/GNU-Linux-Magazine/GLMF-221/Highway-to-Helm
[5] Maistra : https://maistra.io/
[6] Le même scénario « in real life » : https://www.koreus.com/video/match-football-2-ballons.html
[7] Une lecture vivement recommandée pour plus d’informations sur le trafic shadowing : http://blog.christianposta.com/microservices/advanced-traffic-shadowing-patterns-for-microservices-with-istio-service-mesh/
[8] Rapport « Performances and Scalability » : https://istio.io/docs/concepts/performance-and-scalability/
[9] Fortio, tests de charge pour Istio : https://fortio.org/