I log in Kubernetes (e non solo) oggi: aspettative e realtà

I log in Kubernetes (e non solo) oggi: aspettative e realtà

Siamo nel 2019 e non disponiamo ancora di una soluzione standard per l'aggregazione dei log in Kubernetes. In questo articolo vorremmo, utilizzando esempi tratti dalla pratica reale, condividere le nostre ricerche, i problemi incontrati e le loro soluzioni.

Tuttavia, per prima cosa, effettuerò una prenotazione in modo che clienti diversi comprendano cose molto diverse raccogliendo i registri:

  • qualcuno vuole vedere i registri di sicurezza e di controllo;
  • qualcuno: registrazione centralizzata dell'intera infrastruttura;
  • e per alcuni è sufficiente raccogliere solo i log dell'applicazione, escludendo, ad esempio, i bilanciatori.

Di seguito è riportato il taglio seguente su come abbiamo implementato le varie "liste dei desideri" e quali difficoltà abbiamo incontrato.

Teoria: sugli strumenti di registrazione

Informazioni sui componenti di un sistema di registrazione

Il logging ha fatto molta strada, a seguito della quale sono state sviluppate metodologie per la raccolta e l'analisi dei log, che è ciò che usiamo oggi. Negli anni '1950, Fortran introdusse un analogo dei flussi di input/output standard, che aiutava il programmatore a eseguire il debug del suo programma. Questi furono i primi registri informatici che resero la vita più facile ai programmatori di quei tempi. Oggi vediamo in essi il primo componente del sistema di registrazione: fonte o “produttore” dei log.

L'informatica non si è fermata: sono apparse le reti di computer, i primi cluster... Sistemi complessi costituiti da più computer hanno iniziato a funzionare. Ora gli amministratori di sistema erano costretti a raccogliere i log da diverse macchine e, in casi speciali, potevano aggiungere messaggi del kernel del sistema operativo nel caso avessero bisogno di indagare su un errore di sistema. Per descrivere i sistemi centralizzati di raccolta dei log, agli inizi degli anni 2000 è stato pubblicato RFC 3164, che ha standardizzato remote_syslog. Ecco come è apparso un altro componente importante: raccoglitore di registri e il loro stoccaggio.

Con l'aumento del volume dei log e l'introduzione diffusa delle tecnologie web, è sorta la questione di quali log debbano essere mostrati in modo conveniente agli utenti. I semplici strumenti della console (awk/sed/grep) sono stati sostituiti da altri più avanzati visualizzatori di registro - terza componente.

Con l’aumento del volume dei log è diventato chiaro qualcos’altro: i log sono necessari, ma non tutti. Inoltre, registri diversi richiedono livelli di conservazione diversi: alcuni possono andare persi in un giorno, mentre altri devono essere conservati per 5 anni. Quindi, al sistema di registrazione è stato aggiunto un componente per filtrare e instradare i flussi di dati, chiamiamolo così filtro.

Anche lo storage ha fatto un grande salto: dai normali file ai database relazionali, e poi allo storage orientato ai documenti (ad esempio Elasticsearch). Quindi il magazzino è stato separato dal collettore.

Alla fine, il concetto stesso di registro si è espanso fino a diventare una sorta di flusso astratto di eventi che vogliamo preservare per la storia. O meglio, nel caso in cui sia necessario condurre un'indagine o stilare un rapporto analitico...

Di conseguenza, in un periodo di tempo relativamente breve, la raccolta dei log si è sviluppata in un importante sottosistema, che può essere giustamente definito una delle sottosezioni dei Big Data.

I log in Kubernetes (e non solo) oggi: aspettative e realtà
Se un tempo le normali stampe potevano bastare per un “sistema di logging”, ora la situazione è molto cambiata.

Kubernetes e log

Quando Kubernetes è arrivato sull'infrastruttura, non è stato aggirato nemmeno il problema già esistente della raccolta dei log. Per certi versi la cosa è diventata ancora più dolorosa: la gestione della piattaforma infrastrutturale non solo è stata semplificata, ma allo stesso tempo anche complicata. Molti vecchi servizi hanno iniziato a migrare verso i microservizi. Nel contesto dei log, ciò si riflette nel numero crescente di fonti di log, nel loro ciclo di vita speciale e nella necessità di tracciare le relazioni di tutti i componenti del sistema attraverso i log...

Guardando al futuro, posso affermare che ora, sfortunatamente, non esiste un'opzione di registrazione standardizzata per Kubernetes che possa reggere il confronto favorevolmente con tutte le altre. Gli schemi più popolari nella comunità sono i seguenti:

  • qualcuno srotola la pila CDF (Elasticsearch, Fluentd, Kibana);
  • qualcuno sta provando quello recentemente rilasciato Loki o usi Operatore di registrazione;
  • noi (e forse non solo noi?..) Sono ampiamente soddisfatto del mio sviluppo - casa in legno...

Di norma utilizziamo i seguenti bundle nei cluster K8 (per soluzioni self-hosted):

Tuttavia non mi soffermerò sulle istruzioni per la loro installazione e configurazione. Mi concentrerò invece sulle loro carenze e su conclusioni più globali sulla situazione dei log in generale.

Esercitati con i log in K8

I log in Kubernetes (e non solo) oggi: aspettative e realtà

“Registri di tutti i giorni”, quanti di voi siete?..

La raccolta centralizzata di log da un'infrastruttura abbastanza grande richiede risorse considerevoli, che verranno spese per la raccolta, l'archiviazione e l'elaborazione dei log. Durante l'esecuzione di vari progetti, ci siamo trovati di fronte a vari requisiti e problemi operativi da essi derivanti.

Proviamo ClickHouse

Consideriamo l'archiviazione centralizzata di un progetto con un'applicazione che genera registri in modo piuttosto attivo: più di 5000 righe al secondo. Iniziamo a lavorare con i suoi log, aggiungendoli a ClickHouse.

Non appena sarà richiesto il massimo tempo reale, il server a 4 core con ClickHouse sarà già sovraccaricato sul sottosistema del disco:

I log in Kubernetes (e non solo) oggi: aspettative e realtà

Questo tipo di caricamento è dovuto al fatto che stiamo cercando di scrivere in ClickHouse il più rapidamente possibile. E il database reagisce con un maggiore carico del disco, che può causare i seguenti errori:

DB::Exception: Too many parts (300). Merges are processing significantly slower than inserts

Il fatto è che Tabelle MergeTree in ClickHouse (contengono dati di log) hanno le loro difficoltà durante le operazioni di scrittura. I dati inseriti al loro interno generano una partizione temporanea, che viene poi fusa con la tabella principale. Di conseguenza, la registrazione risulta essere molto impegnativa sul disco, ed è anche soggetta alla limitazione di cui abbiamo ricevuto notizia in precedenza: non si possono unire più di 1 sottopartizioni in 300 secondo (si tratta infatti di 300 inserti al secondo).

Per evitare questo comportamento, dovresti scrivere a ClickHouse nel maggior numero di pezzi possibile e non più di 1 volta ogni 2 secondi. Tuttavia, scrivere a grandi raffiche suggerisce che dovremmo scrivere meno frequentemente in ClickHouse. Ciò, a sua volta, può portare a un overflow del buffer e alla perdita di registri. La soluzione è aumentare il buffer Fluentd, ma aumenterà anche il consumo di memoria.

Nota: Un altro aspetto problematico della nostra soluzione con ClickHouse era legato al fatto che il partizionamento nel nostro caso (loghouse) viene implementato tramite tabelle esterne collegate Unisci tabella. Ciò porta al fatto che quando si campionano intervalli di tempo ampi, è necessaria una RAM eccessiva, poiché la metatabella scorre tutte le partizioni, anche quelle che ovviamente non contengono i dati necessari. Tuttavia, ora questo approccio può essere tranquillamente dichiarato obsoleto per le versioni attuali di ClickHouse (c 18.16).

Di conseguenza, diventa chiaro che non tutti i progetti dispongono di risorse sufficienti per raccogliere i log in tempo reale in ClickHouse (più precisamente, la loro distribuzione non sarà appropriata). Inoltre, dovrai utilizzare аккумулятор, su cui torneremo più avanti. Il caso sopra descritto è reale. E in quel momento non eravamo in grado di offrire una soluzione affidabile e stabile che si adattasse al cliente e ci permettesse di raccogliere i registri con un ritardo minimo...

E che dire di Elasticsearch?

Elasticsearch è noto per gestire carichi di lavoro pesanti. Proviamolo nello stesso progetto. Ora il carico appare così:

I log in Kubernetes (e non solo) oggi: aspettative e realtà

Elasticsearch è stato in grado di digerire il flusso di dati, tuttavia, la scrittura di tali volumi su di esso utilizza notevolmente la CPU. Questo viene deciso organizzando un cluster. Tecnicamente questo non è un problema, ma sembra che solo per far funzionare il sistema di raccolta dei log utilizziamo già circa 8 core e abbiamo un componente aggiuntivo altamente caricato nel sistema...

In conclusione: questa opzione può essere giustificata, ma solo se il progetto è grande e la sua direzione è pronta a spendere risorse significative su un sistema di registrazione centralizzato.

Allora sorge spontanea una domanda:

Quali registri sono realmente necessari?

I log in Kubernetes (e non solo) oggi: aspettative e realtà Proviamo a cambiare l'approccio stesso: i log dovrebbero essere contemporaneamente informativi e non coprire ogni evento nel sistema.

Diciamo che abbiamo un negozio online di successo. Quali registri sono importanti? Raccogliere quante più informazioni possibili, ad esempio da un gateway di pagamento, è un’ottima idea. Ma non tutti i log del servizio di slicing delle immagini presenti nel catalogo prodotti sono per noi fondamentali: bastano solo gli errori e il monitoraggio avanzato (ad esempio la percentuale di 500 errori che questo componente genera).

Quindi siamo giunti alla conclusione che la registrazione centralizzata non è sempre giustificata. Molto spesso il cliente desidera raccogliere tutti i log in un unico posto, anche se in realtà dell'intero log è richiesto solo un 5% condizionale di messaggi critici per l'azienda:

  • A volte è sufficiente configurare, ad esempio, solo la dimensione del log del contenitore e del raccoglitore di errori (ad esempio Sentry).
  • Una notifica di errore e un registro locale di grandi dimensioni possono spesso essere sufficienti per indagare sugli incidenti.
  • Avevamo progetti che si accontentavano esclusivamente di test funzionali e sistemi di raccolta degli errori. Lo sviluppatore non aveva bisogno dei registri in quanto tali: vedevano tutto, dalle tracce degli errori.

Illustrazione dalla vita

Un’altra storia può servire da buon esempio. Abbiamo ricevuto una richiesta dal team di sicurezza di uno dei nostri clienti che utilizzava già una soluzione commerciale sviluppata molto prima dell'introduzione di Kubernetes.

Era necessario “fare amicizia” tra il sistema centralizzato di raccolta log e il sensore di rilevamento problemi aziendali - QRadar. Questo sistema può ricevere log tramite il protocollo syslog e recuperarli da FTP. Tuttavia non è stato immediatamente possibile integrarlo con il plugin remote_syslog per fluentd (come si è scoperto, non siamo soli). Si è scoperto che i problemi con la configurazione di QRadar erano da parte del team di sicurezza del cliente.

Di conseguenza, una parte dei log critici per l'azienda è stata caricata su FTP QRadar e l'altra parte è stata reindirizzata tramite syslog remoto direttamente dai nodi. Per questo abbiamo anche scritto grafico semplice - forse aiuterà qualcuno a risolvere un problema simile... Grazie allo schema risultante, il cliente stesso ha ricevuto e analizzato i log critici (utilizzando i suoi strumenti preferiti) e siamo stati in grado di ridurre il costo del sistema di registrazione, risparmiando solo il lo scorso mese.

Un altro esempio è abbastanza indicativo di cosa non fare. Uno dei nostri clienti per la lavorazione ogni eventi provenienti dall'utente, resi multilinea output non strutturato informazioni nel registro. Come puoi immaginare, tali registri erano estremamente scomodi sia da leggere che da archiviare.

Criteri per i log

Tali esempi portano alla conclusione che oltre a scegliere un sistema di raccolta dei registri, è necessario anche farlo progettare anche i registri stessi! Quali sono i requisiti qui?

  • I log devono essere in formato leggibile dalla macchina (ad esempio JSON).
  • I registri dovrebbero essere compatti e con la possibilità di modificare il grado di registrazione per eseguire il debug di possibili problemi. Allo stesso tempo, negli ambienti di produzione dovresti eseguire sistemi con un livello di registrazione simile avvertimento o Errore.
  • I log devono essere normalizzati, ovvero in un oggetto log tutte le righe devono avere lo stesso tipo di campo.

I log non strutturati possono causare problemi con il caricamento dei log nello spazio di archiviazione e l'interruzione completa della loro elaborazione. A titolo illustrativo, ecco un esempio con l'errore 400, che molti hanno sicuramente riscontrato nei registri fluidi:

2019-10-29 13:10:43 +0000 [warn]: dump an error event: error_class=Fluent::Plugin::ElasticsearchErrorHandler::ElasticsearchError error="400 - Rejected by Elasticsearch"

L'errore significa che stai inviando un campo il cui tipo è instabile all'indice con una mappatura già pronta. L'esempio più semplice è un campo nel log nginx con una variabile $upstream_status. Può contenere un numero o una stringa. Per esempio:

{ "ip": "1.2.3.4", "http_user": "-", "request_id": "17ee8a579e833b5ab9843a0aca10b941", "time": "29/Oct/2019:16:18:57 +0300", "method": "GET", "uri": "/staffs/265.png", "protocol": "HTTP/1.1", "status": "200", "body_size": "906", "referrer": "https://example.com/staff", "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36", "request_time": "0.001", "cache_status": "-", "upstream_response_time": "0.001, 0.007", "upstream_addr": "127.0.0.1:9000", "upstream_status": "200", "upstream_response_length": "906", "location": "staff"}
{ "ip": "1.2.3.4", "http_user": "-", "request_id": "47fe42807f2a7d8d5467511d7d553a1b", "time": "29/Oct/2019:16:18:57 +0300", "method": "GET", "uri": "/staff", "protocol": "HTTP/1.1", "status": "200", "body_size": "2984", "referrer": "-", "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36", "request_time": "0.010", "cache_status": "-", "upstream_response_time": "0.001, 0.007", "upstream_addr": "10.100.0.10:9000, 10.100.0.11:9000", "upstream_status": "404, 200", "upstream_response_length": "0, 2984", "location": "staff"}

I registri mostrano che il server 10.100.0.10 ha risposto con un errore 404 e la richiesta è stata inviata a un altro archivio di contenuti. Di conseguenza, il valore nei log è diventato così:

"upstream_response_time": "0.001, 0.007"

Questa situazione è così comune che merita persino una trattazione separata riferimenti nella documentazione.

E l'affidabilità?

Ci sono momenti in cui tutti i registri senza eccezioni sono vitali. E con questo, i tipici schemi di raccolta dei log per K8 proposti/discussi sopra presentano problemi.

Ad esempio, fluente non può raccogliere log da contenitori di breve durata. In uno dei nostri progetti, il contenitore di migrazione del database è durato meno di 4 secondi e poi è stato eliminato, secondo l'annotazione corrispondente:

"helm.sh/hook-delete-policy": hook-succeeded

Per questo motivo, il registro di esecuzione della migrazione non è stato incluso nell'archivio. La politica in questo caso può aiutare. before-hook-creation.

Un altro esempio è la rotazione del log Docker. Supponiamo che esista un'applicazione che scrive attivamente nei log. In condizioni normali riusciamo a elaborare tutti i log, ma non appena si verifica un problema, ad esempio come descritto sopra con un formato errato, l'elaborazione si interrompe e Docker ruota il file. Il risultato è che i registri critici per l'azienda potrebbero andare persi.

Ecco perché è importante separare i flussi di log, incorporando l'invio di quelli più preziosi direttamente nell'applicazione per garantirne la sicurezza. Inoltre, non sarebbe superfluo crearne alcuni “accumulatore” di log, che può sopravvivere a brevi indisponibilità di archiviazione salvando al contempo i messaggi critici.

Infine, non dobbiamo dimenticarlo È importante monitorare correttamente qualsiasi sottosistema. Altrimenti è facile imbattersi in una situazione in cui il fluente è in uno stato CrashLoopBackOff e non invia nulla, e questo promette la perdita di informazioni importanti.

risultati

In questo articolo non esamineremo soluzioni SaaS come Datadog. Molti dei problemi qui descritti sono già stati risolti in un modo o nell'altro da società commerciali specializzate nella raccolta di log, ma non tutti possono utilizzare SaaS per vari motivi (i principali sono il costo e la conformità alla 152-FZ).

La raccolta centralizzata dei log a prima vista sembra un compito semplice, ma non lo è affatto. È importante ricordare che:

  • Solo i componenti critici devono essere registrati in dettaglio, mentre il monitoraggio e la raccolta degli errori possono essere configurati per altri sistemi.
  • I log in produzione dovrebbero essere mantenuti al minimo per non aggiungere carico inutile.
  • I log devono essere leggibili dalla macchina, normalizzati e avere un formato rigoroso.
  • I log veramente critici dovrebbero essere inviati in un flusso separato, separato da quelli principali.
  • Vale la pena considerare un accumulatore di tronchi, che può salvarti da picchi di carico elevato e rendere più uniforme il carico sullo stoccaggio.

I log in Kubernetes (e non solo) oggi: aspettative e realtà
Queste semplici regole, se applicate ovunque, permetterebbero ai circuiti sopra descritti di funzionare, anche se mancano componenti importanti (la batteria). Se non aderisci a tali principi, il compito porterà facilmente te e l'infrastruttura a un altro componente altamente caricato (e allo stesso tempo inefficace) del sistema.

PS

Leggi anche sul nostro blog:

Fonte: habr.com

Aggiungi un commento