Utiliser Istio Multicluster pour "déclencher" des charges de travail entre les clusters

1. Bienvenue

Merci de nous avoir rejoints pour l'atelier de programmation Istio Multi Cloud Burst de Google.Cet atelier de programmation nécessite une expérience pratique de base avec Kubernetes, Node et Go.

Ce dont vous aurez besoin

  • Compte Google Cloud Platform (utilisez un compte existant ou nous vous en fournirons un sans frais)
  • Votre ordinateur portable (installez "kubectl", "gcloud", etc.) ou vous pouvez utiliser Google Cloud Shell.

Points abordés

  • Créer un cluster Kubernetes sur GKE
  • Installer Istio sur un cluster Kubernetes avec Helm
  • Installer Istio Multicluster avec Helm
  • Déployer une application Web à partir de la source vers Kubernetes
  • Écrire et appliquer des règles de routage du trafic à Istio
  • Métriques Prometheus
  • Créer et transférer des images de conteneur dans un cluster Kubernetes

2. Configuration

Vous pouvez suivre cet atelier de programmation sur:

  • Google Cloud Shell (recommandé): shell dans le navigateur, avec des outils installés
  • votre ordinateur portable (suivez les instructions ci-dessous)

Commencer avec Google Cloud Platform

  1. Si vous ne possédez pas de compte GCP, demandez à l'instructeur de vous remettre votre carte de compte utilisateur sans frais.
  2. Accédez à la console Google Cloud, puis cliquez sur "Sélectionner un projet": 5c2d9bf74c78f7e4.png.
  3. Notez l'ID du projet, puis cliquez dessus pour le sélectionner: ecc5e8e97bfa6559.png

Cloud Shell fournit un shell de ligne de commande dans votre navigateur avec les outils dont vous avez besoin installés et authentifiés automatiquement auprès de votre compte Google Cloud Platform. (Si vous ne souhaitez pas exécuter cet exercice dans Cloud Shell, passez à la section suivante.)

Accédez à la console Cloud, puis cliquez sur "Activer Cloud Shell" dans la barre d'outils en haut à droite:

68a17b036ce24ccb.png

Ajouter des outils à Cloud Shell

  1. Installez kubectx**** en téléchargeant les scripts bash à partir de cette page dans un emplacement de $PATH.
  2. Installez helm****: suivez ces instructions.

Vous pouvez également exécuter ces commandes pour installer les deux dans ~/.bin et les ajouter à votre $PATH:

mkdir -p ~/.bin && \
cd ~/.bin && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubectx && \
chmod +x kubectx && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubens && \
chmod +x kubens && \
curl -LO  https://storage.googleapis.com/kubernetes-helm/helm-v2.12.0-linux-amd64.tar.gz && \
tar xzf helm-v2.12.0-linux-amd64.tar.gz && \
rm helm-v2.12.0-linux-amd64.tar.gz && \
mv linux-amd64/helm ./helm && \
rm -r linux-amd64 && \
export PATH=${HOME}/.bin:${PATH}

Voici quelques conseils pour vous aider à utiliser Cloud Shell plus facilement:

1. Détachez le shell dans une nouvelle fenêtre:

2. À l'aide de l'éditeur de fichiers : cliquez sur l'icône en forme de crayon en haut à droite pour lancer un éditeur de fichiers dans le navigateur. Cela vous sera utile, car nous allons copier des extraits de code dans des fichiers.

3. Démarrer de nouveaux onglets:si vous avez besoin de plusieurs requêtes de terminal.

4. Agrandir le texte : la taille de police par défaut dans Cloud Shell peut être trop petite pour être lue.

Ctrl+ sur Linux/Windows⌘+ sur macOS

Si vous préférez utiliser votre propre environnement de poste de travail plutôt que Cloud Shell, configurez les outils suivants:

  1. Installez gcloud: (préinstallé sur Cloud Shell). Suivez les instructions pour installer gcloud sur votre plate-forme. Nous l'utiliserons pour créer un cluster Kubernetes.
  2. Installez kubectl:(préinstallé sur Cloud Shell). Exécutez la commande suivante pour l'installer:
gcloud components install kubectl

Exécutez la commande suivante pour authentifier gcloud. Vous serez alors invité à vous connecter avec votre compte Google. Sélectionnez ensuite le projet prédéfini (ci-dessus) comme projet par défaut. (vous pouvez ignorer la configuration d'une zone de calcul):

gcloud init
  1. Install curl: Préinstallé sur la plupart des systèmes Linux/macOS. Vous en avez probablement déjà un. Dans le cas contraire, recherchez sur Internet comment l'installer.
  2. Installez kubectx**** en téléchargeant les scripts bash depuis cette page dans un emplacement de $PATH.
  3. Installez helm****: suivez ces instructions.

3. Configurer un projet GCP

Activez les API GKE (Google Kubernetes Engine), GCR (Google Container Registry) et GCB (Google Cloud Build) dans votre projet:

gcloud services enable \
  cloudapis.googleapis.com \
  container.googleapis.com \
  containerregistry.googleapis.com \
  cloudbuild.googleapis.com

Configurer des variables d'environnement

Nous allons utiliser notre projet Google Cloud de manière intensive lors de la configuration. Définissons une variable d'environnement pour référence rapide.

export GCLOUD_PROJECT=$(gcloud config get-value project)

Nous allons créer du code et des fichiers de configuration au cours de cet atelier. Créons donc un répertoire de projet et y passons.

mkdir -p src/istio-burst && \
cd src/istio-burst && \
export proj=$(pwd)

4. Créer un cluster Kubernetes "principal"

Vous pouvez facilement créer un cluster Kubernetes géré avec Google Kubernetes Engine (GKE).

La commande suivante crée un cluster Kubernetes:

  • nommé "primary",
  • dans la zone us-west1-a ;
  • la dernière version de Kubernetes disponible ;
  • avec quatre nœuds initiaux
export cluster=primary
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "4" --network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias

(Cela peut prendre environ cinq minutes. Vous pouvez suivre la création du cluster dans la console Cloud.)

Une fois le cluster Kubernetes créé, gcloud configure kubectl avec les identifiants pointant vers le cluster.

gcloud container clusters get-credentials $cluster --zone=$zone

Vous devriez pouvoir utiliser kubectl avec votre nouveau cluster.

Exécutez la commande suivante pour lister les nœuds Kubernetes de votre cluster (leur état doit être "Ready"):

kubectl get nodes

Modifier les noms de kubeconfig pour faciliter l'utilisation

Nous allons passer fréquemment d'un contexte à un autre. Il est donc pratique d'avoir un alias court pour nos clusters.

Cette commande renomme l'entrée kubeconfig que vous venez de créer en primary.

kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}

Définir des autorisations :

Pour déployer Istio, vous devez être administrateur du cluster. Cette commande définit l'adresse e-mail associée à votre compte Google Cloud comme administrateur du cluster.

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole=cluster-admin \
    --user=$(gcloud config get-value core/account)

5. Créer un cluster "burst"

La commande suivante crée un cluster Kubernetes:

  • nommé "burst",
  • dans la zone us-west1-a ;
  • la dernière version de Kubernetes disponible ;
  • Avec un nœud initial
  • Autoscaling activé pour jusqu'à cinq nœuds
export cluster=burst
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "1" --enable-autoscaling --min-nodes=1 --max-nodes=5 \
--network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias

(Cela peut prendre environ cinq minutes. Vous pouvez suivre la création du cluster dans la console Cloud.)

Une fois le cluster Kubernetes créé, gcloud configure kubectl avec les identifiants pointant vers le cluster.

gcloud container clusters get-credentials $cluster --zone=$zone

Vous devriez pouvoir utiliser kubectl avec votre nouveau cluster.

Exécutez la commande suivante pour lister les nœuds Kubernetes de votre cluster (leur état doit être "Ready"):

kubectl get nodes

Modifier les noms de kubeconfig pour faciliter l'utilisation

Cette commande modifie l'entrée kubeconfig que vous venez de créer en burst.

kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}

Définir des autorisations :

Pour déployer Istio Remote, vous devez être administrateur du cluster. Cette commande définit l'adresse e-mail associée à votre compte Google Cloud comme administrateur du cluster.

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole=cluster-admin \
    --user=$(gcloud config get-value core/account)

6. Appliquer des règles de pare-feu

Pour que nos deux clusters puissent communiquer entre eux, nous devons créer une règle de pare-feu.

Exécutez les commandes suivantes pour créer une règle de pare-feu dans Google Cloud Platform qui permettra à nos clusters de communiquer.

function join_by { local IFS="$1"; shift; echo "$*"; }
ALL_CLUSTER_CIDRS=$(gcloud container clusters list \
--filter="(name=burst OR name=primary) AND zone=$zone" \
--format='value(clusterIpv4Cidr)' | sort | uniq)
ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}"))
ALL_CLUSTER_NETTAGS=$(gcloud compute instances list \
--filter="(metadata.cluster-name=burst OR metadata.cluster-name=primary) AND metadata.cluster-location=us-west1-a" \
--format='value(tags.items.[0])' | sort | uniq)
ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
gcloud compute firewall-rules create istio-multicluster-test-pods \
  --allow=tcp,udp,icmp,esp,ah,sctp \
  --direction=INGRESS \
  --priority=900 \
  --source-ranges="${ALL_CLUSTER_CIDRS}" \
  --target-tags="${ALL_CLUSTER_NETTAGS}" --quiet

Nos deux clusters sont configurés et prêts à nous permettre de déployer notre application et Istio dessus.

7. Présentation d'Istio

Qu'est-ce qu'Istio ?

Istio est un plan de contrôle de service mesh qui vise à "connecter, sécuriser, contrôler et observer des services". Il le fait de différentes manières, mais principalement en ajoutant un conteneur proxy ( Envoy) en tant que side-car dans chacun de vos pods Kubernetes déployés. Le conteneur proxy contrôle toutes les communications réseau entre les microservices en tandem avec un hub de règles et de télémétrie à usage général ( Mixer).

a25613cd581825da.png

Ces règles peuvent être appliquées indépendamment de vos déploiements et services Kubernetes. Cela signifie que l'opérateur réseau peut observer l'activité réseau, restreindre, rediriger ou réécrire les règles réseau sans redéployer les applications associées.

Voici quelques-unes des fonctionnalités de gestion du trafic compatibles avec Istio:

  • Disjoncteurs
  • Répartition du trafic en fonction d'un pourcentage
  • Réécriture d'URL
  • terminaison TLS
  • Vérifications d'état
  • Équilibrage de charge

Pour les besoins de cet atelier, nous allons nous concentrer sur la répartition du trafic basée sur un pourcentage.

Termes Istio que nous utiliserons

VirtualService

Un VirtualService définit un ensemble de règles de routage du trafic à appliquer lorsqu'un hôte est adressé.

Passerelle

Une passerelle est un équilibreur de charge qui fonctionne à la périphérie du réseau maillé et reçoit des connexions HTTP/TCP entrantes ou sortantes. Les passerelles peuvent spécifier des ports, des configurations SNI, etc.

DestinationRule

Un DestinationRule définit les règles qui s'appliquent au trafic destiné à un service après le routage. Ils spécifient la configuration de l'équilibrage de charge, la taille du pool de connexions du sidecar et les paramètres de détection des anomalies.

Multicluster Istio

Lorsque nous avons créé nos deux clusters, vous avez peut-être remarqué que le cluster primary comportait quatre nœuds sans autoscaling, tandis que le cluster burst ne comportait qu'un seul nœud avec autoscaling jusqu'à cinq nœuds.

Cette configuration est nécessaire pour deux raisons.

Nous voulons d'abord simuler un scénario de migration sur site vers le cloud. Dans un environnement sur site, vous n'avez pas accès aux clusters d'autoscaling, car vous disposez d'une infrastructure fixe.

Deuxièmement, une configuration à quatre nœuds (comme défini ci-dessus) est requise pour exécuter Istio. Cela soulève la question suivante: si Istio nécessite au moins quatre nœuds, comment notre cluster burst peut-il exécuter Istio avec un seul nœud ? La réponse est que Istio Multicluster installe un ensemble beaucoup plus petit des services Istio et communique avec l'installation Istio dans le cluster principal pour récupérer les règles de stratégie et publier des informations de télémétrie.

8. Présentation de l'architecture de l'application

Présentation des composants

Nous allons déployer une application à trois niveaux à l'aide de NodeJS et de Redis.

Worker

L'application worker est écrite en NodeJS. Elle écoutera les requêtes HTTP POST entrantes, effectuera une opération de hachage dessus et, si une variable d'environnement nommée PREFIX est définie, elle ajoutera cette valeur au début du hachage. Une fois le hachage calculé, l'application envoie le résultat sur le canal "calculation" du serveur Redis spécifié.

Nous utiliserons la variable d'environnement PREFIX plus tard pour illustrer la fonctionnalité multicluster.

Pour information, voici les packages utilisés par l'application.

  • body-parser: : nous permet d'analyser nos requêtes HTTP
  • cors: : permet d'utiliser le partage de ressources entre origines multiples
  • dotenv: Analyse facile des variables d'environnement
  • express: Hébergement de sites Web facile
  • Bibliothèque cliente ioredis: pour communiquer avec les bases de données Redis
  • morgan: Fournit un journal structuré agréable

Frontend

Notre interface utilisateur est également une application NodeJS qui héberge une page Web à l'aide d'Express. Il prend une fréquence saisie par l'utilisateur et envoie des requêtes à notre application worker à ce rythme. Cette application s'abonne également aux messages sur un canal Redis nommé "calculation" et affiche les résultats sur une page Web.

L'application utilise les dépendances suivantes.

  • body-parser: : nous permet d'analyser nos requêtes HTTP
  • dotenv: Analyse facile des variables d'environnement
  • express: Hébergement de sites Web facile
  • Bibliothèque cliente ioredis: pour communiquer avec les bases de données Redis
  • morgan: : fournit des journaux structurés agréables
  • request: : permet d'envoyer des requêtes HTTP
  • socket.io: Permet une communication bidirectionnelle entre la page Web et le serveur

Cette page Web utilise Bootstrap pour le style. Lorsqu'elle est exécutée, elle se présente comme suit :

e5e3b9cbede4cac4.png

Schéma d'architecture

7ae4bc22a58f80a6.png

Schéma de déploiement

Nous allons déployer notre application finale sur les deux clusters que nous avons créés. Tous les composants (frontend, worker et Redis) seront déployés sur le cluster primary, mais seule l'application worker sera déployée sur le cluster burst.

Voici un diagramme décrivant les deux clusters. Les cases en rouge représentent les services Kubernetes, et celles en bleu les déploiements Kubernetes. Les cases jaunes indiquent que nous avons installé Istio.

561db37c510944bd.png

Notez que le cluster burst comporte toujours un service Redis déployé, même s'il n'y a pas de déploiement Redis dans le cluster. Ce service doit être présent dans le cluster pour que le DNS Kubernetes puisse résoudre la requête. Toutefois, lorsque la requête est réellement envoyée, le proxy Istio la redirige vers le déploiement Redis dans le cluster primary.

L'application finale comportera un déploiement supplémentaire exécuté dans le cluster primary nommé istiowatcher.. Cela nous permettra de rediriger automatiquement le trafic vers burst lorsque notre trafic dépassera un certain seuil.

8f6183bdfc3f813c.png

9. Créer des fichiers de déploiement d'application

Nous devons créer un ensemble de fichiers manifestes Kubernetes pour déployer notre application.

Accédez au répertoire racine du projet et créez un dossier nommé kubernetes.

mkdir ${proj}/kubernetes && cd ${proj}/kubernetes

Écrire frontend.yaml

Cela crée un déploiement et un service Kubernetes pour accéder à notre image d'interface utilisateur.

Insérez le code suivant dans frontend.yaml.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend-deployment
  labels:
    app: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: gcr.io/istio-burst-workshop/frontend
        ports:
        - containerPort: 8080
        readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8080
              httpHeaders:
              - name: "Cookie"
                value: "istio_session-id=x-readiness-probe"
        livenessProbe:
          initialDelaySeconds: 10
          httpGet:
            path: "/"
            port: 8080
            httpHeaders:
            - name: "Cookie"
              value: "istio_session-id=x-liveness-probe"
        env:
        - name: PORT
          value: "8080"
        - name: PROCESSOR_URL
          value: "http://worker-service"
        - name: REDIS_URL
          value: "redis-cache-service:6379"
---
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: ClusterIP
  selector:
    app: frontend
  ports:
  - name: http
    port: 80
    targetPort: 8080

Points clés à noter dans Deployment

  • Nous avons spécifié que le port sur lequel l'application s'exécutera est 8080.
  • Nous avons défini l'adresse du worker sur "http://worker-service" et utiliserons la fonctionnalité DNS intégrée de Kubernetes pour résoudre le service qui en résulte.
  • Nous avons défini l'adresse de notre REDIS_URL sur "redis-cache-service:6379" et utiliserons la fonctionnalité DNS intégrée de Kubernetes pour résoudre les adresses IP qui en résultent.
  • Nous avons également défini des vérifications liveness et readiness sur le conteneur pour aider Kubernetes à savoir quand le conteneur est opérationnel.

Écrire worker-service.yaml

Nous écrivons la définition du service Kubernetes dans un fichier distinct de la définition du déploiement, car nous allons réutiliser ce service sur plusieurs clusters, mais nous allons écrire un déploiement différent pour chaque cluster.

Insérez le code suivant dans worker-service.yaml.

apiVersion: v1
kind: Service
metadata:
 name: worker-service
spec:
 type: ClusterIP
 selector:
   app: worker
 ports:
 - name: http
   port: 80
   targetPort: 8081

Écrire worker-primary.yaml

Il s'agit du déploiement de worker que nous allons pousser vers le cluster principal.

Insérez le code suivant dans worker-primary.yaml.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: worker-deployment
 labels:
   app: worker
spec:
 replicas: 1
 selector:
   matchLabels:
     app: worker
 template:
   metadata:
     labels:
       app: worker
       cluster-type: primary-cluster
   spec:
     containers:
     - name: worker
       image: gcr.io/istio-burst-workshop/worker
       imagePullPolicy: Always
       ports:
       - containerPort: 8081
       readinessProbe:
           initialDelaySeconds: 10
           httpGet:
             path: "/_healthz"
             port: 8081
             httpHeaders:
             - name: "Cookie"
               value: "istio_session-id=x-readiness-probe"
       livenessProbe:
         initialDelaySeconds: 10
         httpGet:
           path: "/"
           port: 8081
           httpHeaders:
           - name: "Cookie"
             value: "istio_session-id=x-liveness-probe"
       env:
       - name: PORT
         value: "8081"
       - name: REDIS_URL
         value: "redis-cache-service:6379"

Notez que nous suivons le même schéma en fournissant des sondes liveness et readiness, et en spécifiant les variables d'environnement PORT et REDIS_URL à utiliser par notre application.

Autre point à noter dans ce déploiement : l'absence de la variable d'environnement PREFIX. Cela signifie que les résultats de nos calculs seront des hachages bruts (sans préfixe).

Le dernier point clé de ce déploiement est l'étiquette cluster-type: primary-cluster. Nous l'utiliserons plus tard lorsque nous effectuerons le routage du trafic sur un multicluster Istio.

Écrire redis.yaml

La communication de notre worker vers l'interface utilisateur s'effectue via un canal Redis. Nous devons donc déployer une application Redis dans notre cluster.

Insérez le code suivant dans redis.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: redis-cache
spec:
 template:
   metadata:
     labels:
       app: redis-cache
   spec:
     containers:
     - name: redis
       image: redis:alpine
       ports:
       - containerPort: 6379
       readinessProbe:
         periodSeconds: 5
         tcpSocket:
           port: 6379
       livenessProbe:
         periodSeconds: 5
         tcpSocket:
           port: 6379
       volumeMounts:
       - mountPath: /data
         name: redis-data
       resources:
         limits:
           memory: 256Mi
           cpu: 125m
         requests:
           cpu: 70m
           memory: 200Mi
     volumes:
     - name: redis-data
       emptyDir: {}

Il s'agit d'un déploiement semi-standard d'une application Redis. Il met en place un conteneur basé sur l'image redis:alpine, expose les ports appropriés et définit des limites de ressources raisonnables.

Écrire redis-service.yaml

Nous avons besoin d'un service Kubernetes pour communiquer avec notre application Redis.

Insérez le code suivant dans redis-service.yaml

apiVersion: v1
kind: Service
metadata:
 name: redis-cache-service
spec:
 type: ClusterIP
 selector:
   app: redis-cache
 ports:
 - port: 6379
   targetPort: 6379

Le service nommé redis-cache-service permet d'accéder à notre déploiement Redis.

10. Déployer l'application

Maintenant que nos images ont été transférées vers GCR et que nos fichiers manifestes Kubernetes ont été écrits, il est temps de déployer notre application et de voir comment elle fonctionne.

Exécutez les commandes suivantes pour déployer l'application.

  1. Vérifier que nous sommes dans le bon cluster
kubectx primary
  1. Déployer Redis Cache
kubectl apply -f redis.yaml
  1. Déployer le service Redis
kubectl apply -f redis-service.yaml
  1. Déployer l'interface
kubectl apply -f frontend.yaml
  1. Déployer un nœud de calcul
kubectl apply -f worker-primary.yaml
  1. Déployer le service worker
kubectl apply -f worker-service.yaml

Nous avons déployé notre application sur GKE. Félicitations !

Tester

Attendez que les pods soient en ligne.

kubectl get pods -w

Une fois que tous les pods sont en cours d'exécution, appuyez sur Ctrl+C.

NAME                                   READY     STATUS    RESTARTS   AGE
frontend-deployment-695d95fbf7-76sd8   1/1       Running   0          2m
redis-cache-7475999bf5-nxj8x           1/1       Running   0          2m
worker-deployment-5b9cf9956d-g975p     1/1       Running   0          2m

Vous remarquerez que nous n'avons pas exposé notre frontend via un LoadBalancer. En effet, nous accéderons plus tard à l'application via Istio. Pour vérifier que tout fonctionne, nous allons utiliser kubectl port-forward.. Exécutez la commande suivante pour transférer le port 8080 de votre machine locale (ou Cloud Shell) vers le port 8080 exécutant le déploiement frontend.

kubectl port-forward \
$(kubectl get pods -l app=frontend -o jsonpath='{.items[0].metadata.name}') \
8080:8080

Si vous exécutez l'application en local: ouvrez un navigateur Web et accédez à http://localhost:8080.

Si vous exécutez le code dans Cloud Shell:cliquez sur le bouton "Aperçu sur le Web", puis sélectionnez "Prévisualiser sur le port 8080".

bdb5dc75f415be11.png

L'interface utilisateur devrait s'afficher. Si vous saisissez un nombre dans le champ "Fréquence", des hachages devraient commencer à apparaître.

1caafaffab26897a.png

Félicitations, tout est opérationnel !

Appuyez sur Ctrl+C pour arrêter le transfert de port.

11. Nettoyer l'application déployée

Nous allons appliquer Istio à notre cluster, puis redéployer notre application. Commençons par nettoyer notre application actuelle.

Exécutez les commandes suivantes pour supprimer tous les déploiements et services que vous venez de créer.

  1. Supprimer redis-cache-service
kubectl delete -f redis-service.yaml
  1. Supprimer redis
kubectl delete -f redis.yaml
  1. Supprimer frontend
kubectl delete -f frontend.yaml
  1. Supprimer worker
kubectl delete -f worker-primary.yaml
  1. Supprimer worker-service
kubectl delete -f worker-service.yaml

12. Installer Istio sur le cluster principal

Obtenir Istio

Les versions d'Istio sont hébergées sur GitHub. Les commandes suivantes téléchargent la version 1.0.0 d'Istio et l'extraient.

  1. Accédez au répertoire racine de votre projet.
cd ${proj}
  1. Télécharger l'archive
curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
  1. Extraire et supprimer l'archive
tar xzf istio-1.0.0-linux.tar.gz && rm istio-1.0.0-linux.tar.gz

Créer un modèle Istio

L'exécution de la commande Helm suivante crée le modèle permettant d'installer Istio dans votre cluster.

helm template istio-1.0.0/install/kubernetes/helm/istio \
--name istio --namespace istio-system \
--set prometheus.enabled=true \
--set servicegraph.enabled=true  > istio-primary.yaml

Un fichier nommé istio-primary.yaml est créé dans votre répertoire actuel. Il contient toutes les définitions et spécifications nécessaires pour déployer et exécuter Istio.

Notez les deux paramètres --set. Ils ajoutent la compatibilité avec Prometheus et ServiceGraph au système Istio. Nous utiliserons le service Prometheus plus tard dans l'atelier.

Déployer Istio

Pour déployer Istio, nous devons d'abord créer un espace de noms appelé istio-system dans lequel les déploiements et les services Istio peuvent s'exécuter.

kubectl create namespace istio-system

Enfin, appliquez le fichier istio-primary.yaml que nous avons créé avec Helm.

kubectl apply -f istio-primary.yaml

Étiquette de l'espace de noms par défaut

Istio fonctionne en injectant un service de proxy side-car dans chacun de vos déploiements. Cette opération doit être activée. Nous devons donc étiqueter notre espace de noms default avec istio-injection=enabled pour qu'Istio puisse injecter automatiquement le side-car à notre place.

kubectl label namespace default istio-injection=enabled

Félicitations ! Nous avons un cluster opérationnel avec Istio, prêt à nous permettre de déployer notre application.

13. Déployer notre application avec la gestion de trafic Istio

Créer des fichiers de configuration de gestion du trafic Istio

Istio fonctionne de manière similaire à Kubernetes, car il utilise des fichiers YAML pour la configuration. Dans ce sens, nous devons créer un ensemble de fichiers qui indiquent à Istio comment exposer et acheminer notre trafic.

Créez un répertoire nommé istio-manifests et accédez-y

mkdir ${proj}/istio-manifests && cd ${proj}/istio-manifests

Écrire frontend-gateway.yaml

Ce fichier exposera notre cluster Kubernetes de manière similaire à un LoadBalancer GKE et acheminera tout le trafic entrant vers notre service de frontend.

Créez un fichier nommé frontend-gateway.yaml et insérez-y les éléments suivants.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
 name: frontend-gateway
spec:
 selector:
   istio: ingressgateway # use Istio default gateway implementation
 servers:
 - port:
     number: 80
     name: http
     protocol: HTTP
   hosts:
   - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: frontend-ingress-virtual-service
spec:
 hosts:
 - "*"
 gateways:
 - frontend-gateway
 http:
 - route:
   - destination:
       host: frontend-service
       port:
         number: 80

Écrire redis-virtualservice.yaml

Créez un fichier nommé redis-virtualservice.yaml et insérez-y les éléments suivants :

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: redis-virtual-service
spec:
 hosts:
 - redis-cache-service
 gateways:
 - mesh
 tcp:
 - route:
   - destination:
       host: redis-cache-service.default.svc.cluster.local

Écrire worker-virtualservice.yaml

Créez un fichier nommé worker-virtualservice.yaml et insérez-y les éléments suivants :

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local   
       port:
         number: 80

Déploiement des règles de gestion du trafic Istio

Le déploiement des règles Istio se fait de la même manière que les autres ressources Kubernetes, avec kubectl apply.

  1. Appliquer notre passerelle
kubectl apply -f frontend-gateway.yaml
  1. Appliquer notre VirtualService Redis
kubectl apply -f redis-virtualservice.yaml
  1. Appliquer notre VirtualService de nœud de calcul
kubectl apply -f worker-virtualservice.yaml

Déployer l'application

  1. Revenir au répertoire kubernetes
cd ${proj}/kubernetes
  1. Déployer Redis Cache
kubectl apply -f redis.yaml
  1. Déployer le service Redis
kubectl apply -f redis-service.yaml
  1. Déployer l'interface
kubectl apply -f frontend.yaml
  1. Déployer un nœud de calcul
kubectl apply -f worker-primary.yaml
  1. Déployer le service worker
kubectl apply -f worker-service.yaml

Valider

À ce stade, nous avons redéployé notre application sur un cluster avec Istio et des règles de gestion du trafic.

Attendons que toutes nos charges de travail soient en ligne

Une fois qu'ils sont tous en ligne, récupérez l'IngressGateway que nous avons configuré dans frontend-ingressgateway.yaml.

$ kubectl -n istio-system get svc istio-ingressgateway
NAME                   TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)                                                                                                     AGE
istio-ingressgateway   LoadBalancer   10.36.3.112   35.199.158.10   80:31380/TCP,

Accédez à l'adresse <EXTERNAL-IP> ou exécutez cURL. Le frontend devrait s'afficher.

$ curl 35.199.158.10
<!doctype html>
<html>

<head>
    <title>String Hashr</title>
    <!-- Bootstrap -->
...

14. Installer Istio sur un cluster "burst"

Nous avons passé beaucoup de temps à configurer et à déployer sur notre cluster primary, mais nous avons un autre cluster entier à déployer !

Dans cette section, nous allons récupérer des variables de configuration dans nos deux clusters. Veillez donc à faire attention au cluster auquel nous faisons référence pour chaque commande.

Créer le fichier manifeste Istio distant

Tout comme lorsque nous avons déployé Istio sur le cluster primary, nous allons utiliser Helm pour créer un modèle de déploiement d'Istio à distance sur le cluster burst. Avant de pouvoir le faire, nous devons toutefois obtenir des informations sur notre cluster primary.

Recueillir des informations sur le cluster principal

Passer au cluster primary

kubectx primary

Les commandes suivantes récupèrent les adresses IP de différents pods du cluster principal. Ils sont utilisés par Istio Remote pour communiquer avec le cluster principal.

export PILOT_POD_IP=$(kubectl -n istio-system get pod -l istio=pilot -o jsonpath='{.items[0].status.podIP}')
export POLICY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=policy -o jsonpath='{.items[0].status.podIP}')
export STATSD_POD_IP=$(kubectl -n istio-system get pod -l istio=statsd-prom-bridge -o jsonpath='{.items[0].status.podIP}')
export TELEMETRY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=telemetry -o jsonpath='{.items[0].status.podIP}')
export ZIPKIN_POD_IP=$(kubectl -n istio-system get pod -l app=jaeger -o jsonpath='{range .items[*]}{.status.podIP}{end}')

Créer un modèle à distance

Nous allons maintenant utiliser helm pour créer un fichier nommé istio-remote-burst.yaml que nous pourrons ensuite déployer sur le cluster burst.

Accéder à la racine du projet

cd $proj
helm template istio-1.0.0/install/kubernetes/helm/istio-remote --namespace istio-system \
--name istio-remote \
--set global.remotePilotAddress=${PILOT_POD_IP} \
--set global.remotePolicyAddress=${POLICY_POD_IP} \
--set global.remoteTelemetryAddress=${TELEMETRY_POD_IP} \
--set global.proxy.envoyStatsd.enabled=true \
--set global.proxy.envoyStatsd.host=${STATSD_POD_IP} \
--set global.remoteZipkinAddress=${ZIPKIN_POD_IP} > istio-remote-burst.yaml

Installer Istio Remote sur le cluster de pointe

Pour installer Istio sur notre cluster burst, nous devons suivre les mêmes étapes que pour l'installation sur le cluster primary, mais nous devons utiliser le fichier istio-remote-burst.yaml à la place.

Passer de kubecontext à burst

kubectx burst

Créer l'espace de noms istio-system

kubectl create ns istio-system

Appliquer istio-burst.yaml

kubectl apply -f istio-remote-burst.yaml

Espace de noms par défaut de l'étiquette

Une fois de plus, nous devons ajouter un libellé à l'espace de noms default pour que le proxy puisse être injecté automatiquement.

kubectl label namespace default istio-injection=enabled

Félicitations ! À ce stade, nous avons configuré Istio Remote sur le cluster burst. À ce stade, cependant, les clusters ne peuvent toujours pas communiquer. Nous devons générer un fichier kubeconfig pour le cluster burst que nous pouvons déployer sur le cluster primary pour les associer.

Créer un fichier kubeconfig pour un cluster "burst"

Passer à un cluster burst

kubectx burst

Configurer l'environnement

Nous devons collecter des informations sur le cluster afin de créer un fichier kubeconfig pour celui-ci.

  1. Obtenir le nom du cluster
CLUSTER_NAME=$(kubectl config view --minify=true -o "jsonpath={.clusters[].name}")
  1. Obtenir le nom du serveur de cluster
SERVER=$(kubectl config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
  1. Obtenir le nom du secret pour l'autorité de certification du compte de service istio-multi
SECRET_NAME=$(kubectl get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
  1. Obtenir les données de l'autorité de certification stockées dans le secret précédent
CA_DATA=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}")
  1. Obtenir le jeton stocké dans le secret précédent
TOKEN=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['token']}" | base64 --decode)

Créer un fichier kubeconfig

Une fois toutes ces variables d'environnement définies, nous devons créer notre fichier kubeconfig.

cat <<EOF > burst-kubeconfig
apiVersion: v1
clusters:
   - cluster:
       certificate-authority-data: ${CA_DATA}
       server: ${SERVER}
     name: ${CLUSTER_NAME}
contexts:
   - context:
       cluster: ${CLUSTER_NAME}
       user: ${CLUSTER_NAME}
     name: ${CLUSTER_NAME}
current-context: ${CLUSTER_NAME}
kind: Config
preferences: {}
users:
   - name: ${CLUSTER_NAME}
     user:
       token: ${TOKEN}
EOF

Un fichier nommé burst-kubeconfig est créé dans votre répertoire actuel. Il peut être utilisé par le cluster primary pour authentifier et gérer le cluster burst.

Revenir au cluster principal

kubectx primary

Appliquer le kubeconfig pour "burst", en créant un secret et en le libellant

kubectl create secret generic burst-kubeconfig --from-file burst-kubeconfig -n istio-system

Ajoutez un libellé au secret pour qu'Istio sache l'utiliser pour l'authentification multicluster.

kubectl label secret burst-kubeconfig istio/multiCluster=true -n istio-system

Félicitations ! Les deux clusters sont authentifiés et communiquent entre eux via Istio Multicluster. Déploiement de notre application entre clusters

15. Déployer une application multi-clusters

Créer des déploiements

Accédez au répertoire kubernetes.

cd ${proj}/kubernetes

Créer un déploiement de workers pour le cluster "burst" : worker-burst.yaml

Créez un fichier nommé worker-burst.yaml et insérez-y les éléments suivants:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: worker-deployment
  labels:
    app: worker
spec:
  replicas: 1
  selector:
    matchLabels:
      app: worker
  template:
    metadata:
      labels:
        app: worker
        cluster-type: burst-cluster
    spec:
      containers:
      - name: worker
        image: gcr.io/istio-burst-workshop/worker
        imagePullPolicy: Always
        ports:
        - containerPort: 8081
        readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8081
              httpHeaders:
              - name: "Cookie"
                value: "istio_session-id=x-readiness-probe"
        livenessProbe:
          initialDelaySeconds: 10
          httpGet:
            path: "/"
            port: 8081
            httpHeaders:
            - name: "Cookie"
              value: "istio_session-id=x-liveness-probe"
        env:
        - name: PORT
          value: "8081"
        - name: REDIS_URL
          value: "redis-cache-service:6379"
        - name: PREFIX
          value: "bursty-"

Notez que ce fichier est presque identique à worker-primary.yaml que nous avons créé précédemment. Il existe deux différences principales.

La première différence clé est que nous avons ajouté la variable d'environnement PREFIX avec la valeur "bursty-".

env:
- name: PORT
  value: "8081"
- name: REDIS_URL
  value: "redis-cache-service:6379"
- name: PREFIX
  value: "bursty-"

Cela signifie que notre nœud de calcul dans le cluster burst ajoutera le préfixe "bursty-" à tous les hachages qu'il envoie. Nous pouvons utiliser cette information pour savoir que notre application est véritablement multi-clusters.

La deuxième différence majeure est que nous avons remplacé le libellé cluster-type de ce déploiement de primary-cluster par burst-cluster.

labels:
  app: worker
  cluster-type: burst-cluster

Nous utiliserons ce libellé plus tard lorsque nous mettrons à jour notre VirtualService.

Modifier les services Istio

Pour le moment, nos services Istio ne profitent pas des deux déploiements. 100% de notre trafic est acheminé vers le cluster "principal". Changeons de compte.

Accédez à notre répertoire istio-manifests

cd ${proj}/istio-manifests

Modifier worker-virtualservice.yaml pour inclure DestinationRules

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: worker-virtual-service
spec:
  hosts:
  - worker-service
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: primary
        port:
          number: 80        
      weight: 50
    - destination:
        host: worker-service.default.svc.cluster.local     
        subset: burst  
        port:
          number: 80        
      weight: 50
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: worker-destination-rule
spec:
  host: worker-service
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: primary
    labels:
      cluster-type: primary-cluster
  - name: burst
    labels:
     cluster-type: burst-cluster

Vous pouvez voir que nous avons ajouté une deuxième destination à notre VirtualService. Il fait toujours référence au même hôte (worker-service.default.svc.cluster.local)), mais 50% du trafic est acheminé vers le sous-ensemble primary et les 50% restants vers le sous-ensemble burst.

Nous avons défini le sous-ensemble primary comme étant les déploiements associés au libellé cluster-type: primary-cluster et le sous-ensemble burst comme étant les déploiements associés au libellé cluster-type: burst-cluster.

Cela répartit efficacement notre trafic 50/50 entre les deux clusters.

Déploiement sur un cluster

Déployer redis-service.yaml sur un cluster burst

Passer au fichier kubeconfig burst

kubectx burst

Accédez au répertoire racine de notre projet.

cd ${proj}

Déployez ensuite

Déployez redis-service.yaml sur le cluster de pointe.

kubectl apply -f kubernetes/redis-service.yaml

Déploiement de worker-burst.yaml sur le cluster burst

kubectl apply -f kubernetes/worker-burst.yaml

Déployez worker-service.yaml sur le cluster burst.

kubectl apply -f kubernetes/worker-service.yaml

Appliquer des VirtualServices Istio

Passer au fichier kubeconfig primary

kubectx primary

Déployez ensuite

kubectl apply -f istio-manifests/worker-virtualservice.yaml

Vérifier que le service fonctionne

Pour vérifier que cela fonctionne, accédez à votre point d'entrée Istio et notez qu'environ 50% des hachages sont précédés du préfixe "burst-".

78fb6e235e9f4a07.png

Cela signifie que nous parlons bien entre clusters. Essayez de modifier les pondérations des différents services et d'appliquer le fichier worker-virtualservice.yaml. C'est un excellent moyen d'équilibrer le trafic entre les clusters, mais que se passerait-il si nous pouvions le faire automatiquement ?

16. Exploiter les métriques Prometheus

Présentation de Prometheus

Prometheus est une boîte à outils Open Source de surveillance et d'alerte des systèmes, créée à l'origine chez SoundCloud. Il gère un modèle de données multidimensionnel avec des données de séries temporelles identifiées par le nom de la métrique et des paires clé-valeur.

Pour référence, voici le schéma de l'architecture de Prometheus:

601e1155a825e0c2.png

Lorsqu'il est déployé avec Prometheus, Istio envoie automatiquement diverses métriques au serveur Prometheus. Nous pouvons utiliser ces métriques pour gérer nos clusters instantanément.

Explorer nos métriques Prometheus

Pour commencer, nous devons exposer le déploiement Prometheus.

Accédez à l'onglet "Charges de travail" dans GKE, puis accédez à la charge de travail "prometheus".

b4a7a3cd67db05b3.png

Une fois que vous avez consulté les détails du déploiement, accédez à Actions -> Exposer.

c04a482e55bdfd41.png

Choisissez de transférer vers le port 9090, puis saisissez "Équilibreur de charge".

d5af3ba22a7a6ebb.png

et sélectionnez "Exposer".

Un service sera créé sur une adresse IP accessible au public que nous pourrons utiliser pour explorer nos métriques Prometheus.

Attendez que le point de terminaison soit opérationnel, puis cliquez sur l'adresse IP à côté de "Points de terminaison externes" b1e40ad90851da29.png.

L'interface utilisateur de Prometheus devrait maintenant s'afficher.

ed273552270337ec.png

Prometheus fournit suffisamment de métriques pour constituer son propre atelier. Pour l'instant, nous allons commencer par explorer la métrique istio_requests_total.

L'exécution de cette requête renvoie un ensemble de données. Il s'agit de métriques sur toutes les requêtes qui passent par le maillage de services Istio, et c'est beaucoup ! Nous allons modifier notre expression pour filtrer les résultats qui nous intéressent vraiment:

Requêtes dont le service de destination est worker-service.default.svc.cluster.local et dont la source est frontend-deployment, limitées aux 15 dernières secondes

Cette requête se présente comme suit:

istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s]

Nous disposons ainsi d'un ensemble de données beaucoup plus gérable.

19d551fd5eac3785.png

Mais il reste un peu dense. Nous voulons connaître le nombre de requêtes par seconde, et non toutes les requêtes.

Pour ce faire, nous pouvons utiliser la fonction rate intégrée.

rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])

dbb9dc063a18da9b.png

Nous nous rapprochons de la réponse, mais nous devons réduire ces métriques un peu plus pour les regrouper dans un groupe logique.

Pour ce faire, nous pouvons utiliser les mots clés sum et by pour regrouper et additionner nos résultats.

sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)

898519966930ec56.png

Parfait ! Nous pouvons obtenir les métriques exactes dont nous avons besoin à partir de Prometheus.

Notre requête Prometheus finale

Avec tout ce que nous avons appris, la dernière requête que nous devons poser à Prometheus est

sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)

Nous pouvons maintenant utiliser leur API HTTP pour obtenir la métrique.

Nous pouvons interroger leur API avec notre requête en effectuant une requête HTTP GET comme suit. Remplacez <prometheus-ip-here>

curl http://<prometheus-ip-here>/api/v1/query?query=sum\(rate\(istio_requests_total%7Breporter%3D%22destination%22%2C%0Adestination_service%3D%22worker-service.default.svc.cluster.local%22%2C%0Asource_workload%3D%22frontend-deployment%22%7D%5B15s%5D\)\)%20by%20\(source_workload%2C%0Asource_app%2C%20destination_service\)

Voici un exemple de réponse:

{
    "status": "success",
    "data": {
        "resultType": "vector",
        "result": [
            {
                "metric": {
                    "destination_service": "worker-service.default.svc.cluster.local",
                    "source_app": "frontend",
                    "source_workload": "frontend-deployment"
                },
                "value": [
                    1544404907.503,
                    "18.892886390062788"
                ]
            }
        ]
    }
}

Nous pouvons maintenant extraire la valeur de la métrique du fichier JSON.

Effectuer un nettoyage

Nous devons supprimer le service que nous venons d'utiliser pour exposer Prometheus. Dans la console Google Cloud, accédez en haut du service que nous venons de créer, puis cliquez sur "Supprimer".

d58cb51b4c922751.png

Prochaines étapes :

Après avoir trouvé un moyen de découvrir comment le trafic circule dans le cluster et à quel débit, notre prochaine étape consiste à écrire un petit binaire qui interroge périodiquement Prometheus. Si le nombre de requêtes par seconde vers worker dépasse un certain seuil, nous appliquerons des poids de destination différents à notre service virtuel de worker pour envoyer tout le trafic au cluster burst. Lorsque le nombre de requêtes par seconde passe sous un seuil inférieur, renvoyez tout le trafic vers primary.

17. Créer une rafale inter-clusters

Configuration

Définir tout le trafic du service de travail sur le cluster principal

Nous considérerons que tout le trafic destiné à worker-service est acheminé vers le cluster primary comme étant l'état "par défaut" de notre application.

Modifiez $proj/istio-manifests/worker-virtualservice.yaml pour qu'il se présente comme suit :

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: worker-virtual-service
spec:
  hosts:
  - worker-service
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: primary
        port:
          number: 80        
      weight: 100
    - destination:
        host: worker-service.default.svc.cluster.local     
        subset: burst  
        port:
          number: 80        
      weight: 0
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: worker-destination-rule
spec:
  host: worker-service
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: primary
    labels:
      cluster-type: primary-cluster
  - name: burst
    labels:
     cluster-type: burst-cluster

Assurez-vous d'être connecté au cluster primary.

kubectx primary

Appliquer istio-manifests/worker-virtualservice.yaml

kubectl apply -f istio-manifests/worker-virtualservice.yaml

Écrire le daemon istiowatcher

Nous allons utiliser Go pour écrire ce service en raison de sa rapidité et de sa portabilité. Le flux global de l'application consiste à démarrer et à interroger Prometheus toutes les secondes.

Créez un répertoire dans src nommé istiowatcher.

mkdir -p ${proj}/src/istiowatcher && cd ${proj}/src/istiowatcher

Nous allons appeler istioctl à partir de notre conteneur afin de manipuler le plan de contrôle Istio à partir du cluster.

Écrire istiowatcher.go

Créez un fichier nommé istiowatcher.go dans ce répertoire et insérez-y le code suivant :

package main

import (
        "github.com/tidwall/gjson"
        "io/ioutil"
        "log"
        "net/http"
        "os/exec"
        "time"
)

func main() {
        //These are in requests per second
        var targetLow float64 = 10
        var targetHigh float64 = 15
        // This is for the ticker in milliseconds
        ticker := time.NewTicker(1000 * time.Millisecond)

        isBurst := false

        // Our prometheus query
        reqQuery := `/api/v1/query?query=sum(rate(istio_requests_total{reporter="destination",destination_service="worker-service.default.svc.cluster.local",source_workload="frontend-deployment"}[15s]))by(source_workload,source_app,destination_service)`

        for t := range ticker.C {
                log.Printf("Checking Prometheus at %v", t)

                // Check prometheus
                // Note that b/c we are querying over the past 5 minutes, we are getting a very SLOW ramp of our reqs/second
                // If we wanted this to be a little "snappier" we can scale it down to say 30s
                resp, err := http.Get("http://prometheus.istio-system.svc.cluster.local:9090" + reqQuery)
                if err != nil {
                        log.Printf("Error: %v", err)
                        continue
                }
                defer resp.Body.Close()
                body, _ := ioutil.ReadAll(resp.Body)

                val := gjson.Get(string(body), "data.result.0.value.1")
                log.Printf("Value: %v", val)

                currentReqPerSecond := val.Float()
                log.Printf("Reqs per second %f", currentReqPerSecond)

                if currentReqPerSecond > targetHigh && !isBurst {
                        applyIstio("burst.yaml")
                        log.Println("Entering burst mode")
                        isBurst = true
                } else if currentReqPerSecond < targetLow && isBurst {
                        applyIstio("natural.yaml")
                        log.Println("Returning to natural state.")
                        isBurst = false
                }
        }
}

func applyIstio(filename string) {
        cmd := exec.Command("istioctl", "replace", "-f", filename)
        if err := cmd.Run(); err != nil {
                log.Printf("Error hit applying istio manifests: %v", err)
        }
}

Écrire un fichier Dockerfile

Créez un fichier nommé Dockerfile et insérez-y le code suivant.

FROM golang:1.11.2-stretch as base

FROM base as builder

WORKDIR /workdir
RUN curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
RUN tar xzf istio-1.0.0-linux.tar.gz
RUN cp istio-1.0.0/bin/istioctl ./istioctl

FROM base 

WORKDIR /go/src/istiowatcher
COPY . .
COPY --from=builder /workdir/istioctl /usr/local/bin/istioctl

RUN go get -d -v ./...
RUN go install -v ./...

CMD ["istiowatcher"]

Ce Dockerfile multi-étapes télécharge et extrait la version 1.0.0 d'Istio à la première étape. La deuxième étape copie tout le contenu de notre répertoire dans l'image, puis copie istioctl de l'étape de compilation vers /usr/local/bin (afin qu'il puisse être appelé par notre application), récupère les dépendances, compile le code et définit CMD sur "istiowatcher".

Écrire burst.yaml

Il s'agit du fichier que istiowatcher appliquera lorsque le nombre de requêtes par seconde envoyées à worker depuis frontend dépassera 15.

Créez un fichier nommé burst.yaml et insérez-y le code suivant.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local   
       subset: primary
       port:
         number: 80       
     weight: 0
   - destination:
       host: worker-service.default.svc.cluster.local    
       subset: burst 
       port:
         number: 80       
     weight:  100

Écrire natural.yaml

Nous considérerons qu'il s'agit de l'état "naturel" auquel nous revenons lorsque le nombre de requêtes par seconde entre frontend et worker passe sous la barre des 10. Dans cet état, 100% du trafic est acheminé vers le cluster primary.

Créez un fichier nommé natural.yaml et insérez-y le code suivant :

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local   
       subset: primary
       port:
         number: 80       
     weight: 100
   - destination:
       host: worker-service.default.svc.cluster.local    
       subset: burst 
       port:
         number: 80       
     weight: 0

Créer et transférer istiowatcher

Exécutez la commande suivante pour envoyer le répertoire actuel à Google Cloud Build (GCB), qui créera et taguera l'image dans GCR.

gcloud builds submit -t gcr.io/${GCLOUD_PROJECT}/istiowatcher

Déployer istiowatcher

Accédez à notre répertoire kubernetes

cd ${proj}/kubernetes/

Écrire un fichier de déploiement: istiowatcher.yaml

Créez un fichier nommé istiowatcher.yaml et insérez le code suivant (remplacez <your-project-id>).

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: istiowatcher-deployment
  labels:
    app: istiowatcher
spec:
  replicas: 1
  selector:
    matchLabels:
      app: istiowatcher
  template:
    metadata:
      labels:
        app: istiowatcher
    spec:
      serviceAccountName: istio-pilot-service-account
      automountServiceAccountToken: true
      containers:
      - name: istiowatcher
        image: gcr.io/<your-project-id>/istiowatcher
        imagePullPolicy: Always

Déployer

Vérifier que nous exécutons le script dans le cluster principal

kubectx primary

Déployer istiowatcher.yaml dans l'espace de noms istio-system

kubectl apply -n istio-system -f istiowatcher.yaml

Notez les directives serviceAccountName et automountServiceAccountToken dans le fichier YAML. Nous obtenons ainsi les identifiants nécessaires pour exécuter istioctl à partir du cluster.

Nous devons également le déployer dans l'espace de noms istio-system pour nous assurer de disposer des identifiants pour le istio-pilot-service-account. (il n'existe pas dans l'espace de noms default).

Observez le trafic basculer automatiquement.

Passons maintenant au moment magique ! Allons dans notre frontend et augmentons le nombre de requêtes par seconde à 20.

Notez que cela prend quelques secondes, mais que nous l'augmentons et que tous nos hachages sont précédés du préfixe "bursty-".

En effet, nous échantillonnons Prometheus sur la plage 15s, ce qui entraîne un léger retard dans le temps de réponse. Si nous souhaitons une bande beaucoup plus étroite, nous pouvons modifier notre requête prometheus pour qu'elle soit 5s..

18. Étape suivante

Effectuer un nettoyage

Vous n'avez pas besoin de nettoyer si vous utilisez un compte temporaire fourni pour cet atelier.

Vous pouvez supprimer vos clusters Kubernetes, la règle de pare-feu et les images dans GCR.

gcloud container clusters delete primary --zone=us-west1-a
gcloud container clusters delete burst --zone=us-west1-a
gcloud compute firewall-rules delete istio-multicluster-test-pods 
gcloud container images delete gcr.io/$GCLOUD_PROJECT/istiowatcher

À suivre