Torna ai microservizi con Istio. Parte 2

Torna ai microservizi con Istio. Parte 2

Nota di traduzione.: Prima parte Questo ciclo è stato dedicato alla conoscenza delle funzionalità di Istio e alla loro dimostrazione in azione. Ora si parlerà di aspetti più complessi della configurazione e dell'uso di questo service mesh, in particolare della routing finemente configurabile e della gestione del traffico di rete.

Ricordiamo inoltre che nell'articolo si utilizzano configurazioni (manifesti per Kubernetes e Istio) dal repository istio-mastery.

Gestione del traffico

Con Istio si aprono nuove possibilità nei cluster, consentendo di garantire:

  • Routing dinamico delle richieste: rilascio canarini, test A/B;
  • Bilanciamento del carico: semplice e senza contraddizioni, basato su hash;
  • Ripristino dopo i malfunzionamenti: timeout, retry, circuit breakers;
  • Introduzione di malfunzionamenti: ritardi, interruzione delle richieste, ecc.

Nel prosieguo dell'articolo, queste possibilità saranno mostrate con un esempio di un'applicazione selezionata e introdurranno nuovi concetti. Il primo di questi concetti sarà DestinationRules (ossia regole sul destinatario del traffico/ richieste — nota dell'autore), attraverso le quali attiviamo il test A/B.

Test A/B:  DestinationRules in pratica

Il test A/B si applica nei casi in cui esistono due versioni di un'applicazione (di solito si differenziano visivamente) e non siamo sicuri al 100% di quale delle due migliori l'interazione con l'utente. Pertanto, eseguiamo contemporaneamente entrambe le versioni e raccogliamo metriche.

Per il deployment della seconda versione del frontend, necessaria per dimostrare il test A/B, esegui il seguente comando:

$ kubectl apply -f resource-manifests/kube/ab-testing/sa-frontend-green-deployment.yaml
deployment.extensions/sa-frontend-green creato

Il manifesto del deployment per la "versione verde" differisce in due punti:

  1. L'immagine è basata su un'altra etichetta — istio-green,
  2. I pod hanno l'etichetta version: green.

Poiché entrambi i deployment hanno l'etichetta app: sa-frontend, le richieste instradate dal servizio virtuale sa-external-services verso il servizio sa-frontend, verranno deviate su tutte le sue istanze e il carico sarà distribuito tramite algoritmo round-robin, il che porterà alla seguente situazione:

Torna ai microservizi con Istio. Parte 2
File richiesti non trovati

Questi file non sono stati trovati perché sono chiamati diversamente nelle varie versioni dell'applicazione. Assicuriamoci di questo:

$ curl --silent http://$EXTERNAL_IP/ | tr '"' 'n' | grep main
/static/css/main.c7071b22.css
/static/js/main.059f8e9c.js
$ curl --silent http://$EXTERNAL_IP/ | tr '"' 'n' | grep main
/static/css/main.f87cd8c9.css
/static/js/main.f7659dbb.js

Ciò significa che index.html, richiedendo una versione di file statici, può essere inviato da un bilanciatore di carico a pod che hanno una versione diversa, dove, per motivi comprensibili, tali file non esistono. Pertanto, affinché l'applicazione funzioni, dobbiamo impostare una limitazione: «la stessa versione dell'applicazione che ha restituito index.html deve gestire anche le richieste successive».

Raggiungeremo l'obiettivo con un bilanciamento del carico coerente basato su hash (Consistent Hash Loadbalancing). In questo caso le richieste da un singolo cliente vengono inviate allo stesso esemplare del backend, per il quale viene utilizzata una proprietà predeterminata — ad esempio, l'intestazione HTTP. Questo è implementato tramite DestinationRules.

DestinationRules

Una volta che VirtualService ha indirizzato la richiesta al servizio giusto, tramite DestinationRules possiamo definire le politiche che verranno applicate al traffico destinato agli esemplari di quel servizio:

Torna ai microservizi con Istio. Parte 2
Gestione del traffico con le risorse Istio

Nota: L'impatto delle risorse Istio sul traffico di rete è presentato qui in una forma semplificata per una migliore comprensione. Se vogliamo essere precisi, la decisione su quale esemplare inviare la richiesta viene presa da Envoy all'Ingress Gateway, configurato in CRD.

Con Destination Rules possiamo configurare il bilanciamento del carico in modo che vengano utilizzati hash coerenti e garantiti risposte dallo stesso esemplare del servizio allo stesso utente. La seguente configurazione consente di raggiungere questo obiettivo (destinationrule-sa-frontend.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: sa-frontend
spec:
  host: sa-frontend
  trafficPolicy:
    loadBalancer:
      consistentHash:
        httpHeaderName: version   # 1

1 — l'hash verrà generato in base al contenuto dell'intestazione HTTP versione.

Applica la configurazione utilizzando il seguente comando:

$ kubectl apply -f resource-manifests/istio/ab-testing/destinationrule-sa-frontend.yaml
destinationrule.networking.istio.io/sa-frontend creato

Ora esegui il comando qui sotto e assicurati di ricevere i file corretti quando specifichi l'intestazione versione:

$ curl --silent -H "version: yogo" http://$EXTERNAL_IP/ | tr '"' 'n' | grep main

Nota: Per aggiungere diversi valori all'intestazione e testare i risultati direttamente nel browser, puoi utilizzare questa estensione per Chrome (o questa per Firefox — nota del traduttore).

In generale, le DestinationRules offrono ulteriori possibilità nel bilanciamento del carico — per dettagli chiedere. documentazione ufficiale.

Prima di approfondire VirtualService, rimuoviamo la "versione verde" dell'applicazione e la regola di instradamento del traffico corrispondente eseguendo i seguenti comandi:

$ kubectl delete -f resource-manifests/kube/ab-testing/sa-frontend-green-deployment.yaml
deployment.extensions "sa-frontend-green" eliminato
$ kubectl delete -f resource-manifests/istio/ab-testing/destinationrule-sa-frontend.yaml
destinationrule.networking.istio.io "sa-frontend" eliminato

Mirror: Virtual Services in pratica

Shadowing ("esecuzione in parallelo") o Mirroring ("mirror") è utilizzato quando vogliamo testare una modifica in produzione senza coinvolgere gli utenti finali: per fare questo, dupliciamo ("mirroring") le richieste su una seconda istanza, dove sono state apportate le modifiche necessarie, e osserviamo le conseguenze. In parole semplici, è quando il tuo collega sceglie il problema più critico e fa una pull request in forma di un enorme groviglio che nessuno può in realtà revisionare.

Per testare questo scenario in azione, creiamo una seconda istanza di SA-Logic con bug (buggy), eseguendo il seguente comando:

$ kubectl apply -f resource-manifests/kube/shadowing/sa-logic-service-buggy.yaml
deployment.extensions/sa-logic-buggy creato

E ora eseguiamo il comando per assicurarci che tutte le istanze con app=sa-logic abbiano anche etichette con versioni corrispondenti:

$ kubectl get pods -l app=sa-logic --show-labels
NOME                              PRONTO   ETICHETTE
sa-logic-568498cb4d-2sjwj         2/2     app=sa-logic,version=v1
sa-logic-568498cb4d-p4f8c         2/2     app=sa-logic,version=v1
sa-logic-buggy-76dff55847-2fl66   2/2     app=sa-logic,version=v2
sa-logic-buggy-76dff55847-kx8zz   2/2     app=sa-logic,version=v2

Servizio sa-logic mirato ai pod con etichetta app=sa-logic, quindi tutte le richieste saranno distribuite tra tutte le istanze:

Torna ai microservizi con Istio. Parte 2

… ma vogliamo che le richieste siano dirette verso le istanze con versione v1 e mirrorate verso le istanze con versione v2:

Torna ai microservizi con Istio. Parte 2

Otterremo questo tramite VirtualService in combinazione con DestinationRule, dove le regole definiranno i sottoinsiemi e i percorsi del VirtualService verso un sottoinsieme specifico.

Definizione dei sottoinsiemi nelle Destination Rules

Sottoinsiemi (subsets) sono definiti dalla seguente configurazione (sa-logic-subsets-destinationrule.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: sa-logic
spec:
  host: sa-logic    # 1
  subsets:
  - name: v1        # 2
    labels:
      version: v1   # 3
  - name: v2
    labels:
      version: v2

  1. L'host (host) definisce che questa regola si applica solo ai casi in cui il percorso è diretto verso il servizio sa-logic;
  2. I nomi (name) dei sottoinsiemi sono utilizzati durante l'instradamento verso le istanze del sottoinsieme;
  3. Etichetta (label) determina le coppie chiave-valore a cui devono conformarsi le istanze per fare parte di un sottoinsieme.

Applica la configurazione utilizzando il seguente comando:

$ kubectl apply -f resource-manifests/istio/shadowing/sa-logic-subsets-destinationrule.yaml
destinationrule.networking.istio.io/sa-logic creato

Ora che i sottoinsiemi sono stati definiti, possiamo andare avanti e configurare il VirtualService per applicare le regole alle richieste a sa-logic in modo che:

  1. Vengano instradate al sottoinsieme v1,
  2. Vengano mirroring al sottoinsieme v2.

Il seguente manifesto permette di ottenere quanto previsto (sa-logic-subsets-shadowing-vs.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: sa-logic
spec:
  hosts:
    - sa-logic          
  http:
  - route:
    - destination:
        host: sa-logic  
        subset: v1      
    mirror:             
      host: sa-logic     
      subset: v2

Non sono necessarie spiegazioni qui, quindi vediamo semplicemente all'opera:

$ kubectl apply -f resource-manifests/istio/shadowing/sa-logic-subsets-shadowing-vs.yaml
virtualservice.networking.istio.io/sa-logic creato

Aggiungiamo carico chiamando questo comando:

$ while true; do curl -v http://$EXTERNAL_IP/sentiment 
    -H "Content-type: application/json" 
    -d '{"sentence": "Amo yogobella"}'; 
    sleep .8; done

Esaminiamo i risultati in Grafana, dove possiamo vedere che la versione con bug (buggy) causa il fallimento di circa il 60% delle richieste, ma nessuno di questi fallimenti colpisce gli utenti finali, poiché ricevono risposte dal servizio funzionante.

Torna ai microservizi con Istio. Parte 2
Successo delle risposte delle diverse versioni del servizio sa-logic

Qui abbiamo visto per la prima volta come viene applicato il VirtualService ai sidecar dei nostri servizi: quando sa-web-app fa una richiesta a sa-logic, passa attraverso il sidecar Envoy, che — tramite il VirtualService — è configurato per instradare la richiesta al sottoinsieme v1 e mirroring della richiesta al sottoinsieme v2 del servizio sa-logic.

So: avete già pensato che i Virtual Services siano semplici. Nella prossima sezione espanderemo questa opinione dicendo che sono anche davvero magnifici.

Distribuzioni canary

Il Canary Deployment è il processo di distribuzione di una nuova versione di un'applicazione a un numero ristretto di utenti. Viene utilizzato per assicurarsi che non ci siano problemi nel rilascio e solo dopo, avendo la certezza della qualità sufficiente di quest'ultimo, viene diffuso a ununpubblico più ampio.

Per dimostrare i rilasci canary, continueremo a lavorare con il sottoinsieme buggy a sa-logic.

Non facciamo le cose per metà e inoltriamo subito il 20% degli utenti alla versione con bug (che rappresenterà il nostro rilascio canary), mentre l'80% restante alla normale servizio. Per fare ciò applichiamo il seguente VirtualService (sa-logic-subsets-canary-vs.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: sa-logic
spec:
  hosts:
    - sa-logic    
  http:
  - route: 
    - destination: 
        host: sa-logic
        subset: v1
      weight: 80         # 1
    - destination: 
        host: sa-logic
        subset: v2
      weight: 20 # 1

1 — è il peso (weight), che determina la percentuale di richieste che saranno inviate al destinatario o a un sottoinsieme del destinatario.

Aggiorniamo la configurazione precedente del VirtualService con sa-logic il seguente comando:

$ kubectl apply -f resource-manifests/istio/canary/sa-logic-subsets-canary-vs.yaml
virtualservice.networking.istio.io/sa-logic configurato

… e subito vedremo che parte delle richieste porta a errori:

$ while true; do 
   curl -i http://$EXTERNAL_IP/sentiment 
   -H "Content-type: application/json" 
   -d '{"sentence": "I love yogobella"}' 
   --silent -w "Time: %{time_total}s t Status: %{http_code}n" 
   -o /dev/null; sleep .1; done
Time: 0.153075s Status: 200
Time: 0.137581s Status: 200
Time: 0.139345s Status: 200
Time: 30.291806s Status: 500

I VirtualServices attivano i deploy canary: in questo caso abbiamo ridotto le potenziali conseguenze dei problemi al 20% della base utenti. Ottimo! Ora, ogni volta che non siamo certi del nostro codice (in altre parole, sempre…), possiamo utilizzare il mirroring e i deploy canary.

Timeout e tentativi

Ma non sempre i bug si trovano nel codice. Nella lista di “8 miti dell'informatica distribuita”, il primo errore comune è l'opinione errata che “la rete sia affidabile”. In realtà, la rete non è affidabile, ed è per questo che abbiamo bisogno di timeout (timeouts) e tentativi (retries).

Per la dimostrazione, continueremo a utilizzare il problema attuale sa-logic (buggy), mentre simuleremo l'inaffidabilità della rete con errori casuali.

Supponiamo che il nostro servizio con bug abbia 1/3 di probabilità di rispondere troppo tardi, 1/3 di chiudere con un errore Internal Server Error e 1/3 di restituire la pagina con successo.

Per attenuare le conseguenze di tali problemi e migliorare l'esperienza degli utenti, possiamo:

  1. aggiungere un timeout, se il servizio risponde oltre 8 secondi,
  2. riprovare, se la richiesta fallisce.

Per implementarlo, utilizzeremo la seguente definizione della risorsa (sa-logic-retries-timeouts-vs.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: sa-logic
spec:
  hosts:
    - sa-logic
  http:
  - route: 
    - destination: 
        host: sa-logic
        subset: v1
      weight: 50
    - destination: 
        host: sa-logic
        subset: v2
      weight: 50
    timeout: 8s           # 1
    retries:
      attempts: 3         # 2
      perTryTimeout: 3s # 3

  1. Il timeout per la richiesta è impostato a 8 secondi;
  2. I tentativi di richieste vengono effettuati 3 volte;
  3. Ogni tentativo è considerato non riuscito se il tempo di risposta supera i 3 secondi.

In questo modo abbiamo ottimizzato, poiché l'utente non dovrà attendere più di 8 secondi e faremo tre nuovi tentativi di ottenere una risposta in caso di errori, aumentando le possibilità di una risposta positiva.

Applica la configurazione aggiornata con il seguente comando:

$ kubectl apply -f resource-manifests/istio/retries/sa-logic-retries-timeouts-vs.yaml
virtualservice.networking.istio.io/sa-logic configurato

E controlla nei grafici Grafana che il numero delle risposte riuscite sia aumentato:

Torna ai microservizi con Istio. Parte 2
Miglioramenti nelle statistiche delle risposte riuscite dopo aver aggiunto timeout e ripetizioni

Prima di passare alla sezione successiva (o meglio — già alla parte successiva dell'articolo, poiché in questa non ci saranno più esperimenti pratici — ndr), rimuovi sa-logic-buggy e VirtualService eseguendo i seguenti comandi:

$ kubectl delete deployment sa-logic-buggy
deployment.extensions “sa-logic-buggy” eliminato
$ kubectl delete virtualservice sa-logic
virtualservice.networking.istio.io “sa-logic” eliminato

Pattern Circuit Breaker e Bulkhead

Si tratta di due importanti pattern nell'architettura dei microservizi che permettono il recupero autonomo (self-healing) dei servizi.

Circuit Breaker («interruttore automatico») viene utilizzato per interrompere le richieste a un'istanza di servizio considerata non sana, ripristinandola mentre le richieste dei clienti vengono reindirizzate a istanze sane di questo servizio (ciò aumenta il tasso di risposte riuscite). (Nota del traduttore: Una descrizione più dettagliata del pattern può essere trovata, per esempio, qui.)

Bulkhead («paratia») isola i guasti nei servizi dall'impatto sull'intero sistema. Ad esempio, se il servizio B è guasto e un altro servizio (cliente del servizio B) invia una richiesta al servizio B, esso consumerà il suo pool di thread e non potrà gestire altre richieste (anche se non riguardano il servizio B). (Nota del traduttore: Una descrizione più dettagliata del pattern può essere trovata, per esempio, qui.)

Ometterò i dettagli sull'implementazione di questi pattern, perché è facile trovarli in documentazione ufficiale, e ho già voglia di mostrare l'autenticazione e l'autorizzazione, di cui si parlerà nella parte successiva dell'articolo.

P.S. dal traduttore

Leggi anche nel nostro blog:

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster