Torniamo ai microservizi con Istio. Parte 2

Torniamo ai microservizi con Istio. Parte 2

Nota. trad.: La prima parte Questa serie è stata dedicata all'introduzione delle funzionalità di Istio e alla loro dimostrazione in azione. Parleremo ora degli aspetti più complessi della configurazione e dell'utilizzo di questa rete di servizi e, in particolare, del routing ottimizzato e della gestione del traffico di rete.

Ti ricordiamo inoltre che l'articolo utilizza configurazioni (manifest per Kubernetes e Istio) dal repository istio-padronanza.

Gestione del traffico

Con Istio, nel cluster vengono visualizzate nuove funzionalità per fornire:

  • Instradamento dinamico delle richieste: implementazioni canary, test A/B;
  • Bilancio del carico: semplice e coerente, basato su hash;
  • Recupero dopo cadute: timeout, tentativi, interruttori automatici;
  • Inserimento di difetti: ritardi, richieste perdute, ecc.

Nel prosieguo dell'articolo, queste funzionalità verranno illustrate utilizzando l'applicazione selezionata come esempio e lungo il percorso verranno introdotti nuovi concetti. Il primo di questi concetti sarà DestinationRules (ovvero regole sul destinatario del traffico/richieste - trad. ca.), con l'aiuto del quale attiviamo i test A/B.

Test A/B: DestinationRules nella pratica

Il test A/B viene utilizzato nei casi in cui esistono due versioni di un'applicazione (di solito sono visivamente diverse) e non siamo sicuri al 100% quale migliorerà l'esperienza dell'utente. Pertanto, eseguiamo entrambe le versioni contemporaneamente e raccogliamo parametri.

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

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

Il manifesto di distribuzione per la versione verde differisce in due punti:

  1. L'immagine si basa su un tag diverso: istio-green,
  2. I baccelli hanno un'etichetta version: green.

Poiché entrambe le distribuzioni hanno un'etichetta app: sa-frontend,richieste instradate dal servizio virtuale sa-external-services per il servizio sa-frontend, verrà reindirizzato a tutte le sue istanze e il carico verrà distribuito algoritmo round-robin, che porterà alla seguente situazione:

Torniamo ai microservizi con Istio. Parte 2
I file richiesti non sono stati trovati

Questi file non sono stati trovati perché hanno nomi diversi nelle diverse 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, che richiede una versione di file statici, può essere inviato dal sistema di bilanciamento del carico ai pod che hanno una versione diversa, dove, per ovvi motivi, tali file non esistono. Pertanto, affinché l’applicazione funzioni, dobbiamo impostare una restrizione: “la stessa versione dell'applicazione che ha servito index.html dovrebbe servire le richieste successive'.

Ci arriveremo con un bilanciamento del carico coerente basato su hash (Bilanciamento del carico hash coerente). In questo caso le richieste dallo stesso client vengono inviate alla stessa istanza di backend, per il quale viene utilizzata una proprietà predefinita, ad esempio un'intestazione HTTP. Implementato utilizzando DestinationRules.

Regole di destinazione

Dopo Servizio virtuale inviata una richiesta al servizio desiderato, utilizzando DestinationRules possiamo definire le policy che verranno applicate al traffico destinato alle istanze di questo servizio:

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

Nota: l'impatto delle risorse Istio sul traffico di rete viene presentato qui in modo facile da comprendere. Nello specifico, la decisione su quale istanza inviare la richiesta viene presa dall'Envoy nel Gateway Ingress configurato nel CRD.

Con le regole di destinazione, possiamo configurare il bilanciamento del carico per utilizzare hash coerenti e garantire che la stessa istanza del servizio risponda allo stesso utente. La seguente configurazione consente di ottenere questo risultato (destinazionerule-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 version.

Applicare la configurazione con il seguente comando:

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

Ora esegui il comando seguente e assicurati di ottenere i file giusti quando specifichi l'intestazione version:

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

Nota: Per aggiungere valori diversi nell'intestazione e testare i risultati direttamente nel browser, è possibile utilizzare questa estensione a Chrome (o con questo per Firefox - ca. trad.).

In generale, DestinationRules ha più funzionalità nell'area del bilanciamento del carico: controlla i dettagli in documentazione ufficiale.

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

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

Mirroring: servizi virtuali in pratica

shadowing ("schermatura") o Mirroring (“mirroring”) utilizzato nei casi in cui vogliamo testare un cambiamento nella produzione senza influenzare gli utenti finali: per fare ciò, duplichiamo (“mirror”) le richieste in una seconda istanza in cui sono state apportate le modifiche desiderate e osserviamo le conseguenze. In poche parole, questo è il momento in cui il tuo collega sceglie il problema più critico e presenta una richiesta pull sotto forma di un pezzo di terra così enorme che nessuno può effettivamente esaminarlo.

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 created

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

$ kubectl get pods -l app=sa-logic --show-labels
NAME                              READY   LABELS
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 prende di mira i pod con un'etichetta app=sa-logic, quindi tutte le richieste verranno distribuite tra tutte le istanze:

Torniamo ai microservizi con Istio. Parte 2

... ma vogliamo che le richieste vengano inviate alle istanze v1 e rispecchiate nelle istanze v2:

Torniamo ai microservizi con Istio. Parte 2

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

Definizione di sottoinsiemi nelle regole di destinazione

Sottoinsiemi (sottoinsiemi) sono determinati 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. Ospite (host) definisce che tale norma si applica solo ai casi in cui il percorso va verso il servizio sa-logic;
  2. Titoli (name) i sottoinsiemi vengono utilizzati durante l'instradamento alle istanze del sottoinsieme;
  3. Etichetta (label) definisce le coppie chiave-valore a cui le istanze devono corrispondere per diventare parte del sottoinsieme.

Applicare la configurazione con il seguente comando:

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

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

  1. Instradato a un sottoinsieme v1,
  2. Specchiato in un sottoinsieme v2.

Il seguente manifesto ti consente di realizzare i tuoi piani (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 è necessaria alcuna spiegazione qui, quindi vediamolo in azione:

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

Aggiungiamo il carico chiamando il seguente comando:

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

Diamo un'occhiata ai risultati in Grafana, dove puoi vedere che la versione con bug (buggy) comporta un errore per circa il 60% delle richieste, ma nessuno di questi errori influisce sugli utenti finali poiché ricevono risposta da un servizio in esecuzione.

Torniamo ai microservizi con Istio. Parte 2
Risposte di successo di diverse versioni del servizio sa-logic

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

Lo so, potresti già pensare che i servizi virtuali siano semplici. Nella prossima sezione approfondiremo l'argomento dicendo che sono anche davvero fantastici.

Lancio delle Canarie

Canary Deployment è il processo di distribuzione di una nuova versione di un'applicazione a un numero limitato di utenti. Viene utilizzato per assicurarsi che non ci siano problemi nel rilascio e solo dopo, essendo già sicuri della sua qualità (del rilascio), distribuirlo ad altri utenti.оpubblico più vasto.

Per dimostrare le implementazioni Canary, continueremo a lavorare con un sottoinsieme buggy у sa-logic.

Non perdiamo tempo in sciocchezze e inviamo immediatamente il 20% degli utenti alla versione con bug (questo rappresenterà il nostro lancio canary) e il restante 80% al servizio normale. A tale scopo, utilizzare il seguente servizio virtuale (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 specifica la percentuale di richieste che verranno indirizzate a un destinatario o a un sottoinsieme del destinatario.

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

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

... e vedremo subito che alcune richieste portano a fallimenti:

$ 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 consentono implementazioni Canary: in questo caso, abbiamo ridotto il potenziale impatto dei problemi al 20% della base utenti. Meraviglioso! Ora, in ogni caso in cui non siamo sicuri del nostro codice (in altre parole, sempre...), possiamo utilizzare il mirroring e il canary rollout.

Timeout e tentativi

Ma non sempre i bug finiscono nel codice. Nell'elenco da "8 Idee sbagliate sul calcolo distribuito“In primo luogo c’è l’erronea convinzione che “la rete sia affidabile”. In realtà la rete no affidabile, e per questo motivo abbiamo bisogno di timeout (timeout) e riprova (riprova).

A scopo dimostrativo continueremo a utilizzare la stessa versione problematica sa-logic (buggy) e simuleremo l'inaffidabilità della rete con guasti casuali.

Lascia che il nostro servizio con bug abbia 1/3 di possibilità di impiegare troppo tempo per rispondere, 1/3 di possibilità di terminare con un errore interno del server e 1/3 di possibilità di restituire con successo la pagina.

Per mitigare l’impatto di tali problemi e migliorare la vita degli utenti, possiamo:

  1. aggiungi un timeout se il servizio impiega più di 8 secondi per rispondere,
  2. riprovare se la richiesta fallisce.

Per l'implementazione, utilizzeremo la seguente definizione di 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 della richiesta è fissato a 8 secondi;
  2. Le richieste vengono ritentate 3 volte;
  3. E ogni tentativo viene considerato non riuscito se il tempo di risposta supera i 3 secondi.

Si tratta di un'ottimizzazione perché l'utente non dovrà attendere più di 8 secondi e faremo tre nuovi tentativi per ottenere una risposta in caso di fallimenti, aumentando le possibilità di una risposta positiva.

Applicare 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 configured

E controlla nei grafici Grafana che il numero di risposte riuscite è aumentato sopra:

Torniamo ai microservizi con Istio. Parte 2
Miglioramenti nelle statistiche delle risposte riuscite dopo l'aggiunta di timeout e nuovi tentativi

Prima di passare alla sezione successiva (o meglio, alla parte successiva dell'articolo, perché in questo non ci saranno più esperimenti pratici - trad. ca.), eliminare sa-logic-buggy e VirtualService eseguendo i seguenti comandi:

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

Schemi di interruttori automatici e paratie

Stiamo parlando di due modelli importanti nell'architettura dei microservizi che consentono di ottenere l'auto-ripristino (autoguarigione) Servizi.

Interruttore ("interruttore") utilizzato per terminare le richieste che arrivano a un'istanza di un servizio considerato non integro e ripristinarlo mentre le richieste del client vengono reindirizzate a istanze integre di quel servizio (il che aumenta la percentuale di risposte riuscite). (Nota: una descrizione più dettagliata del modello può essere trovata, ad esempio, qui.)

Paratia ("partizione") isola gli errori di servizio dall'influenza sull'intero sistema. Ad esempio, il servizio B è interrotto e un altro servizio (il client del servizio B) effettua una richiesta al servizio B, provocando l'esaurimento del pool di thread e l'impossibilità di soddisfare altre richieste (anche se non provengono dal servizio B). (Nota: una descrizione più dettagliata del modello può essere trovata, ad esempio, qui.)

Ometterò i dettagli di implementazione di questi pattern perché sono facili da trovare documentazione ufficialee voglio anche mostrare l'autenticazione e l'autorizzazione, di cui parleremo nella parte successiva dell'articolo.

PS da traduttore

Leggi anche sul nostro blog:

Fonte: habr.com

Aggiungi un commento