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.

Occorrente

  • Account Google Cloud (utilizza quello 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
  • Eseguire il deployment di un'applicazione web dal codice sorgente a Kubernetes
  • Scrivere e applicare 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 in-browser con strumenti installati
  • il tuo laptop (segui le istruzioni riportate di seguito)

Iniziare con Google Cloud

  1. Se non hai un account Google Cloud, ritira la scheda dell'account utente senza costi dall'insegnante.
  2. Vai alla console Google Cloud e fai clic su "Seleziona un progetto": 5c2d9bf74c78f7e4.png
  3. Prendi nota dell'"ID" del progetto da qualche parte, quindi 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. Se non vuoi eseguire questo esercizio su Cloud Shell, vai alla sezione successiva.

Vai a Cloud Console 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****: segui 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. Scollega 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. Aumentare le dimensioni del testo: le dimensioni del carattere predefinite su Cloud Shell possono essere troppo piccole per essere lette.

Ctrl+ su Linux/Windows⌘+ su macOS.

Se ti senti più a tuo agio a utilizzare il tuo ambiente di workstation rispetto a 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 il seguente comando per l'installazione:
gcloud components install kubectl

Esegui il seguente comando per autenticare gcloud. Ti verrà chiesto di accedere con il tuo Account Google. Poi, scegli il progetto predefinito (vedi sopra) come progetto predefinito. (puoi saltare la configurazione di una zona di calcolo):

gcloud init
  1. Installa 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****: scarica gli script bash da qui in una posizione in $PATH
  3. Installa helm****: segui queste istruzioni.

3. Configura il 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

Configurare 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 del codice e file di configurazione, quindi creiamo una directory del progetto e passiamoci

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:

  • denominato "principale",
  • 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 monitorare la creazione del cluster in Cloud Console.

Dopo aver creato il cluster Kubernetes, gcloud configura kubectl con le credenziali che rimandano al cluster.

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

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

Esegui il comando seguente per elencare i nodi Kubernetes del tuo cluster (dovrebbero mostrare lo stato "Ready"):

kubectl get nodes

Modificare i nomi dei file Kubeconfig per facilitarne l'utilizzo

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

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 imposta l'indirizzo email associato 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 un 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 monitorare la creazione del cluster in Cloud Console.

Dopo aver creato il cluster Kubernetes, gcloud configura kubectl con le credenziali che rimandano al cluster.

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

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

Esegui il comando seguente per elencare i nodi Kubernetes del tuo cluster (dovrebbero mostrare lo stato "Ready"):

kubectl get nodes

Modificare i nomi del file Kubeconfig per facilitarne l'utilizzo

Questo comando modificherà la voce kubeconfig che hai appena creato 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 imposta l'indirizzo email associato 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 i seguenti comandi per creare una regola firewall nella piattaforma 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

Abbiamo configurato entrambi i cluster e siamo pronti per eseguire il deployment della nostra applicazione e di Istio su di essi.

7. Introduzione a Istio

Che cos'è Istio?

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

a25613cd581825da.png

Questi criteri possono essere applicati 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 i criteri 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 in base alla 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 instradamento del traffico da applicare quando viene indirizzato un host.

Gateway

Un gateway è un bilanciatore del carico che opera all'estremità della 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 i criteri da applicare 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

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

Questa configurazione è dovuta a due motivi.

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

In secondo luogo, una configurazione di 4 nodi (come definito sopra) è il requisito minimo per eseguire Istio. Questo ci porta alla domanda: se Istio richiede un minimo di 4 nodi, come può il nostro cluster burst eseguire Istio con un solo 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.

Lavoratore

L'applicazione di lavoro è scritta in NodeJS e ascolta le richieste HTTP POST in entrata, esegue su di esse un'operazione di hashing e, se è definita una variabile di ambiente denominata PREFIX, antepone l'hash a quel 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.

Come riferimento: questi sono i pacchetti utilizzati dall'applicazione.

  • body-parser: Ci consente di analizzare le richieste HTTP
  • cors: Consente l'utilizzo della Condivisione delle risorse tra origini (CORS)
  • dotenv: Analisi facile delle variabili di ambiente
  • express: Hosting di siti web semplici
  • ioredis: Libreria client per comunicare con i database Redis
  • morgan: Fornisce un log ben 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 frequenza. 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 richieste HTTP
  • dotenv: Analisi facile delle variabili di ambiente
  • express: Hosting di siti web semplici
  • ioredis: Libreria client per comunicare con i database Redis
  • morgan: Fornisce log ben 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. Nel cluster primary verranno eseguiti il deployment di tutti i componenti (frontend, worker e Redis), mentre nel cluster burst verrà eseguita solo l'applicazione worker.

Ecco un diagramma che descrive i due cluster. Le caselle delineate in rosso sono servizi Kubernetes, quelle in blu sono deployment Kubernetes. Le caselle gialle indicano la nostra installazione di Istio.

561db37c510944bd.png

Notare che nel cluster burst è ancora presente un servizio per Redis anche se non è presente alcun deployment per Redis nel cluster. Dobbiamo avere questo servizio nel cluster in modo che Kubernetes DNS possa risolvere la richiesta, ma quando la richiesta viene effettivamente effettuata, il proxy Istio reindirizzerà la richiesta al deployment di Redis nel cluster primary.

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

8f6183bdfc3f813c.png

9. Creare file di deployment dell'applicazione

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

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

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

Scrivere 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 chiave 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 contenitore per aiutare Kubernetes a capire quando il contenitore è attivo e funzionante.

Scrivere worker-service.yaml

Stiamo scrivendo la definizione del servizio Kubernetes in un file separato rispetto alla definizione del deployment perché 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

Scrivere worker-primary.yaml

Questo sarà il deployment di worker che eseguiremo nel 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"

Tieni presente che in questo caso seguiamo lo stesso schema di fornitura di sonde liveness e readiness, nonché di specificazione 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 calcolo saranno hash non elaborati (senza prefisso).

L'ultimo punto chiave di questo deployment è l'etichetta cluster-type: primary-cluster. Lo utilizzeremo più avanti quando eseguiremo il routing del traffico su Istio Multicluster

Scrivere redis.yaml

La comunicazione dal nostro worker al frontend avviene tramite un canale Redis, 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 contenitore basato sull'immagine redis:alpine, espone le porte appropriate e imposta limiti di risorse ragionevoli.

Scrivere 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

Questo fornisce il servizio denominato redis-cache-service per accedere al nostro deployment Redis.

10. Esegui il deployment dell'applicazione

Ora che le nostre immagini sono state caricate su GCR e i manifest Kubernetes sono stati scritti, è un buon momento per eseguire il deployment della nostra applicazione e vedere come funziona.

Esegui i seguenti comandi per eseguire il deployment dell'applicazione

  1. Assicurati che siamo 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 worker
kubectl apply -f worker-primary.yaml
  1. Esegui il deployment del servizio worker
kubectl apply -f worker-service.yaml

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

Test

Attendi che i pod vengano attivati online

kubectl get pods -w

Una volta che tutti i pod sono in stato "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 attivo e funzionante, utilizzeremo kubectl port-forward. Esegui il seguente comando per inoltrare la porta 8080 sulla tua macchina locale (o Cloud Shell) alla porta 8080 su cui è in esecuzione il deployment 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 alla pagina http://localhost:8080

Se esegui l'operazione 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 vedere gli hash.

1caafaffab26897a.png

Congratulazioni, è tutto pronto.

Premi Ctrl+C per interrompere il port forwarding.

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

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

Esegui i seguenti comandi per eliminare tutti i deployment e i servizi appena creati

  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 principale

Ottieni Istio

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

  1. Passa alla radice del progetto
cd ${proj}
  1. Scarica 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 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. Aggiungono il supporto di Prometheus e ServiceGraph al 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 su base di attivazione, quindi dobbiamo etichettare il nostro spazio dei nomi default con istio-injection=enabled in modo che Istio possa iniettare automaticamente il sidecar.

kubectl label namespace default istio-injection=enabled

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

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

Creare file di configurazione per la gestione del traffico Istio

Istio funziona in modo simile a Kubernetes in quanto utilizza file YAML per la configurazione. In questo senso, 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 quella directory

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

Scrivere 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

Scrivere 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

Esegui il deployment delle norme di gestione del traffico Istio

Il deployment dei criteri Istio viene eseguito nello 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 VirtualService di Worker
kubectl apply -f worker-virtualservice.yaml

Esegui il deployment dell'applicazione

  1. Torna alla nostra 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 worker
kubectl apply -f worker-primary.yaml
  1. Esegui il deployment del servizio worker
kubectl apply -f worker-service.yaml

Verifica

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

Aspettiamo che tutti i nostri carichi di lavoro vengano attivati

Una volta che sono tutti online, recupera 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 esegui curl e dovresti vedere il frontend.

$ curl 35.199.158.10
<!doctype html>
<html>

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

14. Installa Istio su un cluster "burst"

Abbiamo impiegato molto tempo per la configurazione e il deployment nel nostro cluster primary, ma abbiamo un altro intero cluster su cui eseguire il deployment.

In questa sezione dovremo acquisire le variabili di configurazione di entrambi i cluster, quindi presta molta attenzione al cluster a cui facciamo riferimento per ogni comando.

Crea il manifest Istio Remote

Come quando abbiamo eseguito il deployment di Istio nel cluster primary, utilizzeremo Helm per creare un modello del deployment di Istio Remote nel cluster burst. Tuttavia, prima di poter procedere, abbiamo bisogno di 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 principale. Questi vengono utilizzati da Istio Remote per comunicare nuovamente 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}')

Creare un modello remoto

Ora utilizzeremo helm per creare un file denominato istio-remote-burst.yaml che potremo poi eseguire 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 nel nostro cluster burst, dobbiamo seguire gli stessi passaggi eseguiti per l'installazione nel 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. Tuttavia, a questo punto i cluster non sono ancora in grado di comunicare. Dobbiamo generare un file kubeconfig per il cluster burst che possiamo eseguire nel cluster primary per collegarli.

Creare il file kubeconfig per il cluster "burst"

Passare al cluster burst

kubectx burst

Configurare l'ambiente

Dobbiamo raccogliere alcune informazioni sul cluster per creare un file kubeconfig.

  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 dell'account di servizio istio-multi
SECRET_NAME=$(kubectl get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
  1. Recupera i dati dell'autorità di certificazione memorizzati nel segreto precedente
CA_DATA=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}")
  1. Recupera 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

Dopo aver impostato tutte le variabili di ambiente, 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 e assegnandogli un'etichetta

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

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

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

Complimenti! Entrambi i cluster sono autenticati e comunicano tra loro tramite Istio Multicluster. Eseguiamo il deployment dell'applicazione in più cluster

15. Esegui il deployment di un'applicazione tra cluster

Creare deployment

Passa alla directory kubernetes

cd ${proj}/kubernetes

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

Crea un file denominato worker-burst.yaml e inserisci 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-"

Tieni presente che questo file è quasi identico a worker-primary.yaml che abbiamo creato in precedenza. Esistono due differenze principali.

La prima differenza principale è 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 inviati il prefisso "bursty-". Possiamo utilizzare questo valore per sapere che la nostra applicazione è veramente cross-cluster.

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

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

Utilizzeremo questa etichetta in seguito quando aggiorneremo il nostro VirtualService.

Modificare i servizi Istio

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

Passa alla nostra directory istio-manifests

cd ${proj}/istio-manifests

Modifica il file 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

Puoi vedere che 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 efficace al 50% 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 directory principale 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

Applicare VirtualServices di Istio

Passa al file kubeconfig primary

kubectx primary

Quindi Esegui il deployment

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

Verificare il funzionamento

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

78fb6e235e9f4a07.png

Ciò significa che stiamo comunicando correttamente tra cluster. Prova a modificare i pesi dei diversi servizi e ad applicare il file worker-virtualservice.yaml. Questo è un ottimo modo per bilanciare il traffico tra i cluster, ma cosa succede se possiamo farlo automaticamente?

16. Sfruttare le metriche di Prometheus

Introduzione a Prometheus

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

Come riferimento, di seguito è riportato il diagramma dell'architettura di Prometheus:

601e1155a825e0c2.png

Quando viene eseguito il deployment di Istio con Prometheus, vengono registrate automaticamente varie metriche sul server Prometheus. Possiamo utilizzare queste metriche per gestire i nostri cluster in tempo reale.

Esplorazione delle nostre metriche di Prometheus

Per iniziare, dobbiamo esporre il deployment di Prometheus.

Vai alla scheda Workload in GKE, vai al 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 "Mostra"

Verrà creato un servizio su un indirizzo IP accessibile pubblicamente che potremo utilizzare per esplorare le nostre metriche Prometheus

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

A questo punto dovresti vedere l'interfaccia utente di Prometheus.

ed273552270337ec.png

Prometheus fornisce metriche sufficienti per essere un proprio laboratorio. Per il momento, però, inizieremo esplorando 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 mesh di servizi Istio, e sono molte. Modificheremo l'espressione per filtrare i risultati in base a 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 ha il seguente aspetto:

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

e ci offre un insieme di dati molto più gestibile

19d551fd5eac3785.png

Ma è ancora un po' confuso. Vogliamo conoscere le richieste al secondo, non tutte le richieste.

Per farlo, possiamo utilizzare la funzione incorporata 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 le metriche esatte di cui abbiamo bisogno da Prometheus.

La nostra query Prometheus finale

Con tutto ciò che abbiamo appreso, l'ultima query che dobbiamo chiedere 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 sull'API con la nostra query inviando una richiesta GET HTTP come questa. 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 nella parte superiore del servizio che abbiamo appena creato e fai clic su "Elimina".

d58cb51b4c922751.png

Passaggi successivi:

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

17. Creare un'esplosione tra cluster

Configurazione

Impostare tutto il traffico per il servizio worker sul cluster principale

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

Modifica $proj/istio-manifests/worker-virtualservice.yaml in modo che abbia il seguente aspetto

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

Scrivere il daemon istiowatcher

Per scrivere questo servizio utilizzeremo Go per la sua velocità e portabilità. Il flusso complessivo dell'applicazione sarà avviarsi ed eseguire ogni secondo una query su Prometheus.

Crea una nuova directory in src denominata istiowatcher

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

Chiameremo istioctl dal nostro contenitore per manipolare il piano di controllo Istio all'interno del cluster.

Scrivere istiowatcher.go

Crea un file nella directory denominato istiowatcher.go e inserisci quanto segue

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

Scrivere il Dockerfile

Crea un nuovo file denominato Dockerfile e inserisci 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 multi-stadio scarica ed estrae la release 1.0.0 di Istio nella prima fase. Il secondo passaggio copia tutto dalla nostra directory nell'immagine, poi copia istioctl dalla fase di compilazione in /usr/local/bin (in modo che possa essere chiamato dalla nostra applicazione), recupera le dipendenze, compila il codice e imposta CMD su "istiowatcher"

Scrivere burst.yaml

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

Crea un nuovo file denominato burst.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   
       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

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

Crea un nuovo file denominato natural.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   
       subset: primary
       port:
         number: 80       
     weight: 100
   - destination:
       host: worker-service.default.svc.cluster.local    
       subset: burst 
       port:
         number: 80       
     weight: 0

Crea e esegui il push di istiowatcher

Esegui il seguente 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 nostra directory kubernetes

cd ${proj}/kubernetes/

Scrivere 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'esecuzione sia 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 all'interno del cluster.

Inoltre, dobbiamo 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 passaggio automatico del traffico.

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

Tieni presente che l'operazione richiede alcuni secondi, ma viene eseguita fino a quando tutti gli hash non hanno il prefisso "bursty-".

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

18. Passaggi successivi

Pulizia

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

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

Passaggi successivi