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

1. Bienvenue

Merci d'avoir participé à l'atelier de programmation Google sur le bursting multicloud Istio.Cet atelier de programmation nécessite une expérience pratique de niveau débutant 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 conteneurs dans un cluster Kubernetes

2. Configuration

Vous pouvez suivre cet atelier de programmation sur :

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

Premiers pas avec Google Cloud Platform

  1. Si vous n'avez pas de compte GCP, demandez à votre instructeur de vous fournir une carte de compte utilisateur sans frais.
  2. Accédez à la console Google Cloud et cliquez sur "Sélectionner un projet" : 5c2d9bf74c78f7e4.png.
  3. Notez l'ID du projet quelque part, puis cliquez sur le projet 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 automatiquement authentifiés sur votre compte Google Cloud Platform. (Si vous ne souhaitez pas exécuter cet exercice sur Cloud Shell, passez à la section suivante.)

Accédez à la console Cloud et 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 depuis ce lien vers un emplacement dans $PATH.
  2. Installez helm**** en suivant ces instructions.

Vous pouvez également exécuter ces commandes pour installer les deux à ~/.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 rapides qui peuvent vous aider à utiliser Cloud Shell :

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

2. Utiliser 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. Ouvrez de nouveaux onglets : si vous avez besoin de plusieurs invites de terminal.

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

Ctrl+ + sur Linux/Windows ou ⌘+ + 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 installer :
gcloud components install kubectl

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

gcloud init
  1. Installer curl: : préinstallé sur la plupart des systèmes Linux/macOS. Vous l'avez probablement déjà. Sinon, recherchez sur Internet comment l'installer.
  2. Installez kubectx**** en téléchargeant les scripts Bash depuis ce lien vers un emplacement dans $PATH.
  3. Installez helm**** en suivant 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 travailler intensivement avec notre projet Google Cloud lors de la configuration. Définissons une variable d'environnement pour une référence rapide.

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

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

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

4. Créer le cluster Kubernetes "primary"

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

La commande suivante crée un cluster Kubernetes :

  • nommée "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

(Cette opération peut prendre environ cinq minutes. Vous pouvez observer 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 maintenant 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 "Prêt") :

kubectl get nodes

Modifier les noms Kubeconfig pour faciliter l'utilisation

Nous allons souvent changer de contexte. 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éfinissez les autorisations :

Pour déployer Istio, vous devez être administrateur de 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é 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

(Cette opération peut prendre environ cinq minutes. Vous pouvez observer 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 maintenant 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 "Prêt") :

kubectl get nodes

Modifier les noms 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éfinissez les autorisations :

Pour déployer Istio Remote, vous devez être administrateur de 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 à accueillir le déploiement de notre application et d'Istio.

7. Présentation d'Istio

Qu'est-ce qu'Istio ?

Istio est un plan de contrôle de maillage de services qui vise à "connecter, sécuriser, contrôler et observer les services". Pour ce faire, il utilise différentes méthodes, mais principalement en ajoutant un conteneur proxy ( Envoy) à chaque pod Kubernetes déployé. Le conteneur de proxy contrôle toutes les communications réseau entre les microservices en tandem avec un hub de télémétrie et de règles à usage général ( Mixer).

a25613cd581825da.png

Ces règles peuvent être appliquées indépendamment de vos déploiements et services Kubernetes, ce qui 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 cet atelier, nous allons nous concentrer sur la répartition du trafic en pourcentage.

Termes Istio que nous allons utiliser

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

Une 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 à partir du side-car et les paramètres de détection des valeurs aberrantes.

Istio Multicluster

Vous avez peut-être remarqué que lorsque nous avons créé nos deux clusters, notre cluster primary comportait quatre nœuds sans autoscaling, et notre cluster burst comportait un nœud avec autoscaling jusqu'à cinq nœuds.

Cette configuration est nécessaire pour deux raisons.

Tout d'abord, nous voulons simuler un scénario "sur site" vers le cloud. Dans un environnement sur site, vous n'avez pas accès aux clusters d'autoscaling, car votre infrastructure est fixe.

Deuxièmement, une configuration à quatre nœuds (telle que définie ci-dessus) est la configuration minimale requise pour exécuter Istio. Cela soulève une question : 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 qu'Istio Multicluster installe un ensemble beaucoup plus petit de services Istio et communique avec l'installation Istio dans le cluster principal pour récupérer les règles de stratégie et publier les 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 Redis.

Worker

L'application Worker est écrite en NodeJS et écoute les requêtes HTTP POST entrantes. Elle effectue une opération de hachage sur ces requêtes et, si une variable d'environnement nommée PREFIX est définie, elle ajoute cette valeur au 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 référence, voici les packages utilisés par l'application.

  • body-parser: Nous permet d'analyser nos requêtes HTTP
  • cors: Permet l'utilisation du partage des 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é

Frontend

Notre interface est également une application NodeJS qui héberge une page Web à l'aide d'express. Il prend en compte la 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
  • request: Permet d'effectuer des requêtes HTTP
  • socket.io: Permet une communication bidirectionnelle entre la page Web et le serveur

Cette page Web utilise Bootstrap pour la mise en forme et, une fois exécutée, ressemble à ce qui suit :

e5e3b9cbede4cac4.png

Schéma d'architecture

7ae4bc22a58f80a6.png

Diagramme de déploiement

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

Voici un schéma décrivant les deux clusters. Les zones encadrées en rouge sont des services Kubernetes, celles en bleu sont des déploiements Kubernetes. Les zones jaunes indiquent l'installation d'Istio.

561db37c510944bd.png

Notez que le cluster burst dispose toujours d'un service pour Redis, même s'il n'y a aucun déploiement pour Redis dans le cluster. Nous devons disposer de ce service dans le cluster pour que Kubernetes DNS puisse résoudre la requête. Toutefois, lorsque la requête est réellement effectué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.. C'est ce qui nous permettra de rediriger dynamiquement le trafic vers burst automatiquement 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éera un déploiement et un service Kubernetes pour accéder à notre image d'interface.

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 l'application s'exécutera sur le port 8080.
  • Nous avons défini l'adresse du nœud de calcul sur "http://worker-service" et nous utiliserons la fonctionnalité DNS intégrée de Kubernetes pour résoudre le service résultant.
  • Nous avons défini l'adresse de notre REDIS_URL sur "redis-cache-service:6379" et nous utiliserons la fonctionnalité DNS intégrée de Kubernetes pour résoudre les adresses IP résultantes.
  • Nous avons également défini des vérifications liveness et readiness pour le conteneur afin d'informer Kubernetes lorsque le conteneur est opérationnel.

Écrire worker-service.yaml

Nous allons écrire 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 transférer 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 modèle pour fournir les sondes liveness et readiness, et pour spécifier les variables d'environnement PORT et REDIS_URL que notre application doit utiliser.

Un autre point à noter dans ce déploiement est 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 le libellé cluster-type: primary-cluster. Nous l'utiliserons plus tard pour le routage du trafic sur Istio Multicluster.

Écrire redis.yaml

La communication de notre nœud de calcul avec l'interface 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 crée 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

Cela fournit un service nommé redis-cache-service pour accéder à notre déploiement Redis.

10. Déployer l'application

Maintenant que nos images sont transférées vers GCR et que nos fichiers manifestes Kubernetes sont écrits, c'est le bon moment pour déployer notre application et voir comment elle fonctionne.

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

  1. Assurez-vous que nous sommes dans le bon cluster.
kubectx primary
  1. Déployer le cache Redis
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 le 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 équilibreur de charge. En effet, nous accéderons à l'application via Istio ultérieurement. Pour vérifier que tout fonctionne correctement, nous allons utiliser kubectl port-forward.. Exécutez la commande suivante pour transférer le port 8080 sur 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" et sélectionnez "Prévisualiser sur le port 8080".

bdb5dc75f415be11.png

L'interface utilisateur doit s'afficher. Si vous saisissez un nombre dans la zone "Fréquence", des hachures devraient commencer à apparaître.

1caafaffab26897a.png

Félicitations, tout est opérationnel !

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

11. Nettoyer l'application déployée

Nous allons appliquer Istio à notre cluster, puis redéployer notre application. Commençons donc 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échargeront la version 1.0.0 d'Istio et la décompresseront.

  1. Accédez à la 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éera le modèle permettant d'installer Istio sur 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

Cette commande crée un fichier nommé istio-primary.yaml dans votre répertoire actuel. Il contient toutes les définitions et spécifications nécessaires au déploiement et à l'exécution d'Istio.

Notez les deux paramètres --set. Ces modules complémentaires ajoutent la compatibilité avec Prometheus et ServiceGraph au système Istio. Nous utiliserons le service Prometheus plus loin 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 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

Espace de noms par défaut des libellés

Istio fonctionne en injectant un service de proxy side-car dans chacun de vos déploiements. Cette opération est effectuée sur la base d'un consentement. Nous devons donc ajouter le libellé istio-injection=enabled à notre espace de noms default pour qu'Istio puisse injecter automatiquement le side-car.

kubectl label namespace default istio-injection=enabled

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

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

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

Istio fonctionne de la même manière que Kubernetes, car il utilise des fichiers YAML pour la configuration. Dans cette optique, nous devons créer un ensemble de fichiers pour indiquer à 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 semblable à un équilibreur de charge GKE et acheminera tout le trafic entrant vers notre service de frontend.

Créez un fichier appelé frontend-gateway.yaml et insérez-y le contenu suivant.

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éployer les règles de gestion du trafic Istio

Le déploiement des règles Istio s'effectue de la même manière que pour 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 Worker
kubectl apply -f worker-virtualservice.yaml

Déployer l'application

  1. Revenir à notre répertoire kubernetes
cd ${proj}/kubernetes
  1. Déployer le cache Redis
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 le 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 des règles de gestion du trafic et Istio.

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 envoyez une requête cURL. L'interface utilisateur devrait s'afficher.

$ curl 35.199.158.10
<!doctype html>
<html>

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

14. Installer Istio sur le cluster "burst"

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

Dans cette section, nous devrons récupérer les variables de configuration dans les deux clusters. Faites donc bien attention au cluster vers lequel nous sommes dirigés pour chaque commande.

Créer le fichier manifeste Istio Remote

Comme lorsque nous avons déployé Istio sur le cluster primary, nous allons utiliser Helm pour créer un modèle de notre déploiement d'Istio Remote sur le cluster primary.burst Avant de pouvoir le faire, nous devons 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. Elles sont utilisées 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 distant

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 burst

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.

Modifier le kubecontext pour le mode rafale

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 du libellé

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. Toutefois, à ce stade, 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 le cluster "burst"

Passer à un cluster de pics

kubectx burst

Configurer l'environnement

Nous devons recueillir des informations sur le cluster pour pouvoir créer un fichier kubeconfig.

  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. Obtenez 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. Récupérer 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 burst-kubeconfig est alors 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

Appliquez le fichier kubeconfig pour "burst" en créant un secret et en lui attribuant un libellé.

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

Attribuez 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éployons notre application sur plusieurs clusters.

15. Déployer une application multicluster

Créer des déploiements

Accédez au répertoire kubernetes.

cd ${proj}/kubernetes

Créez un déploiement de nœuds de calcul 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 à celui de 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 worker du cluster burst préfixera tous les hachages qu'il envoie avec "bursty-". Nous pouvons l'utiliser pour savoir si notre application est réellement multicluster.

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

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

Nous utiliserons ce libellé ultérieurement lorsque nous mettrons à jour notre VirtualService.

Modifier les services Istio

Pour le moment, nos services Istio ne tirent pas parti de nos deux déploiements. 100 % de notre trafic est acheminé vers le cluster "principal". Remédions à cela.

Accédez à notre répertoire istio-manifests.

cd ${proj}/istio-manifests

Modifiez 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 sont acheminés 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 qui ont le libellé cluster-type: primary-cluster et le sous-ensemble burst comme étant les déploiements qui ont le libellé cluster-type: burst-cluster.

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

Déployer sur le cluster

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

Passer au fichier kubeconfig burst

kubectx burst

Accéder à la racine de notre projet

cd ${proj}

Déployez ensuite

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

kubectl apply -f kubernetes/redis-service.yaml

Déployer worker-burst.yaml sur le cluster burst

kubectl apply -f kubernetes/worker-burst.yaml

Déployez worker-service.yaml sur le cluster de pics de charge.

kubectl apply -f kubernetes/worker-service.yaml

Appliquer les VirtualServices Istio

Passer au fichier kubeconfig primary

kubectx primary

Déployer

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

Vérifier que tout fonctionne

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

78fb6e235e9f4a07.png

Cela signifie que nous communiquons bien entre les 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 pour les systèmes, initialement conçue 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 Prometheus :

601e1155a825e0c2.png

Lorsqu'Istio est déployé avec Prometheus, il signale automatiquement diverses métriques au serveur Prometheus. Nous pouvons utiliser ces métriques pour gérer nos clusters à la volée.

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 affichez 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

Choisissez "Exposer".

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

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

Vous devriez maintenant voir l'interface utilisateur de Prometheus.

ed273552270337ec.png

Prometheus fournit suffisamment de métriques pour être 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 grand nombre de données. Il s'agit de métriques sur toutes les requêtes qui transitent par le maillage de services Istio, et il y en a beaucoup ! Nous allons modifier notre expression pour filtrer les résultats et ne conserver que ce qui nous intéresse 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]

et nous donne un ensemble de données beaucoup plus facile à gérer.

19d551fd5eac3785.png

mais il reste un peu dense. Nous voulons connaître les 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 en approchons, mais nous devons encore réduire ces métriques pour les regrouper de manière 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 requête finale que nous devons envoyer à 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 notre métrique du 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 :

Maintenant que nous avons trouvé un moyen de découvrir comment le trafic se déplace dans le cluster et à quelle vitesse, notre prochaine étape consiste à écrire un petit binaire qui interroge périodiquement Prometheus. Si les requêtes par seconde vers worker dépassent un certain seuil, nous appliquerons différentes pondérations de destination sur notre service virtuel de nœud de calcul pour envoyer tout le trafic vers le cluster burst. Une fois que les requêtes par seconde sont inférieures à un seuil bas, renvoyez tout le trafic vers primary.

17. Créer un burst cross-cluster

Configuration

Définir tout le trafic pour le service de nœud de calcul sur le cluster principal

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

Modifiez $proj/istio-manifests/worker-virtualservice.yaml 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 utiliserons 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 nommé "istiowatcher" dans "src".

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

Nous allons appeler istioctl depuis notre conteneur afin de manipuler le plan de contrôle Istio depuis le 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 en plusieurs étapes télécharge et extrait la version 1.0.0 d'Istio lors de la première étape. La deuxième étape consiste à copier tout le contenu de notre répertoire dans l'image, puis à copier istioctl de l'étape de compilation vers /usr/local/bin (afin qu'il puisse être appelé par notre application), à obtenir les dépendances, à compiler le code et à définir CMD sur "istiowatcher".

Écrire burst.yaml

Il s'agit du fichier istiowatcher qui s'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 de frontend à worker tombe en dessous de 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

Compiler et envoyer istiowatcher

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

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

Déployer istiowatcher

Accédez à notre répertoire kubernetes.

cd ${proj}/kubernetes/

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

Créez un fichier nommé istiowatcher.yaml et insérez le contenu 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

Assurez-vous que nous sommes en cours d'exécution 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

Il est important de noter les directives serviceAccountName et automountServiceAccountToken dans le fichier YAML. Cela nous donne les identifiants nécessaires pour exécuter istioctl depuis le cluster.

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

Regardez le trafic basculer automatiquement !

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

Vous remarquerez que cela prend quelques secondes, mais que tous nos hachages sont précédés du préfixe "bursty-".

En effet, nous échantillonnons Prometheus sur une plage de 15s, ce qui retarde un peu notre temps de réponse. Si nous voulions une bande beaucoup plus étroite, nous pourrions modifier notre requête Prometheus pour qu'elle soit 5s..

18. Étape suivante

Effectuer un nettoyage

Vous n'avez pas besoin d'effectuer de nettoyage 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

Et après ?