Istio Multicluster zum „Bursten“ von Arbeitslasten zwischen Clustern verwenden

1. Willkommen

Vielen Dank, dass Sie an diesem Codelab von Google zum Thema „Istio Multi Cloud Burst“ teilnehmen.Für dieses Codelab sind praktische Kenntnisse von Kubernetes, Node und Go für Anfänger erforderlich.

Das brauchen Sie

  • Google Cloud Platform-Konto (vorhandenes Konto verwenden oder kostenloses Konto erstellen)
  • Ihr Laptop (installieren Sie „kubectl“, „gcloud“ usw.) oder Google Cloud Shell.

Lerninhalte

  • Kubernetes-Cluster in GKE erstellen
  • Istio mit Helm in einem Kubernetes-Cluster installieren
  • Istio Multicluster mit Helm installieren
  • Webanwendung aus dem Quellcode 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 einem der folgenden Geräte durcharbeiten:

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

Erste Schritte mit der Google Cloud Platform

  1. Wenn Sie kein GCP-Konto haben, erhalten Sie von Ihrem Kursleiter eine Karte mit einem kostenlosen Nutzerkonto.
  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 auf das Projekt, um es auszuwählen: ecc5e8e97bfa6559.png

Cloud Shell bietet eine Befehlszeile in Ihrem Browser, in der die benötigten Tools installiert sind und die automatisch für Ihr Google Cloud-Konto authentifiziert wird. 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 in der Symbolleiste rechts oben auf „Cloud Shell aktivieren“:

68a17b036ce24ccb.png

Tools zu Cloud Shell hinzufügen

  1. Installieren Sie kubectx****: Laden Sie die Bash-Scripts hier in ein Verzeichnis in $PATH herunter.
  2. Installieren Sie helm**** gemäß dieser Anleitung.

Alternativ können Sie diese Befehle ausführen, um beide in ~/.bin zu installieren und zu Ihrem $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}

Hier sind einige Kurztipps, die die Verwendung von Cloud Shell erleichtern können:

1. Shell in einem neuen Fenster abtrennen:

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 starten:Wenn Sie mehr als einen Terminal-Prompt benötigen.

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

Strg + + unter Linux/Windows, ⌘ + + unter macOS

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

  1. gcloud: installieren (in Cloud Shell vorinstalliert) Folgen Sie der Anleitung, um gcloud auf Ihrer Plattform zu installieren. Damit erstellen wir einen Kubernetes-Cluster.
  2. kubectl: installieren(in Cloud Shell vorinstalliert) Führen Sie den folgenden Befehl aus, um die Installation durchzuführen:
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 zuvor erstellte Projekt (siehe oben) als Standardprojekt aus. Sie können die Konfiguration einer Compute-Zone überspringen:

gcloud init
  1. curl: ist auf den meisten Linux-/macOS-Systemen vorinstalliert. Wahrscheinlich haben Sie es bereits. Suchen Sie andernfalls im Internet nach einer Anleitung zur Installation.
  2. Installieren Sie kubectx****: Laden Sie die Bash-Skripts hier in ein Verzeichnis in $PATH herunter.
  3. Installieren Sie helm**** gemäß dieser Anleitung.

3. GCP-Projekt einrichten

Aktivieren Sie die APIs für 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 intensiv mit unserem Google Cloud-Projekt arbeiten. Legen wir eine Umgebungsvariable für den schnellen Zugriff fest.

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

In diesem Workshop erstellen wir einige 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 Anfangsknoten
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 die Erstellung des Clusters in der Cloud Console verfolgen.)

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

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

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

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

Berechtigungen festlegen:

Für die Bereitstellung von Istio müssen Sie Clusteradministrator sein. Mit diesem Befehl wird die mit Ihrem Google Cloud-Konto verknüpfte E-Mail-Adresse 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 Startknoten
  • 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 die Erstellung des Clusters in der Cloud Console verfolgen.)

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:

Für die Bereitstellung von Istio Remote benötigen Sie die Rolle „Cluster Admin“. Mit diesem Befehl wird die mit Ihrem Google Cloud-Konto verknüpfte E-Mail-Adresse 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 in Google Cloud eine Firewallregel zu erstellen, die die Kommunikation zwischen den Clustern ermöglicht.

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 darauf abzielt, „Dienste zu verbinden, zu sichern, zu steuern und zu beobachten“. Dies geschieht auf verschiedene Weise, hauptsächlich aber durch das Sidecar-ing eines Proxy-Containers ( Envoy) in jeden Ihrer bereitgestellten Kubernetes-Pods. Der Proxy-Container steuert die gesamte Netzwerkkommunikation zwischen Mikrodiensten in Verbindung mit einem universellen 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ät beobachten, Netzwerkrichtlinien einschränken, umleiten oder neu schreiben kann, ohne die zugehörigen Anwendungen neu bereitzustellen.

Einige der von Istio unterstützten Funktionen zur Trafficverwaltung sind:

  • Schutzschalter
  • Prozentuale Traffic-Aufteilung
  • URL-Umschreibung
  • TLS-Terminierung
  • Systemdiagnosen
  • Load Balancing

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

Istio-Begriffe, die wir verwenden

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. In Gateways können Ports, SNI-Konfigurationen usw. angegeben werden.

DestinationRule

Eine DestinationRule definiert Richtlinien, die auf Traffic angewendet werden, der für einen Dienst bestimmt ist, nachdem das Routing erfolgt ist. Sie geben die Konfiguration für das Load-Balancing, die Größe des Verbindungspools vom Sidecar und die Einstellungen für die Ausreißererkennung an.

Istio-Multi-Cluster

Als wir unsere beiden Cluster erstellt haben, hatte der primary-Cluster vier Knoten ohne Autoscaling und der burst-Cluster einen Knoten mit Autoscaling auf bis zu fünf Knoten.

Dafür gibt es zwei Gründe.

Zuerst möchten wir ein Szenario simulieren, in dem Daten von einem lokalen System in die Cloud migriert werden. In einer On-Premise-Umgebung haben Sie keinen Zugriff auf Autoscaling-Cluster, da Sie eine feste Infrastruktur haben.

Zweitens ist eine Einrichtung mit vier Knoten (wie oben definiert) die Mindestanforderung für die Ausführung von Istio. Das wirft die Frage auf: Wenn für Istio mindestens 4 Knoten erforderlich sind, wie kann unser burst-Cluster Istio mit nur einem Knoten ausführen? Bei Istio Multicluster wird eine viel kleinere Gruppe von Istio-Diensten installiert. Die Richtlinienregeln werden von der Istio-Installation im primären Cluster abgerufen und Telemetrieinformationen werden dort veröffentlicht.

8. Übersicht über die Anwendungsarchitektur

Komponenten – Übersicht

Wir stellen eine dreistufige Anwendung mit NodeJS und Redis bereit.

Worker

Die Worker-Anwendung ist in NodeJS geschrieben und wartet auf eingehende POST-HTTP-Anfragen. Sie führt eine Hash-Operation für diese Anfragen aus und stellt dem Hash den Wert voran, wenn eine Umgebungsvariable mit dem Namen PREFIX definiert ist. Sobald der Hash berechnet wurde, sendet die Anwendung das Ergebnis über den Channel „calculation“ auf dem angegebenen Redis-Server.

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

Zur Information: Das 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: Umgebungsvariablen einfach parsen
  • express: Einfaches Websitehosting
  • ioredis:-Clientbibliothek für die Kommunikation mit Redis-Datenbanken
  • morgan: Bietet ein gut strukturiertes Log

Frontend

Unser Frontend ist ebenfalls eine NodeJS-Anwendung, die eine Webseite mit Express hostet. Es nimmt eine vom Nutzer eingegebene Häufigkeit entgegen und sendet Anfragen mit dieser Rate an unsere worker-Anwendung. Diese Anwendung abonniert auch Nachrichten in einem Redis-Kanal mit dem Namen „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: Umgebungsvariablen einfach parsen
  • express: Einfaches Websitehosting
  • ioredis:-Clientbibliothek für die Kommunikation mit Redis-Datenbanken
  • morgan: Bietet gut strukturierte Logs
  • 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. Wenn sie ausgeführt wird, sieht sie so aus:

e5e3b9cbede4cac4.png

Architekturdiagramm

7ae4bc22a58f80a6.png

Bereitstellungsdiagramm

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

Das folgende Diagramm veranschaulicht die beiden Cluster. Die rot umrandeten Kästen sind Kubernetes-Dienste, die blauen Kubernetes-Deployments. Die gelben Kästen stehen für die Installation von Istio.

561db37c510944bd.png

Beachten Sie, dass im burst-Cluster weiterhin ein Dienst für Redis bereitgestellt ist, obwohl es kein Deployment für Redis im Cluster gibt. Wir benötigen diesen Dienst im Cluster, 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 mit dem Namen istiowatcher. ausgeführt wird. Dadurch können wir Traffic dynamisch an burst weiterleiten, wenn unser Traffic einen bestimmten Schwellenwert überschreitet.

8f6183bdfc3f813c.png

9. Bereitstellungsdateien für Anwendungen 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 für den 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 Hinweise zur Deployment

  • Wir haben den Port, auf dem die Anwendung ausgeführt wird, auf 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.
  • Wir haben auch liveness- und readiness-Tests für den Container festgelegt, um Kubernetes darüber zu informieren, wann der Container ausgeführt wird.

worker-service.yaml schreiben

Wir schreiben die Kubernetes-Dienstdefinition in eine separate Datei als die Deployment-Definition, da wir diesen Dienst in mehreren Clustern wiederverwenden, aber für jeden Cluster ein anderes Deployment 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

Dies ist die Bereitstellung von worker, die wir in den primären Cluster übertragen.

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"

Wir folgen dabei demselben Muster, indem wir liveness- und readiness-Probes bereitstellen und die Umgebungsvariablen PORT und REDIS_URL für unsere Anwendung angeben.

Ein weiterer wichtiger Punkt bei dieser Bereitstellung ist das Fehlen der Umgebungsvariable PREFIX. Das bedeutet, dass unsere Berechnungsergebnisse Roh-Hashes sind (ohne Präfix).

Der letzte wichtige Punkt dieser Bereitstellung ist das Label cluster-type: primary-cluster. Wir werden das später beim Traffic-Routing in Istio-Multicluster verwenden.

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 halbwegs standardmäßige Bereitstellung einer Redis-Anwendung. Es wird ein Container basierend auf dem redis:alpine-Image erstellt, die entsprechenden Ports werden freigegeben und sinnvolle Ressourcenlimits werden festgelegt.

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 ein Dienst mit dem Namen redis-cache-service bereitgestellt, über den auf unsere Redis-Bereitstellung zugegriffen werden kann.

10. Anwendung bereitstellen

Nachdem unsere Images in GCR übertragen 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!

Test

Warten Sie, bis die Pods online sind.

kubectl get pods -w

Wenn alle Pods den Status „Running“ (Wird ausgeführt) 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

Sie werden feststellen, dass wir unser Frontend nicht über einen LoadBalancer bereitgestellt haben. Das liegt daran, dass wir später über Istio auf die Anwendung zugreifen werden. 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 der frontend-Bereitstellung weiterzuleiten.

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

Wenn Sie die App lokal ausführen: Öffnen Sie einen Webbrowser und rufen Sie http://localhost:8080 auf.

Wenn Sie Cloud Shell verwenden:Klicken Sie auf die Schaltfläche „Webvorschau“ und wählen Sie „Vorschau auf Port 8080“ aus.

bdb5dc75f415be11.png

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

1caafaffab26897a.png

Herzlichen Glückwunsch! Alles läuft.

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

11. Bereitgestellte Anwendung bereinigen

Wir wenden Istio auf unseren Cluster an und stellen unsere Anwendung dann noch einmal bereit. Deshalb bereinigen wir zuerst unsere aktuelle Anwendung.

Führen Sie die folgenden Befehle aus, um alle gerade erstellten Deployments 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 auf dem primären Cluster installieren

Istio abrufen

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

  1. Wechseln Sie zum Stammverzeichnis Ihres Projekts.
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

Wenn Sie den folgenden Helm-Befehl ausführen, wird die Vorlage für die Installation von Istio in Ihrem Cluster erstellt.

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. Diese Add-ons fügen dem Istio-System Unterstützung für Prometheus und ServiceGraph hinzu. Wir werden den Prometheus-Dienst später im Lab verwenden.

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

Wenden Sie schließlich die mit Helm erstellte istio-primary.yaml-Datei an.

kubectl apply -f istio-primary.yaml

Standard-Namespace für Labels

Istio funktioniert, indem ein Sidecar-Proxy-Dienst in jede Ihrer Bereitstellungen eingefügt wird. Dies erfolgt auf Opt-in-Basis. Daher müssen wir unserem default-Namespace das Label istio-injection=enabled hinzufügen, damit Istio den Sidecar automatisch einfügen kann.

kubectl label namespace default istio-injection=enabled

Glückwunsch! Wir haben einen Cluster eingerichtet, auf dem Istio ausgeführt wird. Jetzt können wir unsere Anwendung bereitstellen.

13. Anwendung mit Istio-Trafficverwaltung bereitstellen

Konfigurationsdateien für die Istio-Trafficverwaltung erstellen

Istio funktioniert ähnlich wie Kubernetes, da es YAML-Dateien für die Konfiguration verwendet. Dazu müssen wir eine Reihe von Dateien erstellen, die Istio mitteilen, wie unser Traffic bereitgestellt und weitergeleitet werden soll.

Erstellen Sie ein Verzeichnis mit dem Namen istio-manifests und rufen Sie es auf.

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

frontend-gateway.yaml schreiben

Mit dieser Datei wird unser Kubernetes-Cluster ähnlich wie ein GKE-LoadBalancer verfügbar gemacht 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-Trafficverwaltungsrichtlinien bereitstellen

Die Bereitstellung der Istio-Richtlinien erfolgt auf dieselbe Weise wie bei anderen Kubernetes-Ressourcen mit kubectl apply.

  1. Unser Gateway verwenden
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. Wechseln Sie zurück in das Verzeichnis kubernetes.
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 jetzt auf einem Cluster mit Istio- und Traffic-Management-Richtlinien neu bereitgestellt.

Warten wir, bis alle unsere 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 führen Sie „curl“ aus. Das Frontend sollte angezeigt werden.

$ curl 35.199.158.10
<!doctype html>
<html>

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

14. Istio auf dem „Burst“-Cluster installieren

Wir haben viel Zeit mit der Einrichtung und Bereitstellung in unserem primary-Cluster verbracht, aber wir haben noch einen weiteren Cluster, in dem wir die Bereitstellung vornehmen können.

In diesem Abschnitt müssen wir Konfigurationsvariablen aus beiden Clustern abrufen. Achten Sie daher genau darauf, auf welchen Cluster wir für jeden Befehl verweisen.

Istio-Remotemanifest erstellen

Ähnlich wie bei der Bereitstellung von Istio für den primary-Cluster verwenden wir Helm, um die Bereitstellung von Istio-Remote für den burst-Cluster zu templatisieren. Bevor wir das tun können, benötigen wir jedoch einige Informationen zu unserem primary-Cluster.

Informationen zum primären Cluster erfassen

Zum Cluster primary 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 verwenden wir helm, um eine Datei mit dem Namen istio-remote-burst.yaml zu erstellen, die wir dann im burst-Cluster bereitstellen können.

Zum Projektstammverzeichnis 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 auf dem Burst-Cluster installieren

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

Kubecontext auf 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

Auch hier müssen wir dem default-Namespace ein Label hinzufügen, damit der Proxy automatisch eingefügt werden kann.

kubectl label namespace default istio-injection=enabled

Glückwunsch! An diesem Punkt haben wir Istio Remote auf dem burst-Cluster 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 die beiden Cluster zu verknüpfen.

kubeconfig für Burst-Cluster erstellen

Zum 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. Name des Clusterservers abrufen
SERVER=$(kubectl config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
  1. Rufen Sie den Namen des Secrets für die Zertifizierungsstelle des Dienstkontos istio-multi ab.
SECRET_NAME=$(kubectl get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
  1. CA-Daten aus dem vorherigen Secret abrufen
CA_DATA=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}")
  1. Das im vorherigen Secret gespeicherte Token abrufen
TOKEN=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['token']}" | base64 --decode)

kubeconfig-Datei erstellen

Nachdem alle Umgebungsvariablen festgelegt wurden, 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 mit dem Namen burst-kubeconfig erstellt, die vom primary-Cluster zur Authentifizierung und Verwaltung des burst-Clusters verwendet werden kann.

Zurück zum primären Cluster wechseln

kubectx primary

kubeconfig für „burst“ anwenden, indem Sie ein Secret erstellen und es labeln

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

Secret so labeln, dass Istio es für die Multicluster-Authentifizierung verwendet

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

Glückwunsch! Beide Cluster sind authentifiziert und kommunizieren über Istio Multicluster miteinander. Stellen wir unsere Anwendung clusterübergreifend bereit.

15. Clusterübergreifende Anwendung bereitstellen

Deployments erstellen

Wechseln Sie in das Verzeichnis kubernetes.

cd ${proj}/kubernetes

Worker-Bereitstellung für den 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-"

Diese Datei ist fast identisch mit der Datei „worker-primary.yaml“, 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-“ voranstellt. So können wir feststellen, ob unsere Anwendung wirklich clusterübergreifend ist.

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

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

Wir verwenden dieses Label später, wenn wir unser VirtualService aktualisieren.

Istio-Dienste ändern

Derzeit nutzen unsere Istio-Dienste nicht beide Bereitstellungen. 100% unseres 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

Wir haben unserem VirtualService ein zweites Ziel hinzugefügt. Es wird weiterhin auf denselben Host verwiesen (worker-service.default.svc.cluster.local)), aber 50% des Traffics werden an die Teilmenge primary und die anderen 50% an die Teilmenge burst 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 unser Traffic effektiv zu gleichen Teilen auf die beiden Cluster aufgeteilt.

Im Cluster bereitstellen

redis-service.yaml im Burst-Cluster bereitstellen

Zur burst-kubeconfig wechseln

kubectx burst

Wechseln Sie zum Stammverzeichnis des Projekts.

cd ${proj}

Stellen Sie sie dann bereit.

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

Zur primary-kubeconfig wechseln

kubectx primary

Dann bereitstellen

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

Funktionsweise prüfen

Wenn Sie prüfen möchten, ob es funktioniert, rufen Sie Ihren Istio-Ingress-Punkt auf. Etwa 50% der Hashes sollten mit „burst-“ beginnen.

78fb6e235e9f4a07.png

Das bedeutet, dass wir erfolgreich clusterübergreifend kommunizieren. Ändern Sie die Gewichtungen der verschiedenen Dienste und wenden Sie die Datei worker-virtualservice.yaml an. Das ist eine gute Möglichkeit, den Traffic zwischen Clustern auszugleichen. Aber was, wenn wir das automatisch erledigen könnten?

16. Prometheus-Messwerte nutzen

Einführung in Prometheus

Prometheus ist ein Open-Source-Toolkit für Systemmonitoring und Benachrichtigungen, das ursprünglich bei SoundCloud entwickelt wurde. Es wird ein mehrdimensionales Datenmodell mit Zeitreihendaten verwaltet, die durch Messwertname und Schlüssel/Wert-Paare identifiziert werden.

Zur Referenz finden Sie hier das Prometheus-Architekturdiagramm:

601e1155a825e0c2.png

Wenn Istio mit Prometheus bereitgestellt wird, werden automatisch verschiedene Messwerte an den Prometheus-Server gemeldet. Mithilfe dieser Messwerte können wir unsere Cluster im laufenden Betrieb verwalten.

Prometheus-Messwerte ansehen

Zuerst müssen wir die Prometheus-Bereitstellung freigeben.

Rufen Sie in GKE den Tab „Arbeitslasten“ auf und führen Sie einen Drilldown zur Arbeitslast „prometheus“ durch.

b4a7a3cd67db05b3.png

Rufen Sie die Details der Bereitstellung auf und wählen Sie „Aktionen“ –> „Freigeben“ aus.

c04a482e55bdfd41.png

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

d5af3ba22a7a6ebb.png

Wählen Sie „Expose“ aus.

Dadurch wird ein Dienst mit einer öffentlich zugänglichen IP-Adresse erstellt, über die wir unsere Prometheus-Messwerte aufrufen können.

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

Sie sollten jetzt die Prometheus-Benutzeroberfläche sehen.

ed273552270337ec.png

Prometheus bietet genügend Messwerte, um einen eigenen Workshop zu füllen. Wir beginnen jedoch erst einmal mit der Messung der istio_requests_total-Messwerte.

Wenn Sie diese Abfrage ausführen, werden viele Daten zurückgegeben. Es handelt sich um Messwerte für alle Anfragen, die das Istio-Service-Mesh durchlaufen, und das sind viele. Wir ändern den Ausdruck, um nur die Daten zu filtern, die uns wirklich interessieren:

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

Die Abfrage sieht so aus:

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

Dadurch erhalten wir einen viel übersichtlicheren Datensatz, mit dem wir arbeiten können.

19d551fd5eac3785.png

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

Dazu können wir die integrierte 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 sind auf dem richtigen Weg, aber wir müssen diese Messwerte noch etwas weiter in einer logischen Gruppe zusammenfassen.

Dazu können wir die Keywords sum und by verwenden, um unsere 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.

Unsere endgültige Prometheus-Abfrage

Mit allem, was wir gelernt haben, lautet die endgültige 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 die HTTP API verwenden, um den Messwert abzurufen.

Wir können ihre API mit unserer Abfrage abfragen, indem wir eine HTTP-GET-Anfrage wie folgt stellen. 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 eine Beispielantwort:

{
    "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 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 auf den Dienst, den wir gerade erstellt haben, und dann auf „Löschen“.

d58cb51b4c922751.png

Vorgehensweise:

Nachdem wir herausgefunden haben, wie und mit welcher Rate Traffic durch den Cluster fließt, besteht der nächste Schritt darin, ein kleines Binärprogramm zu schreiben, das regelmäßig Prometheus abfragt. Wenn die Anfragen pro Sekunde an worker einen bestimmten Schwellenwert überschreiten, werden unterschiedliche Zielgewichte auf unseren Worker-Virtual-Service angewendet, um den gesamten Traffic an den Cluster burst zu senden. Sobald die Anfragen pro Sekunde einen unteren Grenzwert unterschreiten, leiten Sie den gesamten Traffic zurück zu primary.

17. Cross-Cluster-Burst erstellen

Einrichtung

Gesamten Traffic für den Worker-Service an den primären Cluster weiterleiten

Wir betrachten es als „Standardstatus“ unserer Anwendung, wenn der gesamte Traffic, der für worker-service bestimmt ist, an den Cluster primary weitergeleitet wird.

Bearbeiten Sie $proj/istio-manifests/worker-virtualservice.yaml so, dass sie wie folgt aussieht:

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 Sie, ob Sie mit dem Cluster primary verbunden sind.

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 die Sprache schnell und portabel ist. Der allgemeine Ablauf der Anwendung besteht darin, dass sie gestartet wird und jede Sekunde Prometheus abfragt.

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

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

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

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. Außerdem werden die Abhängigkeiten abgerufen, der Code wird kompiliert und CMD wird auf „istiowatcher“ gesetzt.

burst.yaml schreiben

Die Datei istiowatcher wird angewendet, wenn die Anfragen pro Sekunde an worker von frontend 15 überschreiten.

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

Das ist der „natürliche“ Zustand, in den wir zurückkehren, wenn die Anfragen pro Sekunde von frontend an worker unter 10 sinken. 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 per Push übertragen

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

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

istio-watcher 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 Folgendes ein (ersetzen Sie <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

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

Wichtig sind die Direktiven serviceAccountName und automountServiceAccountToken im YAML-Code. Dadurch erhalten wir die Anmeldedaten, die zum Ausführen von istioctl im Cluster erforderlich sind.

Wir müssen diese auch im Namespace istio-system bereitstellen, damit wir die Anmeldedaten für istio-pilot-service-account haben. Sie ist nicht im Namespace default vorhanden.

Sehen Sie zu, wie der Traffic automatisch wechselt.

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

Es dauert einige Sekunden, bis die Hash-Rate ansteigt, aber alle unsere Hashes haben das Präfix „bursty-“.

Das liegt daran, dass wir Prometheus über den Bereich 15s hinweg abtasten, was zu einer leichten Verzögerung der Reaktionszeit führt. Wenn wir ein viel engeres Band haben möchten, können wir unsere Anfrage an Prometheus in 5s. ändern.

18. Nächste Schritte

Clean-up

Wenn Sie ein temporäres Konto verwenden, das für diesen Workshop bereitgestellt wurde, müssen Sie keine Bereinigung durchführen.

Sie können Ihre Kubernetes-Cluster, die Firewallregel und die Bilder 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