Nota del traduttore.: all'inizio di agosto, Red Hat ha pubblicamente affrontato le problematiche di disponibilità emerse nei mesi precedenti per gli utenti del suo servizio (alla base vi è un registro per le immagini dei container, acquisito dall’azienda con l'acquisto di CoreOS). Indipendentemente dall'interesse per questo servizio in particolare, è interessante il percorso che gli ingegneri SRE dell'azienda hanno seguito per diagnosticare e risolvere le cause dell'incidente.

Il 19 maggio, nella prima mattinata (secondo l'ora estiva del fuso orario orientale nordamericano, EDT), il servizio quay.io è andato in crash. L'incidente ha interessato sia i consumatori di quay.io che i progetti Open Source che utilizzano quay.io come piattaforma per la costruzione e distribuzione del software. Red Hat tiene in alta considerazione la fiducia di entrambi.
Il team di ingegneri SRE è entrato subito in azione cercando di stabilizzare il servizio Quay il più rapidamente possibile. Tuttavia, mentre erano impegnati in questo lavoro, i clienti hanno perso la possibilità di pushare nuove immagini, riuscendo a pullare solo occasionalmente quelle esistenti. Per motivi sconosciuti, il database di quay.io si bloccava dopo che il servizio era stato scalato a piena capacità.
«Cosa è cambiato?» — è la prima domanda che ci si pone in questi casi. Abbiamo notato che poco prima del problema, il cluster OpenShift Dedicated (su cui opera quay.io) è iniziato ad essere aggiornato alla versione 4.3.19. Poiché quay.io funziona su Red Hat OpenShift Dedicated (OSD), gli aggiornamenti regolari erano un'operazione routinaria e non avevano mai portato a problemi. Inoltre, nei sei mesi precedenti, abbiamo aggiornato più volte i cluster Quay senza alcuna interruzione del servizio.
Mentre cercavamo di ripristinare il servizio, altri ingegneri hanno iniziato a preparare un nuovo cluster OSD con la versione precedente del software, in modo da poterlo attivare in caso di necessità.
Analisi delle cause principali
Il principale sintomo del guasto era un'onda di decine di migliaia di connessioni al database, che ha reso l'istanza MySQL praticamente inutilizzabile. Questo ha reso difficile diagnosticare il problema. Abbiamo impostato un limite sul numero massimo di connessioni da parte dei clienti per aiutare il team SRE a valutare la situazione. Non abbiamo notato alcun traffico anomalo verso il database: in realtà, la maggior parte delle richieste erano per la lettura, e solo poche per la scrittura.
Abbiamo anche cercato di identificare un modello nel traffico del database che potesse causare questa valanga. Tuttavia, non siamo riusciti a trovare alcuna regolarità nei log. In attesa che il nuovo cluster con OSD 4.3.18 fosse pronto, abbiamo continuato a tentare di avviare i pod di quay.io. Ogni volta che il cluster raggiungeva la piena capacità, il database si bloccava. Questo significava che era necessario riavviare l'istanza RDS oltre a tutti i pod di quay.io.
Entro la sera, abbiamo stabilizzato il servizio in modalità di sola lettura e disattivato il massimo numero di funzionalità non essenziali (ad esempio, la raccolta dei rifiuti nello spazio dei nomi) per ridurre il carico sul database. I blocchi si sono fermati, ma la causa non è stata trovata. Il nuovo cluster OSD era pronto, e abbiamo trasferito il servizio, collegato il traffico e continuato il monitoraggio.
Quay.io funzionava stabilmente nel nuovo cluster OSD, quindi siamo tornati ai log del database, ma non siamo riusciti a trovare alcuna correlazione che spiegasse i blocchi. Gli ingegneri di OpenShift hanno lavorato insieme a noi, cercando di capire se le modifiche in Red Hat OpenShift 4.3.19 potessero aver causato problemi con Quay. Tuttavia, non è emerso nulla. Non siamo riusciti a replicare il problema in laboratorio..
Secondo guasto
Il 28 maggio, poco prima di mezzogiorno EDT, quay.io è di nuovo andato in crash con gli stessi sintomi: il funzionamento del database veniva bloccato. E di nuovo, abbiamo messo in campo tutte le forze per indagare. Prima di tutto, era necessario ripristinare il servizio. Tuttavia quella volta il riavvio dell'RDS e il riavvio dei pod di quay.io non hanno portato a nulla: un'altra valanga di connessioni ha travolto il database. Ma perché?
Quay è scritto in Python, e ogni pod funziona come un unico container monolitico. Nell'ambiente di esecuzione del container vengono eseguite simultaneamente molte attività parallele. Utilizziamo la libreria gevent sotto gunicorn per gestire le richieste web. Quando arriva una richiesta a Quay (tramite la nostra API o l'API di Docker), viene assegnato un worker gevent. Di solito, questo worker dovrebbe connettersi al database. Dopo il primo crash, abbiamo scoperto che i worker gevent si connettevano al database utilizzando le impostazioni predefinite.
Considerando il notevole numero di pod Quay e le migliaia di richieste in arrivo al secondo, un alto numero di connessioni al database potrebbe teoricamente sovraccaricare l'istanza MySQL. Grazie al monitoraggio, si sapeva che Quay gestisce in media 5.000 richieste al secondo. Circa lo stesso numero di connessioni al database. 5.000 connessioni rientravano senza problemi nelle capacità della nostra istanza RDS (cosa non si può dire per decine di migliaia). Per qualche motivo si verificavano picchi imprevisti nel numero di connessioni, tuttavia non notavamo alcuna correlazione con le richieste in arrivo.
Questa volta eravamo decisi a trovare e risolvere la causa del problema, e non a limitarci a un riavvio. Sono state apportate modifiche al codice sorgente di Quay sono state apportate modifiche che limitano il numero di connessioni al DB per ogni worker. gevent. Questo numero è diventato un parametro nella configurazione: ora è possibile modificarlo «al volo», senza dover ricompilare una nuova immagine del container. Per determinare quanti collegamenti possono essere gestiti realmente, sono stati eseguiti diversi test nell'ambiente di staging, in cui sono stati impostati valori diversi per vedere come ciò influisse sugli scenari di test di carico. Alla fine è emerso che Quay inizia a restituire errori 502 quando il numero di collegamenti supera i 10.000.
Abbiamo subito distribuito questa nuova versione in produzione e abbiamo iniziato a monitorare il grafico delle connessioni al database. In passato, il database si bloccava dopo circa 20 minuti. Dopo 30 minuti senza problemi, abbiamo iniziato a sperare, e dopo un'ora abbiamo acquisito sicurezza. Abbiamo ripristinato il traffico in scrittura sul sito e abbiamo iniziato l'analisi postmortem.
Essendo riusciti a aggirare il problema che causava il blocco, non abbiamo scoperto le sue vere cause. È stato confermato che non era correlata a modifiche nella versione OpenShift 4.3.19, poiché lo stesso è accaduto anche nella versione 4.3.18, che prima funzionava con Quay senza problemi.
Nel cluster si nascondeva chiaramente qualcos'altro.
Un'indagine dettagliata
Quay.io ha utilizzato per sei anni le impostazioni predefinite per la connessione al database senza alcun problema. Cosa è cambiato? È evidente che, per tutto questo tempo, il traffico su quay.io è cresciuto incessantemente. Nel nostro caso, sembrava che si fosse raggiunto una sorta di soglia, che ha attivato un effetto valanga di connessioni. Abbiamo continuato a esaminare i log del database dopo il secondo crash, ma non abbiamo trovato alcuna coerenza o legami evidenti.
Nel frattempo, il team SRE si stava occupando di miglioramenti nella visibilità delle richieste in Quay e nella salute generale del servizio. Sono state implementate nuove metriche e dashboard, che mostrano quali parti di Quay sono più richieste dai clienti.
Quay.io ha funzionato normalmente fino al 9 giugno. La mattina (ora EDT) abbiamo assistito ancora a un significativo aumento del numero di connessioni al database. Questa volta non ci sono stati tempi di inattività., poiché il nuovo parametro limitava il numero e non permetteva di superare la larghezza di banda di MySQL. Tuttavia, per circa mezz'ora molti utenti hanno segnalato un lento funzionamento di quay.io. Abbiamo rapidamente raccolto tutti i dati possibili utilizzando gli strumenti di monitoraggio aggiunti. È emerso un modello inaspettato.
Poco prima del balzo nel numero delle connessioni, un gran numero di richieste è arrivato all'API App Registry. L'App Registry è una funzione poco conosciuta di quay.io. Consente di memorizzare cose come grafici Helm e contenitori con metadati ricchi. La maggior parte degli utenti di quay.io non utilizza questa funzione, ma è ampiamente sfruttata da Red Hat OpenShift. OperatorHub all'interno di OpenShift memorizza tutti gli operatori nell'App Registry. Questi operatori costituiscono la base per l'ecosistema delle applicazioni OpenShift e il modello operativo (nell'ambito delle operazioni del 'secondo giorno', Day 2) orientato ai partner.
Ogni cluster OpenShift 4 utilizza operatori dall'OperatorHub integrato per pubblicare un catalogo di operatori disponibili per l'installazione e fornire aggiornamenti per quelli già installati. Con l'aumento della popolarità di OpenShift 4, è aumentato anche il numero di cluster in tutto il mondo. Ognuno di questi cluster carica il contenuto degli operatori per avviare l'OperatorHub integrato, utilizzando l'App Registry all'interno di quay.io come backend. Nella ricerca della fonte del problema, abbiamo trascurato il fatto che, con l'aumento graduale della popolarità di OpenShift, aumentava anche il carico su una delle funzionalità raramente utilizzate di quay.io..
Abbiamo effettuato un'analisi del traffico delle richieste di App Registry ed esaminato il codice del registro. Sono emerse immediatamente delle carenze che causavano query al database non ottimizzate. Sotto carico leggero non causavano problemi, ma con l'aumento del carico diventavano una fonte di difficoltà. App Registry aveva due endpoint problematici che rispondevano male all'aumento del carico: il primo restituiva l'elenco di tutti i pacchetti nel repository, il secondo restituiva tutti i blob per un pacchetto.
Risoluzione delle cause
Per tutta la settimana scorsa ci siamo concentrati sull'ottimizzazione del codice dell'App Registry e del suo ambiente. Sono state riscritte chiaramente le query SQL inefficaci e sono stati eliminati i chiamate superflue al comando tar (veniva attivata ad ogni estrazione dei blob), è stata implementata la cache ovunque fosse possibile. Successivamente, sono stati eseguiti test di performance estesi e confrontata la velocità di App Registry prima e dopo le modifiche.
Le richieste API, che prima richiedevano fino a trenta secondi, ora vengono eseguite in millisecondi. La settimana successiva, abbiamo implementato le modifiche in produzione, e da allora quay.io funziona in modo stabile. Durante questo periodo si sono verificati alcuni picchi di traffico sull'endpoint di App Registry, ma i miglioramenti apportati hanno prevenuto interruzioni nel funzionamento del database.
Cosa abbiamo imparato?
È chiaro che ogni servizio cerca di evitare tempi di inattività. Nel nostro caso, crediamo che i recenti problemi abbiano aiutato a rendere quay.io migliore. Per noi, abbiamo tratto alcune lezioni fondamentali che vogliamo condividere:
- I dati su chi e come utilizza il tuo servizio non sono mai superflui.. Poiché Quay "funzionava semplicemente", non abbiamo mai sentito la necessità di dedicare tempo all'ottimizzazione del traffico e alla gestione del carico. Tutto ciò ha creato un falso senso di sicurezza, facendo credere che il servizio potesse scalare all'infinito.
- Quando il servizio va in down, il ripristino del suo funzionamento è la massima priorità. Poiché Quay ha continuato a soffrire di una base di dati bloccata durante il primo guasto, le nostre procedure standard non hanno avuto l'effetto desiderato e non siamo riusciti a ripristinare il servizio con esse. Questo ha portato a una situazione in cui è stato necessario spendere tempo ad analizzare e raccogliere dati nella speranza di trovare la causa principale, invece di concentrare tutti gli sforzi sul ripristino della funzionalità.
- Valuta l'impatto di ciascuna delle funzionalità del servizio. I clienti utilizzavano raramente App Registry, quindi non era una priorità per il nostro team. Quando alcune funzioni del prodotto vengono utilizzate poco, i loro bug «emergono» raramente e gli sviluppatori smettono di monitorare il codice. È facile cadere nell'illusione che sia normale — fino a quando all'improvviso quella funzione non finisce al centro di un grande incidente.
E ora?
Il lavoro per garantire la stabilità del servizio non si ferma mai e continuiamo a migliorarlo costantemente. I volumi di traffico su quay.io continuano a crescere e abbiamo la consapevolezza di dover fare tutto il possibile per meritare la fiducia dei clienti. Pertanto, attualmente stiamo lavorando sui seguenti obiettivi:
- Distribuzione di repliche di database in sola lettura per aiutare il servizio a gestire il traffico corrispondente in caso di problemi con l'istanza principale di RDS.
- Aggiornamento dell'istanza di RDS. L'attuale versione non presenta di per sé problemi. Piuttosto, vogliamo semplicemente eliminare un falso indizio (su cui ci siamo orientati durante il guasto); mantenere il software aggiornato eliminerà un altro fattore in caso di futuri blackout.
- Caching aggiuntivo su tutto il cluster. Continuiamo a cercare aree in cui il caching può ridurre il carico sul database.
- Aggiunta di un firewall per applicazioni web (WAF), per vedere chi e perché si connette a quay.io.
- A partire dal prossimo rilascio, i cluster Red Hat OpenShift abbandoneranno l'App Registry in favore dei cataloghi degli operatori (Operator Catalogs), basati su immagini di container disponibili su quay.io.
- Una sostituzione a lungo termine dell'App Registry potrebbe essere il supporto delle specifiche degli artefatti Open Container Initiative (OCI). Attualmente è implementato come funzionalità nativa di Quay e sarà disponibile per gli utenti una volta finalizzate le specifiche.
Tutto quanto sopra fa parte degli investimenti in corso di Red Hat in quay.io mentre passiamo da un piccolo team "in stile startup" a una piattaforma matura gestita da SRE. Sappiamo che molti dei nostri clienti fanno affidamento su quay.io nel loro lavoro quotidiano (incluso Red Hat!) e ci impegniamo a essere il più trasparenti possibile riguardo ai recenti problemi e agli sforzi in corso per migliorare.
P.S. dal traduttore
Leggete anche nel nostro blog:
- «»;
- «»;
- «».
Fonte: habr.com
