Netramesh: soluzione mesh di servizi leggera

Nel passaggio da un'applicazione monolitica a un'architettura a microservizi, ci troviamo ad affrontare nuove sfide.

In un'applicazione monolitica, in genere è abbastanza semplice determinare in quale parte del sistema si è verificato l'errore. Molto probabilmente il problema è nel codice del monolite stesso o nel database. Ma quando cominciamo a cercare un problema in un’architettura a microservizi, tutto non è più così ovvio. Dobbiamo trovare l'intero percorso seguito dalla richiesta dall'inizio alla fine e selezionarlo tra centinaia di microservizi. Inoltre, molti di loro dispongono anche di proprie strutture di stoccaggio, che possono causare anche errori logici, nonché problemi di prestazioni e tolleranza agli errori.

Netramesh: soluzione mesh di servizi leggera

Ho cercato a lungo uno strumento che aiutasse a far fronte a tali problemi (ne ho scritto su Habré: 1, 2), ma alla fine ho creato la mia soluzione open source. In questo articolo parlo dei vantaggi dell'approccio service mesh e condivido un nuovo strumento per la sua implementazione.

La traccia distribuita è una soluzione comune al problema della ricerca di errori nei sistemi distribuiti. Ma cosa succede se questo approccio alla raccolta di informazioni sulle interazioni di rete non è ancora stato implementato nel sistema o, peggio, in parte del sistema funziona già correttamente, ma in parte no, poiché non è stato aggiunto ai vecchi servizi? ? Per determinare l’esatta causa principale di un problema, è necessario avere un quadro completo di ciò che sta accadendo nel sistema. È particolarmente importante comprendere quali microservizi sono coinvolti nei principali percorsi aziendali critici.

Qui può venirci in aiuto l’approccio service mesh, che si occuperà di tutti i macchinari di raccolta delle informazioni di rete ad un livello inferiore a quello in cui operano i servizi stessi. Questo approccio ci consente di intercettare tutto il traffico e analizzarlo al volo. Inoltre, le applicazioni non devono nemmeno sapere nulla al riguardo.

Approccio della rete di servizi

L'idea principale dell'approccio service mesh è quella di aggiungere un altro livello infrastrutturale sulla rete, che ci consentirà di fare qualsiasi cosa con l'interazione tra servizi. La maggior parte delle implementazioni funziona nel modo seguente: a ciascun microservizio viene aggiunto un contenitore sidecar aggiuntivo con un proxy trasparente, attraverso il quale viene fatto passare tutto il traffico in entrata e in uscita del servizio. Ed è proprio qui che possiamo bilanciare i clienti, applicare politiche di sicurezza, imporre restrizioni sul numero di richieste e raccogliere informazioni importanti sull'interazione dei servizi in produzione.

Netramesh: soluzione mesh di servizi leggera

Soluzioni

Esistono già diverse implementazioni di questo approccio: Istio и linkerd2. Forniscono molte funzionalità pronte all'uso. Ma allo stesso tempo, si verifica un grosso sovraccarico di risorse. Inoltre, quanto più grande è il cluster in cui opera tale sistema, tanto maggiori saranno le risorse necessarie per mantenere la nuova infrastruttura. In Avito gestiamo cluster Kubernetes che contengono migliaia di istanze del servizio (e il loro numero continua a crescere rapidamente). Nella sua attuale implementazione, Istio consuma circa 300 Mb di RAM per istanza del servizio. A causa dell'elevato numero di possibilità, il bilanciamento trasparente influisce anche sul tempo di risposta complessivo dei servizi (fino a 10 ms).

Di conseguenza, abbiamo esaminato esattamente le funzionalità di cui avevamo bisogno in questo momento e abbiamo deciso che il motivo principale per cui abbiamo iniziato a implementare tali soluzioni era la capacità di raccogliere informazioni di tracciamento dall’intero sistema in modo trasparente. Volevamo anche avere il controllo sull'interazione dei servizi ed eseguire varie manipolazioni con le intestazioni trasferite tra i servizi.

Di conseguenza, siamo giunti alla nostra decisione:  Netramesh.

Netramesh

Netramesh è una soluzione service mesh leggera con la possibilità di scalare all'infinito, indipendentemente dal numero di servizi nel sistema.

Gli obiettivi principali della nuova soluzione erano un basso consumo di risorse e prestazioni elevate. Tra le caratteristiche principali, abbiamo subito voluto poter inviare in modo trasparente i tracing span al nostro sistema Jaeger.

Oggi, la maggior parte delle soluzioni cloud sono implementate a Golang. E, naturalmente, ci sono ragioni per questo. Scrivere applicazioni di rete in Golang che funzionino in modo asincrono con l'I/O e si adattino ai core secondo necessità è comodo e abbastanza semplice. E, cosa molto importante, le prestazioni sono sufficienti per risolvere questo problema. Ecco perché abbiamo scelto anche Golang.

Производительность

Abbiamo concentrato i nostri sforzi sul raggiungimento della massima produttività. Per una soluzione distribuita accanto a ciascuna istanza del servizio, è richiesto un piccolo consumo di RAM e tempo di CPU. E, naturalmente, anche il ritardo nella risposta dovrebbe essere minimo.

Vediamo che risultati abbiamo ottenuto.

RAM

Netramesh consuma ~10 Mb senza traffico e 50 Mb al massimo con un carico fino a 10000 RPS per istanza.

Il proxy Istio Envoy consuma sempre ~300 Mb nei nostri cluster con migliaia di istanze. Ciò non ne consente la scalabilità all'intero cluster.

Netramesh: soluzione mesh di servizi leggera

Netramesh: soluzione mesh di servizi leggera

Con Netramesh abbiamo ottenuto una riduzione di circa 10 volte del consumo di memoria.

CPU

L'utilizzo della CPU è relativamente uguale sotto carico. Dipende dal numero di richieste per unità di tempo al sidecar. Valori a 3000 richieste al secondo al picco:

Netramesh: soluzione mesh di servizi leggera

Netramesh: soluzione mesh di servizi leggera

C'è un altro punto importante: Netramesh: una soluzione senza piano di controllo e senza carico non consuma tempo della CPU. Con Istio, i sidecar aggiornano sempre gli endpoint del servizio. Di conseguenza, possiamo vedere questa immagine senza carico:

Netramesh: soluzione mesh di servizi leggera

Usiamo HTTP/1 per la comunicazione tra i servizi. L'aumento del tempo di risposta per Istio durante l'inoltro tramite Envoy è stato fino a 5-10 ms, il che è parecchio per i servizi pronti a rispondere in un millisecondo. Con Netramesh questo tempo è sceso a 0.5-2ms.

scalabilità

La piccola quantità di risorse consumate da ciascun proxy consente di posizionarlo accanto a ciascun servizio. Netramesh è stato creato intenzionalmente senza un componente del piano di controllo per mantenere semplicemente leggero ciascun sidecar. Spesso nelle soluzioni service mesh, il piano di controllo distribuisce le informazioni di rilevamento dei servizi a ciascun sidecar. Insieme ad esso vengono fornite informazioni sui timeout e sulle impostazioni di bilanciamento. Tutto ciò ti consente di fare molte cose utili, ma, sfortunatamente, aumenta le dimensioni dei sidecar.

Scoperta del servizio

Netramesh: soluzione mesh di servizi leggera

Netramesh non aggiunge alcun meccanismo aggiuntivo per il rilevamento dei servizi. Tutto il traffico viene proxy in modo trasparente tramite il sidecar Netra.

Netramesh supporta il protocollo applicativo HTTP/1. Per definirlo, viene utilizzato un elenco configurabile di porte. In genere, il sistema dispone di diverse porte attraverso le quali avviene la comunicazione HTTP. Ad esempio per l'interazione tra servizi e richieste esterne utilizziamo 80, 8890, 8080. In questo caso possono essere impostati tramite una variabile d'ambiente NETRA_HTTP_PORTS.

Se utilizzi Kubernetes come orchestratore e il relativo meccanismo di entità servizio per la comunicazione all'interno del cluster tra servizi, il meccanismo rimane esattamente lo stesso. Innanzitutto, il microservizio ottiene un indirizzo IP del servizio utilizzando kube-dns e apre una nuova connessione ad esso. Questa connessione viene prima stabilita con il netra-sidecar locale e tutti i pacchetti TCP arrivano inizialmente a netra. Successivamente, netra-sidecar stabilisce una connessione con la destinazione originale. Il NAT sul pod IP sul nodo rimane esattamente lo stesso di senza netra.

Tracciamento distribuito e inoltro del contesto

Netramesh fornisce le funzionalità necessarie per inviare intervalli di traccia sulle interazioni HTTP. Netra-sidecar analizza il protocollo HTTP, misura i ritardi delle richieste ed estrae le informazioni necessarie dalle intestazioni HTTP. Alla fine, otteniamo tutte le tracce in un unico sistema Jaeger. Per una configurazione più dettagliata, puoi anche utilizzare le variabili d'ambiente fornite dalla libreria ufficiale Jaeger vai alla libreria.

Netramesh: soluzione mesh di servizi leggera

Netramesh: soluzione mesh di servizi leggera

Ma c'è un problema. Fino a quando i servizi non generano e inviano un'intestazione uber speciale, non vedremo gli intervalli di traccia connessi nel sistema. E questo è ciò di cui abbiamo bisogno per trovare rapidamente la causa dei problemi. Anche in questo caso Netramesh ha una soluzione. I proxy leggono le intestazioni HTTP e, se non contengono l'ID di traccia uber, ne generano uno. Netramesh memorizza inoltre le informazioni sulle richieste in entrata e in uscita in un sidecar e le abbina arricchendole con le necessarie intestazioni delle richieste in uscita. Tutto quello che devi fare nei servizi è inviare una sola intestazione X-Request-Id, che può essere configurato utilizzando una variabile di ambiente NETRA_HTTP_REQUEST_ID_HEADER_NAME. Per controllare la dimensione del contesto in Netramesh, puoi impostare le seguenti variabili d'ambiente: NETRA_TRACING_CONTEXT_EXPIRATION_MILLISECONDS (il tempo per il quale il contesto verrà archiviato) e NETRA_TRACING_CONTEXT_CLEANUP_INTERVAL (frequenza di pulizia del contesto).

È anche possibile combinare più percorsi sul proprio sistema contrassegnandoli con uno speciale token di sessione. Netra ti consente di installare HTTP_HEADER_TAG_MAP per trasformare le intestazioni HTTP nei corrispondenti tag di intervallo di tracciamento. Questo può essere particolarmente utile per i test. Dopo aver superato il test funzionale, puoi vedere quale parte del sistema è stata interessata filtrando in base alla chiave di sessione corrispondente.

Determinazione della fonte della richiesta

Per determinare da dove proviene la richiesta, puoi utilizzare la funzionalità di aggiunta automatica di un'intestazione con l'origine. Utilizzando una variabile d'ambiente NETRA_HTTP_X_SOURCE_HEADER_NAME È possibile specificare un nome di intestazione che verrà installato automaticamente. Usando NETRA_HTTP_X_SOURCE_VALUE è possibile impostare il valore su cui verrà impostata l'intestazione X-Source per tutte le richieste in uscita.

Ciò consente che la distribuzione di questa utile intestazione sia distribuita uniformemente su tutta la rete. Quindi puoi utilizzarlo nei servizi e aggiungerlo a log e parametri.

Instradamento del traffico e componenti interni di Netramesh

Netramesh è costituito da due componenti principali. Il primo, netra-init, imposta le regole di rete per intercettare il traffico. Lui usa regole di reindirizzamento iptables intercettare tutto o parte del traffico sul sidecar, che è il secondo componente principale di Netramesh. Puoi configurare quali porte devono essere intercettate per le sessioni TCP in entrata e in uscita: INBOUND_INTERCEPT_PORTS, OUTBOUND_INTERCEPT_PORTS.

Lo strumento ha anche una caratteristica interessante: il routing probabilistico. Se utilizzi Netramesh esclusivamente per la raccolta degli span di tracciamento, in un ambiente di produzione puoi risparmiare risorse e abilitare il routing probabilistico utilizzando le variabili NETRA_INBOUND_PROBABILITY и NETRA_OUTBOUND_PROBABILITY (da 0 a 1). Il valore predefinito è 1 (tutto il traffico viene intercettato).

Dopo l'intercettazione riuscita, il sidecar netra accetta la nuova connessione e la utilizza SO_ORIGINAL_DST opzione socket per ottenere la destinazione originale. Netra apre quindi una nuova connessione all'indirizzo IP originale e stabilisce una comunicazione TCP bidirezionale tra le parti, ascoltando tutto il traffico che passa. Se la porta è definita come HTTP, Netra tenta di analizzarla e tracciarla. Se l'analisi HTTP fallisce, Netra ritorna a TCP e invia in modo trasparente i byte.

Costruire un grafico delle dipendenze

Dopo aver ricevuto una grande quantità di informazioni di tracciamento in Jaeger, desidero ottenere un grafico completo delle interazioni nel sistema. Ma se il tuo sistema è molto carico e si accumulano miliardi di intervalli di tracciamento ogni giorno, aggregarli non diventa un compito così facile. Esiste un modo ufficiale per farlo: dipendenze-scintilla. Tuttavia, ci vorranno ore per costruire un grafico completo e ti costringerai a scaricare l'intero set di dati da Jaeger nelle ultime XNUMX ore.

Se si utilizza Elasticsearch per archiviare gli intervalli di traccia, è possibile utilizzare una semplice utility Golang, che costruirà lo stesso grafico in pochi minuti utilizzando le caratteristiche e le capacità di Elasticsearch.

Netramesh: soluzione mesh di servizi leggera

Come utilizzare Netramesh

Netra può essere facilmente aggiunto a qualsiasi servizio che esegue qualsiasi orchestratore. Puoi vedere un esempio qui.

Al momento, Netra non ha la capacità di implementare automaticamente i sidecar ai servizi, ma ci sono piani per l'implementazione.

Il futuro di Netramesh

Scopo principale Netramesh è quello di ottenere costi minimi delle risorse e prestazioni elevate, fornendo capacità di base per l'osservabilità e il controllo della comunicazione tra servizi.

In futuro, Netramesh supporterà altri protocolli a livello di applicazione oltre a HTTP. Il routing L7 sarà disponibile nel prossimo futuro.

Utilizza Netramesh se riscontri problemi simili e scrivici con domande e suggerimenti.

Fonte: habr.com

Aggiungi un commento