Il principio di indeterminazione di Heisenberg afferma che non è possibile misurare contemporaneamente la posizione di un oggetto e la sua velocità. Se l'oggetto è in movimento, non ha una posizione definita. Se invece ha una posizione, allora non ha velocità.

Per quanto riguarda i microservizi sulla piattaforma Red Hat OpenShift (gestiti da Kubernetes), grazie al software open source, possono riportare contemporaneamente sia le loro prestazioni che la loro integrità. Questo, ovviamente, non contraddice il vecchio Heisenberg, ma elimina l'indeterminatezza nel lavoro con le applicazioni cloud. Istio consente di organizzare facilmente il tracciamento e il monitoraggio di tali applicazioni per mantenere tutto sotto controllo.
Definiamo la terminologia
Sotto tracciamento (Tracing) мы понимаем логирование системной активности. Звучит довольно общо, но на самом деле одно из основных правил здесь в том, чтобы сбрасывать данные трассировки в соответствующее хранилище, не заботясь об их форматировании. А вся работа по поиску и анализу данных возлагается на их потребителя. В Istio используется система трассировки Jaeger, реализующая модель данных OpenTracing.
Трассами (Traces, и слово «трассы» здесь используется в значении «следы», как например, в баллистической экспертизе) мы будем называть данные, которые полностью описывают прохождение запроса или единицу работы, как говорится, «от и до». Например, всё, что происходит с момента, когда пользователь жмет кнопку на веб-странице, и до момента возврата данных, включая все задействованные при этом микросервисы. Можно сказать, что одна трасса полностью описывает (или моделирует) прохождение запроса туда и обратно. В интерфейсе Jaeger трассы раскладывается на составляющие по оси времени, вроде того, как цепь можно разложить на отдельные звенья. Только вместо звеньев трасса состоит из так называемых span’ов.
Span – è l'intervallo dal momento in cui inizia l'esecuzione di un'unità di lavoro fino al suo completamento. Continuando l'analogia, si può dire che ogni span rappresenti un anello separato della catena. Uno span può avere (o non avere) uno o più span figli. Di conseguenza, lo span di livello superiore (root span) avrà la stessa durata complessiva della traccia a cui si riferisce.
Monitoraggio – è, in effetti, l'osservazione stessa del tuo sistema – a occhi, attraverso l'interfaccia utente o tramite strumenti di automazione. Alla base del monitoraggio ci sono i dati di tracciamento. In Istio, il monitoraggio è realizzato tramite Prometheus e ha una UI corrispondente. Prometheus supporta il monitoraggio automatico con avvisi (Alerts) e gestori di avvisi (Alert Managers).
Facciamo delle tacche
Per abilitare la tracciatura, l'applicazione deve creare una collezione di span. Questi devono essere esportati in Jaeger, che a sua volta genera una rappresentazione visiva della tracciatura. Tra le altre cose, questi span contrassegnano il nome dell'operazione e le relative marcature temporali di inizio e fine. La trasmissione degli span avviene tramite il reindirizzamento degli header HTTP destinati a Jaeger dalle richieste in entrata a quelle in uscita. A seconda del linguaggio di programmazione utilizzato, può essere necessaria una piccola modifica del codice sorgente delle applicazioni. Di seguito è riportato un esempio di codice Java (utilizzando il framework Spring Boot) che aggiunge gli header B3 (in stile Zipkin) alla tua richiesta nella classe di configurazione di Spring:

Vengono utilizzate le seguenti impostazioni degli header:

Se usi Java, puoi lasciare il codice invariato, ma devi semplicemente aggiungere alcune righe nel file POM di Maven e impostare le variabili d'ambiente. Ecco quali righe devono essere aggiunte al file POM.XML per integrare il Jaeger Tracer Resolver:

E le corrispondenti variabili d'ambiente vengono impostate nel Dockerfile:

Tutto è pronto, ora tutto è configurato e i nostri microservizi inizieranno a generare dati di tracciamento.
Diamo un'occhiata generale.
Istio include un semplice pannello di controllo basato su Grafana. Una volta che tutto è configurato e funzionante sulla piattaforma Red Hat OpenShift PaaS (nel nostro esempio, Red Hat OpenShift e Kubernetes sono implementati su minishift), questo pannello può essere 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 qui sotto:

Qui possiamo vedere che il microservizio customer chiama il microservizio preference v1, che a sua volta chiama i microservizi recommendation v1 e v2. Nel pannello Grafana ci sono sezioni Dashboard Row per metriche di alto livello, come il volume totale delle richieste (Global Request Volume), le percentuali di richieste riuscite (success rates) e gli errori 4xx. Inoltre, c'è una visualizzazione Server Mesh con grafici per ciascun servizio e una sezione Services Row per visualizzare dettagli specifici su ciascun contenitore per ogni servizio.
Ora andiamo più a fondo.
Una tracciatura di Istio ben configurata consente di analizzare rapidamente le prestazioni del sistema, letteralmente "pronta all'uso". Nell'interfaccia utente di Jaeger, è possibile visualizzare le tracciature e vedere quanto in profondità si estendono, oltre a individuare visivamente i colli di bottiglia delle prestazioni. Se si utilizza Red Hat OpenShift sulla piattaforma minishift, si avvia l'interfaccia utente di Jaeger con il seguente comando:
minishift openshift service jaeger-query --in-browser

Cosa si può dire sulla tracciatura in questo screenshot:
- È suddivisa 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 consentono di comprendere rapidamente la situazione quando le prestazioni dell'intero sistema sono compromesse a causa di un servizio che non funziona bene.
Ora rendiamo la sfida più complessa e avviamo due istanze del microservizio recommendation:v2 con il comando oc scale --replicas=2 deployment/recommendation-v2. Ecco quali saranno i nostri pod dopo questo.

Se ora torniamo su Jaeger ed 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 pod specifico. In tal caso, dobbiamo prestare attenzione al campo node_id:

Dove e come si muove tutto
Ora ci spostiamo nell'interfaccia di Prometheus e, come prevedibile, vediamo che le richieste tra la seconda e la prima versione del servizio recommendation si dividono nel rapporto 2:1, precisamente in base al numero di pod attivi. Inoltre, questo grafico cambierà dinamicamente durante il ridimensionamento dei pod verso l'alto e verso il basso, il che sarà particolarmente utile durante il Canary Deployment (tratteremo questa strategia di distribuzione più dettagliatamente la prossima volta).

Tutto è solo all'inizio
In effetti, oggi abbiamo toccato solo leggermente la vasta sorgente di informazioni utili su Jaeger, Grafana e Prometheus. In realtà, questo era il nostro obiettivo: indirizzarvi nella direzione giusta e aprire le prospettive di Istio.
E ricorda, tutto questo è già integrato in Istio. Utilizzando determinati linguaggi di programmazione (come Java) e framework (come Spring Boot), è possibile implementare tutto questo senza alterare il codice delle applicazioni. Certo, il codice dovrà essere leggermente modificato se utilizzi altri linguaggi, in particolare Node.js o C#. Tuttavia, poiché il tracciamento (leggi, "tracciamento") è una condizione imprescindibile per la creazione di sistemi cloud robusti, dovrai comunque modificare il codice, che tu abbia Istio o meno. Quindi, perché non sfruttare i tuoi sforzi in modo più utile?
Almeno per poter rispondere sempre alle domande "dove?" e "quanto velocemente?" con certezza al 100%.
Chaos engineering in Istio: così era previsto
Sapere come rompere le cose aiuta a fare in modo che non si rompano
Il testing del software è una questione non solo complessa, ma anche fondamentale. D'altra parte, testare la correttezza (ad esempio, verificare se una funzione restituisce il risultato corretto) è una cosa, mentre testare in condizioni di rete inaffidabile è un compito completamente diverso (si ritiene spesso che la rete funzioni sempre senza interruzioni, e questo è uno dei principali miti sulle computazioni distribuite). Una delle difficoltà nel risolvere questo problema è come simulare malfunzionamenti nel sistema o introdurli intenzionalmente, eseguendo quella che viene chiamata fault injection. Questo può essere fatto modificando il codice sorgente dell'applicazione stessa. Ma in tal caso, non stai testando il tuo codice originale, ma una sua versione che imita appositamente i malfunzionamenti. Di conseguenza, rischi di finire nelle spire mortali della fault injection e di incappare in geyser bugs – malfunzionamenti che scompaiono quando si cerca di individuarli.
Ora mostreremo come Istio aiuta a gestire queste complessità in un batter d'occhio.
Com'è tutto quando va a meraviglia
Consideriamo il seguente scenario: abbiamo due pod per il nostro microservizio di raccomandazione, preso da un tutorial su Istio. Un pod è contrassegnato come v1, mentre l'altro come v2. Come possiamo vedere, tutto funziona perfettamente fino ad ora:

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

Si noti che specifichiamo esplicitamente che in metà dei casi deve restituire un errore 503.
Ecco come apparirà uno screenshot del comando curl eseguito in un ciclo dopo che attiviamo questa regola per simulare i guasti. Come possiamo vedere, metà delle richieste restituisce un errore 503, indipendentemente da quale pod – v1 o v2 – siano indirizzate:

Per ripristinare il normale funzionamento, basta 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 verificarne la resilienza, ma la capacità di prevedere e gestire i ritardi dovrebbe impressionarvi ancora di più. Inoltre, i ritardi nella vita reale si verificano più frequentemente dei guasti. Un microservizio lento è un veleno 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 imputati artificialmente.
Tenete presente che dopo questo tipo di test, potrebbe essere necessario (o desiderabile) rielaborare il vostro codice. La buona notizia è che in questo caso agirete in modo proattivo e non reattivo. È proprio così che deve 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:

In metà dei casi ci sarà un ritardo di 7 secondi. E questo non è affatto come se avessimo inserito nel codice sorgente un comando sleep, poiché Istio ritarda effettivamente la richiesta di 7 secondi. Poiché Istio supporta il tracciamento di Jaeger, questo ritardo è facilmente osservabile nell'interfaccia di Jaeger, come mostrato nello screenshot qui sotto. Nota la lunga richiesta nell'angolo in alto a destra del grafico – la sua durata è di 7.02 secondi:

Questo scenario consente di testare il codice in condizioni di ritardi di rete. E chiaramente, rimuovendo questa regola, elimineremo il ritardo artificiale. Ripetiamolo, ma abbiamo fatto tutto questo senza toccare il codice sorgente.
Non ritirarsi e non arrendersi
Un'altra funzione utile per il chaos engineering in Istio è il ripetuto tentativo di contattare un servizio un numero prestabilito di volte. L'idea è di non smettere di provare quando la prima richiesta termina con un errore 503 - e magari, al N-esimo tentativo, avremo fortuna. Potrebbe essere che il servizio si sia semplicemente preso una pausa momentanea per qualche motivo. Sì, è necessario indagare e risolvere questo problema. Ma per ora, proviamo a mantenere il sistema in funzione.
Quindi, vogliamo che il servizio restituisca occasionalmente un errore 503, e Istio dopo tenterà nuovamente di collegarsi ad esso. Ed è chiaro che ci serve un modo per generare l'errore 503, senza modificare il codice stesso...
Aspetta, fermi! Abbiamo già fatto questo.
Questo file farà in modo che il servizio recommendation-v2 restituisca un errore 503 nella metà dei casi:

È ovvio che parte delle richieste terminerà con un fallimento:

Ora attiviamo la funzione Retry di Istio:

Questa regola di routing effettuerà tre tentativi con un intervallo di due secondi e dovrebbe ridurre (e idealmente eliminare completamente) gli errori 503:

Riepiloghiamo: abbiamo configurato Istio in modo che, da un lato, generi un errore 503 per metà delle richieste. Dall'altro lato, lo stesso Istio effettua tre tentativi di riconnettersi al servizio in caso di errore 503. Il risultato? Tutto funziona meravigliosamente. Così, utilizzando la funzione Retry, abbiamo mantenuto la nostra promessa di non arrenderci e di non deflettere.
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 non aspettano
E ora, capovolgiamo la situazione e consideriamo uno scenario in cui non arrendersi e non deflettere sia valido solo per un certo periodo di tempo fisso. Dopodiché, dobbiamo semplicemente interrompere i tentativi di elaborare la richiesta, per non costringere tutti ad aspettare un servizio che rallenta. In altre parole, non proteggiamo una posizione persa, ma ci ritiriamo su una linea di riserva per non deludere l'utente del sito e non costringerlo a trovarsi nell'incertezza.
In Istio, è possibile impostare un timeout per l'esecuzione delle richieste. Se il servizio supera questo timeout, verrà restituito un errore 504 (Gateway Timeout) – anche questo è gestito tramite la configurazione di Istio. Tuttavia, dovremo aggiungere nel codice sorgente del servizio un comando sleep (e poi naturalmente eseguire il rebuild e il redeploy) per simulare un funzionamento lento del servizio. Purtroppo, altrimenti non è possibile.
Quindi, abbiamo inserito un sleep di tre secondi nel codice del servizio recommendation v2, ricompilato l'immagine corrispondente e fatto il redeploy del container, ora aggiungiamo un timeout utilizzando la seguente regola di routing di Istio:

Nello screenshot sopra, si può vedere che interrompiamo il tentativo di contattare il servizio recommendation se non riceviamo risposta entro un secondo, cioè ancora prima che si verifichi l'errore 504. Dopo aver applicato questa regola di routing (e aggiunto un sleep di tre secondi nel codice del servizio recommendation:v2), otterremo questo:

Ribadiamo, ma il timeout può essere impostato senza toccare il codice sorgente. Un ulteriore vantaggio qui è che ora puoi modificare il tuo codice in modo da reagire al timeout e testare facilmente queste modifiche con Istio.
E ora tutto insieme
Introdurre un po' di caos con Istio è un ottimo modo per testare il tuo codice e la resilienza del tuo sistema nel suo complesso. I modelli di fallback, bulkhead e circuit breaker, i meccanismi per generare malfunzionamenti e ritardi artificiali, nonché i tentativi di ripetizione e i timeout, saranno molto utili per costruire sistemi cloud resilienti. In combinazione con Kubernetes e Red Hat OpenShift, questi strumenti aiuteranno ad affrontare il futuro con sicurezza.
Fonte: habr.com
