Questo documento del framework dell'architettura di Google Cloud fornisce i principi di progettazione per progettare i servizi in modo che possano sopportare gli errori e scalare in risposta alla domanda dei clienti. Un servizio affidabile continua a rispondere alle richieste dei clienti quando c'è una domanda elevata per il servizio o quando c'è un evento di manutenzione. I seguenti principi di progettazione e le migliori pratiche per l'affidabilità devono essere parte dell'architettura di sistema e del piano di deployment.
Crea ridondanza per una maggiore disponibilità
I sistemi con esigenze di elevata affidabilità non devono avere single point of failure e le loro risorse devono essere replicate su più domini in errore. Un dominio in errore un pool di risorse che possono generare errori in modo indipendente, come un'istanza VM, una zona o regione. Quando replichi tra domini in errore, ottieni un valore aggregato più elevato rispetto alle singole istanze. Per maggiori informazioni le informazioni, vedi Regioni e zone.
Come esempio specifico di ridondanza che potrebbe far parte del sistema per isolare gli errori nella registrazione DNS nelle singole zone, utilizzare nomi DNS di zona per consentire l'accesso reciproca tra le istanze sulla stessa rete.
Progettare un'architettura multizona con failover per l'alta disponibilità
Rendi la tua applicazione resiliente ai guasti zonali progettandola in modo da utilizzare pool di risorse distribuite su più zone, con replica dei dati, bilanciamento del carico e failover automatico tra le zone. Esegui repliche zonali di ogni livello dello stack dell'applicazione ed elimina tutte le dipendenze tra zone nell'architettura.
Replica i dati tra le regioni per il ripristino di emergenza
Replica o archivia i dati in una regione remota per abilitare il ripristino di emergenza nel di un'interruzione del servizio o di una perdita di dati a livello di regione. Quando si utilizza la replica, il recupero perché i sistemi di archiviazione nella regione remota dispongono già di dati i dati sono quasi aggiornati, a parte la possibile perdita di una piccola quantità a causa di un ritardo di replica. Quando utilizzi l'archiviazione periodica anziché continua replica, ripristino di emergenza prevede il ripristino di dati da backup o archivi in una nuova regione. Questa procedura di solito comporta tempi di inattività più lunghi rispetto all'attivazione continua replica del database aggiornata e potrebbe comportare una maggiore perdita di dati a causa dell'intervallo di tempo tra operazioni di backup consecutive. Indipendentemente dall'approccio utilizzato, l'intero stack dell'applicazione deve essere sottoposto a redeployment e avviato nella nuova regione e il servizio non sarà disponibile durante questa operazione.
Per una discussione dettagliata sui concetti e le tecniche di ripristino di emergenza, vedi Architettura del ripristino di emergenza per le interruzioni dell'infrastruttura cloud.
Progetta un'architettura multiregionale per la resilienza alle interruzioni regionali
Se il servizio deve funzionare in modo continuo anche nel raro caso in cui un'intera regione abbia un guasto, progettalo in modo da utilizzare pool di risorse di calcolo distribuite in regioni diverse. Esegui repliche regionali di ogni livello dello stack di applicazioni.
Usa la replica dei dati tra regioni e il failover automatico quando una regione verso il basso. Alcuni servizi Google Cloud, come Spanner, prevedono varianti per più regioni. Per garantire resilienza contro gli errori a livello di regione, utilizza questi servizi multiregionali ove possibile. Per ulteriori informazioni su regioni e servizi la disponibilità, consulta Località di Google Cloud.
Assicurati che non ci siano dipendenze tra regioni, in modo che l'ampiezza l'impatto di un errore a livello di regione è limitato a quella regione.
Elimina i single point of failure a livello di regione, ad esempio un database principale a regione singola che potrebbero causare un'interruzione globale quando non sono raggiungibili. Tieni presente che le architetture multiregione spesso hanno un costo maggiore, quindi valuta le esigenze aziendali rispetto al costo prima di adottare questo approccio.
Per ulteriori indicazioni sull'implementazione della ridondanza nei domini di errore, consulta il documento di ricerca Deployment Archetypes for Cloud Applications (PDF).
Elimina i colli di bottiglia della scalabilità
Identifica i componenti di sistema che non possono crescere oltre i limiti di risorse di una singola VM o di una singola zona. Alcune applicazioni si scalano verticalmente, aggiungendo più core della CPU, memoria o larghezza di banda di rete a una singola istanza VM per gestire l'aumento del carico. La scalabilità di queste applicazioni presenta limiti rigidi, e spesso devi configurarle manualmente per gestire la crescita.
Se possibile, ridisegna questi componenti per scalarli orizzontalmente, ad esempio con lo sharding o il partizionamento, su VM o zone. Per gestire l'aumento del traffico o dell'utilizzo, aggiungi altri shard. Utilizza tipi di VM standard che possono essere aggiunti automaticamente per gestire l'aumento del carico per ogni shard. Per ulteriori informazioni, vedi Pattern per app scalabili e resilienti.
Se non riesci a riprogettare l'applicazione, puoi sostituire i componenti gestiti da te con servizi cloud completamente gestiti progettati per la scalabilità orizzontale senza dell'azione dell'utente.
Esegui la riduzione controllata dei livelli di servizio in caso di sovraccarico
Progetta i tuoi servizi in modo che tollerino il sovraccarico. I servizi devono rilevare il sovraccarico e rispondere all'utente con risposte di qualità inferiore o ridurre parzialmente il traffico, senza smettere di funzionare completamente in caso di sovraccarico.
Ad esempio, un servizio può rispondere alle richieste degli utenti con pagine web statiche e disattivare temporaneamente il comportamento dinamico, che è più costoso da elaborare. Oppure può consentire operazioni di sola lettura e disattivare temporaneamente gli aggiornamenti dei dati.
Gli operatori devono essere avvisati per correggere la condizione di errore quando un servizio si deteriora.
Previeni e attenua i picchi di traffico
Non sincronizzare le richieste tra i client. Troppi client che inviano traffico allo stesso istante causano picchi di traffico che potrebbero causare errori a cascata.
Implementa strategie di mitigazione degli picchi lato server, come il throttling, la coda, la riduzione del carico o l'interruzione del circuito, la riduzione controllata e la priorità alle richieste critiche.
Le strategie di mitigazione sul cliente includono limitazione lato client e il backoff esponenziale con il jitter.
Elimina e convalida gli input
Per evitare input errati, casuali o dannosi che causano interruzioni del servizio violazioni della sicurezza, sanificare e convalidare parametri di input per le API i nostri strumenti operativi. Ad esempio: Apigee e Google Cloud Armor possono contribuire a proteggerti dagli attacchi di iniezione.
Utilizza regolarmente i test di fuzz in cui un test harness chiama intenzionalmente le API con input random, vuoti o troppo grandi. Esegui questi test in un ambiente di test isolato.
Gli strumenti operativi devono convalidare automaticamente le modifiche alla configurazione prima dell'implementazione e rifiutarle se la convalida non va a buon fine.
Fail Safe in modo da preservare il funzionamento
In caso di errore dovuto a un problema, i componenti del sistema non dovrebbero riuscire in una che consenta all'intero sistema di continuare a funzionare. Questi problemi possono essere un bug del software, un input o una configurazione errati, un'interruzione imprevista dell'istanza o un errore umano. I servizi che utilizzi ti aiutano a determinare se devi essere troppo permissivo o troppo semplicistico, anziché troppo restrittivo.
Considera i seguenti scenari di esempio e come rispondere all'errore:
- In genere è meglio che un componente del firewall con una configurazione errata o vuota sia in modalità di fail open e consenta il passaggio del traffico di rete non autorizzato per un breve periodo di tempo mentre l'operatore corregge l'errore. Questo comportamento mantiene il servizio disponibile, invece di chiuderlo in modo errato e bloccare il 100% del traffico. Il servizio deve basarsi su controlli di autenticazione e autorizzazione più approfonditi e lo stack di applicazioni per proteggere le aree sensibili mentre passa tutto il traffico.
- Tuttavia, è meglio un componente del server delle autorizzazioni che controlli l'accesso. la chiusura dei dati utente potrebbe non riuscire e bloccare tutti gli accessi. Questo comportamento causa un'interruzione del servizio quando la configurazione è danneggiata, ma evita il rischio di una fuga di dati utente riservati se non si verificano errori.
In entrambi i casi, l'errore deve generare un avviso ad alta priorità in modo che un operatore possa correggere la condizione di errore. I componenti di servizio devono essere impostati su "fail open", a meno che non rappresentino rischi estremi per l'attività.
Progettare le chiamate API e i comandi operativi in modo che sia possibile provarli nuovamente
Le API e gli strumenti operativi devono rendere le chiamate sicure per i nuovi tentativi, il più possibile. Un approccio naturale a molte condizioni di errore è riprovare l'azione precedente, ma potresti non sapere se il primo tentativo è riuscito.
L'architettura del sistema dovrebbe rendere le azioni idempotenti, se esegui le un'azione identica su un oggetto due o più volte di seguito, dovrebbe produrre gli stessi risultati di una singola chiamata. Le azioni non idempotenti richiedono un codice più complesso per evitare la corruzione dello stato del sistema.
Identifica e gestisci le dipendenze dei servizi
I progettisti e i proprietari di servizi devono mantenere un elenco completo delle dipendenze da altri componenti di sistema. La progettazione del servizio deve includere anche errori delle dipendenze o riduzione controllata se il ripristino completo non è fattibile. Prendi delle dipendenze dai servizi cloud usati dal tuo sistema e dalle dipendenze esterne, come le API di servizi di terze parti, riconoscendo che ogni dipendenza dal sistema con un tasso di errore diverso da zero.
Quando imposti target di affidabilità, riconosce che lo SLO per un servizio è matematicamente vincolato dagli SLO di tutte le sue dipendenze critiche. Non puoi essere più affidabile dello SLO più basso di una delle dipendenze. Per ulteriori informazioni, consulta il calcolo della disponibilità del servizio.
Dipendenze di avvio
I servizi si comportano in modo diverso all'avvio rispetto al loro comportamento in stato stabile. Le dipendenze di avvio possono differire significativamente dallo stato stabile delle dipendenze di runtime.
Ad esempio, all'avvio, un servizio potrebbe dover caricare informazioni sull'utente o sull'account da un servizio di metadati utente che invoca raramente. Quando molte repliche del servizio si riavviano dopo un arresto anomalo o una manutenzione di routine, possono aumentare notevolmente il carico sulle dipendenze di avvio, soprattutto quando le cache sono vuote e devono essere ricompilate.
Testa l'avvio del servizio sotto carico ed esegui il provisioning delle dipendenze di avvio. Valuta la possibilità di ridurre la qualità di un progetto salvando una copia dei dati recupera dalle dipendenze di avvio critiche. Questo comportamento consente al servizio riavviare con dati potenzialmente inattivi anziché essere in grado di avviarsi quando una dipendenza critica ha un'interruzione. Il servizio può caricare dati aggiornati in un secondo momento, quando possibile per tornare al normale funzionamento.
Le dipendenze di avvio sono importanti anche quando esegui il bootstrap di un servizio in una nuova completamente gestito di Google Cloud. Progetta lo stack delle applicazioni con un'architettura a livelli, senza dipendenze cicliche tra i livelli. Le dipendenze cicliche possono sembrare tollerabili perché non bloccano le modifiche incrementali a una singola applicazione. Tuttavia, le dipendenze cicliche possono rendere difficile o impossibile il riavvio dopo che un disastro ha causato l'interruzione dell'intero stack di servizi.
Riduci al minimo le dipendenze critiche
Riduci al minimo il numero di dipendenze critiche per il servizio, ovvero altri componenti il cui errore causerà inevitabilmente interruzioni del servizio. Per rendere il servizio più resiliente a errori o rallentamenti in altri componenti di cui dipende, valuta i seguenti principi e tecniche di progettazione di esempio per convertire le dipendenze critiche in dipendenze non critiche:
- Aumenta il livello di ridondanza nelle dipendenze critiche. L'aggiunta di altre repliche riduce la probabilità che un intero componente non sia disponibile.
- Utilizza richieste asincrone ad altri servizi anziché bloccare una risposta o utilizza la messaggistica di pubblicazione/sottoscrizione per disaccoppiare le richieste dalle risposte.
- Memorizza nella cache le risposte di altri servizi per recuperare dopo un'indisponibilità a breve termine delle dipendenze.
Per rendere meno dannosi gli errori o la lentezza del servizio rispetto ad altri componenti che dipendono da questo aspetto, considera i seguenti esempi di tecniche e principi di progettazione:
- Utilizza code di richieste prioritarie e dai una maggiore priorità alle richieste in cui un utente sta aspettando una risposta.
- Fornisci le risposte dalla cache per ridurre latenza e carico.
- Fail Safe in modo da preservare il funzionamento.
- Esegui la riduzione controllata in caso di sovraccarico del traffico.
Assicurati che sia possibile eseguire il rollback di ogni modifica
Se non esiste un modo ben definito per annullare determinati tipi di modifiche a un servizio, modifica il design del servizio in modo da supportare il rollback. Testa periodicamente le procedure di rollback. Le API per ogni componente o microservizio devono essere il controllo delle versioni, con compatibilità con le versioni precedenti i client continuano a funzionare correttamente man mano che l'API si evolve. Questo principio di progettazione è essenziale per consentire l'implementazione graduale delle modifiche alle API, con un rollback rapido se necessario.
L'implementazione del rollback può essere onerosa per le applicazioni mobile. Configurazione remota Firebase è un servizio Google Cloud per semplificare il rollback delle funzionalità.
Non puoi eseguire subito il rollback delle modifiche allo schema del database, quindi eseguile in più fasi. Progetta ogni fase per consentire richieste di lettura e aggiornamento sicure dello schema all'ultima versione dell'applicazione e alla versione precedente. Questo approccio di progettazione ti consente di eseguire il rollback in sicurezza in caso di problemi con la versione più recente.
Consigli
Per applicare le indicazioni del framework dell'architettura al tuo ambiente, segui questi consigli:
- Implementare il backoff esponenziale con la randomizzazione nella logica dei nuovi tentativi di errore di le applicazioni client.
- Implementare un'architettura multiregionale con failover automatico per la disponibilità del servizio.
- Utilizza il bilanciamento del carico per distribuire le richieste degli utenti tra shard e regioni.
- Progetta l'applicazione in modo che funzioni in modo degradato in caso di sovraccarico. Fornire risposte parziali o funzionalità limitate anziché non funzionare del tutto.
- Stabilisci un processo basato sui dati per la pianificazione della capacità e utilizza i test di carico e previsioni di traffico per determinare quando eseguire il provisioning delle risorse.
- Stabilisci procedure di ripristino di emergenza e testale periodicamente.
Passaggi successivi
- Crea strumenti e processi operativi affidabili (prossimo documento di questa serie)
- Esplora i consigli negli altri pilastri del Framework dell'architettura.