Strumentazione di sistemi distribuiti per visibilità operativa - David Yanacek - Strumentazione di sistemi distribuiti per visibilità operativa

Pagina creata da Enrico Morandi
 
CONTINUA A LEGGERE
Strumentazione di sistemi distribuiti
          per visibilità operativa
                                       David Yanacek

Strumentazione di sistemi distribuiti per visibilità operativa
Copyright © 2019, Amazon Web Services, Inc. o società affiliate. Tutti i diritti riservati.
Strumentazione di sistemi distribuiti per visibilità operativa

Immergersi a capofitto nei log
Quando sono entrato a fare parte dal team di Amazon dopo il college, uno dei miei primi esercizi
di inserimento è stato quello di rendere operativo il server Web amazon.com sul mio desktop di
sviluppatore. Non ci sono riuscito al primo tentativo, e non ero sicuro di cosa avevo fatto di
sbagliato. Un collega disponibile mi ha suggerito di guardare i log per vedere cosa non
funzionava. Per fare ciò, ha detto che avrei dovuto "cat the log file" (la parola inglese "cat"
significa "gatto" in italiano, mentre in questo contesto è un modo utilizzato nel linguaggio
informatico per dire "estrarre il file dei log"). Ero convinto che mi stessero giocando una specie di
scherzo o che facessero una battuta sui gatti che non capivo. Ho adoperato Linux al college solo
per compilare e usare il controllo del codice sorgente e per usare un editor di testo. Quindi non
sapevo che "cat" è in realtà un comando per stampare un file sul terminale che potevo inserire in
un altro programma per cercare schemi.
I miei colleghi mi hanno indicato strumenti come cat, grep, sed e awk. Dotato di questo nuovo
set di strumenti, mi sono immerso nei log del server Web amazon.com sul mio desktop di
sviluppatore. L'applicazione per server Web era già stata strumentata per emettere tutti i tipi di
informazioni utili nei suoi log. Questo mi ha permesso di vedere quale configurazione mancava
e che impediva l'avvio del server Web, che mostrava dove poteva essersi verificato un arresto
anomalo o che indicava dove non riusciva a comunicare con un servizio a valle. Il sito Web è
composto da molti pezzi in movimento ed era essenzialmente una black box per me all'inizio.
Tuttavia, dopo essermi immerso a capofitto nel sistema, ho imparato a capire come funzionava
il server e come interagire con le sue dipendenze semplicemente osservando l'output della
strumentazione.

Perché strumentazione
Mentre mi sono spostato da un team all'altro negli anni in Amazon, ho scoperto che la
strumentazione sia una lente inestimabile tramite la quale io e altri in Amazon guardiamo per
scoprire come funziona un sistema. Tuttavia, la strumentazione è utile per qualcosa di più della
semplice conoscenza dei sistemi. È il nucleo della cultura operativa di Amazon. La grande
strumentazione ci aiuta a vedere l'esperienza che stiamo offrendo ai nostri clienti.
Questa attenzione alle prestazioni operative si espande in tutta l'azienda. All'interno dei servizi
associati ad amazon.com, una maggiore latenza si traduce in una scarsa esperienza di acquisto e
quindi in una riduzione dei tassi di conversione. Per i clienti che utilizzano AWS, essi dipendono
dall'elevata disponibilità e dalla bassa latenza dei servizi AWS.
Ad Amazon, non consideriamo solo la latenza media. Ci concentriamo ancora più da vicino sugli
outlier di latenza, come il 99,9° e il 9,99° percentile. Questo perché se una richiesta su 1.000 o
10.000 è lenta, è comunque un'esperienza negativa. Scopriamo che quando riduciamo l'elevata
latenza percentile in un sistema, i nostri sforzi hanno l'effetto collaterale di ridurre la latenza
mediana. Al contrario, scopriamo che quando riduciamo la latenza mediana, ciò riduce meno
frequentemente l'elevata latenza percentile.
L'altro motivo per cui ci concentriamo sull'elevata latenza percentile è che un'elevata latenza in
un servizio può avere un effetto moltiplicatore su altri servizi. Amazon si basa su un'architettura
orientata ai servizi. Molti servizi collaborano tra loro per ottenere risultati, ad esempio il

                                                      2
Strumentazione di sistemi distribuiti per visibilità operativa

rendering di una pagina Web su amazon.com. Di conseguenza, un aumento della latenza di un
servizio in profondità nella catena delle chiamate, anche se l'aumento è in un percentile elevato,
ha un grande effetto a catena sulla latenza sperimentata dall'utente finale.
I grandi sistemi di Amazon sono costituiti da numerosi servizi cooperanti. Ogni servizio è
sviluppato e gestito da un singolo team (i "servizi" grandi sono composti da più servizi o
componenti dietro le quinte). Il team al quale appartiene un servizio è noto come proprietario del
servizio. Ogni membro di quel team ragiona come un proprietario e un operatore del servizio,
indipendentemente dal fatto che quel membro sia uno sviluppatore di software, un ingegnere di
rete, un manager o che svolga qualsiasi altro ruolo. In quanto proprietari, i team fissano obiettivi
sulle prestazioni operative di tutti i servizi associati. Ci assicuriamo inoltre di avere la visibilità delle
operazioni del servizio per garantire il raggiungimento di tali obiettivi, la gestione di eventuali
problemi e il mantenimento di obiettivi ancora più elevati l'anno successivo. Per fissare obiettivi e
ottenere tale visibilità, i team devono strumentare i sistemi.
La strumentazione ci consente inoltre di rilevare e rispondere tatticamente agli eventi operativi. La
strumentazione inserisce i dati in pannelli di controllo operativi, in modo che gli operatori possano
visualizzare le metriche in tempo reale. Fornisce inoltre i dati agli allarmi, che attivano e
coinvolgono gli operatori quando il sistema si comporta in modo imprevisto. Gli operatori
utilizzano l'output dettagliato della strumentazione per diagnosticare rapidamente perché le cose
sono andate male. Da lì, possiamo mitigare il problema e tornare più tardi per evitare che il
problema si ripeta. Senza una buona strumentazione in tutto il codice, dedichiamo tempo
prezioso alla diagnosi dei problemi.

Che cosa misurare
Per gestire i servizi secondo i nostri elevati standard di disponibilità e latenza, noi, in quanto
proprietari dei servizi, dobbiamo misurare il comportamento dei nostri sistemi.
Per ottenere i dati di telemetria necessari, i proprietari dei servizi misurano le prestazioni
operative da più punti per ottenere più prospettive su come le cose si comportano end-to-end.
Questo è complicato anche in un'architettura semplice. Considera un servizio che i clienti
chiamano tramite un sistema di bilanciamento del carico: il servizio comunica con una cache
remota e un database remoto. Vogliamo che ogni componente emetta metriche sul suo
comportamento. Vogliamo anche metriche su come ogni componente percepisce il
comportamento di altri componenti. Quando le metriche di tutte queste prospettive vengono
riunite, il proprietario di un servizio può rintracciare rapidamente l'origine dei problemi e
investigare per trovare la causa.
Molti servizi AWS forniscono automaticamente informazioni operative sulle tue risorse. Ad
esempio, Amazon DynamoDB fornisce ad Amazon CloudWatch le metriche sui tassi di successo e
di errore e sulla latenza, così come vengono misurati dal servizio. Tuttavia, quando costruiamo
sistemi che utilizzano questi servizi, abbiamo bisogno di molta più visibilità sul comportamento
dei nostri sistemi. La strumentazione richiede un codice esplicito che registra la durata delle
attività, la frequenza con cui vengono esercitati determinati percorsi del codice, i metadati su ciò
su cui l'attività stava lavorando e su quali parti delle attività hanno avuto esito positivo o negativo.
Se un team non aggiungesse una strumentazione esplicita, sarebbe costretto a gestire il proprio
servizio come una black box.

                                                        3
Strumentazione di sistemi distribuiti per visibilità operativa

Ad esempio, se abbiamo implementato un'operazione API del servizio che ha recuperato le
informazioni sul prodotto in base all'ID prodotto, il codice potrebbe apparire come nell'esempio
seguente. Questo codice cerca le informazioni sul prodotto in una cache locale, seguita da una
cache remota, seguita da un database:
public GetProductInfoResponse getProductInfo(GetProductInfoRequest
request) {

    // check our local cache
    ProductInfo info = localCache.get(request.getProductId());

    // check the remote cache if we didn't find it in the local cache
    if (info == null) {
      info = remoteCache.get(request.getProductId());

         localCache.put(info);
    }

    // finally check the database if we didn't have it in either cache
    if (info == null) {
      info = db.query(request.getProductId());

         localCache.put(info);
         remoteCache.put(info);
    }

    return info;
}

Se gestissi questo servizio, avrei bisogno di molta strumentazione in questo codice per essere in
grado di comprenderne il comportamento in produzione. Avrei bisogno della capacità di risolvere
le richieste non riuscite o lente e di monitorare le tendenze e i segni che le diverse dipendenze
siano sottovalutate o che si comportino male. Ecco lo stesso codice, annotato con alcune delle
domande a cui dovrei essere in grado di rispondere sul sistema di produzione nel suo insieme o
per una particolare richiesta:
public GetProductInfoResponse getProductInfo(GetProductInfoRequest
request) {

    // Which product are we looking up?
    // Who called the API? What product category is this in?

    // Did we find the item in the local cache?
    ProductInfo info = localCache.get(request.getProductId());

    if (info   == null) {
      // Was   the item in the remote cache?
      // How   long did it take to read from the remote cache?
      // How   long did it take to deserialize the object from the cache?
      info =   remoteCache.get(request.getProductId());

        // How full is the local cache?
        localCache.put(info);

                                                     4
Strumentazione di sistemi distribuiti per visibilità operativa

    }

  // finally check the database if we didn't have it in either cache
  if (info == null) {
    // How long did the database query take?
    // Did the query succeed?
    // If it failed, is it because it timed out? Or was it an invalid
query? Did we lose our database connection?
    // If it timed out, was our connection pool full? Did we fail to
connect to the database? Or was it just slow to respond?
    info = db.query(request.getProductId());

        // How long did populating the caches take?
        // Were they full and did they evict other items?
        localCache.put(info);
        remoteCache.put(info);
    }

    // How big was this product info object?
    return info;
}

Il codice per rispondere a tutte queste domande (e ad altre ancora) è un po' più lungo
dell'attuale logica di business. Alcune librerie possono aiutare a ridurre la quantità di codice
della strumentazione, ma lo sviluppatore deve ancora porre le domande sulla visibilità di cui le
librerie avranno bisogno, e quindi lo sviluppatore deve essere intenzionale sul cablaggio nella
strumentazione.
Quando si risolve un problema di una richiesta che scorre attraverso un sistema distribuito, può
essere difficile capire cosa sia successo se si guarda a quella richiesta solo in base a una
interazione. Per mettere insieme il puzzle, troviamo utile riunire in un unico punto tutte le
misurazioni su tutti questi sistemi. Prima di poterlo fare, ogni servizio deve essere strumentato per
registrare un ID traccia per ogni attività e per propagare tale ID traccia a ciascun altro servizio che
collabora a quell'attività. La raccolta della strumentazione tra i sistemi per un determinato ID
traccia può essere eseguita sia dopo il fatto, secondo necessità, sia quasi in tempo reale
utilizzando un servizio come AWS X-Ray.

Eseguire il drill-down
La strumentazione consente la risoluzione dei problemi a più livelli, dallo sguardo alle metriche
per vedere se ci sono anomalie troppo sottili per innescare allarmi, fino all'esecuzione di
un'indagine per scoprire la causa di tali anomalie.
Al livello più alto, la strumentazione è aggregata in metriche che possono attivare allarmi ed
essere visualizzate su pannelli di controllo. Questi metriche aggregate consentono agli operatori
di monitorare il tasso complessivo delle richieste, la latenza delle chiamate del servizio e i tassi
di errore. Questi allarmi e metriche ci informano di anomalie o modifiche che devono essere
esaminate.
Dopo aver visto un'anomalia, dobbiamo capire perché quell'anomalia si sta verificando. Per
rispondere a questa domanda, contiamo su metriche rese disponibili da ancora più strumenti.

                                                      5
Strumentazione di sistemi distribuiti per visibilità operativa

Instrumentando il tempo necessario per eseguire le varie parti per servire una richiesta, possiamo
vedere quale parte dell'elaborazione è più lenta del normale o innesca errori più spesso.
Sebbene i timer e le metriche aggregati possano aiutarci a escludere le cause o a evidenziare
un'area di indagine, non sempre forniscono una spiegazione completa. Ad esempio, potremmo
essere in grado di capire dalle metriche che gli errori provengono da una particolare operazione
API, ma le metriche potrebbero non rivelare abbastanza dettagli sul motivo per cui tale
operazione fallisce. A questo punto, esaminiamo i dati di log non elaborati e dettagliati emessi dal
servizio per quella finestra temporale. I log non elaborati mostrano quindi l'origine del problema,
ovvero il particolare errore che si sta verificando o particolari aspetti della richiesta che stanno
attivando un caso edge.

Come strumentiamo
La strumentazione richiede la codifica. Significa che quando stiamo implementando nuove
funzionalità, dobbiamo dedicare del tempo per aggiungere un codice addizionale per indicare cosa
è successo, se ha avuto successo o meno e quanto tempo ci è voluto. Poiché la strumentazione è
un'attività di codifica così comune, nel corso degli anni sono emerse in Amazon pratiche per
affrontare modelli comuni: la standardizzazione per librerie di strumentazione comuni e la
standardizzazione per report metrici strutturati basati su log.
La standardizzazione delle librerie di strumentazione delle metriche aiuta gli autori delle librerie a
dare ai relativi consumatori visibilità sul funzionamento della libreria. Ad esempio, i client HTTP
comunemente utilizzati si integrano con queste librerie comuni, quindi, se un team di un servizio
implementa una chiamata remota a un altro servizio, riceverà automaticamente la strumentazione
su tale chiamata.
Quando un'applicazione strumentata viene eseguita e svolge un lavoro, i dati di telemetria
risultanti vengono scritti in un file di log strutturato. Generalmente, viene emesso come una voce
di log per "unità di lavoro", indipendentemente dal fatto che si tratti di una richiesta a un servizio
HTTP o di un messaggio estratto da una coda.
Ad Amazon, le misurazioni nell'applicazione non sono aggregate e occasionalmente scaricate su
un sistema di aggregazione delle metriche. Tutti i timer e i contatori per ogni lavoro sono scritti in
un file di log. Da lì, i log vengono elaborati e le metriche aggregate vengono calcolate
successivamente da qualche altro sistema. In questo modo, finiamo con tutto, dalle metriche
operative aggregate di alto livello ai dati dettagliati sulla risoluzione dei problemi al livello della
richiesta, il tutto con un unico approccio alla strumentazione del codice. Ad Amazon prima
registriamo e poi produciamo metriche aggregate.

                                                      6
Strumentazione di sistemi distribuiti per visibilità operativa

Strumentazione tramite registrazione
Più comunemente strumentiamo i nostri servizi per emettere due tipi di dati di log: dati richiesta e
dati di debug. I dati di log della richiesta sono in genere rappresentati come una singola voce di
log strutturata per ogni unità di lavoro. Questi dati contengono proprietà sulla richiesta e su chi ha
effettuato la richiesta, a cosa serviva la richiesta, i contatori della frequenza con cui sono avvenute
le cose e i timer della relativa durata. Il log delle richieste funge da log di controllo e da traccia per
tutto ciò che è successo nel servizio. I dati di debug includono dati non strutturati o vagamente
strutturati delle righe di debug emesse dall'applicazione. In genere, si tratta di voci di log non
strutturate come errore Log4j o avvisi di righe di log. Ad Amazon questi due tipi di dati vengono
generalmente emessi in file di log separati, in parte per motivi storici, ma anche perché può essere
conveniente eseguire l'analisi dei log su un formato omogeneo delle voci di log.
Agenti come quello di CloudWatch Logs elaborano entrambi i tipi di dati di log in tempo reale e
inviano i log a CloudWatch Logs. A sua volta, CloudWatch Logs produce metriche aggregate sul
servizio in tempo quasi reale. Gli allarmi di Amazon CloudWatch leggono queste metriche
aggregate e attivano allarmi.
Anche se può essere costoso registrare così tanti dettagli su ogni richiesta, ad Amazon riteniamo
che sia incredibilmente importante farlo. Dopotutto, dobbiamo esaminare i blip di disponibilità, i
picchi di latenza e i problemi segnalati dai clienti. Senza log dettagliati, non possiamo fornire
risposte ai clienti e non saremo in grado di migliorare il loro servizio.

Entrare nei dettagli
Il tema del monitoraggio e degli allarmi è vasto. In questo articolo non tratteremo argomenti
come l'impostazione e l'ottimizzazione delle soglie di allarme, l'organizzazione di pannelli di
controllo operativi, la misurazione delle prestazioni sia sul lato server che sul lato client,
l'esecuzione continua di applicazioni "canary" e la scelta del sistema giusto da utilizzare per
aggregare le metriche e analizzare i log.
Questo articolo si concentra sulla necessità di strumentare le nostre applicazioni per produrre i
giusti dati di misurazione non elaborati. Descriveremo le cose che i team di Amazon si sforzano
di includere (o evitare) nella strumentazione delle loro applicazioni.

Best practice per il log delle richieste
In questa sezione descriverò le buone abitudini che abbiamo imparato nel tempo ad Amazon sulla
registrazione dei nostri dati strutturati "per unità di lavoro". Un log che soddisfa questi criteri
contiene contatori che rappresentano la frequenza con cui accadono le cose, timer contenenti il
tempo impiegato e proprietà che includono metadati su ciascuna unità di lavoro.

                                                       7
Strumentazione di sistemi distribuiti per visibilità operativa

Come registriamo
     Emetti una voce di log delle richieste per ogni unità di lavoro. Un'unità di lavoro è in
      genere una richiesta ricevuta dal nostro servizio o un messaggio che estrae da una coda.
      Scriviamo una voce di log del servizio per ogni richiesta ricevuta dal nostro servizio. Non
      uniamo più unità di lavoro insieme. In questo modo, quando risolviamo i problemi di una
      richiesta non riuscita, abbiamo una singola voce di log da esaminare. Questa voce contiene
      i parametri di input rilevanti sulla richiesta per vedere cosa stava tentando di fare,
      informazioni su chi era l'intermediario e tutte le informazioni sulla temporizzazione e sul
      contatore in un unico posto.
     Non emettere più di una voce di log delle richieste per una determinata richiesta.
      Nell'implementazione di un servizio non bloccante, potrebbe sembrare conveniente
      emettere una voce di log separata per ogni fase di una pipeline di elaborazione. Invece,
      abbiamo più successo nella risoluzione dei problemi di questi sistemi eseguendo il
      plumbing di un handle su un singolo "oggetto metriche" tra le fasi della pipeline e quindi
      serializzando le metriche come unità dopo che tutte le fasi sono state completate. La
      presenza di più voci di log per unità di lavoro rende più difficile l'analisi dei log e aumenta
      di un moltiplicatore i costi generali di registrazione già alti. Se stiamo scrivendo un nuovo
      servizio non bloccante, proviamo a pianificare le metriche di registrazione del ciclo di vita
      in anticipo, perché diventa molto difficile eseguire il refactoring e correggerlo in seguito.
     Suddividi le attività di lunga durata in più voci di log. Contrariamente alla precedente
      raccomandazione, se abbiamo un'attività di flusso di lavoro di lunga durata, di più minuti o
      di più ore, possiamo decidere di emettere periodicamente una voce di log separata in
      modo da poter determinare se sono stati fatti progressi o dove sta rallentando.
     Registra i dettagli della richiesta prima di fare operazioni come la convalida. Riteniamo
      importante per la risoluzione dei problemi e per la registrazione di controllo registrare
      sufficienti informazioni sulla richiesta in modo da sapere cosa stava cercando di realizzare.
      Abbiamo anche scoperto che è importante registrare queste informazioni il prima
      possibile, prima che la richiesta abbia la possibilità di essere respinta dalla logica di
      convalida, autenticazione o limitazione delle richieste. Se stiamo registrando informazioni
      dalla richiesta in arrivo, ci assicuriamo di purificare l'input (codificare, escape e troncare)
      prima di registrarlo. Ad esempio, non vogliamo includere stringhe lunghe di 1 MB nella
      nostra voce di log del servizio se l'intermediario ne ha passata una. Ciò rischierebbe di
      riempire i nostri dischi e di costarci più del previsto per lo storage dei log. Un altro esempio
      di purificazione è quello di filtrare i caratteri di controllo ASCII o le sequenze di escape
      relative al formato del log. Potrebbe essere fonte di confusione se l'intermediario passasse
      da solo una voce di log del servizio e se fosse in grado di iniettarla nei nostri log! Vedi
      anche: https://xkcd.com/327/
     Pianifica un modo per registrare con un livello maggiore di dettaglio. Per la risoluzione
      di alcuni tipi di problemi, il log non avrà abbastanza dettagli sulle richieste problematiche
      per capire perché non siano riuscite. Tali informazioni potrebbero essere disponibili nel
      servizio, ma il volume delle informazioni potrebbe essere troppo elevato per giustificare la
      registrazione per tutto il tempo. Può essere utile disporre di una manopola di
      configurazione che è possibile comporre per aumentare temporaneamente il livello di
      dettaglio del log mentre si esamina un problema. È possibile attivare la manopola su
      singoli host o per singoli clienti o a una frequenza di campionamento in tutta la flotta. È
      importante ricordarsi di riabbassare la manopola una volta terminata l'operazione.

                                                    8
Strumentazione di sistemi distribuiti per visibilità operativa

     Mantieni i nomi delle metriche brevi (ma non troppo brevi). Amazon ha utilizzato la
      stessa serializzazione del log del servizio per oltre 15 anni. In questa serializzazione, ogni
      nome di contatore e di timer viene ripetuto in testo semplice in ogni voce di log del
      servizio. Per ridurre al minimo i costi generali di registrazione, utilizziamo nomi di timer
      brevi ma descrittivi. Amazon sta iniziando ad adottare nuovi formati di serializzazione
      basati su un protocollo di serializzazione binario noto come Amazon Ion. In definitiva, è
      importante scegliere un formato che gli strumenti di analisi dei log possano comprendere
      e che sia anche il più efficiente possibile per serializzare, deserializzare e archiviare.
     Assicurati che i volumi di log siano sufficientemente grandi per gestire la registrazione
      al throughput massimo. Eseguiamo il test di carico dei nostri servizi al massimo carico
      sostenuto (o persino sovraccarico) per ore. Quando il nostro servizio gestisce il traffico in
      eccesso, dobbiamo assicurarci che esso disponga ancora delle risorse per spedire i log off-
      box alla velocità con cui producono nuove voci di log, altrimenti i dischi si riempiranno. È
      inoltre possibile configurare la registrazione in modo che avvenga su una partizione del
      file system diversa dalla partizione radice, in modo che il sistema non si rompa di fronte a
      una registrazione eccessiva. Discuteremo di altre mitigazioni per questo in seguito, ad
      esempio l'utilizzo del campionamento dinamico proporzionale al throughput, ma
      indipendentemente dalla strategia, è fondamentale eseguire il test.
     Considera il comportamento del sistema quando i dischi si riempiono. Quando il disco
      di un server si riempie, il server non può accedere al disco. Quando ciò accade, un servizio
      dovrebbe smettere di accettare richieste o eliminare i log e continuare a funzionare senza
      monitoraggio? Operare senza la registrazione è rischioso, quindi testiamo i sistemi per
      garantire che vengano rilevati server con dischi quasi completi.
     Sincronizza gli orologi. La nozione di "tempo" nei sistemi distribuiti è notoriamente
      complicata. Non facciamo affidamento sulla sincronizzazione dell'orologio negli algoritmi
      distribuiti, ma è necessaria per dare un senso ai log. Eseguiamo daemon come Chrony o
      ntpd per la sincronizzazione dell'orologio e monitoriamo i server per la deviazione
      dell'orologio. Per rendere questo più facile, consulta l'Amazon Time Sync Service.
     Emetti conteggi zero per le metriche di disponibilità. I conteggi degli errori sono utili,
      ma anche le percentuali di errore possono essere utili. Per instrumentare una metrica della
      "percentuale di disponibilità", una strategia che abbiamo trovato utile è quella di emettere
      un 1 quando la richiesta ha esito positivo e uno 0 quando la richiesta ha esito negativo.
      Quindi la statistica "media" della metrica risultante è la percentuale di disponibilità.
      L'emissione intenzionale di un punto dati 0 può essere utile anche in altre situazioni.
      Ad esempio, se un'applicazione esegue l'elezione del leader, emettere periodicamente un
      1 quando un processo è il leader e uno 0 quando il processo non è il leader può essere utile
      per monitorare l'integrità dei follower. In questo modo, se un processo smette di emettere
      uno 0, è più facile sapere che qualcosa al suo interno si è rotto e non sarà in grado di
      subentrare se succede qualcosa al leader.

Che cosa registriamo
     Registra la disponibilità e la latenza di tutte le dipendenze. Lo abbiamo trovato
      particolarmente utile nel rispondere alle domande su "perché la richiesta è stata lenta?"
      o "perché la richiesta non è riuscita?" Senza questo log, possiamo solo confrontare i grafici
      delle dipendenze con i grafici di un servizio e indovinare se un picco nella latenza di un
      servizio dipendente abbia portato al fallimento di una richiesta che stiamo esaminando.

                                                    9
Strumentazione di sistemi distribuiti per visibilità operativa

    Molti framework dei servizi e dei client descrivono le metriche automaticamente, ma altri
    framework (come SDK AWS, ad esempio), richiedono strumenti manuali.
   Dividi le metriche di dipendenza per chiamata, per risorsa, per codice di stato, ecc. Se
    interagiamo più volte con la stessa dipendenza nella stessa unità di lavoro, includiamo le
    metriche relative a ciascuna chiamata separatamente e chiariamo con quale risorsa
    interagiva ciascuna richiesta. Ad esempio, nel chiamare Amazon DynamoDB, alcuni team
    hanno trovato utile includere metriche di tempistica e di latenza per tabella, nonché per
    codice di errore e persino per il numero di tentativi. Ciò semplifica la risoluzione dei casi in
    cui il servizio era lento a causa di tentativi falliti di controllo condizionale. Queste metriche
    hanno anche rivelato casi in cui gli aumenti di latenza percepiti dal client erano in realtà
    dovuti alla limitazione dei tentativi o alla paginazione attraverso un set di risultati e non
    alla perdita di pacchetti o alla latenza di rete.
   Registra le profondità della coda di memoria quando vi si accede. Se una richiesta
    interagisce con una coda e ne stiamo estraendo un oggetto o inserendo qualcosa,
    registriamo la profondità della coda corrente nell'oggetto metriche mentre ci siamo.
    Per le code in memoria, questa informazione è molto economica da ottenere. Per le
    code distribuite, questi metadati potrebbero essere disponibili gratuitamente in
    risposta alle chiamate API. Questa registrazione aiuterà a trovare backlog e fonti di
    latenza in futuro. Inoltre, quando estraiamo le cose da una coda, misuriamo quanto
    tempo sono rimaste nella coda. Ciò significa che dobbiamo aggiungere la nostra
    metrica "tempo di accodamento" al messaggio prima di accodarlo in primo luogo.
   Aggiungi un contatore addizionale per ogni motivo di errore. Si consiglia l'aggiunta di
    codice che conteggi il motivo dell'errore specifico per ogni richiesta non riuscita. Il log
    dell'applicazione includerà informazioni che hanno portato all'errore e un messaggio di
    eccezione dettagliato. Tuttavia, abbiamo inoltre trovato utile vedere le tendenze nei
    motivi di errore nelle metriche nel tempo senza dover estrarre tali informazioni dal log
    dell'applicazione. È utile iniziare con una metrica separata per ogni classe di errore di
    eccezione.
   Organizza gli errori per categoria di causa. Se tutti gli errori sono raggruppati nella
    stessa metrica, la metrica diventa rumorosa e inutile. Come minimo, abbiamo trovato
    importante separare gli errori che erano per "colpa del client" dagli errori che erano per
    "colpa del server". Oltre a ciò, un'ulteriore suddivisione potrebbe essere utile. Ad esempio,
    in DynamoDB i client possono effettuare richieste condizionate di scrittura che
    restituiscono un errore se l'elemento che stanno modificando non corrisponde alle
    condizioni preliminari della richiesta. Questi errori sono intenzionali e ci aspettiamo che
    accadano di tanto in tanto. Considerando che gli errori di "richiesta non valida" da parte
    dei client sono molto probabilmente bug che dobbiamo correggere.
   Registra metadati importanti sull'unità di lavoro. In un log metrico strutturato,
    includiamo anche sufficienti metadati sulla richiesta in modo da poter determinare in
    seguito da chi proveniva la richiesta e che cosa stava tentando di fare. Ciò include i
    metadati che un cliente si aspetterebbe che avessimo nel log quando si presenta con
    problemi. Ad esempio, DynamoDB registra il nome della tabella con cui una richiesta
    interagisce e i metadati, quali se l'operazione di lettura sia stata una lettura coerente o
    meno. Tuttavia, non registra i dati archiviati o recuperati dal database.
   Proteggi i log con controllo degli accessi e crittografia. Poiché i log contengono un certo
    livello di informazioni riservate, adottiamo misure per proteggere e garantire la sicurezza
    di tali dati. Queste misure includono la crittografia dei log, la limitazione dell'accesso agli
    operatori che si occupano della risoluzione dei problemi e il confronto regolare di tale
    accesso con una metrica storica o "baseline".

                                                  10
Strumentazione di sistemi distribuiti per visibilità operativa

   Evita di inserire informazioni eccessivamente sensibili nei log. I log devono contenere
    alcune informazioni sensibili per essere utili. In Amazon, riteniamo importante che i log
    includano informazioni sufficienti per sapere da chi proviene una determinata richiesta,
    ma tralasciamo informazioni eccessivamente sensibili, come parametri di richiesta che non
    influiscono sul routing o sul comportamento dell'elaborazione della richiesta. Ad esempio,
    se il codice analizza un messaggio del cliente e l'analisi non riesce, è importante non
    registrare il payload per proteggere la privacy dei clienti, per quanto difficile questo possa
    rendere la risoluzione dei problemi in un secondo momento. Utilizziamo gli strumenti per
    prendere decisioni su ciò che può essere registrato con consenso esplicito anziché con
    rifiuto esplicito, per impedire la registrazione di un nuovo parametro sensibile aggiunto in
    seguito. Servizi come Amazon API Gateway consentono di configurare quali dati includere
    nel loro log di accesso, che funge da buon meccanismo di consenso esplicito.
   Registra un ID traccia e propagalo nelle chiamate back-end. Una determinata richiesta
    del cliente coinvolgerà probabilmente molti servizi che lavorano in collaborazione. Può
    trattarsi di un minimo di due o tre servizi per molte richieste AWS, fino a molti più servizi
    per le richieste amazon.com. Per dare senso a ciò che è accaduto durante la risoluzione dei
    problemi di un sistema distribuito, propaghiamo lo stesso ID traccia tra questi sistemi in
    modo da poter allineare i log dei vari sistemi per vedere dove si siano verificati gli errori.
    Un ID traccia è una sorta di ID richiesta meta che viene stampato dal servizio "front door"
    su un'unità di lavoro distribuita; il servizio "front door" rappresenta il punto di partenza per
    l'unità di lavoro. AWS X-Ray è un servizio che aiuta fornendo una parte di questa
    propagazione. Abbiamo ritenuto importante passare la traccia alla nostra dipendenza. In
    un ambiente multi-thread, è molto difficile e soggetto a errori che il framework esegua
    questa propagazione per nostro conto, quindi abbiamo preso l'abitudine di passare gli ID
    traccia e altri contenuti della richiesta (come un oggetto metriche!) nelle firme del metodo.
    Abbiamo anche trovato utile distribuire un oggetto di contesto nelle firme del metodo, in
    modo da non dover effettuare il refactoring quando troviamo un modello simile da
    passare in futuro. Per i team AWS, non si tratta solo di risolvere i problemi dei nostri
    sistemi, si tratta anche dei clienti che devono risolvere i loro problemi. I clienti si affidano
    alle tracce AWS X-Ray trasmesse tra i servizi AWS quando interagiscono tra loro per conto
    del cliente. Ciò richiede di propagare gli ID traccia di AWS X-Ray dei clienti tra i servizi in
    modo che possano ottenere dati di traccia completi.
   Registra diverse metriche di latenza in base al codice dello stato e alla dimensione. Gli
    errori sono spesso rapidi, come l'accesso negato, la limitazione delle richieste e le risposte
    agli errori di convalida. Se le richieste dei client iniziano a essere limitate a un ritmo
    elevato, la latenza potrebbe apparire ingannevolmente buona. Per evitare questo
    inquinamento delle metriche, registriamo un timer separato per le risposte riuscite e ci
    concentriamo su quella metrica nei nostri pannelli di controllo e negli allarmi invece di
    utilizzare una metrica temporale generica. Allo stesso modo, se esiste un'operazione che
    può essere più lenta a seconda della dimensione dell'input o della risposta, consigliamo
    l'emissione di una metrica di latenza che è classificata, come SmallRequestLatency e
    LargeRequestLatency. Inoltre, garantiamo che la nostra richiesta e le nostre risposte siano
    adeguatamente limitate per evitare complesse modalità di brownout e di guasto, ma
    anche in un servizio attentamente progettato, questa tecnica per le metriche del bucket
    può isolare il comportamento del cliente e mantenere il rumore distraente fuori dai
    pannelli di controllo.

                                                 11
Strumentazione di sistemi distribuiti per visibilità operativa

Best practice per il log dell'applicazione
Questa sezione descrive le buone abitudini che abbiamo appreso in Amazon sulla registrazione dei
dati di log di debug non strutturati.

      Mantieni il log dell'applicazione libero da spam. Anche se potremmo avere istruzioni
       INFO e DEBUG a livello di log sul percorso della richiesta per aiutare con lo sviluppo e il
       debug in ambienti di test, consigliamo di disabilitare questi livelli di log in produzione.
       Invece di fare affidamento sul log dell'applicazione per le informazioni di traccia delle
       richieste, pensiamo al log del servizio come a una posizione per le informazioni di traccia
       dove possiamo facilmente produrre metriche e vedere tendenze aggregate nel tempo.
       Tuttavia, non esiste una regola in bianco e nero qui. Il nostro approccio è quello di rivedere
       continuamente i nostri log per verificare se sono troppo rumorosi (o non abbastanza
       rumorosi) e regolare i livelli dei log nel tempo. Ad esempio, quando analizziamo i log
       troviamo spesso istruzioni di log troppo rumorose o metriche che vorremmo avere.
       Fortunatamente, questi miglioramenti sono spesso facili da apportare, quindi abbiamo
       preso l'abitudine di archiviare elementi di backlog di follow-up rapido per mantenere puliti
       i nostri log.
      Includi l'ID richiesta corrispondente. Quando risolviamo un errore nel log
       dell'applicazione, spesso vogliamo vedere i dettagli sulla richiesta o sull'intermediario che
       ha causato l'errore. Se entrambi i log contengono lo stesso ID richiesta, possiamo
       facilmente passare da un log all'altro. Le librerie di registrazione dell'applicazione
       scriveranno l'ID richiesta corrispondente se configurato correttamente e l'ID richiesta è
       impostato come un ThreadLocal. Se un'applicazione è multithread, si consiglia di prestare
       particolare attenzione a impostare l'ID richiesta corretto quando un thread inizia a lavorare
       su una nuova richiesta.
      Stabilisci limiti di velocità per lo spam di errori nel log dell'applicazione. In genere, un
       servizio non emette molto nel log dell'applicazione, ma se improvvisamente inizia a
       mostrare un grande volume di errori, potrebbe improvvisamente iniziare a scrivere un'alta
       percentuale di voci di log molto grandi con tracce di stack. Un modo che abbiamo scoperto
       per proteggerci è limitare la frequenza con cui un determinato logger effettuerà la
       registrazione.
      Privilegia le stringhe di formato rispetto al formato di stringa # o alla concatenazione
       di stringhe. Le operazioni API precedenti del log dell'applicazione accettano un singolo
       messaggio di stringa anziché l'API stringa di formato varargs di log4j2. Se il codice è
       dotato di istruzioni DEBUG, ma la produzione è configurata a livello di ERRORE, è possibile
       sprecare il lavoro per la formattazione delle stringhe di messaggi DEBUG che vengono
       ignorate. Alcune operazioni dell'API di registrazione supportano il passaggio di oggetti
       arbitrari i cui metodi toString() verranno chiamati solo se la voce di log verrà scritta.
      Registra gli ID richiesta da chiamate di servizio non riuscite. Se viene chiamato un
       servizio e restituisce un errore, è probabile che il servizio abbia restituito un ID richiesta.
       Abbiamo trovato utile includere l'ID richiesta nel nostro log in modo che, se avessimo
       bisogno di dare seguito a quel proprietario del servizio, avremmo modo di trovare
       facilmente le relative voci di log nel servizio corrispondente. Gli errori di timeout lo
       rendono difficile perché il servizio potrebbe non aver ancora restituito un ID richiesta o
       una libreria client potrebbe non averlo analizzato. Tuttavia, se abbiamo un ID richiesta di
       ritorno dal servizio, lo registriamo.

                                                    12
Strumentazione di sistemi distribuiti per visibilità operativa

Best practice per servizi con throughput elevato
Per la stragrande maggioranza dei servizi su Amazon, la registrazione di ogni richiesta non impone
un costo irragionevole. Servizi di throughput più elevati entrano in un'area più grigia, ma spesso
registriamo comunque ogni richiesta. Ad esempio, è naturale presumere che DynamoDB, che serve
al massimo oltre 20 milioni di richieste al secondo del solo traffico interno di Amazon, non
registrerebbe molto, ma in effetti registra ogni richiesta per la risoluzione dei problemi e per
motivi di audit e conformità. Ecco alcuni suggerimenti di alto livello che utilizziamo ad Amazon
per rendere la registrazione più efficiente con un throughput per host più elevato:

      Campionamento dei log. Invece di scrivere ogni voce, considera la possibilità di scrivere
       ogni N voci. Ogni voce include anche quante voci sono state ignorate in modo che i sistemi
       di aggregazione delle metriche possano stimare il volume di log reale nelle metriche che
       calcola. Altri algoritmi di campionamento come reservoir sampling forniscono campioni
       più rappresentativi. Altri algoritmi danno la priorità agli errori di registrazione o alle
       richieste lente rispetto alle richieste veloci e riuscite. Tuttavia, con il campionamento si
       perde la capacità di aiutare i clienti e risolvere guasti specifici. Alcuni requisiti di
       conformità lo vietano del tutto.
      Serializzazione offload e log flush su un thread separato. Questa è una modifica facile
       ed è comunemente usata.
      Rotazione frequente del log. La rotazione dei registri dei file di log ogni ora potrebbe
       essere conveniente, quindi hai meno file da gestire, ma ruotando ogni minuto, molte cose
       migliorano. Ad esempio, l'agente che legge e comprime il file di log leggerà il file dalla
       cache della pagina anziché dal disco e la CPU e l'IO dai log di compressione e spedizione
       verranno distribuiti nell'arco dell'ora anziché attivarsi sempre alla fine del ora.
      Scrivi log precompressi. Se un agente di spedizione dei log comprime i log prima di
       inviarli a un servizio di archiviazione, la CPU e il disco del sistema aumenteranno
       periodicamente. È possibile ammortizzare questo costo e ridurre della metà l'IO del disco,
       eseguendo lo streaming dei log compressi sul disco. Questo comporta alcuni rischi.
       Abbiamo trovato utile utilizzare un algoritmo di compressione in grado di gestire file
       troncati in caso di crash dell'applicazione.
      Scrivi su un ramdisk/tmpfs. Potrebbe essere più semplice per un servizio scrivere i log in
       memoria fino a quando non vengono spediti dal server invece di scrivere i log su disco.
       Nella nostra esperienza, questo funziona meglio con la rotazione del log ogni minuto
       rispetto alla rotazione del log ogni ora.
      Aggregati in memoria. Se è necessario gestire centinaia di migliaia di transazioni al
       secondo su un singolo computer, potrebbe essere troppo costoso scrivere una singola
       voce di log per richiesta. Tuttavia, perdi molta osservabilità saltando questo, quindi
       abbiamo trovato utile non ottimizzare prematuramente.
      Monitora l'utilizzo delle risorse. Prestiamo attenzione a quanto siamo vicini a
       raggiungere un limite di dimensionamento. Misuriamo il nostro IO e la CPU per server e
       quante di queste risorse vengono consumate dagli agenti di registrazione. Quando
       eseguiamo i test di carico, li eseguiamo abbastanza a lungo in modo da poter dimostrare
       che i nostri agenti di spedizione del log possono tenere passo con il nostro throughput.

                                                    13
Strumentazione di sistemi distribuiti per visibilità operativa

Predisponi gli strumenti di analisi dei log corretti
Ad Amazon, gestiamo i servizi che scriviamo, quindi dobbiamo tutti diventare esperti nella
risoluzione dei relativi problemi. Ciò include la possibilità di eseguire analisi dei log senza sforzo.
Ci sono molti strumenti a nostra disposizione, dall'analisi dei log locali per esaminare un numero
relativamente piccolo di log, all'analisi dei log distribuiti per setacciare e aggregare i risultati di un
enorme volume di log.
Abbiamo ritenuto importante investire negli strumenti e nei runbook dei team per l'analisi dei log.
Se i log sono piccoli ora, ma un servizio prevede di crescere nel tempo, prestiamo attenzione a
quando i nostri strumenti attuali smettono di ridimensionare, in modo da poter investire
nell'adozione di una soluzione di analisi dei log distribuita.

Analisi dei log locali
Il processo di analisi dei log potrebbe richiedere esperienza in varie utilità della riga di comando
di Linux. Ad esempio, un'operazione comune "trova gli indirizzi IP top talker nel log" è
semplicemente:

cat log | grep -P "^RemoteIp=" | cut -d= -f2 | sort | uniq -c | sort -nr |
head -n20

Tuttavia, ci sono un sacco di altri strumenti che sono utili per rispondere a domande più
complesse con i nostri log, tra cui:

       jq: https://stedolan.github.io/jq/
       RecordStream: https://github.com/benbernard/RecordStream

Analisi dei log distribuiti
Qualsiasi servizio di analisi dei big data può essere utilizzato per eseguire analisi dei log distribuiti
(ad esempio Amazon EMR, Amazon Athena, Amazon Aurora e Amazon Redshift). Tuttavia, alcuni
servizi sono dotati di sistemi di registrazione, ad esempio Amazon CloudWatch Logs.

       CloudWatch Logs Insights
       AWS X-Ray: https://aws.amazon.com/xray/
       Amazon Athena: https://aws.amazon.com/athena/

Conclusione
In quanto proprietario di servizi e sviluppatore di software, passo gran parte del mio tempo a
guardare gli output della strumentazione, quali grafici su pannelli di controllo e singoli file di log,
e a utilizzare strumenti di analisi dei log distribuiti come CloudWatch Logs Insights. Queste sono
alcune delle mie cose preferite da fare. Quando ho bisogno di una pausa dopo aver terminato un
compito impegnativo, ricarico le batterie e mi ricompenso con alcune analisi di log. Comincio con

                                                      14
Strumentazione di sistemi distribuiti per visibilità operativa

domande come "perché questa metrica è aumentata qui?" o "la latenza di questa operazione
potrebbe essere inferiore?" Quando le mie domande portano a un vicolo cieco, spesso capisco
alcune misurazioni che potrebbero essere utili nel codice, quindi aggiungo la strumentazione, i
test e invio una revisione del codice ai miei compagni di squadra.

Nonostante il fatto che molte metriche vengano fornite con i servizi gestiti che utilizziamo,
dobbiamo dedicare molta attenzione alla strumentazione dei nostri servizi in modo da avere la
visibilità di cui abbiamo bisogno per gestirli in modo efficace. Durante gli eventi operativi,
dobbiamo determinare rapidamente perché stiamo riscontrando un problema e cosa possiamo
fare per mitigarlo. Avere le giuste metriche sui nostri pannelli di controllo è cruciale in modo che
possiamo fare quella diagnosi rapidamente. Inoltre, poiché cambiamo sempre i nostri servizi,
aggiungiamo nuove funzionalità e cambiamo il modo in cui interagiscono con le loro dipendenze,
l'esercizio di aggiornamento e di aggiunta della giusta strumentazione è sempre in corso.

Collegamenti
      "Look at your data" ("Guarda i tuoi dati"), dell'ex collega di Amazon John Rauser:
       https://www.youtube.com/watch?v=coNDCIMH8bk (incluso il minuto 13:22 dove stampa
       letteralmente i log per guardarli meglio)
      "Investigating anomalies" ("Indagare le anomalie") dell'ex collega di Amazon John Rauser:
       https://www.youtube.com/watch?v=-3dw09N5_Aw
      "How humans see data" ("Come gli umani vedono i dati") dell'ex collega di Amazon John
       Rauser: https://www.youtube.com/watch?v=fSgEeI2Xpdc
      https://www.akamai.com/uk/en/about/news/press/2017-press/akamai-releases-spring-
       2017-state-of-online-retail-performance-report.jsp

                                                     15
Puoi anche leggere