Retour aux microservices avec Istio. Partie 2

Retour aux microservices avec Istio. Partie 2

Noter. trad.: La première partie Cette série était consacrée à la présentation des fonctionnalités d'Istio et à leur démonstration en action. Nous allons maintenant parler d'aspects plus complexes de la configuration et de l'utilisation de ce maillage de services, et en particulier de la gestion fine du routage et du trafic réseau.

Nous vous rappelons également que l'article utilise des configurations (manifestes pour Kubernetes et Istio) du référentiel istio-maîtrise.

gestion du trafic

Avec Istio, de nouvelles fonctionnalités apparaissent dans le cluster pour offrir :

  • Routage dynamique des requêtes: déploiements Canary, tests A/B ;
  • L'équilibrage de charge: simple et cohérent, basé sur des hachages ;
  • Récupération après des chutes: timeouts, tentatives, disjoncteurs ;
  • Insérer des défauts: retards, demandes abandonnées, etc.

Au fur et à mesure de la suite de l'article, ces capacités seront illustrées en utilisant l'application sélectionnée comme exemple et de nouveaux concepts seront introduits en cours de route. Le premier de ces concepts sera DestinationRules (c'est-à-dire les règles concernant le destinataire du trafic/des demandes - traduction approximative.), à l’aide duquel nous activons les tests A/B.

Tests A/B : DestinationRules en pratique

Les tests A/B sont utilisés dans les cas où il existe deux versions d’une application (généralement elles sont visuellement différentes) et où nous ne savons pas à 100 % laquelle améliorera l’expérience utilisateur. Par conséquent, nous exécutons les deux versions simultanément et collectons des métriques.

Pour déployer la deuxième version du frontend, requise pour la démonstration des tests A/B, exécutez la commande suivante :

$ kubectl apply -f resource-manifests/kube/ab-testing/sa-frontend-green-deployment.yaml
deployment.extensions/sa-frontend-green created

Le manifeste de déploiement pour la version verte diffère à deux endroits :

  1. L'image est basée sur une balise différente - istio-green,
  2. Les pods ont une étiquette version: green.

Puisque les deux déploiements ont une étiquette app: sa-frontend, requêtes acheminées par un service virtuel sa-external-services pour le service sa-frontend, sera redirigé vers toutes ses instances et la charge sera répartie via algorithme de round-robin, ce qui conduira à la situation suivante :

Retour aux microservices avec Istio. Partie 2
Les fichiers demandés n'ont pas été trouvés

Ces fichiers n'ont pas été trouvés car ils sont nommés différemment selon les versions de l'application. Assurons-nous de ceci :

$ curl --silent http://$EXTERNAL_IP/ | tr '"' 'n' | grep main
/static/css/main.c7071b22.css
/static/js/main.059f8e9c.js
$ curl --silent http://$EXTERNAL_IP/ | tr '"' 'n' | grep main
/static/css/main.f87cd8c9.css
/static/js/main.f7659dbb.js

Cela signifie que index.html, demandant une version de fichiers statiques, peut être envoyé par l'équilibreur de charge vers des pods qui ont une version différente, où, pour des raisons évidentes, ces fichiers n'existent pas. Par conséquent, pour que l'application fonctionne, nous devons définir une restriction : «la même version de l'application qui a servi index.html devrait répondre aux demandes ultérieures».

Nous y arriverons avec un équilibrage de charge cohérent basé sur le hachage (Équilibrage de charge de hachage cohérent)... Dans ce cas les requêtes du même client sont envoyées à la même instance backend, pour lequel une propriété prédéfinie est utilisée, par exemple un en-tête HTTP. Implémenté à l'aide de DestinationRules.

Règles de destination

Après Service Virtuel envoyé une requête au service souhaité, en utilisant DestinationRules nous pouvons définir des politiques qui seront appliquées au trafic destiné aux instances de ce service :

Retour aux microservices avec Istio. Partie 2
Gestion du trafic avec les ressources Istio

Noter : L'impact des ressources Istio sur le trafic réseau est présenté ici d'une manière facile à comprendre. Pour être précis, la décision sur l'instance à laquelle envoyer la demande est prise par l'Envoy dans la passerelle d'entrée configurée dans le CRD.

Avec les règles de destination, nous pouvons configurer l'équilibrage de charge pour utiliser des hachages cohérents et garantir que la même instance de service répond au même utilisateur. La configuration suivante vous permet d'y parvenir (destinationrule-sa-frontend.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: sa-frontend
spec:
  host: sa-frontend
  trafficPolicy:
    loadBalancer:
      consistentHash:
        httpHeaderName: version   # 1

1 - le hachage sera généré en fonction du contenu de l'en-tête HTTP version.

Appliquez la configuration avec la commande suivante :

$ kubectl apply -f resource-manifests/istio/ab-testing/destinationrule-sa-frontend.yaml
destinationrule.networking.istio.io/sa-frontend created

Exécutez maintenant la commande ci-dessous et assurez-vous d'obtenir les bons fichiers lorsque vous spécifiez l'en-tête version:

$ curl --silent -H "version: yogo" http://$EXTERNAL_IP/ | tr '"' 'n' | grep main

Noter: Pour ajouter différentes valeurs dans l'en-tête et tester les résultats directement dans le navigateur, vous pouvez utiliser cette extension vers Chrome (ou par ceci pour Firefox - env. trad.).

En général, DestinationRules a plus de capacités dans le domaine de l'équilibrage de charge - vérifiez les détails dans documents officiels.

Avant d’étudier plus en détail VirtualService, supprimons la « version verte » de l’application et la règle de sens de circulation correspondante en exécutant les commandes suivantes :

$ kubectl delete -f resource-manifests/kube/ab-testing/sa-frontend-green-deployment.yaml
deployment.extensions “sa-frontend-green” deleted
$ kubectl delete -f resource-manifests/istio/ab-testing/destinationrule-sa-frontend.yaml
destinationrule.networking.istio.io “sa-frontend” deleted

Mise en miroir : les services virtuels en pratique

Ombre (« blindage ») ou mise en miroir (« miroir ») utilisé dans les cas où l'on veut tester un changement en production sans affecter les utilisateurs finaux : pour ce faire, on duplique (« miroir ») les requêtes vers une seconde instance où les changements souhaités ont été effectués, et on regarde les conséquences. En termes simples, c'est à ce moment-là que votre collègue choisit le problème le plus critique et fait une pull request sous la forme d'un énorme tas de terre que personne ne peut réellement examiner.

Pour tester ce scénario en action, créons une deuxième instance de SA-Logic avec des bugs (buggy) en exécutant la commande suivante :

$ kubectl apply -f resource-manifests/kube/shadowing/sa-logic-service-buggy.yaml
deployment.extensions/sa-logic-buggy created

Et maintenant, exécutons la commande pour nous assurer que toutes les instances avec app=sa-logic Ils comportent également des étiquettes avec les versions correspondantes :

$ kubectl get pods -l app=sa-logic --show-labels
NAME                              READY   LABELS
sa-logic-568498cb4d-2sjwj         2/2     app=sa-logic,version=v1
sa-logic-568498cb4d-p4f8c         2/2     app=sa-logic,version=v1
sa-logic-buggy-76dff55847-2fl66   2/2     app=sa-logic,version=v2
sa-logic-buggy-76dff55847-kx8zz   2/2     app=sa-logic,version=v2

Service sa-logic cible les pods avec une étiquette app=sa-logic, donc toutes les requêtes seront réparties entre toutes les instances :

Retour aux microservices avec Istio. Partie 2

... mais nous voulons que les requêtes soient envoyées aux instances v1 et mises en miroir sur les instances v2 :

Retour aux microservices avec Istio. Partie 2

Nous y parviendrons grâce à VirtualService en combinaison avec DestinationRule, où les règles détermineront les sous-ensembles et les itinéraires du VirtualService vers un sous-ensemble spécifique.

Définition de sous-ensembles dans les règles de destination

Sous-ensembles (sous-ensembles) sont déterminés par la configuration suivante (sa-logic-subsets-destinationrule.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: sa-logic
spec:
  host: sa-logic    # 1
  subsets:
  - name: v1        # 2
    labels:
      version: v1   # 3
  - name: v2
    labels:
      version: v2

  1. Hôte (host) définit que cette règle s'applique uniquement aux cas où l'itinéraire se dirige vers le service sa-logic;
  2. Titres (name) les sous-ensembles sont utilisés lors du routage vers des instances de sous-ensembles ;
  3. Étiquette (label) définit les paires clé-valeur auxquelles les instances doivent correspondre pour faire partie du sous-ensemble.

Appliquez la configuration avec la commande suivante :

$ kubectl apply -f resource-manifests/istio/shadowing/sa-logic-subsets-destinationrule.yaml
destinationrule.networking.istio.io/sa-logic created

Maintenant que les sous-ensembles sont définis, nous pouvons continuer et configurer le VirtualService pour appliquer des règles aux requêtes adressées à sa-logic afin qu'elles :

  1. Routé vers un sous-ensemble v1,
  2. Mis en miroir sur un sous-ensemble v2.

Le manifeste suivant vous permet de réaliser vos projets (sa-logic-subsets-shadowing-vs.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: sa-logic
spec:
  hosts:
    - sa-logic          
  http:
  - route:
    - destination:
        host: sa-logic  
        subset: v1      
    mirror:             
      host: sa-logic     
      subset: v2

Aucune explication n'est nécessaire ici, alors voyons-le simplement en action :

$ kubectl apply -f resource-manifests/istio/shadowing/sa-logic-subsets-shadowing-vs.yaml
virtualservice.networking.istio.io/sa-logic created

Ajoutons la charge en appelant la commande suivante :

$ while true; do curl -v http://$EXTERNAL_IP/sentiment 
    -H "Content-type: application/json" 
    -d '{"sentence": "I love yogobella"}'; 
    sleep .8; done

Regardons les résultats dans Grafana, où vous pouvez voir que la version avec des bugs (buggy) entraîne un échec pour environ 60 % des requêtes, mais aucun de ces échecs n'affecte les utilisateurs finaux car un service en cours d'exécution y répond.

Retour aux microservices avec Istio. Partie 2
Réponses réussies des différentes versions du service sa-logic

Ici, nous avons vu pour la première fois comment VirtualService est appliqué aux Envoyés de nos services : lorsque sa-web-app fait une demande à sa-logic, il passe par le side-car Envoy, qui - via VirtualService - est configuré pour acheminer la requête vers le sous-ensemble v1 et refléter la requête vers le sous-ensemble v2 du service sa-logic.

Je sais, vous pensez peut-être déjà que les services virtuels sont simples. Dans la section suivante, nous développerons cela en disant qu'ils sont également vraiment géniaux.

Déploiements canaris

Le déploiement Canary est le processus de déploiement d'une nouvelle version d'une application auprès d'un petit nombre d'utilisateurs. Il est utilisé pour s'assurer qu'il n'y a pas de problèmes dans la version et seulement après cela, étant déjà confiant dans sa qualité (de la version), la distribuer à d'autres utilisateurs.оpublic plus large.

Pour démontrer les déploiements Canary, nous continuerons à travailler avec un sous-ensemble buggy у sa-logic.

Ne perdons pas de temps en bagatelles et envoyons immédiatement 20 % des utilisateurs vers la version avec des bugs (cela représentera notre déploiement Canary), et les 80 % restants vers le service normal. Pour ce faire, utilisez le VirtualService suivant (sa-logic-subsets-canary-vs.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: sa-logic
spec:
  hosts:
    - sa-logic    
  http:
  - route: 
    - destination: 
        host: sa-logic
        subset: v1
      weight: 80         # 1
    - destination: 
        host: sa-logic
        subset: v2
      weight: 20 # 1

1 est le poids (weight), qui spécifie le pourcentage de demandes qui seront dirigées vers un destinataire ou un sous-ensemble du destinataire.

Mettons à jour la configuration précédente de VirtualService pour sa-logic avec la commande suivante :

$ kubectl apply -f resource-manifests/istio/canary/sa-logic-subsets-canary-vs.yaml
virtualservice.networking.istio.io/sa-logic configured

... et on verra tout de suite que certaines requêtes conduisent à des échecs :

$ while true; do 
   curl -i http://$EXTERNAL_IP/sentiment 
   -H "Content-type: application/json" 
   -d '{"sentence": "I love yogobella"}' 
   --silent -w "Time: %{time_total}s t Status: %{http_code}n" 
   -o /dev/null; sleep .1; done
Time: 0.153075s Status: 200
Time: 0.137581s Status: 200
Time: 0.139345s Status: 200
Time: 30.291806s Status: 500

Les services virtuels permettent les déploiements Canary : dans ce cas, nous avons réduit l'impact potentiel des problèmes à 20 % de la base d'utilisateurs. Merveilleux! Désormais, dans tous les cas où nous ne sommes pas sûrs de notre code (en d'autres termes - toujours...), nous pouvons utiliser la mise en miroir et les déploiements Canary.

Délais d'attente et tentatives

Mais les bugs ne se retrouvent pas toujours dans le code. Dans la liste de "8 idées fausses sur l'informatique distribuée« En premier lieu, il y a la croyance erronée selon laquelle « le réseau est fiable ». En réalité, le réseau aucun fiable, et pour cette raison nous avons besoin de délais d'attente (délais d'attente) et réessaye (réessais).

Pour la démonstration, nous continuerons à utiliser la même version problématique sa-logic (buggy), et nous simulerons le manque de fiabilité du réseau avec des pannes aléatoires.

Laissez notre service avec des bogues avoir 1/3 de chance de mettre trop de temps à répondre, 1/3 de chance de se terminer par une erreur interne du serveur et 1/3 de chance de renvoyer la page avec succès.

Pour atténuer l'impact de ces problèmes et améliorer la vie des utilisateurs, nous pouvons :

  1. ajouter un délai d'attente si le service met plus de 8 secondes à répondre,
  2. réessayez si la demande échoue.

Pour la mise en œuvre, nous utiliserons la définition de ressource suivante (sa-logic-retries-timeouts-vs.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: sa-logic
spec:
  hosts:
    - sa-logic
  http:
  - route: 
    - destination: 
        host: sa-logic
        subset: v1
      weight: 50
    - destination: 
        host: sa-logic
        subset: v2
      weight: 50
    timeout: 8s           # 1
    retries:
      attempts: 3         # 2
      perTryTimeout: 3s # 3

  1. Le délai d'attente de la demande est fixé à 8 secondes ;
  2. Les demandes sont réessayées 3 fois ;
  3. Et chaque tentative est considérée comme infructueuse si le temps de réponse dépasse 3 secondes.

Il s'agit d'une optimisation car l'utilisateur n'aura pas à attendre plus de 8 secondes et nous ferons trois nouvelles tentatives pour obtenir une réponse en cas d'échec, augmentant ainsi les chances d'une réponse réussie.

Appliquez la configuration mise à jour avec la commande suivante :

$ kubectl apply -f resource-manifests/istio/retries/sa-logic-retries-timeouts-vs.yaml
virtualservice.networking.istio.io/sa-logic configured

Et vérifiez dans les graphiques Grafana que le nombre de réponses réussies a augmenté au-dessus :

Retour aux microservices avec Istio. Partie 2
Améliorations des statistiques de réponses réussies après l'ajout de délais d'attente et de tentatives

Avant de passer à la section suivante (ou plutôt, à la partie suivante de l'article, car dans celle-ci il n'y aura plus d'expériences pratiques - environ trad.), supprimer sa-logic-buggy et VirtualService en exécutant les commandes suivantes :

$ kubectl delete deployment sa-logic-buggy
deployment.extensions “sa-logic-buggy” deleted
$ kubectl delete virtualservice sa-logic
virtualservice.networking.istio.io “sa-logic” deleted

Modèles de disjoncteurs et de cloisons

Nous parlons de deux modèles importants dans l'architecture des microservices qui vous permettent de réaliser l'auto-récupération (auto-guérison) prestations de service.

Circuit Breaker ("disjoncteur") utilisé pour mettre fin aux demandes arrivant sur une instance d'un service considérée comme non saine et la restaurer tandis que les demandes des clients sont redirigées vers des instances saines de ce service (ce qui augmente le pourcentage de réponses réussies). (Remarque : une description plus détaillée du modèle peut être trouvée, par exemple, ici.)

Cloison ("cloison") empêche les pannes de service d’affecter l’ensemble du système. Par exemple, le service B est en panne et un autre service (le client du service B) envoie une requête au service B, ce qui l'oblige à épuiser son pool de threads et à être incapable de répondre à d'autres requêtes (même si elles ne proviennent pas du service B). (Remarque : une description plus détaillée du modèle peut être trouvée, par exemple, ici.)

J'omettrai les détails d'implémentation de ces modèles car ils sont faciles à trouver dans documents officiels, et je veux aussi vraiment afficher l'authentification et l'autorisation, qui seront abordées dans la prochaine partie de l'article.

PS du traducteur

A lire aussi sur notre blog :

Source: habr.com

Ajouter un commentaire