Utilizziamo lo stesso set di principi tecnici per ottenere resilienza e disponibilità per tutti i tipi di servizio, perché le sfide tecniche fondamentali della creazione di sistemi distribuiti con tolleranza agli errori, scalabili e distribuiti sono le stesse per tutti i tipi di servizio.
Per raggiungere la resilienza e la disponibilità continua, è necessario comprendere e poi gestire tutte le cause dell'indisponibilità, quali prestazioni degradate e errori non gestiti, nei sistemi su scala cloud. Esiste un enorme numero di cause di questo tipo ed è per questo che le raggruppiamo in categorie in base alla loro natura fondamentale
L'analisi della disponibilità dei sistemi IT Enterprise si concentra da sempre sulla categoria di guasto hardware. Tuttavia, nel caso dei sistemi cloud, il guasto dell'hardware è un problema relativamente secondario e ben noto. Ora è possibile evitare o mitigare la maggior parte dei singoli punti di guasto hardware. Ad esempio, i rack possono disporre di due alimentatori e di unità di distribuzione dell'alimentazione associate. Inoltre, molti componenti supportano l'hot swap. Problemi e perdite hardware su larga scala sono naturalmente possibili, ad esempio a causa di calamità naturali. Tuttavia, dalla nostra esperienza e dai report contenuti nelle analisi a posteriori pubbliche di altri fornitori di soluzioni cloud è emerso che il guasto o la perdita di un intero data center accade molto raramente, rispetto alle altre cause di indisponibilità. È comunque necessario gestire i guasti hardware su larga scala (ad esempio con un disaster recovery e altri meccanismi), ma non si tratta certo del problema di disponibilità più importante.
Le cause principali dell'indisponibilità nei sistemi di scala cloud sono le seguenti:
- Bug dei software
- Errori di configurazione
- Errori umani da parte degli operatori
Nota: la lezione principale appresa dal settore è che finora queste tre forme di errore umano rappresentano le cause più rilevanti dell'indisponibilità. Sebbene sia possibile ridurne la frequenza attraverso strumenti, automazione e formazione, non è possibile eliminarle del tutto. Pertanto, devono essere affrontate come problema primario nell'architettura, nella progettazione e nell'implementazione dei sistemi.
- Per qualsiasi motivo, è considerata una varianza inaccettabile a livello di prestazioni (latenza o throughput), incluse le seguenti:
- "Noisy neighbor" multi-tenant (errore dei meccanismi QoS)
- Impossibile rifiutare l'overload (involontario o dannoso) con successo, continuando a fare un lavoro utile
- Thrashing distribuito, storage di messaggi, bombardamento di nuovi tentativi di invio e altre costose interazioni "emergenti"
- Shock termici (cache vuote) dopo l'accensione e lo spegnimento, soprattutto l'accensione e lo spegnimento simultaneo di più sistemi
- Sovraccarico durante il ridimensionamento del sistema (ad esempio durante un nuovo partizionamento orizzontale)
- Impossibilità a limitare il "raggio di esplosione" (numero di clienti e sistemi interessati) di uno qualsiasi dei problemi precedenti
Queste problematiche sono universali: fanno parte delle "leggi della fisica" per i sistemi distribuiti su scala cloud.
Per superare il problema, adottiamo strategie ingegneristiche comprovate per ognuna delle categorie precedenti. Ecco le più importanti:
- Principi dell'architettura e della progettazione del sistema
- Nuovi concetti architettonici ( che, in genere, derivano dall'applicazione dei principi)
- Procedure di assistenza tecnica
Principi dell'architettura e della progettazione del sistema
Sebbene esistano molti di questi principi, ci concentreremo su quelli più pertinenti alla resilienza e alla disponibilità.
Computazione orientata al recupero
Per gestire i bug software e gli errori da parte degli operatori, che hanno effetti relativamente localizzati, seguiamo i principi della computazione orientata al recupero1. A livello generale, ciò significa che invece di provare a garantire l'assenza assoluta di problemi (condizione impossibile da testare), ci concentriamo sulla gestione di qualsiasi problema con discrezione, in modo tale che sia possibile eseguire i test. In particolare, ci impegniamo a ridurre il tempo medio di recupero (MTTR), che è una combinazione di tempi medi per rilevare, diagnosticare e mitigare.
Il nostro scopo è quello di recuperare così rapidamente che gli utenti non sono impattati dal problema. Ecco i punti da noi seguiti per raggiungere questo obiettivo:
- Rilevare rapidamente e automaticamente i sintomi dei bug e degli errori da parte degli operatori, mediante l'uso pervasivo delle asserzioni nel codice, il monitoraggio attivo e gli avvisi a tutti i livelli.
- Raggruppare le funzionalità in molte unità di isolamento con filtro avanzato ben distinte (thread, processi, fibre, computer di stato e così via) che sono accoppiate in modo indiretto, ossia non condividono direttamente la memoria che potrebbe essere danneggiata.
- Al rilevamento dei sintomi di un bug o di un errore da parte di un operatore, riavviare automaticamente il più rapidamente possibile l'unità di inclusione di isolamento. Il riavvio rappresenta un metodo pratico per provare a eseguire il recupero da un errore arbitrario, perché tenta di ristabilire uno stato che è stato correttamente sottoposto a test e pertanto ripristina le invarianti.
- Se il recupero a un livello con filtro avanzato di isolamento non funziona (ad esempio, le asserzioni continuano ad attivarsi in tale livello con troppa frequenza causando crash con esecuzione in loop), passare alla unità di dimensioni successive (processo, runtime, host, data center logico, paging verso un operatore reale).
- Crea meccanismi per abilitare un "annullamento a livello di sistema", incluso il controllo delle versioni di tutti gli stati e le configurazioni persistenti, in modo da identificare e annullare in modo rapido i commit non validi.
Ridurre al minimo gli effetti dei problemi
Per gestire i bug e gli errori che potrebbero avere effetti più estesi, creiamo meccanismi che riducono al minimo il "raggio di esplosione" di qualsiasi problema. Ciò significa che dedichiamo la nostra attenzione a limitare drasticamente il numero di clienti, sistemi o risorse interessate dai problemi, inclusi quelli particolarmente impegnativi associati a "presenze di disturbo" multi-tenant, sovraccarico offerto, capacità ridotta e thrashing distribuito. A tale scopo, utilizzare vari limiti di isolamento e procedure di gestione delle modifiche (vedere le sezioni seguenti).
Concetti architettonici derivanti da principi di progettazione
Esistono molti di questi concetti, ma verranno descritti i concetti per limitare il raggio di esplosione.
Concetti di posizionamento racchiusi nella nostra API pubblica: region, domini di disponibilità e domini di errore
Poiché il concetto di dominio di errore è relativamente nuovo, lo descriveremo in un modo più dettagliato.
I domini di errore vengono usati per limitare il raggio di esplosione dei problemi che si verificano quando un sistema viene modificato in modo attivo: ad esempio, distribuzioni, applicazione di patch, riavvii dell'hypervisor e manutenzione fisica.
La garanzia è che, in un determinato dominio di disponibilità, le risorse al massimo in un dominio di errore vengano modificate in qualsiasi punto specifico nel tempo. Se qualcosa va storto nel processo di modifica, alcune o tutte le risorse nel dominio di errore potrebbero non essere disponibili per un po' di tempo, ma gli altri domini di errore nel dominio di disponibilità non vengono interessati. Ogni dominio di disponibilità contiene almeno tre domini di errore per consentire che i sistemi di replica basati su quorum (ad esempio, Oracle Data Guard) siano ospitati con alta disponibilità all'interno di un singolo dominio di disponibilità.
Di conseguenza, per una categoria dominante di problemi inerenti alla disponibilità, quali bug software, errori di configurazione, errori commessi dagli operatori e problematiche a livello di prestazioni durante una procedura di modifica, ogni dominio di errore svolge il ruolo di data center logico distinto all'interno di un dominio di disponibilità.
I domini di errore, inoltre, proteggono da alcuni tipi di errori hardware localizzati. Le proprietà dei domini di errore garantiscono che le risorse inserite in domini di errore differenti non condividano, nei limiti del possibile, alcun potenziale singolo punto di guasto hardware all'interno del dominio di disponibilità. Ad esempio, le risorse in domini di errore diversi non condividono lo stesso switch di rete "top-of-rack" perché la progettazione standard di tali switch manca di ridondanza.
La capacità dei domini di errore di proteggersi da problemi in hardware o nell'ambiente fisico si ferma a livello locale. A differenza dei domini di disponibilità e delle aree, i domini di errore non forniscono alcun isolamento fisico dell'infrastruttura su larga scala. Nel recondito caso in cui si verifichi un disastro naturale o un guasto in tutta l'infrastruttura del dominio di disponibilità, è probabile che ne vengano interessate le risorse presenti in più domini di errore contemporaneamente.
I nostri servizi interni utilizzano i domini di errore allo stesso modo in cui dovrebbero farlo i clienti. Ad esempio, i servizi di volumi a blocchi, storage degli oggetti e storage di file memorizzano le repliche dei dati in tre domini di errore separati. Tutti i componenti dei piani di controllo e dati vengono gestiti in hosting in tutti e tre i domini di errore (o in una region con più domini di disponibilità, in più domini di disponibilità).
Celle di servizio
Le celle del servizio consentono di limitare il raggio di esplosione dei problemi verificatisi anche quando un sistema non viene modificato in modo attivo. Tali problemi possono sorgere perché il carico di lavoro di un sistema cloud multi-tenant è soggetto a cambiamenti repentini, anche imponenti, e perché possono verificarsi guasti parziali in qualsiasi sistema largamente distribuito in qualunque momento. Questi scenari potrebbero attivare impercettibili bug nascosti o problemi emergenti legati alle prestazioni.
Le celle dei servizi, inoltre, limitano il raggio di esecuzione in alcuni scenari rari ma impegnativi in cui il sistema viene modificato in modo attivo. Un classico esempio è quando la distribuzione verso un singolo dominio di errore ha esito positivo, vale a dire quando non ci errori o modifiche alle prestazioni, ma non appena viene aggiornato il secondo dominio di errore o quello finale, le nuove interazioni all'interno del sistema (su scala cloud completa con carico di lavoro di produzione) causano un problema alle prestazioni.
Tieni presente che l'uso delle celle del servizio è un pattern a livello di architettura e non un concetto esplicitamente citato nell'API o nell'SDK Oracle Cloud. Qualsiasi sistema multi-tenant può utilizzare tale pattern, perché non richiede supporto speciale dalla piattaforma cloud.
Le celle del servizio effettuano le operazioni riportate di seguito.
- Ogni istanza del servizio (, ad esempio, in una determinata region o in un dominio di disponibilità specifico per i servizi locali del dominio di disponibilità) è composta da più distribuzioni separate dello stack software del servizio. Ogni distribuzione separata viene chiamata cella. Ogni cella viene gestita in hosting nella rispettiva infrastruttura nei limiti del possibile. Su piccola scala, le celle non condividono gli host o le VM.
- Un servizio potrebbe iniziare con una manciata di celle in ogni dominio di disponibilità o region. Man mano che il servizio si ridimensiona per soddisfare l'incremento di richieste, vengono aggiunte altre celle per mantenere il limite sulla dimensione del raggio di esplosione di qualsiasi problema. Su larga scala, un servizio diffuso potrebbe disporre di molte celle. In altri termini, le celle offrono un multiplexing da n a m dei carichi di lavoro dei clienti agli ambienti di hosting separati: isole distinte di isolamento delle risorse. Le celle non dispongono di una cardinalità certa, come esiste per i domini di errore. Come già citato in precedenza, una scelta ovvia per la cardinalità dei domini di errore è di tre per ogni dominio di disponibilità per consentire la gestione in hosting dei sistemi di replica basati su quorum con alta disponibilità all'interno di un singolo dominio di disponibilità.
- Ogni "unità naturale" del carico di lavoro di un cliente viene assegnata a una determinata cella. La definizione di "unità naturale" dipende dalla natura del servizio specifico. Ad esempio, per quanto riguarda il nostro servizio di flusso di lavoro condiviso interno (descritto più avanti), l'unità naturale potrebbe essere "tutti i flussi di lavoro in tale dominio di disponibilità o area di un determinato piano di controllo".
- Davanti a ogni gruppo di celle si trova un livello di instradamento minimalista o un'API per la ricerca automatica degli endpoint delle celle. Ad esempio, il sistema di streaming/messaggistica dispone di un'API per trovare l'endpoint del piano dati corrente di un determinato topic e l'area di memorizzazione dei metadati interna prevede un endpoint separato per ogni cella. Tuttavia, altri servizi basati su cella hanno un singolo endpoint del piano dati e un livello di instradamento condiviso. Il livello di instradamento è una potenziale causa di errori correlati di più celle, ma tale situazione può essere mitigata tenendo il livello di instradamento su un piano estremamente semplice, prevedibile e performante (nessuna operazione costosa) ed eseguendone il provisioning con una notevole capacità di spazio e avanzati meccanismi per la quota QoS e il throttling.
- I proprietari del servizio possono spostare un carico di lavoro da una cella all'altra, in base alle esigenze. Ecco alcuni esempi:
- Evitare il problema "noisy neighbor" multi-tenant spostando un carico di lavoro pesante in modo da non gravare sugli altri utenti di una cella.
- Eseguire il recupero da un sovraccarico o da un calo, forse causato da un attacco denial of service distribuito. Disponiamo di meccanismi di quota e limitazione per difendersi da tali attacchi, ma talvolta si verificano casi perimetrali in cui un determinato caso d'uso (API, pattern di accesso) è più difficile per il servizio di quanto il sistema di quota o throttling attualmente comprenda. Le celle offrono un meccanismo per la mitigazione a breve termine.
- Separare i carichi di lavoro critici in celle diverse per ridurre in modo significativo la probabilità di un guasto correlato. Ad esempio, per il servizio di flusso di lavoro condiviso interno per i piani di controllo, ogni piano di controllo di "base strategico" (ad esempio, piattaforma, computazione, networking e volumi a blocchi) viene assegnato a celle diverse e, pertanto, ha una correlazione di errore notevolmente inferiore rispetto a quanto accadrebbe se le celle non venissero usate o se i piani venissero assegnati alla stessa cella.
Nota: questa modalità di usare le celle consente ai clienti di non dover necessariamente prendere in considerazione le dipendenze interne dei servizi per creare applicazioni resilienti. Tenere conto del grafico delle dipendenze rimane ancora una buona prassi da seguire (ulteriori informazioni al riguardo verranno fornite più avanti nel presente documento), ma l'esigenza di farlo si riduce quando un meccanismo di decorrelazione è già attivo.
Da ciò ne consegue che ogni cella del servizio non è altro che un tipo di "data center logico": un raggruppamento logico di isolamento delle prestazioni e degli errori all'interno di un singolo dominio di disponibilità o una region.
In sintesi, le celle del servizio e i domini di errore si completano a vicenda nei modi seguenti:
- I problemi vengono protetti dai domini di errore durante la modifica attiva di un sistema.
- Le celle del servizio limitano il raggio di esplosione quando un sistema subisce problemi potenzialmente gravi, indipendentemente dal fatto che venga modificato in modo attivo o meno.
Quando eseguiamo le distribuzioni e l'applicazione delle patch, le proprietà dei domini di errore e delle celle del servizio vengono combinate in una strategia unificata.
Procedure di assistenza tecnica
Poiché garantire l'affidabilità dei sistemi cloud richiede un livello eccellente di funzionalità di test e operative, abbiamo a disposizione un numero elevato di procedure tecniche. Ecco alcune delle procedure più importanti basate sui concetti descritti nella sezione precedente:
- La distribuzione dei servizi avviene in maniera incrementale, con un'attenta convalida tra i passi e un rollback riflessivo qualora si verifichino casi inaspettati. In termini pratici, il processo è il seguente:
- Distribuiamo una cella del servizio alla volta in ogni dominio di disponibilità. Per ciascuna cella, la distribuzione viene eseguita in un dominio di errore alla volta, finché non saranno stati completati tutti i domini di errore. Successivamente, passiamo alla cella successiva presente in tale dominio di disponibilità.
- Dopo ogni passo della distribuzione (dopo ogni dominio di errore e cella), verifichiamo che la modifica sia funzionante come previsto, ossia assenza di prestazioni ridotte o presenza di errori commessi inavvertitamente, all'interno o all'esterno. Se qualcosa sembra essere sbagliato o imprevisto, eseguirne il rollback riflessivo della modifica. Diamo notevole risalto alla fase di preparazione e test, incluso quello automatizzato, delle procedure di rollback, incluse le modifiche che interessano lo stato persistente o gli schemi.
- In questo modo, distribuiamo la modifica su un dominio di disponibilità alla volta in ogni region. La distribuzione viene eseguita in tutte le region di un realm in modo da non modificare contemporaneamente un'eventuale coppia di region che un cliente potrebbe utilizzare per i siti primari e di recupero da errori irreversibili.
- Verifichiamo regolarmente che i meccanismi di gestione degli errori e altre mitigazioni funzionino come previsto e non peggiorino il problema su larga scala. In assenza di un test di questo tipo, non è insolito che i meccanismi di gestione degli errori (ad esempio, i tentativi, gli algoritmi di recupero da crash e gli algoritmi di riconfigurazione dei computer di stato) abbiano dei bug, siano troppo costosi o interagiscano in modi sorprendenti, provocando il thrashing distribuito o altri problemi gravi a livello di prestazioni.
- Verifichiamo la nostra capacità di eseguire rapidamente e in sicurezza il rollback per ripristinare l'ultima versione nota in buono stato del software e della configurazione, inclusi lo stato persistente e lo schema, come descritto in precedenza.