Debug dei problemi di rete nei microservizi

Diamo uno sguardo ad alcuni dei possibili punti deboli che una struttura a microservizi può manifestare, quindi esploriamo alcuni dei possibili strumenti che possiamo utilizzare per affrontarli.

 

Article: Understanding Software System Behaviour With ML and Time Series Data - Mobile Monitoring Solutions


In generale, molte delle nuove sfide di debug che si dovranno affrontare con i microservizi distribuiti possono essere classificate come problemi di rete tra le diverse parti dell’infrastruttura.

Si deve notare che la comunicazione tra servizi nei sistemi distribuiti viene implementata come comunicazione sincrona di  tipo request/response (REST, gRPC, GraphQL) o messaggistica asincrona guidata da eventi (Kafka, AMQP,MQTT e molti altri).

I meccanismi sincroni fino ad adesso sono stati chiari vincitori, almeno alla fine del 2020, perché è molto più facile sviluppare, testare e mantenere il codice sincrono. Ma portano con sé una serie di problemi e considerazioni. Diamo prima di tutto uno sguardo ad alcuni dei possibili punti di attrito, quindi esploriamo alcuni dei possibili strumenti che possiamo utilizzare per affrontarli.

Livelli di rete inconsistenti

I tuoi microservizi potrebbero essere distribuiti in diversi cloud pubblici o in locale, il che significa che il servizio tra tutti i livelli di rete potrà variare drasticamente tra i servizi. Questa è spesso la causa di timeout improvvisi e non riproducibili e picchi di maggiore latenza e bassa velocità raggiunta. Queste sono spesso una triste routine quotidiana, la maggior parte delle quali è fuori dal nostro controllo.

Service discovery

I microservizi sono dinamici, quindi dovrebbe esserlo anche il routing. Spesso nemmeno al servizio stesso è chiaro dove si trova esattamente nella topologia il servizio associato con cui si lui sta dialogando, quindi sono necessari strumenti specializzati per consentire a ciascun servizio di rilevare dinamicamente i suoi pari.

Errori a cascata e colli di bottiglia che si propagano

Qualsiasi microservizio potrebbe iniziare a rispondere più lentamente alle richieste di rete da altri servizi a causa di un elevato utilizzo di CPU, memoria insufficiente, query DB a esecuzione prolungata e altri fattori. Ciò potrebbe finire per provocare una reazione a catena che rallenterà altri servizi, causando ancora più colli di bottiglia o facendo perdere loro le connessioni.

Ripristino dagli errori e fault tolerance

I microservizi, per definizione, hanno molte più parti mobili che possono guastarsi lungo il percorso rispetto alle applicazioni monolitiche. Ciò rende necessario avere un piano di exit strategy dagli inevitabili errori di comunicazione. Questa stategia diviene sia critica che complicata.

Linguaggi differenti

Gli SDK di rete specifici del linguaggio del microservizio possono gestire diversi casi limite in un modo diverso, il che aggiunge instabilità e caos alla comunicazione tra i servizi.

Complessità del bilanciamento del carico

In un mondo di applicazioni monolitiche, il traffico è principalmente nord-sud (da Internet ai server delle applicazioni) e ci sono molte soluzioni ben note come gateway API e load balancer che si prendono cura del carico. Le applicazioni di microservizi comunicano tra loro costantemente, aggiungendo molto più traffico est-ovest, che introduce un ulteriore livello di complessità.

Limitazioni della scalabilità

Uno dei maggiori vantaggi dell’approccio a microservizi, come già accennato, è la scalabilità indipendente: ogni parte del sistema può essere scalata da sola. La comunicazione sincrona uccide letteralmente questo vantaggio: se il tuo API Gateway comunica in modo sincrono con un database o qualsiasi altro servizio downstream, qualsiasi picco di carico nel traffico nord-sud soprafferà immediatamente quei servizi downstream. Di conseguenza, tutti i servizi lungo la strada avranno bisogno di una scalabilità rapida e immediata.

Configurazioni di sicurezza difficili

Il traffico est-ovest richiede molti più certificati SSL, firewall, configurazioni dei criteri ACL e rafforzamenti di sicurezza non banali e soggetti a errori, soprattutto se eseguiti manualmente.

 

Building high performance microservices in finance with Apache Thrift

Per riassumere, si può dire che l’implementazione di uno stile sincrono di comunicazione tra i servizi  diverta contraddittorio se si vuole letteralmente spaccare un monolite in microservizi. Alcuni affermano addirittura che questo trasforma i tuoi microservizi in molti singoli monoliti che non comunicano più tra loro. Se questo appena scritto sembra un pò estremo, è pur sempre vero che i meccanismi RPC sincroni introducono accoppiamenti stretti, errori a cascata , colli di bottiglia e sovraccaricano i bilanciatori di carico e di rilevamento dei servizi. Questi problemi rendono difficile la scalabilità dell’applicazione.

Strumenti e soluzioni correnti

Distribuisci i microservizi con OpenShift

Per un’architettura di tipo request/response sincrona, un mesh di servizi (Service Mesh) è l’attuale soluzione standard de facto. In poche parole, una rete di servizi gestisce tutto il traffico da servizio a servizio, est-ovest. Consiste di tre parti principali: un data plane (in cui vivono le informazioni che devono essere spostate tra i servizi), un componente sidecar (che funge da livello di trasporto) e un control plane (che configura e controlla il data plane).

L’idea di una service mesh è quella di scaricare tutte le attività e i problemi di comunicazione tra i servizi su un livello di astrazione separato che si occupi di tutti questi problemi di trasporto, consentendo al codice del microservizio di concentrarsi solo sulla logica di business. una idea fantastica per chi sviluppa software scalabile tra cloud multipli. Le tipiche soluzioni di service mesh offrono almeno alcune delle seguenti funzionalità:

  • Funzionalità di controllo del traffico : regole di instradamento (routing),retries, failover, instradamento dinamico delle richieste per test A/B, rollouts graduali, canary release , circuit breackers, ecc.
  • Monitoraggio dell’integrità, come health check,  controlli dei timeouts, circuit bracking.
  • Applicazione di policy : throttling e traffic shaping, limititatori di velocità e quote.
  • Sicurezza : TLS e mTLS, segmentazione a livello di applicazione, gestione dei token.
  • Configurazione e gestione dei secrets
  • Osservabilità e monitoraggio del traffico : metriche principali (volume di richieste, percentuali di successo e latenze), tracciamento distribuito e altro ancora.

Le soluzioni di Service Mesh ( e i tools annessi) affrontano la maggior parte delle sfide sopra menzionate, eliminano la necessità di costosi gateway API e bilanciatori del carico per il traffico est-ovest, standardizzano la gestione dei problemi di rete e la configurazione tra i servizi costruiti su più linguaggi e si occupanodi fare scoprire i servizi tra loro.

Tuttavia, le il Service Mesh a volte non è un soluzione ideale e non è la panacea per  tutti i mali. Ragioaniamo un momento per parlare di alcune possibili insidie ​​che questi possono avere:

  • Relativamente nuova : la tecnologia è ancora nella sua fase iniziale di adozione ed è soggetta a modifiche costanti e di cambianmenti non retro-compatibili.
  • Costo : il Service Mesh può richiedere un investimento iniziale in una piattaforma, una spesa che può essere difficile da giustificare quando le applicazioni sono ancora in evoluzione.
  • Prestazioni : una riduzione delle prestazioni (sia latenza in rete che consumo di risorse di runtime) è inevitabile e praticamente imprevedibile.
  • Complessità operativa : la funzionalità Service Mesh può duplicare la logica dell’applicazione esistente e questo può portare, ad esempio, a tentativi ridondanti o transazioni duplicate. Inoltre, in virtù del fatto che un altro strato è stato aggiunto nel processo, bisogna anche occupoarsi di questo.
  • Le topologie multi-cluster in genere non sono ben supportate.

Lo svantaggio più importante, tuttavia, è la mancanza di supporto per una l’architettura asincrona guidata dagli eventi. Anche se qui non discuteremo della differenza tra comunicazione sincrona e asincrona dei microservizi, l’approccio asincrono si adatta molto meglio al paradigma dei microservizi.
Per ora, prima di parlre di queste migliorie gestionali, vediamo quali problemi e sfide offre la comunicazione asincrona insieme ai suoi vantaggi:

  • Transazioni distribuite : a causa della comunicazione asincrona tra i servizi, l’atomicità delle operazioni sul database potrebbe non essere raggiunta su qualche DB. Potrebbe essere necessario implementare un livello di astrazione aggiuntivo per rendere le transazioni atomiche e ACID, il che non è un compito banale: un protocollo di commit a due fasi può causare colli di bottiglia delle prestazioni (o anche deadlock!), e il modello Saga è piuttosto complicato, quindi i problemi di coerenza potrebbe essere piuttosto comune. Si noti che questo non è un problema di rete di per sé e, in senso stretto, è rilevante anche per la comunicazione sincrona.
  • TCO delle code dei messaggi:  Sistemi di code messaggi come AMPQ Kafka o MQTT non sono facili da integrare, testare, configurare e gestire. La manutenzione e le configurazioni diventano molto più semplici utilizzando soluzioni gestite (ad es. AWS SQS e SNS), ma potresti dover affrontare problemi di budget e vendor lock-in.
  • Osservabilità : capire cosa sta succedendo nelle applicazioni distribuite che comunicano in modo asincrono è difficile. I tre pilastri dell’osservabilità – log, metriche e tracce – sono estremamente difficili da implementare e gestire in un modo che abbia senso per un ulteriore debug e monitoraggio (il che è anche vero per la comunicazione sincrona, tra l’altro).

Le code di messaggi asincrone sono più una soluzione che un problema, almeno rispetto alle difficoltà che derivano dalla comunicazione sincrona. Per quanto riguarda i problemi che questo metodo comporta – a causa delle complessità intrinseche, non ci sono soluzioni miracolose che li risolveranno – ci si deve occupare sin da subito della progettazione, dell’implementazione e del funzionamento in modo da garantire un’elevata affidabilità.

Per concludere

Spero che questa sia stata una buona introduzione all’arte piuttosto intricata del debug dei microservizi. Si noti che la complessità qui presentata sembra minare i vantaggi che si ottengono utilizzando questo modello.
Vogliamo concludere questo articolo ricordando che, come per tutta la tecnologia, non esiste un’unica risposta corretta. Valutare i bisogni, specificare e monitorare sempre cosa comporterà l’adempimento e fare scelte consapevoli in base alla situazione è la migliore pratica.
Non ci soluzioni miracolose, solo design ben congegnati con vantaggi e svantaggi in ogni punto del percorso.