Utilizzo di Istio Multicluster per "esplodere" i carichi di lavoro tra i cluster

1. Ti diamo il benvenuto

Grazie per aver partecipato al codelab Istio Multi Cloud Burst di Google.Questo codelab richiede esperienza pratica di livello principiante con Kubernetes, Node e Go.

Cosa ti serve

  • Account Google Cloud Platform (utilizza un account esistente o ti forniremo account senza costi)
  • Il tuo laptop (installa "kubectl", "gcloud" e così via) oppure puoi utilizzare Google Cloud Shell.

Cosa imparerai

  • Come creare un cluster Kubernetes su GKE
  • Come installare Istio su un cluster Kubernetes con Helm
  • Come installare Istio multicluster con Helm
  • Deployment di un'applicazione web dal codice sorgente a Kubernetes
  • Scrittura e applicazione di regole di routing del traffico a Istio
  • Metriche di Prometheus
  • Crea ed esegui il push di immagini container all'interno di un cluster Kubernetes

2. Preparazione

Puoi seguire questo codelab su:

  • Google Cloud Shell (consigliato): shell nel browser, con strumenti installati
  • il tuo laptop (segui le istruzioni riportate di seguito)

Inizia con Google Cloud Platform

  1. Se non hai un account GCP, ritira la scheda dell'account utente senza costi dall'istruttore.
  2. Vai alla console Google Cloud e fai clic su "Seleziona un progetto": 5c2d9bf74c78f7e4.png
  3. Prendi nota dell'ID del progetto e fai clic sul progetto per sceglierlo: ecc5e8e97bfa6559.png

Cloud Shell fornisce una shell a riga di comando all'interno del browser con gli strumenti necessari installati e autenticati automaticamente nel tuo account Google Cloud Platform. Se non vuoi eseguire questo esercizio su Cloud Shell, vai alla sezione successiva.

Vai alla console Cloud e fai clic su "Attiva Cloud Shell" nella barra degli strumenti in alto a destra:

68a17b036ce24ccb.png

Aggiungere strumenti a Cloud Shell

  1. Installa kubectx****: scarica gli script bash da qui in una posizione in $PATH.
  2. Installa helm**** seguendo queste istruzioni.

In alternativa, esegui questi comandi per installare entrambi in ~/.bin e aggiungerli a $PATH:

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

Ecco alcuni suggerimenti rapidi che possono semplificare l'utilizzo di Cloud Shell:

1. Stacca la shell in una nuova finestra:

2. Utilizzo dell'editor di file: fai clic sull'icona a forma di matita in alto a destra per avviare un editor di file nel browser. Ti sarà utile perché copieremo gli snippet di codice nei file.

3. Avvia nuove schede: se hai bisogno di più prompt del terminale.

4. Ingrandisci il testo: le dimensioni predefinite del carattere in Cloud Shell potrebbero essere troppo piccole per essere lette.

Ctrl + - su Linux/Windows⌘ + - su macOS.

Se preferisci utilizzare il tuo ambiente workstation anziché Cloud Shell, configura i seguenti strumenti:

  1. Installa gcloud: (preinstallato su Cloud Shell). Segui le istruzioni per installare gcloud sulla tua piattaforma. Lo utilizzeremo per creare un cluster Kubernetes.
  2. Installa kubectl:(preinstallato su Cloud Shell). Esegui questo comando per installare:
gcloud components install kubectl

Esegui questo comando per autenticare gcloud. Ti verrà chiesto di accedere con il tuo Account Google. Poi scegli il progetto pre-creato (mostrato sopra) come progetto predefinito. (Puoi saltare la configurazione di una zona di computing):

gcloud init
  1. Installazione di curl: preinstallato sulla maggior parte dei sistemi Linux/macOS. Probabilmente ce l'hai già. In caso contrario, cerca su internet come installarlo.
  2. Installa kubectx****: scaricando gli script bash da qui in una posizione in $PATH
  3. Installa helm**** seguendo queste istruzioni.

3. Configura progetto Google Cloud

Abilita le API GKE (Google Kubernetes Engine), GCR (Google Container Registry) e GCB (Google Cloud Build) nel tuo progetto:

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

Configura le variabili di ambiente

Durante la configurazione lavoreremo molto con il nostro progetto Google Cloud, quindi impostiamo una variabile di ambiente per riferimento rapido

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

Durante questo workshop creeremo alcuni file di codice e di configurazione, quindi creiamo una directory di progetto e passiamo a questa directory

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

4. Crea il cluster Kubernetes "principale"

Puoi creare facilmente un cluster Kubernetes gestito con Google Kubernetes Engine (GKE).

Il comando seguente creerà un cluster Kubernetes:

  • denominata "primary",
  • nella zona us-west1-a,
  • L'ultima versione di Kubernetes disponibile.
  • con 4 nodi iniziali
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

L'operazione potrebbe richiedere circa 5 minuti. Puoi osservare la creazione del cluster nella console Google Cloud.

Dopo la creazione del cluster Kubernetes, gcloud configura kubectl con le credenziali che puntano al cluster.

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

Ora dovresti essere in grado di utilizzare kubectl con il nuovo cluster.

Esegui questo comando per elencare i nodi Kubernetes del tuo cluster (dovrebbero mostrare lo stato "Pronto"):

kubectl get nodes

Modificare i nomi di Kubeconfig per facilità d'uso

Passeremo spesso da un contesto all'altro, quindi avere un alias breve per i nostri cluster è utile.

Questo comando rinomina la voce kubeconfig appena creata in primary

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

Imposta le autorizzazioni:

Per eseguire il deployment di Istio devi essere un amministratore del cluster. Questo comando imposterà l'email associata al tuo account Google Cloud come amministratore del cluster

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

5. Crea cluster "burst"

Il comando seguente creerà un cluster Kubernetes:

  • denominato "burst",
  • nella zona us-west1-a,
  • L'ultima versione di Kubernetes disponibile.
  • Con 1 nodo iniziale
  • Scalabilità automatica abilitata fino a 5 nodi
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

L'operazione potrebbe richiedere circa 5 minuti. Puoi osservare la creazione del cluster nella console Google Cloud.

Dopo la creazione del cluster Kubernetes, gcloud configura kubectl con le credenziali che puntano al cluster.

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

Ora dovresti essere in grado di utilizzare kubectl con il nuovo cluster.

Esegui questo comando per elencare i nodi Kubernetes del tuo cluster (dovrebbero mostrare lo stato "Pronto"):

kubectl get nodes

Modificare i nomi di Kubeconfig per facilità d'uso

Questo comando modificherà la voce kubeconfig appena creata in burst

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

Imposta le autorizzazioni:

Per eseguire il deployment di Istio Remote devi essere un amministratore del cluster. Questo comando imposterà l'email associata al tuo account Google Cloud come amministratore del cluster

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

6. Applica regole firewall

Affinché i due cluster possano comunicare tra loro, dobbiamo creare una regola firewall.

Esegui questi comandi per creare una regola firewall in Google Cloud che consenta ai nostri cluster di comunicare

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

Entrambi i cluster sono configurati e pronti per il deployment dell'applicazione e di Istio.

7. Introduzione a Istio

Che cos'è Istio?

Istio è un piano di controllo del service mesh che mira a "connettere, proteggere, controllare e osservare i servizi". Lo fa in vari modi, ma principalmente inserendo un container proxy ( Envoy) in ogni pod Kubernetes di cui è stato eseguito il deployment. Il container proxy controlla tutte le comunicazioni di rete tra i microservizi in tandem con un hub di criteri e telemetria generico ( Mixer).

a25613cd581825da.png

Queste norme possono essere applicate indipendentemente dai deployment e dai servizi Kubernetes, il che significa che l'operatore di rete può osservare l'attività di rete, limitare, reindirizzare o riscrivere le norme di rete senza eseguire nuovamente il deployment delle applicazioni associate.

Alcune delle funzionalità di gestione del traffico supportate da Istio sono:

  • Interruttori di sicurezza
  • Suddivisione del traffico basata sulla percentuale
  • Riscrittura URL
  • terminazione TLS
  • Controlli di integrità
  • Bilanciamento del carico

Ai fini di questo workshop, ci concentreremo sulla suddivisione del traffico in base alla percentuale.

Termini di Istio con cui lavoreremo

VirtualService

Un VirtualService definisce un insieme di regole di routing del traffico da applicare quando viene indirizzato un host.

Gateway

Un gateway è un bilanciatore del carico che opera al limite del mesh e riceve connessioni HTTP/TCP in entrata o in uscita. I gateway possono specificare porte, configurazioni SNI e così via.

DestinationRule

Una DestinationRule definisce le policy che si applicano al traffico destinato a un servizio dopo l'instradamento. Specificano la configurazione per il bilanciamento del carico, le dimensioni del pool di connessioni dal sidecar e le impostazioni di rilevamento degli outlier.

Istio multicluster

Potresti aver notato che, quando abbiamo creato i due cluster, il cluster primary era composto da 4 nodi senza scalabilità automatica, mentre il cluster burst era composto da 1 nodo con scalabilità automatica fino a 5 nodi.

Esistono due motivi per questa configurazione.

Innanzitutto, vogliamo simulare uno scenario "on-premise" nel cloud. In un ambiente on-premise non hai accesso ai cluster di scalabilità automatica perché hai un'infrastruttura fissa.

In secondo luogo, una configurazione a 4 nodi (come definita sopra) è il requisito minimo per eseguire Istio. Sorge quindi la domanda: se Istio richiede un minimo di 4 nodi, come può il nostro cluster burst eseguire Istio con 1 nodo? La risposta è che Istio Multicluster installa un insieme molto più piccolo di servizi Istio e comunica con l'installazione di Istio nel cluster principale per recuperare le regole dei criteri e pubblicare le informazioni di telemetria.

8. Panoramica dell'architettura dell'applicazione

Panoramica dei componenti

Eseguiremo il deployment di un'applicazione a tre livelli utilizzando NodeJS e Redis.

Worker

L'applicazione di lavoro è scritta in NodeJS e ascolterà le richieste HTTP POST in entrata, eseguirà un'operazione di hashing e, se è definita una variabile di ambiente denominata PREFIX, anteporrà l'hash a questo valore. Una volta calcolato l'hash, l'applicazione invia il risultato sul canale "calculation" sul server Redis specificato.

Utilizzeremo la variabile di ambiente PREFIX in un secondo momento per dimostrare la funzionalità multicluster.

Per riferimento, questi sono i pacchetti utilizzati dall'applicazione.

  • body-parser: Ci consente di analizzare le nostre richieste HTTP
  • cors: Consente l'utilizzo della condivisione delle risorse tra origini (CORS)
  • dotenv: Analisi semplice delle variabili di ambiente
  • express: Hosting di siti web semplice
  • Libreria client ioredis: per comunicare con i database Redis
  • morgan: Fornisce un log strutturato

Frontend

Il nostro frontend è anche un'applicazione NodeJS che ospita una pagina web utilizzando express. Prende una frequenza inserita dall'utente e invia richieste alla nostra applicazione worker a quella velocità. Questa applicazione si iscrive anche ai messaggi su un canale Redis denominato "calculation" e mostra i risultati in una pagina web.

L'applicazione utilizza le seguenti dipendenze.

  • body-parser: Ci consente di analizzare le nostre richieste HTTP
  • dotenv: Analisi semplice delle variabili di ambiente
  • express: Hosting di siti web semplice
  • Libreria client ioredis: per comunicare con i database Redis
  • morgan: Fornisce log strutturati
  • request: Consente di effettuare richieste HTTP
  • socket.io: Consente la comunicazione bidirezionale dalla pagina web al server

Questa pagina web utilizza Bootstrap per lo stile e, quando viene eseguita, ha il seguente aspetto

e5e3b9cbede4cac4.png

Diagramma dell'architettura

7ae4bc22a58f80a6.png

Diagramma di deployment

Eseguiremo il deployment dell'applicazione finale nei due cluster che abbiamo creato. Il cluster primary avrà tutti i componenti (frontend, worker e Redis) di cui è stato eseguito il deployment, mentre il cluster burst avrà solo l'applicazione worker di cui è stato eseguito il deployment.

Di seguito è riportato un diagramma che descrive i due cluster. I riquadri evidenziati in rosso sono servizi Kubernetes, quelli in blu sono deployment Kubernetes. I riquadri gialli indicano l'installazione di Istio.

561db37c510944bd.png

Nota come il cluster burst abbia ancora un servizio per Redis di cui è stato eseguito il deployment, anche se non esiste un deployment per Redis nel cluster. Abbiamo bisogno di questo servizio nel cluster in modo che Kubernetes DNS possa risolvere la richiesta, ma quando la richiesta viene effettivamente effettuata, il proxy Istio la reindirizza al deployment Redis nel cluster primary.

L'applicazione finale avrà un deployment aggiuntivo in esecuzione nel cluster primary denominato istiowatcher.. Questo ci consentirà di reindirizzare dinamicamente il traffico a burst automaticamente quando il traffico supera una determinata soglia.

8f6183bdfc3f813c.png

9. Crea file di deployment dell'applicazione

Dobbiamo creare un insieme di manifest Kubernetes per eseguire il deployment della nostra applicazione

Passa alla directory principale del progetto e crea una nuova cartella denominata kubernetes.

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

Scrivi frontend.yaml

Verranno creati sia un deployment che un servizio Kubernetes per accedere all'immagine frontend.

Inserisci quanto segue in frontend.yaml.

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

Aspetti importanti da notare in Deployment

  • Abbiamo specificato che la porta su cui verrà eseguita l'applicazione è 8080
  • Abbiamo impostato l'indirizzo del worker su "http://worker-service" e utilizzeremo la funzionalità DNS integrata di Kubernetes per risolvere il servizio risultante.
  • Abbiamo impostato l'indirizzo del nostro REDIS_URL su "redis-cache-service:6379" e utilizzeremo la funzionalità DNS integrata di Kubernetes per risolvere gli indirizzi IP risultanti.
  • Abbiamo anche impostato i probe liveness e readiness sul container per aiutare Kubernetes a sapere quando il container è attivo e in esecuzione.

Scrivi worker-service.yaml

Stiamo scrivendo la definizione del servizio Kubernetes in un file separato rispetto alla definizione del deployment, in quanto riutilizzeremo questo servizio in più cluster, ma scriveremo un deployment diverso per ogni cluster.

Inserisci quanto segue in worker-service.yaml

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

Scrivi worker-primary.yaml

Questo sarà il deployment di worker che verrà eseguito sul cluster principale.

Inserisci quanto segue in worker-primary.yaml.

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

Nota che seguiamo lo stesso pattern di fornitura dei probe liveness e readiness, nonché di specifica delle variabili di ambiente PORT e REDIS_URL da utilizzare per la nostra applicazione.

Un'altra cosa da notare in questo deployment è la mancanza della variabile di ambiente PREFIX. Ciò significa che i risultati del nostro calcolo saranno hash non elaborati (senza prefisso).

L'ultimo punto chiave di questo deployment è l'etichetta cluster-type: primary-cluster. Lo utilizzeremo in seguito quando eseguiremo il routing del traffico su Istio multicluster

Scrivi redis.yaml

La comunicazione dal worker al frontend avviene tramite un canale Redis e, pertanto, dobbiamo eseguire il deployment di un'applicazione Redis nel nostro cluster.

Inserisci quanto segue in redis.yaml

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

Si tratta di un deployment semi-standard di un'applicazione Redis. Crea un container basato sull'immagine redis:alpine, espone le porte appropriate e imposta limiti di risorse ragionevoli.

Scrivi redis-service.yaml

Abbiamo bisogno di un servizio Kubernetes per comunicare con la nostra applicazione Redis

Inserisci quanto segue in redis-service.yaml

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

In questo modo, il servizio denominato redis-cache-service può accedere al nostro deployment Redis.

10. Esegui il deployment dell'applicazione

Ora che le nostre immagini sono state inviate a GCR e i nostri manifest Kubernetes sono stati scritti, è il momento di eseguire il deployment della nostra applicazione e vedere come funziona.

Esegui i seguenti comandi per eseguire il deployment dell'applicazione

  1. Assicurati di trovarti nel cluster giusto
kubectx primary
  1. Esegui il deployment della cache Redis
kubectl apply -f redis.yaml
  1. Esegui il deployment del servizio Redis
kubectl apply -f redis-service.yaml
  1. Esegui il deployment del frontend
kubectl apply -f frontend.yaml
  1. Esegui il deployment del lavoratore
kubectl apply -f worker-primary.yaml
  1. Esegui il deployment del servizio di lavoro
kubectl apply -f worker-service.yaml

Abbiamo eseguito il deployment della nostra applicazione su GKE. Complimenti!

Test

Attendi che i pod siano online

kubectl get pods -w

Quando tutti i pod sono in esecuzione, premi Ctrl + C.

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

Noterai che non abbiamo esposto il nostro frontend tramite un bilanciatore del carico. Questo perché in seguito accederemo all'applicazione tramite Istio. Per verificare che tutto sia in esecuzione, utilizzeremo kubectl port-forward. Esegui il comando seguente per inoltrare la porta 8080 sulla tua macchina locale (o Cloud Shell) alla porta 8080 che esegue il deployment di frontend.

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

Se esegui l'app localmente: apri un browser web e vai all'indirizzo http://localhost:8080.

Se esegui l'applicazione in Cloud Shell, fai clic sul pulsante "Anteprima web" e seleziona "Anteprima sulla porta 8080".

bdb5dc75f415be11.png

Dovresti vedere il frontend. Se inserisci un numero nella casella "Frequenza", dovresti iniziare a visualizzare i segni di cancelletto.

1caafaffab26897a.png

Congratulazioni, tutto è pronto e funzionante.

Premi Ctrl+C per interrompere l'inoltro della porta.

11. Pulisci l'applicazione di cui è stato eseguito il deployment

Applicheremo Istio al nostro cluster e poi eseguiremo nuovamente il deployment della nostra applicazione, quindi puliamo prima l'applicazione attuale.

Esegui questi comandi per eliminare tutti i deployment e i servizi che hai appena creato

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

12. Installa Istio sul cluster primario

Scarica Istio

Le release di Istio sono ospitate su GitHub. I seguenti comandi scaricheranno la versione 1.0.0 di Istio e la decomprimeranno.

  1. Passa alla radice del progetto
cd ${proj}
  1. Scaricare l'archivio
curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
  1. Estrai e rimuovi l'archivio
tar xzf istio-1.0.0-linux.tar.gz && rm istio-1.0.0-linux.tar.gz

Crea il modello Istio

L'esecuzione del seguente comando Helm creerà il modello per installare Istio nel cluster.

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

Viene creato un file denominato istio-primary.yaml nella directory corrente che contiene tutte le definizioni e le specifiche necessarie per eseguire il deployment e l'esecuzione di Istio.

Nota i due parametri --set. Questi componenti aggiuntivi supportano Prometheus e ServiceGraph nel sistema Istio. Utilizzeremo il servizio Prometheus più avanti nel lab.

Esegui il deployment di Istio

Per eseguire il deployment di Istio, dobbiamo prima creare uno spazio dei nomi denominato istio-system in cui possono essere eseguiti i deployment e i servizi Istio.

kubectl create namespace istio-system

Infine, applica il file istio-primary.yaml che abbiamo creato con Helm

kubectl apply -f istio-primary.yaml

Spazio dei nomi predefinito dell'etichetta

Istio funziona inserendo un servizio proxy sidecar in ogni deployment. Questa operazione viene eseguita in base all'attivazione, quindi dobbiamo etichettare lo spazio dei nomi default con istio-injection=enabled in modo che Istio possa inserire automaticamente il sidecar.

kubectl label namespace default istio-injection=enabled

Complimenti! Abbiamo un cluster in esecuzione con Istio pronto per il deployment della nostra applicazione.

13. Esegui il deployment della nostra applicazione con la gestione del traffico di Istio

Creare file di configurazione di gestione del traffico Istio

Istio funziona in modo simile a Kubernetes, in quanto utilizza file YAML per la configurazione. In quest'ottica, dobbiamo creare un insieme di file che indichino a Istio come esporre e instradare il nostro traffico.

Crea una directory denominata istio-manifests e passa a questa directory

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

Scrivi frontend-gateway.yaml

Questo file esporrà il nostro cluster Kubernetes in modo simile a un bilanciatore del carico GKE e indirizzerà tutto il traffico in entrata al nostro servizio frontend.

Crea un file denominato frontend-gateway.yaml e inserisci quanto segue.

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

Scrivi redis-virtualservice.yaml

Crea un file denominato redis-virtualservice.yaml e inserisci quanto segue:

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

Scrivi worker-virtualservice.yaml

Crea un file denominato worker-virtualservice.yaml e inserisci quanto segue:

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

Implementa le policy di gestione del traffico Istio

Il deployment dei criteri Istio viene eseguito allo stesso modo delle altre risorse Kubernetes, con kubectl apply

  1. Applica il nostro gateway
kubectl apply -f frontend-gateway.yaml
  1. Applica il nostro VirtualService Redis
kubectl apply -f redis-virtualservice.yaml
  1. Applica il nostro Worker VirtualService
kubectl apply -f worker-virtualservice.yaml

Esegui il deployment dell'applicazione

  1. Tornare alla directory kubernetes
cd ${proj}/kubernetes
  1. Esegui il deployment della cache Redis
kubectl apply -f redis.yaml
  1. Esegui il deployment del servizio Redis
kubectl apply -f redis-service.yaml
  1. Esegui il deployment del frontend
kubectl apply -f frontend.yaml
  1. Esegui il deployment del lavoratore
kubectl apply -f worker-primary.yaml
  1. Esegui il deployment del servizio di lavoro
kubectl apply -f worker-service.yaml

Verifica

A questo punto abbiamo eseguito nuovamente il deployment della nostra applicazione su un cluster con Istio e policy di gestione del traffico.

Attendiamo che tutti i nostri workload vengano attivati.

Una volta che sono tutti online, recupera l'IngressGateway che abbiamo configurato in frontend-ingressgateway.yaml

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

Vai all'indirizzo <EXTERNAL-IP> o invia una richiesta curl e dovresti visualizzare il frontend.

$ curl 35.199.158.10
<!doctype html>
<html>

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

14. Installare Istio sul cluster "burst"

Abbiamo dedicato molto tempo alla configurazione e al deployment sul nostro cluster primary, ma abbiamo un altro cluster completo su cui eseguire il deployment.

In questa sezione dovremo recuperare le variabili di configurazione in entrambi i cluster, quindi presta molta attenzione al cluster a cui è indirizzato ogni comando.

Crea il manifest remoto di Istio

Come quando abbiamo eseguito il deployment di Istio nel cluster primary, utilizzeremo Helm per creare un modello del deployment di Istio remoto nel cluster burst. Prima di poterlo fare, però, dobbiamo ottenere alcune informazioni sul nostro cluster primary

Raccogliere informazioni sul cluster principale

Passa al cluster primary

kubectx primary

I seguenti comandi recuperano gli indirizzi IP di vari pod nel cluster primario. Questi vengono utilizzati da Istio Remote per comunicare con il cluster principale.

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

Crea modello remoto

Ora utilizzeremo helm per creare un file denominato istio-remote-burst.yaml, che potremo poi eseguire il deployment nel cluster burst.

Passa alla radice del progetto

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

Installa Istio Remote sul cluster burst

Per installare Istio sul nostro cluster burst, dobbiamo seguire gli stessi passaggi eseguiti per l'installazione sul cluster primary, ma dobbiamo utilizzare il file istio-remote-burst.yaml.

Modificare kubecontext in burst

kubectx burst

Crea lo spazio dei nomi istio-system

kubectl create ns istio-system

Applica istio-burst.yaml

kubectl apply -f istio-remote-burst.yaml

Spazio dei nomi predefinito dell'etichetta

Ancora una volta, dobbiamo etichettare lo spazio dei nomi default in modo che il proxy possa essere inserito automaticamente.

kubectl label namespace default istio-injection=enabled

Complimenti! A questo punto abbiamo configurato Istio Remote sul cluster burst. A questo punto, tuttavia, i cluster non sono ancora in grado di comunicare. Dobbiamo generare un file kubeconfig per il cluster burst che possiamo distribuire nel cluster primary per collegarli.

Crea kubeconfig per il cluster "burst"

Passare al cluster di scatto a raffica

kubectx burst

Configura l'ambiente

Per creare un file kubeconfig per il cluster, dobbiamo raccogliere alcune informazioni.

  1. Recupera il nome del cluster
CLUSTER_NAME=$(kubectl config view --minify=true -o "jsonpath={.clusters[].name}")
  1. Ottenere il nome del server del cluster
SERVER=$(kubectl config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
  1. Recupera il nome del secret per l'autorità di certificazione del service account istio-multi
SECRET_NAME=$(kubectl get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
  1. Recupera i dati dell'autorità di certificazione archiviati nel segreto precedente
CA_DATA=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}")
  1. Ottieni il token memorizzato nel segreto precedente
TOKEN=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['token']}" | base64 --decode)

Creare il file kubeconfig

Con tutte queste variabili di ambiente impostate, dobbiamo creare il file kubeconfig

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

Verrà creato un nuovo file denominato burst-kubeconfig nella directory corrente, che può essere utilizzato dal cluster primary per autenticare e gestire il cluster burst.

Tornare al cluster principale

kubectx primary

Applica il file kubeconfig per "burst" creando un secret ed etichettandolo

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

Etichetta il secret in modo che Istio sappia di utilizzarlo per l'autenticazione multicluster

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

Complimenti! Abbiamo autenticato entrambi i cluster e li abbiamo fatti comunicare tra loro tramite Istio Multicluster. Eseguiamo il deployment della nostra applicazione cross-cluster

15. Eseguire il deployment di un'applicazione cross-cluster

Crea deployment

Passa alla directory kubernetes

cd ${proj}/kubernetes

Crea il deployment del worker per il cluster "burst": worker-burst.yaml

Crea un file denominato worker-burst.yaml e inserisci al suo interno quanto segue:

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

Nota che è quasi identico a worker-primary.yaml che abbiamo creato in precedenza. Esistono due differenze principali.

La prima differenza fondamentale è che abbiamo aggiunto la variabile di ambiente PREFIX con il valore "bursty-".

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

Ciò significa che il nostro worker nel cluster burst anteporrà a tutti gli hash che invia il prefisso "bursty-", che possiamo utilizzare per sapere che la nostra applicazione è effettivamente cross-cluster.

La seconda differenza fondamentale è che abbiamo modificato l'etichetta cluster-type in questo deployment da primary-cluster a burst-cluster

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

Utilizzeremo questa etichetta in un secondo momento quando aggiorneremo il nostro VirtualService.

Modificare i servizi Istio

Al momento i nostri servizi Istio non sfruttano entrambi i nostri deployment. Il 100% del nostro traffico viene indirizzato al cluster "principale". Cambiamolo.

Passa alla directory istio-manifests

cd ${proj}/istio-manifests

Modifica worker-virtualservice.yaml per includere DestinationRules

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

Come puoi vedere, abbiamo aggiunto una seconda destinazione al nostro VirtualService. Fa ancora riferimento allo stesso host (worker-service.default.svc.cluster.local)), ma il 50% del traffico viene indirizzato al sottoinsieme primary e l'altro 50% al sottoinsieme burst.

Abbiamo definito il sottoinsieme primary come i deployment con l'etichetta cluster-type: primary-cluster e il sottoinsieme burst come i deployment con l'etichetta cluster-type: burst-cluster.

In questo modo, il traffico viene suddiviso in modo uniforme tra i due cluster.

Esegui il deployment nel cluster

Esegui il deployment di redis-service.yaml nel cluster burst

Passa a kubeconfig burst

kubectx burst

Passa alla radice del progetto

cd ${proj}

Quindi esegui il deployment

Esegui il deployment di redis-service.yaml nel cluster burst

kubectl apply -f kubernetes/redis-service.yaml

Esegui il deployment di worker-burst.yaml nel cluster burst

kubectl apply -f kubernetes/worker-burst.yaml

Esegui il deployment di worker-service.yaml nel cluster burst

kubectl apply -f kubernetes/worker-service.yaml

Applica i servizi virtuali Istio

Passa a kubeconfig primary

kubectx primary

Quindi, esegui il deployment.

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

Verifica che funzioni

Per verificare che funzioni, vai al punto di ingresso Istio e nota che circa il 50% degli hash ha il prefisso "burst-".

78fb6e235e9f4a07.png

Ciò significa che la comunicazione tra i cluster è riuscita. Prova a modificare i pesi sui diversi servizi e ad applicare il file worker-virtualservice.yaml. Questo è un ottimo modo per bilanciare il traffico tra i cluster, ma cosa succederebbe se potessimo farlo automaticamente?

16. Sfruttare le metriche di Prometheus

Introduzione a Prometheus

Prometheus è un toolkit di avviso e monitoraggio dei sistemi open source originariamente creato da SoundCloud. Mantiene un modello di dati multidimensionale con dati delle serie temporali identificati dal nome della metrica e dalle coppie chiave/valore.

Per riferimento, ecco il diagramma dell'architettura di Prometheus:

601e1155a825e0c2.png

Istio, quando viene eseguito il deployment con Prometheus, invia automaticamente varie metriche al server Prometheus. Possiamo utilizzare queste metriche per gestire i nostri cluster al volo.

Esplorare le metriche di Prometheus

Per iniziare, dobbiamo esporre il deployment di Prometheus.

Vai alla scheda Workload in GKE e visualizza in dettaglio il workload "prometheus".

b4a7a3cd67db05b3.png

Una volta visualizzati i dettagli del deployment, vai ad Azioni -> Esponi.

c04a482e55bdfd41.png

Scegli di inoltrare alla porta 9090 e digita "Bilanciatore del carico".

d5af3ba22a7a6ebb.png

e scegli "Esposizione".

In questo modo verrà creato un servizio su un indirizzo IP accessibile pubblicamente che possiamo utilizzare per esplorare le metriche di Prometheus

Attendi che l'endpoint diventi operativo e, una volta fatto, fai clic sull'indirizzo IP accanto a "Endpoint esterni" b1e40ad90851da29.png

Ora dovresti visualizzare la UI di Prometheus.

ed273552270337ec.png

Prometheus fornisce metriche sufficienti per essere un workshop a sé stante. Per il momento, però, inizieremo a esplorare la metrica istio_requests_total.

L'esecuzione di questa query restituisce una serie di dati. Si tratta di metriche su tutte le richieste che passano attraverso il service mesh Istio, e sono molte. Modificheremo l'espressione per filtrare i risultati e visualizzare solo ciò che ci interessa davvero:

Richieste in cui il servizio di destinazione è worker-service.default.svc.cluster.local e la cui origine è frontend-deployment limitata agli ultimi 15 secondi

La query è simile alla seguente:

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

e ci fornisce un insieme di dati molto più gestibile su cui lavorare.

19d551fd5eac3785.png

ma è ancora un po' denso. Vogliamo conoscere le richieste al secondo, non tutte le richieste.

Per ottenere questo risultato, possiamo utilizzare la funzione integrata rate

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

dbb9dc063a18da9b.png

Ci stiamo avvicinando, ma dobbiamo ridurre ulteriormente queste metriche in un gruppo logico.

Per farlo, possiamo utilizzare le parole chiave sum e by per raggruppare e sommare i risultati.

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

Perfetto! Possiamo ottenere da Prometheus le metriche esatte di cui abbiamo bisogno.

Query Prometheus finale

Con tutto ciò che abbiamo imparato, la query finale che dobbiamo porre a Prometheus è

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)

Ora possiamo utilizzare la relativa API HTTP per ottenere la metrica.

Possiamo eseguire query sulla loro API con la nostra query effettuando una richiesta GET HTTP come segue. Sostituisci <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\)

Ecco un esempio di risposta:

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

Ora possiamo estrarre il valore della metrica dal JSON

Pulizia

Dobbiamo eliminare il servizio che abbiamo appena utilizzato per esporre Prometheus. Nella console Google Cloud, vai in cima al servizio che abbiamo appena creato e fai clic su "Elimina".

d58cb51b4c922751.png

Passaggi successivi:

Dopo aver trovato un modo per scoprire come si sposta il traffico nel cluster e a quale velocità, il passaggio successivo consiste nello scrivere un piccolo binario che esegue periodicamente query su Prometheus e, se le richieste al secondo a worker superano una determinata soglia, applica pesi di destinazione diversi al nostro servizio virtuale di worker per inviare tutto il traffico al cluster burst. Una volta che le richieste al secondo scendono al di sotto di una soglia inferiore, invia tutto il traffico di nuovo a primary.

17. Crea un burst cross-cluster

Configurazione

Imposta tutto il traffico per il servizio worker sul cluster principale

Considereremo tutto il traffico destinato a worker-service e indirizzato al cluster primary come lo stato "predefinito" della nostra applicazione

Modifica $proj/istio-manifests/worker-virtualservice.yaml in modo che sia simile a quanto segue

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

Assicurati di essere connesso al cluster primary

kubectx primary

Applica istio-manifests/worker-virtualservice.yaml

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

Scrivi il daemon istiowatcher

Utilizzeremo Go per scrivere questo servizio per la sua velocità e portabilità. Il flusso generale dell'applicazione sarà di avviarsi e, ogni secondo, interrogare Prometheus,

Crea una nuova directory in src denominata istiowatcher

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

Chiameremo istioctl dall'interno del nostro container per manipolare il control plane Istio dall'interno del cluster.

Scrivi istiowatcher.go

Crea un file denominato istiowatcher.go in questa directory e inserisci il seguente contenuto:

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

Scrivi Dockerfile

Crea un nuovo file denominato Dockerfile e inserisci al suo interno quanto segue.

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

Questo Dockerfile in più fasi scarica ed estrae la release 1.0.0 di Istio nella prima fase. La seconda fase copia tutto dalla nostra directory nell'immagine, quindi copia istioctl dalla fase di build a /usr/local/bin (in modo che possa essere chiamato dalla nostra applicazione), recupera le dipendenze, compila il codice e imposta CMD su "istiowatcher".

Scrivi burst.yaml

Questo è il file che istiowatcher applicherà quando le richieste al secondo a worker da frontend superano 15.

Crea un nuovo file denominato burst.yaml e inserisci al suo interno quanto segue.

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

Scrivere natural.yaml

Considereremo questo lo stato "naturale" a cui torniamo quando le richieste al secondo da frontend a worker scendono sotto 10. In questo stato, il 100% del traffico viene indirizzato al cluster primary.

Crea un nuovo file denominato natural.yaml e inserisci al suo interno quanto segue

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

Crea e invia istiowatcher

Esegui questo comando per inviare la directory corrente a Google Cloud Build (GCB), che creerà e taggherà l'immagine in GCR.

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

Esegui il deployment di istiowatcher

Passa alla directory kubernetes

cd ${proj}/kubernetes/

Scrivi un file di deployment: istiowatcher.yaml

Crea un file denominato istiowatcher.yaml e inserisci quanto segue (sostituisci <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

Esegui il deployment

Assicurati che l'operazione venga eseguita nel cluster principale

kubectx primary

Esegui il deployment di istiowatcher.yaml nello spazio dei nomi istio-system

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

È importante notare le direttive serviceAccountName e automountServiceAccountToken nel file YAML. In questo modo otteniamo le credenziali necessarie per eseguire istioctl dall'interno del cluster.

Dobbiamo anche eseguire il deployment all'interno dello spazio dei nomi istio-system per assicurarci di disporre delle credenziali per istio-pilot-service-account. (non esiste nello spazio dei nomi default).

Guarda il traffico passare automaticamente da un'app all'altra.

Ora il momento magico. Andiamo al nostro frontend e aumentiamo le richieste al secondo a 20.

Nota che ci vogliono alcuni secondi, ma aumenta e tutti i nostri hash hanno il prefisso "bursty-".

Questo perché stiamo campionando Prometheus nell'intervallo 15s, il che rallenta un po' il nostro tempo di risposta. Se volessimo una banda molto più stretta, potremmo modificare la query in prometheus in modo che sia 5s.

18. Passaggi successivi

Pulizia

Non è necessario eseguire la pulizia se utilizzi un account temporaneo fornito per questo workshop.

Puoi eliminare i cluster Kubernetes, la regola firewall e le immagini in GCR

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

Prospettive future