Ciao Habr! Negli ultimi due mesi abbiamo vissuto in una situazione molto interessante e vorrei condividere la nostra storia sul ridimensionamento dell'infrastruttura. Durante questo periodo, SberMarket ha quadruplicato gli ordini e lanciato il servizio in 4 nuove città. La crescita esplosiva della domanda di consegna di generi alimentari ci ha imposto di ridimensionare la nostra infrastruttura. Leggi le conclusioni più interessanti e utili sotto il taglio.

Mi chiamo Dima Bobylev, sono il direttore tecnico di SberMarket. Poiché questo è il primo post sul nostro blog, dirò qualche parola su di me e sull'azienda. Lo scorso autunno ho partecipato al concorso per giovani leader di Runet. Per il concorso I su come vediamo la cultura interna e l'approccio allo sviluppo dei servizi in SberMarket. E sebbene non sia riuscito a vincere la competizione, ho formulato da solo i principi di base per lo sviluppo dell'ecosistema IT.
Quando si gestisce un team, è importante comprendere e trovare un equilibrio tra ciò di cui l'azienda ha bisogno e le esigenze di ogni singolo sviluppatore. Ora SberMarket cresce 13 volte l'anno e questo influisce sul prodotto, richiedendo un costante aumento del volume e del ritmo di sviluppo. Nonostante ciò, dedichiamo tempo sufficiente agli sviluppatori per l'analisi preliminare e la codifica di alta qualità. L'approccio formato aiuta non solo nella creazione di un prodotto funzionante, ma anche nel suo ulteriore ridimensionamento e sviluppo. Come risultato di questa crescita, SberMarket è già diventato un leader tra i servizi di consegna di generi alimentari: consegniamo circa 18 ordini al giorno, anche se all'inizio di febbraio erano circa 3500.

Un giorno, un cliente ha chiesto a un corriere SberMarket di consegnargli la spesa senza contatto, proprio sul balcone.
Ma veniamo allo specifico. Negli ultimi mesi, abbiamo ridimensionato attivamente l'infrastruttura della nostra azienda. Questa esigenza è stata spiegata da fattori esterni e interni. Contemporaneamente all'ampliamento della base clienti, il numero di negozi connessi è passato dai 90 di inizio anno agli oltre 200 di metà maggio. Ovviamente ci siamo preparati, abbiamo riservato l'infrastruttura principale e abbiamo contato sulla possibilità di ridimensionamento verticale e orizzontale di tutte le macchine virtuali ospitate nel cloud Yandex. Tuttavia, la pratica ha dimostrato: "Tutto ciò che può andare storto andrà storto". E oggi voglio condividere le situazioni più curiose accadute in queste settimane. Spero che la nostra esperienza ti sia utile.
Schiavo in piena prontezza al combattimento
Anche prima dell'inizio della pandemia, ci siamo trovati di fronte a un aumento del numero di richieste ai nostri server di backend. La tendenza a ordinare generi alimentari con consegna a domicilio ha iniziato a prendere slancio e con l'introduzione delle prime misure di autoisolamento in relazione a COVID-19, il carico è cresciuto notevolmente sotto i nostri occhi durante la giornata. Era necessario scaricare rapidamente i server master del database principale e trasferire alcune delle richieste di lettura ai server di replica (slave).
Ci stavamo preparando in anticipo per questo passaggio e 2 server slave erano già in esecuzione per tale manovra. Hanno lavorato principalmente su attività batch per la generazione di feed di informazioni per lo scambio di dati con i partner. Questi processi hanno creato un carico extra e giustamente sono stati tolti dalla parentesi un paio di mesi prima.
Poiché la replica è avvenuta sullo Slave, abbiamo aderito al concetto che le applicazioni possono funzionare solo con loro in modalità di sola lettura. Il Disaster Recovery Plan presupponeva che, in caso di disastro, potessimo semplicemente montare lo Slave al posto del Master e trasferire tutte le richieste di scrittura e lettura allo Slave. Tuttavia, volevamo anche utilizzare le repliche per le esigenze del reparto di analisi, quindi i server non erano completamente impostati sullo stato di sola lettura e ogni host aveva il proprio set di utenti e alcuni avevano autorizzazioni di scrittura per salvare i risultati dei calcoli intermedi.
Fino a un certo livello di carico, avevamo abbastanza master sia per la scrittura che per la lettura durante l'elaborazione delle richieste http. A metà marzo, proprio quando Sbermarket ha deciso di passare completamente al lavoro da remoto, abbiamo iniziato a moltiplicare la crescita degli RPS. Sempre più clienti sono entrati in autoisolamento o hanno lavorato da casa, il che si è riflesso negli indicatori di carico.
Le prestazioni del "master" non erano più sufficienti, quindi abbiamo iniziato a trasferire alcune delle richieste di lettura più pesanti alla replica. Per indirizzare in modo trasparente le richieste di scrittura al master, e le richieste di lettura allo slave, abbiamo utilizzato la gemma di rubino"". Abbiamo creato un utente speciale con _readonly suffisso senza permessi di scrittura. Ma a causa di un errore nella configurazione di uno degli host, parte delle richieste di scrittura sono andate al server slave per conto di un utente che disponeva dei diritti appropriati.
Il problema non si è manifestato immediatamente, perché. l'aumento del carico ha aumentato l'arretrato degli schiavi. L'incoerenza dei dati è stata scoperta al mattino, quando, dopo le importazioni notturne, gli schiavi non "recuperavano" il padrone. Abbiamo attribuito questo a un carico elevato sul servizio stesso e alle importazioni associate al lancio di nuovi negozi. Ma era inaccettabile fornire i dati con un ritardo di molte ore e abbiamo trasferito i processi al secondo slave analitico, perché avevaоRisorse più grandi e non è stato caricato con richieste di lettura (ed è così che ci siamo spiegati la mancanza di ritardo di replica).
Quando abbiamo scoperto le ragioni della "diffusione" dello schiavo principale, quello analitico aveva già fallito per lo stesso motivo. Nonostante la presenza di due server aggiuntivi, sui quali avevamo programmato di trasferire il carico in caso di crash del master, a causa di uno sfortunato errore, si è scoperto che non ce n'era uno in un momento critico.
Ma poiché non solo abbiamo scaricato il database (il ripristino in quel momento era di circa 5 ore), ma anche un'istantanea del server principale, siamo riusciti ad avviare la replica entro 2 ore. È vero, dopo ciò, ci aspettavamo di eseguire il rollback del registro di replica per molto tempo (perché il processo è in modalità single-thread, ma questa è una storia completamente diversa).
Conclusione: Dopo un tale incidente, è diventato chiaro che la pratica di limitare le scritture per gli utenti doveva essere abbandonata e l'intero server doveva essere dichiarato di sola lettura. Con questo approccio, puoi essere certo che le repliche saranno disponibili in un momento critico.
L'ottimizzazione anche di una query pesante può riportare in vita il database
Sebbene aggiorniamo costantemente il catalogo sul sito, le richieste che abbiamo fatto ai server Slave hanno consentito un leggero ritardo rispetto al Master. Il tempo durante il quale abbiamo scoperto ed eliminato il problema degli schiavi "usciti improvvisamente fuori strada" è stato più della "barriera psicologica" (durante questo periodo, i prezzi potevano essere aggiornati e i clienti vedevano dati obsoleti), e siamo stati costretti a cambiare tutte le query al server del database principale. Di conseguenza, il sito era lento... ma almeno funzionava. E mentre lo Slave si stava riprendendo, non ci restava altro che l'ottimizzazione.
Mentre i server Slave si stavano riprendendo, i minuti si trascinavano lentamente, il Master rimaneva sovraccarico e abbiamo dedicato tutti i nostri sforzi all'ottimizzazione delle attività attive secondo la regola di Pareto: abbiamo scelto le query TOP che danno la maggior parte del carico e abbiamo iniziato la messa a punto. Questo è stato fatto al volo.
Un effetto interessante è stato che MySQL, caricato fino agli occhi, risponde anche a un leggero miglioramento dei processi. L'ottimizzazione di un paio di query che hanno fornito solo il 5% del carico totale ha già mostrato un notevole offload della CPU. Di conseguenza, siamo riusciti a fornire una riserva accettabile di risorse affinché il Master possa lavorare con il database e ottenere il tempo necessario per ripristinare le repliche.
Conclusione: Anche una piccola ottimizzazione ti consente di "sopravvivere" al sovraccarico per diverse ore. Questo è stato appena sufficiente per ripristinare i server con le repliche. A proposito, discuteremo il lato tecnico dell'ottimizzazione delle query in uno dei seguenti post. Quindi iscriviti al nostro blog se può esserti utile.
Organizzare il monitoraggio dello stato di salute dei servizi dei partner
Elaboriamo gli ordini dei clienti e pertanto i nostri servizi interagiscono costantemente con API di terze parti: si tratta di gateway per l'invio di SMS, piattaforme di pagamento, sistemi di routing, un geocodificatore, il servizio fiscale federale e molti altri sistemi. E quando il carico ha iniziato a crescere rapidamente, abbiamo iniziato a imbatterci nei limiti dell'API dei nostri servizi partner, a cui prima non avevamo nemmeno pensato.
Il superamento imprevisto delle quote di servizio dei partner può causare tempi di inattività. Molte API bloccano i client che superano i limiti e, in alcuni casi, un eccesso di richieste può sovraccaricare la produzione del partner.
Ad esempio, al momento della crescita del numero di consegne, i servizi di accompagnamento non potevano far fronte ai compiti della loro distribuzione e determinazione del percorso. Di conseguenza, si è scoperto che gli ordini erano stati effettuati, ma il servizio che ha creato il percorso non funzionava. Devo dire che i nostri logisti hanno fatto il quasi impossibile in queste condizioni e la chiara interazione del team ha contribuito a compensare i guasti temporanei del servizio. Ma non è realistico elaborare sempre manualmente un tale volume di domande e dopo un po' incontreremmo un divario inaccettabile tra gli ordini e la loro esecuzione.
Sono state adottate una serie di misure organizzative e il lavoro ben coordinato del team ha contribuito a guadagnare tempo mentre concordavamo nuove condizioni e attendevamo l'ammodernamento dei servizi da parte di alcuni partner. Esistono altre API che offrono un'elevata resistenza e tariffe empie in caso di traffico elevato. Ad esempio, all'inizio, abbiamo utilizzato una nota API di mappatura per determinare l'indirizzo del punto di consegna. Ma alla fine del mese hanno ricevuto un conto tondo per quasi 2 milioni di rubli. Successivamente, abbiamo deciso di sostituirlo rapidamente. Non farò pubblicità, ma dirò che le nostre spese sono diminuite in modo significativo.

Conclusione: È imperativo monitorare le condizioni di lavoro di tutti i servizi partner e tenerle a mente. Anche se oggi sembra che per te siano “con un ampio margine”, ciò non significa che domani non diventeranno un ostacolo alla crescita. E, naturalmente, è meglio concordare in anticipo i termini finanziari dell'aumento delle richieste del servizio.
A volte si scopre che"(c) non aiuta
Siamo abituati a "gag" nel database principale o sui server delle applicazioni, ma durante il ridimensionamento possono comparire problemi dove non erano previsti Per la ricerca full-text sul sito, utilizziamo il motore Apache Solr. Con l'aumentare del carico, abbiamo notato una diminuzione del tempo di risposta e il carico della CPU del server ha raggiunto il 100%. Cosa potrebbe essere più semplice: dai al contenitore Solr più risorse.
Invece del previsto aumento delle prestazioni, il server è semplicemente "morto". Si è caricato immediatamente al 100% e ha risposto ancora più lentamente. Inizialmente avevamo 2 core e 2 GB di RAM. Abbiamo deciso di fare ciò che di solito aiuta: abbiamo dato al server 8 core e 32 GB. Tutto è peggiorato molto (ti diremo esattamente come e perché in un post separato).
In pochi giorni abbiamo capito le complessità di questo problema e ottenuto prestazioni ottimali con 8 core e 32 GB. Questa configurazione ci consente di continuare ad aumentare il carico oggi, il che è molto importante, perché la crescita non è solo in termini di clienti, ma anche nel numero di negozi collegati: in 2 mesi il loro numero è raddoppiato.
Conclusione: I metodi standard come "aggiungere più ferro" non sempre funzionano. Pertanto, quando si ridimensiona qualsiasi servizio, è necessario avere una buona comprensione di come utilizza le risorse e testarlo in anticipo, il suo funzionamento in nuove condizioni.
Stateless è la chiave per un semplice ridimensionamento orizzontale
In generale, il nostro team aderisce a un approccio ben noto: i servizi non dovrebbero avere uno stato interno (stateless) e dovrebbero essere indipendenti dall'ambiente di runtime. Questo ci ha permesso di sopravvivere all'aumento del carico con un semplice ridimensionamento orizzontale. Ma abbiamo avuto un'eccezione di servizio: un gestore per lunghe attività in background. È stato coinvolto nell'invio di e-mail e sms, nell'elaborazione di eventi, nella generazione di feed, nell'importazione di prezzi e azioni e nell'elaborazione di immagini. È successo così che dipendeva dall'archiviazione locale dei file ed era in un'unica copia.
Quando il numero di attività nella coda del processore è aumentato (e questo è avvenuto naturalmente con un aumento del numero di ordini), le prestazioni dell'host che ospitava il processore e l'archiviazione dei file sono diventate un fattore limitante. Di conseguenza, l'aggiornamento della gamma e dei prezzi, l'invio di notifiche agli utenti e molte altre funzioni critiche bloccate nella coda si sono interrotte. Il team operativo ha rapidamente migrato l'archiviazione dei file a un'archiviazione di rete simile a S3 e questo ci ha permesso di aumentare diverse macchine potenti per ridimensionare il gestore delle attività in background.
Conclusione: La regola Stateless va osservata per tutti i componenti nessuno escluso, anche se sembra “che qui sicuramente non riposeremo”. È meglio dedicare un po 'di tempo alla corretta organizzazione del lavoro di tutti i sistemi piuttosto che riscrivere il codice in fretta e riparare un servizio sovraccarico.
7 principi per la crescita intensiva
Nonostante la disponibilità di capacità aggiuntiva, nel processo di crescita, abbiamo calpestato alcuni rastrelli. Durante questo periodo, il numero di ordini è aumentato di oltre 4 volte. Ora consegniamo già più di 17 ordini al giorno in 000 città e prevediamo di espandere ulteriormente la geografia: nella prima metà del 62, il servizio dovrebbe essere lanciato in tutta la Russia. Per far fronte al crescente carico di lavoro, tenendo conto dei dossi già pieni, abbiamo ricavato per noi stessi 2020 principi base per lavorare in un ambiente di crescita costante:
- Gestione degli incidenti. Abbiamo creato una bacheca in Jira, in cui ogni incidente è riportato sotto forma di ticket. Questo ti aiuterà effettivamente a stabilire le priorità e a completare le attività relative agli incidenti. In effetti, in sostanza, non è terribile commettere errori: è terribile commettere errori due volte nella stessa occasione. Per quei casi in cui gli incidenti si ripetono prima che la causa possa essere corretta, dovrebbero essere preparate istruzioni per l'azione, perché durante un carico pesante è importante reagire alla velocità della luce.
- Monitoraggio richiesto per tutti gli elementi dell'infrastruttura senza eccezioni. È stato grazie a lui che siamo stati in grado di prevedere la crescita del carico e scegliere correttamente i "colli di bottiglia" per dare priorità all'eliminazione. Molto probabilmente, sotto un carico elevato, tutto ciò a cui non avevi nemmeno pensato si romperà o inizierà a rallentare. Pertanto, è meglio creare nuovi avvisi subito dopo l'inizio dei primi incidenti per monitorarli e anticiparli.
- Avvisi corretti solo necessario con un forte aumento del carico. Innanzitutto, devono segnalare esattamente ciò che è rotto. In secondo luogo, non dovrebbero esserci molti avvisi, perché l'abbondanza di avvisi non critici porta a ignorare tutti gli avvisi in generale.
- Le applicazioni devono essere stateless. Abbiamo fatto in modo che non ci siano eccezioni a questa regola. È necessaria la completa indipendenza dall'ambiente di runtime. Per fare ciò, puoi archiviare i dati condivisi in un database o, ad esempio, direttamente in S3. Meglio ancora, segui le regole.. Durante un forte aumento del tempo, semplicemente non c'è modo di ottimizzare il codice e dovrai far fronte al carico aumentando direttamente le risorse di calcolo e il ridimensionamento orizzontale.
- Quote e prestazioni di servizi esterni. Con una rapida crescita, il problema potrebbe sorgere non solo nella tua infrastruttura, ma anche in un servizio esterno. La cosa più fastidiosa è quando ciò accade non per un guasto, ma per il raggiungimento di quote o limiti. Quindi i servizi esterni dovrebbero scalare così come te stesso.
- Processi e code separati. Questo aiuta molto quando si verifica un plug su uno dei gateway. Non avremmo riscontrato ritardi nella trasmissione dei dati se le code piene per l'invio di SMS non interferissero con lo scambio di notifiche tra i sistemi informativi. E sarebbe più facile aumentare il numero dei lavoratori se lavorassero separatamente.
- realtà finanziarie. Quando c'è una crescita esplosiva dei flussi di dati, non c'è tempo per pensare a tariffe e abbonamenti. Ma devono essere ricordati, soprattutto se sei una piccola azienda. Una fattura elevata può essere impostata dal proprietario di qualsiasi API, nonché dal tuo provider di hosting. Quindi leggi attentamente i contratti.
conclusione
Non senza perdite, ma siamo sopravvissuti a questa fase e oggi cerchiamo di aderire a tutti i principi trovati e ogni macchina ha la capacità di aumentare facilmente le prestazioni x4 per far fronte ad alcune sorprese.
Nei post seguenti, condivideremo la nostra esperienza nell'analisi del calo delle prestazioni in Apache Solr, oltre a parlare dell'ottimizzazione delle query e di come l'interazione con il servizio fiscale federale aiuti l'azienda a risparmiare denaro. Iscriviti al nostro blog per non perderti nulla, e dicci nei commenti se hai avuto problemi simili durante la crescita del traffico.

Solo gli utenti registrati possono partecipare al sondaggio. Per favore.
Ti è mai capitato di avere un rallentamento/calo del servizio a causa di un forte aumento del carico dovuto a:
55,6%Incapacità di aggiungere rapidamente risorse di calcolo10
16,7%Limiti dell'infrastruttura del provider di hosting3
33,3%Limiti API6 di terze parti
27,8%Violazioni dei principi degli apolidi loro applicazioni5
88,9%Codice non ottimale dei propri servizi16
18 utenti hanno votato. 6 utenti si sono astenuti.
Fonte: habr.com
