Il principio di indeterminazione di Heisenberg afferma che non è possibile misurare simultaneamente la posizione di un oggetto e la sua velocità. Se un oggetto si muove, non ha una posizione definita. E se ha una posizione, significa che non ha velocità.

Per quanto riguarda i microservizi sulla piattaforma Red Hat OpenShift (e gestiti da Kubernetes), grazie al software open source appropriato possono riportare sia sulle loro prestazioni che sul loro stato di funzionamento. Questo, naturalmente, non contraddice il vecchio Heisenberg, ma elimina l'indeterminatezza nel lavoro con le applicazioni cloud. Istio facilita il tracciamento e il monitoraggio di tali applicazioni, mantenendo tutto sotto controllo.
Definiamo la terminologia
Sotto tracciamento (Tracing) intendiamo la registrazione delle attività di sistema. Sembra piuttosto generico, ma in realtà una delle regole principali qui è quella di scaricare i dati di tracciamento nel rispettivo archivio, senza preoccuparsi del loro formato. Tutto il lavoro di ricerca e analisi dei dati ricade sul consumatore. In Istio viene utilizzato il sistema di tracciamento Jaeger, che implementa il modello dati OpenTracing.
Tracce (Traces, e la parola "tracce" qui è usata nel senso di "impronte", come ad esempio nella perizia balistica) noi ci riferiamo ai dati che descrivono completamente il passaggio di una richiesta o di un'unità di lavoro, cosiddetto, "dall'inizio alla fine". Ad esempio, tutto ciò che accade dal momento in cui un utente preme un pulsante sulla pagina web fino al momento in cui vengono restituiti i dati, incluso tutti i microservizi coinvolti. Si può dire che una traccia descrive completamente (o modella) il passaggio della richiesta da e verso. Nell'interfaccia di Jaeger, le tracce vengono scomposte in componenti lungo l'asse temporale, un po' come una catena può essere scomposta in singoli anelli. Solo che invece di anelli, la traccia è composta da quelli che vengono chiamati span.
Span – è l'intervallo dall'inizio dell'esecuzione di un'unità di lavoro fino al suo completamento. Proseguendo con l'analogia, si può dire che ogni span rappresenta un singolo anello della catena. Uno span può avere (o non avere) uno o più span figli. Di conseguenza, lo span di livello più alto (root span) avrà la stessa durata complessiva della traccia a cui appartiene.
Monitoraggio – questa è, in sostanza, l'osservazione del vostro sistema – attraverso la UI o via strumenti di automazione. La base del monitoraggio sono i dati di tracciamento. In Istio, il monitoraggio è realizzato con Prometheus e ha una UI corrispondente. Prometheus supporta il monitoraggio automatico utilizzando avvisi Alerts e Alert Managers.
Lasciamo segni
Per abilitare il tracciamento, l'applicazione deve creare una collezione di span. Dopodiché, devono essere esportati in Jaeger, affinché quest'ultimo crei una rappresentazione visiva del tracciamento. Tra l'altro, questi span etichettano il nome dell'operazione, nonché i timestamp di inizio e fine. La trasmissione degli span avviene reindirizzando gli header HTTP delle richieste entranti verso quelle uscenti per Jaeger. A seconda del linguaggio di programmazione utilizzato, potrebbe essere necessaria una piccola modifica del codice sorgente dell'applicazione. Di seguito è fornito un esempio di codice Java (utilizzando il framework Spring Boot) che aggiunge gli header B3 (stile Zipkin) alla vostra richiesta nella classe di configurazione di Spring:

Vengono utilizzate le seguenti impostazioni degli header:

Se state usando Java, non è necessario modificare il codice ma basta aggiungere alcune righe al file POM di Maven e definire le variabili d'ambiente. Ecco le righe da aggiungere al file POM.XML per implementare il Jaeger Tracer Resolver:

Le relative variabili d'ambiente sono definite nel Dockerfile:

Tutto qui, ora è tutto configurato e i nostri microservizi inizieranno a generare dati di tracciamento.
Un'occhiata generale
Istio include un semplice pannello di controllo basato su Grafana. Quando tutto è configurato e funziona sulla piattaforma Red Hat OpenShift PaaS (nel nostro esempio Red Hat OpenShift e Kubernetes sono stati implementati su minishift), questo pannello viene avviato con il seguente comando:
open "$(minishift openshift service grafana -u)\/d\/1\/istio-dashboard?refresh=5⩝Id=1"
Il pannello Grafana consente di valutare rapidamente le prestazioni del sistema. Un frammento di questo pannello è mostrato nell'immagine sottostante:

Qui si vede che il microservizio customer chiama il microservizio preference v1, e quest'ultimo a sua volta chiama i microservizi recommendation v1 e v2. Nel pannello Grafana c'è un blocco Dashboard Row per metriche di alto livello, come il volume totale delle richieste (Global Request Volume), la percentuale delle richieste riuscite (success rates), e gli errori 4xx. Inoltre, c'è una visualizzazione Server Mesh con grafici per ogni servizio e un blocco Services Row per visualizzare dettagli specifici per ogni contenitore di ciascun servizio.
Ora approfondiamo
Con un tracciamento Istio ben configurato, si può, per così dire, esplorare l'analisi delle prestazioni del sistema direttamente 'out of the box'. Nella UI di Jaeger è possibile visualizzare le tracce e vedere quanto profondamente e lontano si estendono, oltre a identificare visivamente i colli di bottiglia nelle prestazioni. Utilizzando Red Hat OpenShift sulla piattaforma minishift, si avvia la UI di Jaeger con il seguente comando:
minishift openshift service jaeger-query --in-browser

Cosa si può dire sul tracciamento in questo schermo:
- È suddiviso in 7 span.
- Il tempo totale di esecuzione è di 6.99 ms.
- Il microservizio recommendation, che è l'ultimo nella catena, impiega 0.69 ms.
Diagrammi di questo tipo permettono di comprendere rapidamente la situazione quando le prestazioni dell'intero sistema sono compromesse a causa di un servizio singolo che non funziona correttamente.
Adesso rendiamo la situazione più complessa lanciando due istanze del microservizio recommendation:v2 con il comando oc scale —replicas=2 deployment/recommendation-v2. Ecco come saranno i nostri pod dopo questo:

Se ora torniamo su Jaeger e espandiamo lo span per il servizio recommendation, vedremo a quale pod vengono instradate le richieste. In questo modo, possiamo facilmente localizzare i rallentamenti a livello di un singolo pod. Bisogna prestare attenzione al campo node_id:

Dove e come si distribuiscono le richieste
Passiamo ora all'interfaccia Prometheus e, come ci si aspettava, vediamo che le richieste tra la seconda e la prima versione del servizio recommendation sono ripartite in un rapporto di 2:1, in base al numero di pod attivi. Inoltre, questo grafico cambierà dinamicamente durante il ridimensionamento dei pod, il che sarà particolarmente utile durante il Canary Deployment (analizzeremo più a fondo questo schema di distribuzione la prossima volta).

È solo l'inizio
In realtà, oggi abbiamo solo toccato leggermente il tesoro di informazioni utili su Jaeger, Grafana e Prometheus. In effetti, questo è stato il nostro obiettivo: indirizzarvi nella giusta direzione e aprire le prospettive su Istio.
E ricordate, tutto questo è già integrato in Istio. Utilizzando alcuni linguaggi di programmazione (ad esempio, Java) e framework (come Spring Boot), tutto questo può essere realizzato senza toccare il codice delle applicazioni. Sì, sarà necessario modificare leggermente il codice se utilizzate altri linguaggi, in particolare Node.js o C#. Ma poiché la tracciabilità (leggi, "tracciamento") è una delle condizioni essenziali per costruire sistemi cloud affidabili, dovrete comunque modificare il codice, che abbiate o meno Istio. Allora perché non investire i vostri sforzi in modo più produttivo?
Almeno per poter rispondere sempre alle domande "dove?" e "quanto velocemente?" con il 100% di certezza.
Chaos engineering in Istio: era così che era stato previsto
Saper rompere le cose aiuta a far sì che non si rompano.
Il testing del software è qualcosa di non solo complesso, ma anche importante. Allo stesso tempo, il testing della correttezza (ad esempio, se una funzione restituisce il risultato corretto) è una cosa, mentre testare in condizioni di rete inaffidabile è un compito completamente diverso (si tende a pensare che la rete funzioni sempre senza interruzioni, e questo è il primo dei tre errori più comuni relativi al calcolo distribuito). Una delle difficoltà nel risolvere questo problema riguarda il modo di simulare i guasti nel sistema o di introdurli deliberatamente, eseguendo quello che si chiama fault injection. Questo può essere fatto modificando il codice sorgente dell'applicazione. Ma in tal caso non testerete più il vostro codice originale, bensì una sua versione specificamente progettata per simulare guasti. Di conseguenza, rischiate di cadere nelle mortali braccia del fault injection e di imbattersi nei gaisemberg – guasti che scompaiono al tentativo di scoprirli.
Adesso vi mostreremo come Istio aiuta a gestire queste difficoltà in un batter d'occhio.
Com'è tutto quando va tutto bene.
Consideriamo il seguente scenario: abbiamo due pod per il nostro microservizio di raccomandazione, che abbiamo preso dal manuale di Istio. Un pod è contrassegnato come v1 e l'altro come v2. Come possiamo vedere, per ora funziona tutto perfettamente:

(A proposito, il numero a destra è semplicemente un contatore delle chiamate per ogni pod.)
Ma ciò di cui abbiamo bisogno non è affatto questo, giusto? Beh, proviamo a rompere tutto senza toccare il codice sorgente.
Stiamo causando interruzioni nel funzionamento del microservizio
Di seguito è fornito un file yaml per la regola di routing Istio, che in metà dei casi restituirà un errore (fallimento server 503):

Si prega di notare che specifichiamo chiaramente che in metà dei casi deve essere restituito un errore 503.
Ecco come apparirà uno screenshot del comando curl in esecuzione in un ciclo dopo aver attivato questa regola per simulare i fallimenti. Come vediamo, metà delle richieste restituisce un errore 503, indipendentemente dal pod a cui vengono inviate, sia esso v1 o v2:

Per ripristinare il normale funzionamento, è sufficiente rimuovere questa regola, nel nostro caso con il comando istioctl delete routerule recommendation-503 -n tutorial. Qui Tutorial è il nome del progetto Red Hat OpenShift in cui si trova il nostro tutorial su Istio.
Introduciamo ritardi artificiali
Gli errori 503 artificiali aiutano a testare il sistema per la resilienza ai guasti, ma la capacità di prevedere e gestire i ritardi dovrebbe impressionarti ancora di più. E i ritardi nella vita reale si verificano più frequentemente dei guasti. Un microservizio che lavora lentamente è un cancro che danneggia l'intero sistema. Grazie a Istio, è possibile testare il codice relativo alla gestione dei ritardi senza modificarlo. Per iniziare, mostreremo come farlo nel caso di ritardi di rete introdotti artificialmente.
Si prega di notare che dopo un test di questo tipo, potrebbe essere necessario (o desiderare) rivedere il proprio codice. La buona notizia qui è che, in tal caso, agirete in modo proattivo, non reattivo. È così che dovrebbe essere costruito il ciclo di sviluppo: codifica-test-feedback-codifica-test...
Ecco come appare la regola che... Anche se sapete cosa? Istio è così semplice e questo file yaml è così chiaro che tutto in questo esempio parla da sé, basta dare un'occhiata:

Nel metà dei casi avremo un ritardo di 7 secondi. E questo non è affatto lo stesso che se avessimo inserito nel codice sorgente il comando sleep, poiché Istio ritarda realmente la richiesta di 7 secondi. Poiché Istio supporta il tracciamento Jaeger, questo ritardo è chiaramente visibile nell'interfaccia utente di Jaeger, come mostrato nello screenshot qui sotto. Nota la lunga richiesta in alto a destra del diagramma: la sua durata è di 7,02 secondi:

Questo scenario consente di testare il codice in condizioni di ritardo di rete. Ed è chiaro che, eliminando questa regola, rimuoveremo il ritardo artificiale. Ripetiamoci, ma abbiamo fatto tutto ciò senza toccare il codice sorgente.
Non ritirarsi e non arrendersi
Un'altra funzione utile di Istio per il chaos engineering è il numero di tentativi di interazione con il servizio. Il senso qui è quello di non fermarsi quando la prima richiesta termina con un errore 503 — e così, forse, al N-esimo tentativo avremo successo. Forse il servizio ha semplicemente bisogno di una breve pausa per un motivo o per un altro. Sì, quella causa andrebbe indagata e risolta. Ma quello sarà da fare dopo, per ora cerchiamo di far funzionare il sistema.
Quindi, vogliamo che il servizio restituisca occasionalmente un errore 503, e Istio dopo questo proverà a riconnettersi. E qui è chiaramente necessario un modo per generare un errore 503, senza toccare il codice stesso...
Fermo, aspetta! Lo abbiamo appena fatto.
Questo file farà in modo che il servizio recommendation-v2 restituisca un errore 503 nel 50% dei casi:

È evidente che parte delle richieste terminerà con un errore:

E ora attiviamo la funzione Retry di Istio:

Questa regola di instradamento effettua tre tentativi con intervalli di due secondi e dovrebbe ridurre (ideale sarebbe eliminarle del tutto) gli errori 503:

Riassumendo: abbiamo fatto in modo che Istio, da un lato, generasse un errore 503 per metà delle richieste. E dall'altro, lo stesso Istio esegue tre tentativi di riconnettersi al servizio in caso di errore 503. Di conseguenza, tutto funziona semplicemente alla grande. Così, utilizzando la funzione Retry, abbiamo mantenuto la nostra promessa di non ritirarci e non arrenderci.
E sì, l'abbiamo fatto di nuovo, senza toccare il codice. Tutto ciò di cui avevamo bisogno erano due regole di instradamento di Istio:

Come non deludere l'utente o sette aspettano un solo
E adesso capovolgiamo la situazione e consideriamo uno scenario in cui non vale la pena ritirarsi o arrendersi solo per un periodo di tempo fisso. E poi bisogna semplicemente interrompere i tentativi di elaborare la richiesta, per non far aspettare tutti un servizio che sta causando ritardi. In altre parole, non proteggeremo una posizione persa, ma ci ritireremo su una linea di riserva, per non deludere l'utente del sito e non costringerlo a vagare nell'incertezza.
In Istio, è possibile impostare un timeout per l'esecuzione della richiesta. Se il servizio supera questo timeout, viene restituito un errore 504 (Gateway Timeout) – tutto questo avviene tramite la configurazione di Istio. Ma dovremo aggiungere nel codice sorgente del servizio un comando sleep (e poi, naturalmente, eseguire un rebuild e un redeploy), per simulare il funzionamento lento del servizio. Purtroppo, non c'è altro modo.
Quindi, abbiamo inserito un sleep di tre secondi nel codice del servizio recommendation v2, ricostruito l'immagine corrispondente e effettuato il redeploy del contenitore, e ora aggiungiamo un timeout utilizzando la seguente regola di instradamento di Istio:

Nello screenshot sopra, si vede che smettiamo di tentare di contattare il servizio recommendation se non riceviamo una risposta entro un secondo, ovvero ancora prima che si verifichi l'errore 504. Dopo aver applicato questa regola di instradamento (e aver aggiunto un sleep di tre secondi nel codice del servizio recommendation:v2), otteniamo quanto segue:

Ripetiamo, ma il timeout può essere impostato senza toccare il codice sorgente. Un ulteriore vantaggio qui è che ora puoi modificare il tuo codice in modo che risponda al timeout, e testare facilmente queste modifiche con Istio.
E adesso tutto insieme
Aggiungere un po' di caos tramite Istio è un ottimo modo per testare il tuo codice e l'affidabilità del tuo sistema in generale. I modelli di fallback, bulkhead e circuit breaker, i meccanismi per creare guasti e ritardi artificiali, nonché i retry e i timeout saranno molto utili nella creazione di sistemi cloud resilienti. In combinazione con Kubernetes e Red Hat OpenShift, questi strumenti aiuteranno ad affrontare il futuro con sicurezza.
Fonte: habr.com
