Istio Multicluster zum „Bursten“ von Arbeitslasten zwischen Clustern verwenden

1. Willkommen

Vielen Dank, dass Sie am Istio Multi Cloud Burst Codelab von Google teilnehmen.Für dieses Codelab benötigen Sie praktische Erfahrung mit Kubernetes, Node und Go auf Anfängerniveau.

Voraussetzungen

  • Google Cloud Platform-Konto (Sie können ein vorhandenes Konto verwenden oder wir stellen Ihnen ein kostenloses Konto zur Verfügung)
  • Auf Ihrem Laptop (installieren Sie „kubectl“, „gcloud“ usw.) oder Sie können Google Cloud Shell verwenden.

Lerninhalte

  • Kubernetes-Cluster in GKE erstellen
  • Istio mit Helm in einem Kubernetes-Cluster installieren
  • Istio Multicluster mit Helm installieren
  • Webanwendung aus der Quelle in Kubernetes bereitstellen
  • Traffic-Routing-Regeln für Istio schreiben und anwenden
  • Prometheus-Messwerte
  • Container-Images in einem Kubernetes-Cluster erstellen und per Push übertragen

2. Einrichtung

Sie können dieses Codelab auf einer der folgenden Plattformen ausführen:

  • Google Cloud Shell (empfohlen): Browser-Shell mit vorinstallierten Tools
  • auf Ihrem Laptop (folgen Sie der Anleitung unten)

Mit der Google Cloud Platform beginnen

  1. Wenn Sie noch kein GCP-Konto haben, holen Sie sich eine kostenlose Nutzerkontokarte vom Dozenten ab.
  2. Rufen Sie die Google Cloud Console auf und klicken Sie auf „Projekt auswählen“: 5c2d9bf74c78f7e4.png
  3. Notieren Sie sich die ID des Projekts und klicken Sie dann darauf, um es auszuwählen: ecc5e8e97bfa6559.png

Cloud Shell bietet eine Befehlszeile in Ihrem Browser mit den erforderlichen Tools, die automatisch in Ihrem Google Cloud-Konto authentifiziert werden. Wenn Sie diese Übung nicht in Cloud Shell ausführen möchten, fahren Sie mit dem nächsten Abschnitt fort.

Rufen Sie die Cloud Console auf und klicken Sie rechts oben in der Symbolleiste auf „Cloud Shell aktivieren“:

68a17b036ce24ccb.png

Cloud Shell Tools hinzufügen

  1. Installieren Sie kubectx****, indem Sie die Bash-Scripts von hier an einen Ort in $PATH herunterladen.
  2. Installieren Sie helm****: Folgen Sie dazu dieser Anleitung.

Alternativ können Sie die folgenden Befehle ausführen, um beide in ~/.bin zu installieren und zu $PATH hinzuzufügen:

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}

Einige praktische Tipps, die die Verwendung von Cloud Shell erleichtern:

1. Trennen Sie die Shell in einem neuen Fenster:

2. Dateieditor verwenden : Klicken Sie oben rechts auf das Stiftsymbol, um einen Dateieditor im Browser zu starten. Das ist nützlich, da wir Code-Snippets in Dateien kopieren werden.

3. Neue Tabs öffnen:Wenn Sie mehr als einen Terminalprompt benötigen.

4. Text vergrößern : Die Standardschriftgröße in Cloud Shell ist möglicherweise zu klein zum Lesen.

Strg + unter Linux/Windows bzw.⌘ + unter macOS

Wenn Sie Ihre eigene Workstation-Umgebung lieber als Cloud Shell verwenden, richten Sie die folgenden Tools ein:

  1. Installieren Sie gcloud: (in Cloud Shell vorinstalliert). Folgen Sie der Anleitung, um gcloud auf Ihrer Plattform zu installieren. Damit erstellen wir einen Kubernetes-Cluster.
  2. Installieren Sie kubectl:(in Cloud Shell vorinstalliert). Führen Sie den folgenden Befehl aus, um Folgendes zu installieren:
gcloud components install kubectl

Führen Sie den folgenden Befehl aus, um gcloud zu authentifizieren. Sie werden aufgefordert, sich mit Ihrem Google-Konto anzumelden. Wählen Sie dann das vorab erstellte Projekt (siehe oben) als Standardprojekt aus. Sie können die Konfiguration einer Compute-Zone überspringen:

gcloud init
  1. Installieren Sie curl:. Dieser ist auf den meisten Linux-/macOS-Systemen vorinstalliert. Sie haben es wahrscheinlich schon. Andernfalls können Sie im Internet nach einer Anleitung zur Installation suchen.
  2. Installieren Sie kubectx****, indem Sie die Bash-Scripts von hier an einen Ort in $PATH herunterladen.
  3. Installieren Sie helm****: Folgen Sie dazu dieser Anleitung.

3. GCP-Projekt einrichten

Aktivieren Sie die APIs GKE (Google Kubernetes Engine), GCR (Google Container Registry) und GCB (Google Cloud Build) in Ihrem Projekt:

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

Umgebungsvariablen einrichten

Wir werden während der Einrichtung viel mit unserem Google Cloud-Projekt arbeiten. Legen Sie dazu eine Umgebungsvariable fest, die Sie schnell zur Hand haben.

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

In diesem Workshop erstellen wir Code und Konfigurationsdateien. Erstellen wir also ein Projektverzeichnis und wechseln wir dorthin.

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

4. „Primären“ Kubernetes-Cluster erstellen

Mit der Google Kubernetes Engine (GKE) können Sie ganz einfach verwaltete Kubernetes-Cluster erstellen.

Mit dem folgenden Befehl wird ein Kubernetes-Cluster erstellt:

  • mit dem Namen „primary“,
  • in der Zone „us-west1-a“,
  • Die neueste verfügbare Kubernetes-Version,
  • mit 4 ursprünglichen Knoten
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

Das kann etwa 5 Minuten dauern. Sie können sich die Erstellung des Clusters in der Cloud Console ansehen.

Nachdem der Kubernetes-Cluster erstellt wurde, konfiguriert gcloud kubectl mit den Anmeldedaten, die auf den Cluster verweisen.

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

Sie sollten kubectl jetzt mit Ihrem neuen Cluster verwenden können.

Führen Sie den folgenden Befehl aus, um die Kubernetes-Knoten Ihres Clusters aufzulisten. Sie sollten den Status „Bereit“ haben:

kubectl get nodes

Kubeconfig-Namen zur besseren Verwendung ändern

Wir wechseln häufig zwischen Kontexten, daher ist ein kurzer Alias für unsere Cluster praktisch.

Mit diesem Befehl wird der soeben erstellte kubeconfig-Eintrag in primary umbenannt.

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

Berechtigungen festlegen

Zum Bereitstellen von Istio müssen Sie Clusteradministrator sein. Mit diesem Befehl wird die E-Mail-Adresse, die mit Ihrem Google Cloud-Konto verknüpft ist, als Clusteradministrator festgelegt.

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

5. Burst-Cluster erstellen

Mit dem folgenden Befehl wird ein Kubernetes-Cluster erstellt:

  • mit dem Namen „burst“,
  • in der Zone „us-west1-a“,
  • Die neueste verfügbare Kubernetes-Version,
  • Mit 1 Anfangsknoten
  • Autoscaling für bis zu 5 Knoten aktiviert
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

Das kann etwa 5 Minuten dauern. Sie können sich die Erstellung des Clusters in der Cloud Console ansehen.

Nachdem der Kubernetes-Cluster erstellt wurde, konfiguriert gcloud kubectl mit den Anmeldedaten, die auf den Cluster verweisen.

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

Sie sollten kubectl jetzt mit Ihrem neuen Cluster verwenden können.

Führen Sie den folgenden Befehl aus, um die Kubernetes-Knoten Ihres Clusters aufzulisten. Sie sollten den Status „Bereit“ haben:

kubectl get nodes

Kubeconfig-Namen zur besseren Nutzung ändern

Mit diesem Befehl wird der gerade erstellte kubeconfig-Eintrag in burst geändert.

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

Berechtigungen festlegen

Zum Bereitstellen von Istio Remote müssen Sie Clusteradministrator sein. Mit diesem Befehl wird die E-Mail-Adresse, die mit Ihrem Google Cloud-Konto verknüpft ist, als Clusteradministrator festgelegt.

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

6. Firewallregeln anwenden

Damit unsere beiden Cluster miteinander kommunizieren können, müssen wir eine Firewallregel erstellen.

Führen Sie die folgenden Befehle aus, um eine Firewallregel in der Google Cloud-Plattform zu erstellen, die es unseren Clustern ermöglicht, zu kommunizieren.

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

Wir haben beide Cluster eingerichtet und können unsere Anwendung und Istio darauf bereitstellen.

7. Einführung in Istio

Was ist Istio?

Istio ist eine Service Mesh-Steuerungsebene, die Dienste „verbinden, schützen, steuern und beobachten“ soll. Dies geschieht auf unterschiedliche Weise, vor allem aber durch das Einfügen eines Proxy-Containers ( Envoy) als Sidecar in jeden Ihrer bereitgestellten Kubernetes-Pods. Der Proxy-Container steuert die gesamte Netzwerkkommunikation zwischen Mikrodiensten in Kombination mit einem allgemeinen Richtlinien- und Telemetrie-Hub ( Mixer).

a25613cd581825da.png

Diese Richtlinien können unabhängig von Ihren Kubernetes-Bereitstellungen und ‑Diensten angewendet werden. Das bedeutet, dass der Netzwerkbetreiber die Netzwerkaktivitäten beobachten, Netzwerkrichtlinien einschränken, umleiten oder neu schreiben kann, ohne die zugehörigen Anwendungen neu bereitzustellen.

Zu den von Istio unterstützten Funktionen zur Trafficverwaltung gehören:

  • Schutzschalter
  • Prozentuale Trafficaufteilung
  • URL-Umschreibung
  • TLS-Terminierung
  • Systemdiagnosen
  • Load Balancing

In diesem Workshop konzentrieren wir uns auf die prozentuale Aufteilung des Traffics.

Istio-Begriffe, mit denen wir arbeiten werden

VirtualService

Ein VirtualService definiert eine Reihe von Traffic-Routingregeln, die angewendet werden sollen, wenn ein Host adressiert wird.

Gateway

Ein Gateway ist ein Load Balancer, der am Rand des Mesh-Netzwerks ausgeführt wird und eingehende oder ausgehende HTTP/TCP-Verbindungen empfängt. Gateways können Ports, SNI-Konfigurationen usw. angeben.

DestinationRule

Mit einer DestinationRule werden Richtlinien definiert, die auf Traffic angewendet werden, der nach dem Routing für einen Dienst bestimmt ist. Sie geben die Konfiguration für das Load Balancing, die Größe des Verbindungspools aus dem Sidecar und die Einstellungen für die Anomalieerkennung an.

Istio Multicluster

Sie haben vielleicht beim Erstellen unserer beiden Cluster bemerkt, dass unser primary-Cluster 4 Knoten ohne Autoscaling und unser burst-Cluster 1 Knoten mit Autoscaling auf bis zu 5 Knoten hatte.

Dafür gibt es zwei Gründe.

Zuerst möchten wir ein On-Premise-zu-Cloud-Szenario simulieren. In einer On-Premise-Umgebung haben Sie keinen Zugriff auf Autoscaling-Cluster, da Sie eine feste Infrastruktur haben.

Zweitens: Für die Ausführung von Istio ist eine Konfiguration mit vier Knoten (wie oben definiert) die Mindestanforderung. Das wirft die Frage auf: Wenn Istio mindestens vier Knoten erfordert, wie kann unser burst-Cluster Istio mit nur einem Knoten ausführen? Die Antwort ist, dass bei Istio Multicluster ein viel kleinerer Teil der Istio-Dienste installiert wird und mit der Istio-Installation im primären Cluster kommuniziert wird, um die Richtlinienregeln abzurufen und Telemetrieinformationen zu veröffentlichen.

8. Anwendungsarchitektur – Übersicht

Komponenten – Übersicht

Wir stellen eine dreistufige Anwendung mit NodeJS und Redis bereit.

Arbeiter

Die Worker-Anwendung ist in NodeJS geschrieben und überwacht eingehende POST-HTTP-Anfragen. Sie führt einen Hash-Vorgang darauf aus und fügt dem Hash, falls eine Umgebungsvariable namens PREFIX definiert ist, diesen Wert vorangestellt hinzu. Nachdem der Hash berechnet wurde, sendet die Anwendung das Ergebnis über den Kanal „calculation“ an den angegebenen Redis-Server.

Wir verwenden die Umgebungsvariable PREFIX später, um die Multicluster-Funktion zu demonstrieren.

Zur Information: Dies sind die Pakete, die von der Anwendung verwendet werden.

  • body-parser: Ermöglicht das Parsen von HTTP-Anfragen
  • cors: Ermöglicht die Verwendung von Cross-Origin Resource Sharing
  • dotenv: Einfaches Parsen von Umgebungsvariablen
  • express: Einfaches Websitehosting
  • ioredis: Clientbibliothek zur Kommunikation mit Redis-Datenbanken
  • morgan: Bietet ein gut strukturiertes Protokoll

Frontend

Unser Front-End ist ebenfalls eine NodeJS-Anwendung, die eine Webseite mit Express hostet. Es wird eine vom Nutzer eingegebene Häufigkeit verwendet und Anfragen werden mit dieser Rate an unsere worker-Anwendung gesendet. Diese Anwendung abonniert auch Nachrichten auf einem Redis-Kanal namens „calculation“ und zeigt die Ergebnisse auf einer Webseite an.

Die Anwendung verwendet die folgenden Abhängigkeiten.

  • body-parser: Ermöglicht das Parsen von HTTP-Anfragen
  • dotenv: Einfaches Parsen von Umgebungsvariablen
  • express: Einfaches Websitehosting
  • ioredis: Clientbibliothek zur Kommunikation mit Redis-Datenbanken
  • morgan: Bietet gut strukturierte Protokolle
  • request: Ermöglicht das Stellen von HTTP-Anfragen
  • socket.io: Ermöglicht die bidirektionale Kommunikation von der Webseite zum Server

Auf dieser Webseite wird Bootstrap für das Styling verwendet. Sie sieht dann so aus:

e5e3b9cbede4cac4.png

Architekturdiagramm

7ae4bc22a58f80a6.png

Bereitstellungsdiagramm

Wir stellen unsere endgültige Anwendung in den beiden von uns erstellten Clustern bereit. Im Cluster primary sind alle Komponenten (frontend, worker und Redis) bereitgestellt, im Cluster burst jedoch nur die Anwendung worker.

Das folgende Diagramm zeigt die beiden Cluster. Die rot umrandeten Felder sind Kubernetes-Dienste, die blauen Felder sind Kubernetes-Bereitstellungen. Die gelben Felder stehen für unsere Istio-Installation.

561db37c510944bd.png

Beachten Sie, dass im Cluster burst noch immer ein Dienst für Redis bereitgestellt wird, obwohl es im Cluster kein Redis-Deployment gibt. Dieser Dienst muss sich im Cluster befinden, damit Kubernetes DNS die Anfrage auflösen kann. Wenn die Anfrage jedoch tatsächlich gestellt wird, leitet der Istio-Proxy die Anfrage an die Redis-Bereitstellung im primary-Cluster weiter.

Die endgültige Anwendung hat ein zusätzliches Deployment, das im primary-Cluster namens istiowatcher. ausgeführt wird. So können wir den Traffic automatisch auf die burst umleiten, wenn er einen bestimmten Grenzwert überschreitet.

8f6183bdfc3f813c.png

9. Anwendungsbereitstellungsdateien erstellen

Wir müssen eine Reihe von Kubernetes-Manifesten erstellen, um unsere Anwendung bereitzustellen.

Wechseln Sie in das Stammverzeichnis des Projekts und erstellen Sie einen neuen Ordner mit dem Namen kubernetes.

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

frontend.yaml schreiben

Dadurch werden sowohl ein Kubernetes-Deployment als auch ein Dienst zum Zugriff auf unser Frontend-Image erstellt.

Fügen Sie Folgendes in frontend.yaml ein:

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

Wichtige Informationen in der Deployment

  • Wir haben den Port, auf dem die Anwendung ausgeführt wird, als 8080 festgelegt.
  • Wir haben die Adresse für den Worker auf „http://worker-service“ festgelegt und verwenden die integrierte DNS-Funktion von Kubernetes, um den resultierenden Dienst aufzulösen.
  • Wir haben die Adresse für unsere REDIS_URL auf „redis-cache-service:6379“ festgelegt und verwenden die integrierte DNS-Funktion von Kubernetes, um die resultierenden IP-Adressen aufzulösen.
  • Außerdem haben wir liveness- und readiness-Prüfungen für den Container festgelegt, damit Kubernetes weiß, wann der Container einsatzbereit ist.

worker-service.yaml schreiben

Wir schreiben die Kubernetes-Dienstdefinition in einer separaten Datei als die Bereitstellungsdefinition, da wir diesen Dienst in mehreren Clustern wiederverwenden, aber für jeden Cluster eine andere Bereitstellung schreiben.

Fügen Sie Folgendes in worker-service.yaml ein:

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

worker-primary.yaml schreiben

Das ist die Bereitstellung von worker, die wir an den primären Cluster pushen.

Fügen Sie Folgendes in worker-primary.yaml ein:

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"

Beachten Sie, dass wir hier dasselbe Muster verfolgen, indem wir liveness- und readiness-Sonden bereitstellen und die Umgebungsvariablen PORT und REDIS_URL für unsere Anwendung angeben.

Bei dieser Bereitstellung fällt außerdem auf, dass die Umgebungsvariable PREFIX fehlt. Das bedeutet, dass unsere Berechnungsergebnisse Roh-Hashes sind, also ohne Präfix.

Der letzte wichtige Punkt dieser Bereitstellung ist das cluster-type: primary-cluster-Label. Das verwenden wir später, wenn wir das Traffic-Routing in Istio Multicluster behandeln.

redis.yaml schreiben

Die Kommunikation von unserem Worker zurück zum Frontend erfolgt über einen Redis-Kanal. Daher müssen wir eine Redis-Anwendung in unserem Cluster bereitstellen.

Fügen Sie Folgendes in redis.yaml ein:

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: {}

Dies ist eine semi-standardmäßige Bereitstellung einer Redis-Anwendung. Es richtet einen Container auf Grundlage des redis:alpine-Images ein, stellt die entsprechenden Ports bereit und legt sinnvolle Ressourcenlimits fest.

redis-service.yaml schreiben

Wir benötigen einen Kubernetes-Dienst, um mit unserer Redis-Anwendung zu kommunizieren.

Fügen Sie Folgendes in redis-service.yaml ein:

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

Dadurch wird der Dienst redis-cache-service für den Zugriff auf unsere Redis-Bereitstellung bereitgestellt.

10. Anwendung bereitstellen

Nachdem unsere Images in GCR gepusht und unsere Kubernetes-Manifeste geschrieben wurden, ist es an der Zeit, unsere Anwendung bereitzustellen und zu sehen, wie sie funktioniert.

Führen Sie die folgenden Befehle aus, um die Anwendung bereitzustellen.

  1. Prüfen, ob wir uns im richtigen Cluster befinden
kubectx primary
  1. Redis-Cache bereitstellen
kubectl apply -f redis.yaml
  1. Redis-Dienst bereitstellen
kubectl apply -f redis-service.yaml
  1. Frontend bereitstellen
kubectl apply -f frontend.yaml
  1. Worker bereitstellen
kubectl apply -f worker-primary.yaml
  1. Worker-Dienst bereitstellen
kubectl apply -f worker-service.yaml

Wir haben unsere Anwendung in GKE bereitgestellt. Glückwunsch!

Testen

Warten, bis die Pods online sind

kubectl get pods -w

Sobald alle Pods den Status „Running“ (Laufend) haben, drücken Sie Strg + 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

Wie Sie sehen, haben wir unser Frontend nicht über einen Load Balancer freigegeben. Das liegt daran, dass wir später über Istio auf die Anwendung zugreifen. Um zu testen, ob alles funktioniert, verwenden wir kubectl port-forward.. Führen Sie den folgenden Befehl aus, um Port 8080 auf Ihrem lokalen Computer (oder in Cloud Shell) an Port 8080 weiterzuleiten, auf dem die frontend-Bereitstellung ausgeführt wird.

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

Lokaler Betrieb: Öffnen Sie einen Webbrowser und rufen Sie http://localhost:8080 auf.

Wenn Sie die Anwendung in Cloud Shell ausführen:Klicken Sie auf die Schaltfläche „Webvorschau“ und wählen Sie „Vorschau auf Port 8080“ aus.

bdb5dc75f415be11.png

Das Frontend sollte angezeigt werden. Wenn Sie in das Feld „Häufigkeit“ eine Zahl eingeben, sollten Hash-Werte angezeigt werden.

1caafaffab26897a.png

Herzlichen Glückwunsch, alles ist eingerichtet.

Drücken Sie Ctrl+C, um die Portweiterleitung zu beenden.

11. Bereitgestellte Anwendung bereinigen

Wir wenden Istio auf unseren Cluster an und stellen dann unsere Anwendung neu bereit. Bereinigen wir also zuerst unsere aktuelle Anwendung.

Führen Sie die folgenden Befehle aus, um alle gerade erstellten Bereitstellungen und Dienste zu löschen.

  1. redis-cache-service löschen
kubectl delete -f redis-service.yaml
  1. redis löschen
kubectl delete -f redis.yaml
  1. frontend löschen
kubectl delete -f frontend.yaml
  1. worker löschen
kubectl delete -f worker-primary.yaml
  1. worker-service löschen
kubectl delete -f worker-service.yaml

12. Istio im primären Cluster installieren

Istio abrufen

Die Istio-Releases werden auf GitHub gehostet. Mit den folgenden Befehlen wird die Version 1.0.0 von Istio heruntergeladen und entpackt.

  1. Zum Stammverzeichnis des Projekts wechseln
cd ${proj}
  1. Archiv herunterladen
curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
  1. Archiv extrahieren und entfernen
tar xzf istio-1.0.0-linux.tar.gz && rm istio-1.0.0-linux.tar.gz

Istio-Vorlage erstellen

Mit dem folgenden Helm-Befehl wird die Vorlage erstellt, mit der Istio in Ihrem Cluster installiert wird.

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

Dadurch wird im aktuellen Verzeichnis eine Datei mit dem Namen istio-primary.yaml erstellt, die alle Definitionen und Spezifikationen enthält, die zum Bereitstellen und Ausführen von Istio erforderlich sind.

Beachten Sie die beiden --set-Parameter. Dadurch wird dem Istio-System Prometheus- und ServiceGraph-Unterstützung hinzugefügt. Wir verwenden den Prometheus-Dienst später im Lab.

Istio bereitstellen

Um Istio bereitzustellen, müssen wir zuerst einen Namespace mit dem Namen istio-system erstellen, in dem die Istio-Bereitstellungen und ‑Dienste ausgeführt werden können.

kubectl create namespace istio-system

Und schließlich wenden wir die istio-primary.yaml-Datei an, die wir mit Helm erstellt haben.

kubectl apply -f istio-primary.yaml

Standard-Namespace für Label

Istio fügt jedem Ihrer Deployments einen Sidecar-Proxydienst hinzu. Dies geschieht auf Opt-in-Basis. Wir müssen also unseren Namespace default mit istio-injection=enabled labeln, damit Istio den Sidecar automatisch für uns einfügen kann.

kubectl label namespace default istio-injection=enabled

Glückwunsch! Wir haben einen Cluster mit Istio eingerichtet, in dem wir unsere Anwendung bereitstellen können.

13. Anwendung mit Istio-Traffic-Management bereitstellen

Istio-Konfigurationsdateien für die Trafficverwaltung erstellen

Istio funktioniert ähnlich wie Kubernetes, da auch hier YAML-Dateien zur Konfiguration verwendet werden. Daher müssen wir eine Reihe von Dateien erstellen, in denen Istio mitgeteilt wird, wie der Traffic freigegeben und weitergeleitet werden soll.

Erstellen Sie ein Verzeichnis mit dem Namen istio-manifests und wechseln Sie dorthin.

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

frontend-gateway.yaml schreiben

Mit dieser Datei wird unser Kubernetes-Cluster ähnlich wie ein GKE-Load Balancer freigegeben und der gesamte eingehende Traffic wird an unseren Frontend-Dienst weitergeleitet.

Erstellen Sie eine Datei mit dem Namen frontend-gateway.yaml und fügen Sie Folgendes ein:

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

redis-virtualservice.yaml schreiben

Erstellen Sie eine Datei mit dem Namen redis-virtualservice.yaml und fügen Sie Folgendes ein:

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

worker-virtualservice.yaml schreiben

Erstellen Sie eine Datei mit dem Namen worker-virtualservice.yaml und fügen Sie Folgendes ein:

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

Istio-Richtlinien für die Trafficverwaltung bereitstellen

Die Bereitstellung der Istio-Richtlinien erfolgt auf die gleiche Weise wie bei anderen Kubernetes-Ressourcen, wobei kubectl apply

  1. Unser Gateway anwenden
kubectl apply -f frontend-gateway.yaml
  1. Redis-VirtualService anwenden
kubectl apply -f redis-virtualservice.yaml
  1. Worker-VirtualService anwenden
kubectl apply -f worker-virtualservice.yaml

Anwendung bereitstellen

  1. Kehren Sie zum Verzeichnis kubernetes zurück.
cd ${proj}/kubernetes
  1. Redis-Cache bereitstellen
kubectl apply -f redis.yaml
  1. Redis-Dienst bereitstellen
kubectl apply -f redis-service.yaml
  1. Frontend bereitstellen
kubectl apply -f frontend.yaml
  1. Worker bereitstellen
kubectl apply -f worker-primary.yaml
  1. Worker-Dienst bereitstellen
kubectl apply -f worker-service.yaml

Bestätigen

Wir haben unsere Anwendung in einem Cluster mit Istio und Traffic-Management-Richtlinien neu bereitgestellt.

Warten Sie, bis alle Arbeitslasten online sind

Sobald alle online sind, rufen Sie das Ingress-Gateway ab, das wir in frontend-ingressgateway.yaml konfiguriert haben.

$ 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,

Rufen Sie entweder die Adresse <EXTERNAL-IP> auf oder curlen Sie sie. Das Frontend sollte angezeigt werden.

$ curl 35.199.158.10
<!doctype html>
<html>

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

14. Istio in einem Burst-Cluster installieren

Wir haben viel Zeit damit verbracht, unseren primary-Cluster einzurichten und bereitzustellen. Aber wir haben noch einen ganzen anderen Cluster, in dem wir unsere Anwendung bereitstellen müssen.

In diesem Abschnitt müssen wir Konfigurationsvariablen aus beiden Clustern abrufen. Achten Sie daher genau darauf, auf welchen Cluster sich die einzelnen Befehle beziehen.

Istio-Remote-Manifest erstellen

Ähnlich wie bei der Bereitstellung von Istio im primary-Cluster verwenden wir Helm, um die Bereitstellung von Istio remote im burst-Cluster zu modellieren. Bevor wir das tun können, benötigen wir jedoch einige Informationen zu unserem primary-Cluster.

Informationen zum primären Cluster erfassen

Zum primary-Cluster wechseln

kubectx primary

Mit den folgenden Befehlen werden die IP-Adressen verschiedener Pods im primären Cluster abgerufen. Diese werden von Istio Remote verwendet, um mit dem primären Cluster zu kommunizieren.

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}')

Remote-Vorlage erstellen

Jetzt erstellen wir mit helm eine Datei namens istio-remote-burst.yaml, die wir dann im Cluster burst bereitstellen können.

Zum Projektstamm wechseln

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

Istio Remote im Burst-Cluster installieren

Um Istio in unserem burst-Cluster zu installieren, müssen wir dieselben Schritte ausführen wie bei der Installation im primary-Cluster, aber stattdessen die Datei istio-remote-burst.yaml verwenden.

Kubecontext in „burst“ ändern

kubectx burst

Namespace „istio-system“ erstellen

kubectl create ns istio-system

Istio-burst.yaml anwenden

kubectl apply -f istio-remote-burst.yaml

Standard-Namespace für Labels

Wir müssen dem Namespace default wieder ein Label hinzufügen, damit der Proxy automatisch eingefügt werden kann.

kubectl label namespace default istio-injection=enabled

Glückwunsch! Wir haben Istio Remote jetzt auf dem Cluster burst eingerichtet. Zu diesem Zeitpunkt können die Cluster jedoch noch nicht miteinander kommunizieren. Wir müssen eine kubeconfig-Datei für den Cluster burst generieren, die wir im Cluster primary bereitstellen können, um sie miteinander zu verknüpfen.

Kubeconfig für Burst-Cluster erstellen

Zu Burst-Cluster wechseln

kubectx burst

Umgebung einrichten

Wir benötigen einige Informationen zum Cluster, um eine kubeconfig-Datei dafür zu erstellen.

  1. Name des Clusters abrufen
CLUSTER_NAME=$(kubectl config view --minify=true -o "jsonpath={.clusters[].name}")
  1. Clusterservernamen abrufen
SERVER=$(kubectl config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
  1. Name des Secrets für die Zertifizierungsstelle des Dienstkontos istio-multi abrufen
SECRET_NAME=$(kubectl get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
  1. Daten der Zertifizierungsstelle abrufen, die im vorherigen Secret gespeichert sind
CA_DATA=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}")
  1. Im vorherigen Secret gespeichertes Token abrufen
TOKEN=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['token']}" | base64 --decode)

Kubeconfig-Datei erstellen

Nachdem alle Umgebungsvariablen festgelegt sind, müssen wir die kubeconfig-Datei erstellen.

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

Dadurch wird im aktuellen Verzeichnis eine neue Datei namens burst-kubeconfig erstellt, die vom Cluster primary zur Authentifizierung und Verwaltung des Clusters burst verwendet werden kann.

Zurück zum primären Cluster wechseln

kubectx primary

Kubeconfig für „burst“ anwenden, indem Sie ein Secret erstellen und ein Label hinzufügen

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

Labeln Sie das Secret, damit Istio weiß, dass es für die Multicluster-Authentifizierung verwendet werden soll.

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

Glückwunsch! Wir haben beide Cluster authentifiziert und sie kommunizieren über Istio Multicluster miteinander. Clusterübergreifende Bereitstellung unserer Anwendung

15. Clusterübergreifende Anwendung bereitstellen

Deployments erstellen

Wechseln Sie zum Verzeichnis kubernetes.

cd ${proj}/kubernetes

Arbeiterbereitstellung für „Burst“-Cluster erstellen: worker-burst.yaml

Erstellen Sie eine Datei mit dem Namen worker-burst.yaml und fügen Sie Folgendes ein:

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

Beachten Sie, dass diese Datei fast identisch mit der Datei „worker-primary.yaml“ ist, die wir zuvor erstellt haben. Es gibt zwei Hauptunterschiede.

Der erste wichtige Unterschied besteht darin, dass wir die Umgebungsvariable PREFIX mit dem Wert „bursty-“ hinzugefügt haben.

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

Das bedeutet, dass unser Worker im Cluster burst allen gesendeten Hashes das Präfix „bursty-“ vorangestellt. So können wir erkennen, dass unsere Anwendung wirklich clusterübergreifend ist.

Der zweite wichtige Unterschied besteht darin, dass wir das cluster-type-Label dieser Bereitstellung von primary-cluster in burst-cluster geändert haben.

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

Dieses Label verwenden wir später, wenn wir unseren VirtualService aktualisieren.

Istio-Dienste ändern

Derzeit nutzen unsere Istio-Dienste nicht beide Bereitstellungen. 100% des Traffics werden an den „primären“ Cluster weitergeleitet. Das wollen wir ändern.

Wechseln Sie in das Verzeichnis istio-manifests.

cd ${proj}/istio-manifests

worker-virtualservice.yaml bearbeiten, um DestinationRules einzufügen

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

Wie Sie sehen, haben wir unserem VirtualService ein zweites Ziel hinzugefügt. Er verweist weiterhin auf denselben Host (worker-service.default.svc.cluster.local)), aber 50% des Traffics werden an die primary-Untergruppe und die anderen 50% an die burst-Untergruppe weitergeleitet.

Wir haben die Teilmenge primary als Bereitstellungen mit dem Label cluster-type: primary-cluster und die Teilmenge burst als Bereitstellungen mit dem Label cluster-type: burst-cluster definiert.

Dadurch wird der Traffic effektiv zu 50 % auf die beiden Cluster aufgeteilt.

Im Cluster bereitstellen

redis-service.yaml im Burst-Cluster bereitstellen

Zu burst-kubeconfig wechseln

kubectx burst

Zum Projektstamm wechseln

cd ${proj}

Dann bereitstellen

redis-service.yaml im Burst-Cluster bereitstellen

kubectl apply -f kubernetes/redis-service.yaml

worker-burst.yaml im Burst-Cluster bereitstellen

kubectl apply -f kubernetes/worker-burst.yaml

worker-service.yaml im Burst-Cluster bereitstellen

kubectl apply -f kubernetes/worker-service.yaml

Istio VirtualServices anwenden

Zu primary-kubeconfig wechseln

kubectx primary

Dann bereitstellen

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

Funktion prüfen

Rufen Sie dazu Ihren Istio-Ingress-Punkt auf. Sie sehen dann, dass etwa 50% der Hashes mit „burst-“ beginnen.

78fb6e235e9f4a07.png

Das bedeutet, dass wir jetzt clusterübergreifend kommunizieren. Versuchen Sie, die Gewichte für die verschiedenen Dienste zu ändern und die worker-virtualservice.yaml-Datei anzuwenden. Das ist eine gute Möglichkeit, den Traffic zwischen Clustern auszugleichen. Aber was wäre, wenn wir das automatisch tun könnten?

16. Prometheus-Messwerte nutzen

Einführung in Prometheus

Prometheus ist ein Open-Source-Toolkit für die Überwachung und Benachrichtigung von Systemen, das ursprünglich bei SoundCloud entwickelt wurde. Es wird ein mehrdimensionales Datenmodell mit Zeitreihendaten verwaltet, die anhand des Messwertnamens und Schlüssel/Wert-Paaren identifiziert werden.

Hier ist das Prometheus-Architekturdiagramm als Referenz:

601e1155a825e0c2.png

Wenn Istio mit Prometheus bereitgestellt wird, meldet es automatisch verschiedene Messwerte an den Prometheus-Server. Mit diesen Messwerten können wir unsere Cluster im laufenden Betrieb verwalten.

Prometheus-Messwerte

Zuerst müssen wir die Prometheus-Bereitstellung freigeben.

Rufen Sie in GKE den Tab „Arbeitslasten“ auf und gehen Sie zur Arbeitslast „prometheus“.

b4a7a3cd67db05b3.png

Klicken Sie in den Bereitstellungsdetails auf „Aktionen“ -> „Freigeben“.

c04a482e55bdfd41.png

Wählen Sie die Weiterleitung an Port 9090 aus und geben Sie „Load Balancer“ ein.

d5af3ba22a7a6ebb.png

Wählen Sie „Entwickeln“ aus.

Dadurch wird ein Dienst mit einer öffentlich zugänglichen IP-Adresse erstellt, mit der wir unsere Prometheus-Messwerte untersuchen können.

Warten Sie, bis der Endpunkt betriebsbereit ist, und klicken Sie dann auf die IP-Adresse neben „Externe Endpunkte“ b1e40ad90851da29.png.

Sie sollten jetzt die Prometheus-Benutzeroberfläche sehen.

ed273552270337ec.png

Prometheus bietet genügend Messwerte, um ein eigener Workshop zu sein. Fürs Erste konzentrieren wir uns jedoch auf den Messwert istio_requests_total.

Die Ausführung dieser Abfrage gibt eine Menge Daten zurück. Es sind Messwerte zu allen Anfragen, die über das Istio Service Mesh laufen. Das sind eine Menge! Wir ändern den Ausdruck, um nur die Ergebnisse zu sehen, die uns wirklich interessieren:

Anfragen, bei denen der Zieldienst worker-service.default.svc.cluster.local und die Quelle frontend-deployment ist, auf die letzten 15 Sekunden beschränkt

Diese Abfrage sieht so aus:

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

Außerdem haben wir dann einen überschaubaren Datensatz, mit dem wir arbeiten können.

19d551fd5eac3785.png

Aber es ist immer noch etwas dicht. Wir möchten die Anfragen pro Sekunde wissen, nicht alle Anfragen.

Dazu können wir die vordefinierte Funktion rate verwenden.

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

dbb9dc063a18da9b.png

Wir kommen dem Ziel näher, müssen diese Messwerte aber noch etwas weiter in eine logische Gruppe einteilen.

Dazu können wir die Keywords sum und by verwenden, um die Ergebnisse zu gruppieren und zu summieren.

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

Super! Wir können die genauen Messwerte, die wir benötigen, aus Prometheus abrufen.

Die endgültige Prometheus-Abfrage

Mit all dem, was wir gelernt haben, lautet die letzte Abfrage, die wir an Prometheus stellen müssen:

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)

Jetzt können wir den Messwert über die HTTP API abrufen.

Wir können die API mit unserer Abfrage abfragen, indem wir eine HTTP-GET-Anfrage wie diese senden. Ersetzen Sie <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\)

Hier ist ein Beispiel:

{
    "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"
                ]
            }
        ]
    }
}

Jetzt können wir den Messwert aus dem JSON-Objekt extrahieren.

Clean-up

Wir müssen den Dienst löschen, den wir gerade zum Bereitstellen von Prometheus verwendet haben. Klicken Sie in der Google Cloud Console oben im Bereich des gerade erstellten Dienstes auf „Löschen“.

d58cb51b4c922751.png

Vorgehensweise:

Nachdem wir herausgefunden haben, wie und mit welcher Geschwindigkeit sich der Traffic durch den Cluster bewegt, ist unser nächster Schritt, eine kleine Binärdatei zu schreiben, die Prometheus regelmäßig abfragt. Wenn die Anzahl der Anfragen pro Sekunde an worker einen bestimmten Grenzwert überschreitet, werden unterschiedliche Zielgewichte auf unseren virtuellen Worker-Dienst angewendet, um den gesamten Traffic an den burst-Cluster zu senden. Sobald die Anzahl der Anfragen pro Sekunde unter einen niedrigeren Grenzwert fällt, wird der gesamte Traffic an primary zurückgesendet.

17. Clusterübergreifenden Burst erstellen

Einrichtung

Allen Traffic für den Worker-Dienst an den primären Cluster weiterleiten

Wir betrachten den Zustand, in dem der gesamte Traffic für worker-service an den Cluster primary weitergeleitet wird, als „Standardstatus“ unserer Anwendung.

Bearbeiten Sie $proj/istio-manifests/worker-virtualservice.yaml so:

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

Prüfen, ob eine Verbindung zum primary-Cluster besteht

kubectx primary

Istio-Manifests/worker-virtualservice.yaml anwenden

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

Istiowatcher-Daemon schreiben

Wir verwenden Go, um diesen Dienst zu schreiben, da es sich um eine schnelle und portable Sprache handelt. Der Ablauf der Anwendung besteht darin, dass sie gestartet wird und jede Sekunde Prometheus abfragt.

Erstellen Sie in „src“ ein neues Verzeichnis mit dem Namen „istiowatcher“.

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

Wir rufen istioctl von unserem Container aus auf, um die Istio-Steuerungsebene innerhalb des Clusters zu manipulieren.

„istiowatcher.go“ schreiben

Erstellen Sie in diesem Verzeichnis eine Datei mit dem Namen istiowatcher.go und fügen Sie Folgendes ein:

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)
        }
}

Dockerfile schreiben

Erstellen Sie eine neue Datei mit dem Namen Dockerfile und fügen Sie Folgendes ein:

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"]

In diesem mehrstufigen Dockerfile wird in der ersten Phase die Version 1.0.0 von Istio heruntergeladen und extrahiert. In der zweiten Phase wird alles aus unserem Verzeichnis in das Image kopiert. Anschließend wird istioctl aus der Build-Phase in /usr/local/bin kopiert, damit es von unserer Anwendung aufgerufen werden kann. Die Abhängigkeiten werden abgerufen, der Code wird kompiliert und CMD wird auf „istiowatcher“ gesetzt.

burst.yaml schreiben

Diese Datei wird von istiowatcher angewendet, wenn die Anzahl der Anfragen pro Sekunde an worker von frontend über 15 liegt.

Erstellen Sie eine neue Datei mit dem Namen burst.yaml und fügen Sie Folgendes ein:

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

natural.yaml schreiben

Dies ist der „natürliche“ Zustand, zu dem wir zurückkehren, wenn die Anzahl der Anfragen pro Sekunde von frontend auf worker unter 10 fällt. In diesem Zustand wird 100% des Traffics an den Cluster primary weitergeleitet.

Erstellen Sie eine neue Datei mit dem Namen natural.yaml und fügen Sie Folgendes ein:

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

Istiowatcher erstellen und pushen

Führen Sie den folgenden Befehl aus, um das aktuelle Verzeichnis an Google Cloud Build (GCB) zu senden. Dort wird das Image in GCR erstellt und getaggt.

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

Istiowatcher bereitstellen

Wechseln Sie in das Verzeichnis kubernetes.

cd ${proj}/kubernetes/

Bereitstellungsdatei schreiben: istiowatcher.yaml

Erstellen Sie eine Datei mit dem Namen istiowatcher.yaml und fügen Sie den folgenden Code ein. Ersetzen Sie dabei <your-project-id> durch Ihre Projekt-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

Bereitstellen

Prüfen, ob wir im primären Cluster ausgeführt werden

kubectx primary

istiowatcher.yaml im Namespace istio-system bereitstellen

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

Beachten Sie die Anweisungen serviceAccountName und automountServiceAccountToken in der YAML-Datei. So erhalten wir die Anmeldedaten, die zum Ausführen von istioctl innerhalb des Clusters erforderlich sind.

Außerdem müssen wir es im Namespace istio-system bereitstellen, damit wir die Anmeldedaten für die istio-pilot-service-account haben. (Sie ist nicht im Namespace default vorhanden).

Sehen Sie sich an, wie der Traffic automatisch umgeleitet wird.

Jetzt kommt der magische Moment! Gehen wir zum Frontend und erhöhen die Anzahl der Anforderungen pro Sekunde auf 20.

Es dauert einige Sekunden, bis die Leistung erreicht wird. Alle Hashes haben das Präfix „bursty-“.

Das liegt daran, dass wir Prometheus in einem Bereich von 15s erfassen, was zu einer etwas längeren Antwortzeit führt. Wenn wir eine viel engere Bandbreite wünschen, könnten wir unsere Abfrage für Prometheus in 5s. ändern.

18. Nächste Schritte

Clean-up

Wenn Sie ein für diesen Workshop bereitgestelltes temporäres Konto verwenden, müssen Sie nichts bereinigen.

Sie können Ihre Kubernetes-Cluster, die Firewallregel und die Images in GCR löschen.

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

Ausblick