Pattern per app scalabili e resilienti

Last reviewed 2024-03-19 UTC

Questo documento introduce alcuni pattern e pratiche per la creazione di app resilienti e scalabili, due obiettivi essenziali di molti esercizi di architettura moderna. Un'app ben progettata fa lo scale up e lo scale down man mano che la domanda aumenta e diminuisce ed è abbastanza resiliente da resistere a interruzioni del servizio. La creazione e il funzionamento di app che soddisfano questi requisiti richiedono un'attenta pianificazione e progettazione.

Scalabilità: adeguare la capacità per soddisfare la domanda

La scalabilità è la misura della capacità di un sistema di gestire quantità variabili di lavoro aggiungendo o rimuovendo risorse dal sistema. Ad esempio, un'app web scalabile è un'app che funziona bene con uno o più utenti e che gestisce in modo controllato picchi e cali di traffico.

La flessibilità di regolare le risorse utilizzate da un'app è un fattore chiave per il passaggio al cloud. Con una progettazione adeguata, puoi ridurre i costi rimuovendo le risorse sottoutilizzate senza compromettere le prestazioni o l'esperienza utente. Allo stesso modo, puoi mantenere una buona esperienza utente durante periodi di traffico elevato aggiungendo altre risorse. In questo modo, l'app può consumare solo le risorse necessarie per soddisfare la domanda.

Google Cloud fornisce prodotti e funzionalità per aiutarti a creare app scalabili ed efficienti:

  • Le macchine virtuali di Compute Engine e i cluster di Google Kubernetes Engine (GKE) si integrano con i gestori della scalabilità automatica che consentono di aumentare o ridurre il consumo di risorse in base a metriche da te definite.
  • La piattaforma serverless di Google Cloud fornisce computing, database e altri servizi gestiti in grado di scalare rapidamente da zero a volumi di richieste elevati, pagando solo per ciò che utilizzi.
  • I prodotti di database come BigQuery, Spanner e Bigtable possono offrire prestazioni coerenti su grandi dimensioni di dati.
  • Cloud Monitoring fornisce metriche per tutte le app e l'infrastruttura, aiutandoti a prendere decisioni di scalabilità basate sui dati.

Resilienza: progettare per resistere agli errori

Un'app resiliente è un'app che continua a funzionare nonostante gli errori dei componenti di sistema. La resilienza richiede la pianificazione a tutti i livelli dell'architettura. Influenza il modo in cui disponi l'infrastruttura e la rete e come progetti l'app e l'archiviazione dei dati. La resilienza si estende anche alle persone e alla cultura.

Creare e gestire app resilienti è difficile. Questo è particolarmente vero per le app distribuite, che potrebbero contenere più livelli di infrastruttura, reti e servizi. Si possono verificare errori e interruzioni e, migliorare la resilienza della tua app è un percorso continuo. Con un'attenta pianificazione, puoi migliorare la capacità della tua app di resistere agli errori. Con processi adeguati e una cultura organizzativa, puoi anche imparare dagli errori per aumentare ulteriormente la resilienza della tua app.

Google Cloud fornisce strumenti e servizi per aiutarti a creare app resilienti e ad alta disponibilità:

  • I servizi Google Cloud sono disponibili in regioni e zone in tutto il mondo e ti consentono di eseguire il deployment della tua app in modo da soddisfare al meglio i tuoi obiettivi di disponibilità.
  • I gruppi di istanze Compute Engine e i cluster GKE possono essere distribuiti e gestiti tra le zone disponibili in una regione.
  • I dischi permanenti a livello di regione di Compute Engine vengono replicati in modo sincrono tra le zone di una regione.
  • Google Cloud offre una gamma di opzioni di bilanciamento del carico per gestire il traffico delle app, compreso il bilanciamento del carico globale che può indirizzare il traffico verso una regione integro più vicino ai tuoi utenti.
  • La piattaforma serverless di Google Cloud include prodotti gestiti per il calcolo e il database che offrono ridondanza e bilanciamento del carico integrati.
  • Google Cloud supporta CI/CD tramite strumenti nativi e integrazioni con le tecnologie open source più diffuse, per contribuire ad automatizzare la creazione e il deployment delle tue app.
  • Cloud Monitoring fornisce metriche relative ad app e infrastruttura per aiutarti a prendere decisioni basate sui dati sulle prestazioni e sull'integrità delle tue app.

Driver e vincoli

Esistono vari requisiti e motivazioni per migliorare la scalabilità e la resilienza della tua app. Potrebbero anche esserci dei vincoli che limitano la tua capacità di soddisfare gli obiettivi di scalabilità e resilienza. L'importanza relativa di questi requisiti e vincoli varia a seconda del tipo di app, del profilo degli utenti, della scalabilità e della maturità della tua organizzazione.

Driver

Per stabilire le priorità dei tuoi requisiti, prendi in considerazione i fattori che fanno parte delle diverse parti dell'organizzazione.

Potenziali di sviluppo

I fattori più comuni dal punto di vista commerciale sono:

  • Ottimizza i costi e il consumo delle risorse.
  • Riduci al minimo il tempo di inattività delle app.
  • Assicurati che la domanda degli utenti possa essere soddisfatta durante i periodi di utilizzo elevato.
  • Migliorare la qualità e la disponibilità del servizio.
  • Assicurati che l'esperienza utente e l'affidabilità siano mantenute durante tutte le interruzioni.
  • Aumenta la flessibilità e l'agilità per gestire le mutevoli richieste del mercato.

Driver per lo sviluppo

Di seguito sono riportati alcuni fattori comuni dal lato sviluppo:

  • Riduci al minimo il tempo dedicato all'analisi degli errori.
  • Allargare il tempo dedicato allo sviluppo di nuove funzionalità.
  • Riduci al minimo il lavoro ripetitivo attraverso l'automazione.
  • Crea app utilizzando le pratiche e i pattern più recenti del settore.

Driver operativi

I requisiti da considerare dal punto di vista operativo includono quanto segue:

  • Ridurre la frequenza degli errori che richiedono un intervento umano.
  • Aumenta la capacità di ripristino automatico in caso di errori.
  • Riduci al minimo il lavoro ripetitivo attraverso l'automazione.
  • Riduci al minimo l'impatto del malfunzionamento di un particolare componente.

Vincoli

I vincoli potrebbero limitare la tua capacità di aumentare la scalabilità e la resilienza della tua app. Assicurati che le tue decisioni di progettazione non introducano o contribuiscano a questi vincoli:

  • Dipendenze da hardware o software difficili da scalare.
  • Dipendenze da hardware o software difficili da utilizzare in una configurazione ad alta disponibilità.
  • Dipendenze tra app.
  • Limitazioni relative alle licenze.
  • Mancanza di competenze o esperienza nei team di sviluppo e operativi.
  • Resistenza organizzativa all'automazione.

Modelli e pratiche

La parte restante di questo documento definisce pattern e pratiche per aiutarti a creare app resilienti e scalabili. Questi pattern riguardano tutte le parti del ciclo di vita delle app, inclusi la progettazione dell'infrastruttura, l'architettura dell'app, le scelte di archiviazione, i processi di deployment e la cultura dell'organizzazione.

Nei modelli emergono tre temi:

  • Automazione. La creazione di app scalabili e resilienti richiede l'automazione. L'automazione del provisioning, dei test e dei deployment delle app dell'infrastruttura aumenta la coerenza e la velocità e riduce al minimo gli errori umani.
  • Accoppiamento allentato. Trattare il tuo sistema come una raccolta di componenti indipendenti a basso accoppiamento consente flessibilità e resilienza. L'indipendenza riguarda la distribuzione fisica delle risorse, l'architettura dell'app e la progettazione dello spazio di archiviazione.
  • Design basato sui dati. La raccolta di metriche per comprendere il comportamento della tua app è fondamentale. Le decisioni su quando scalare la tua app o se un determinato servizio non è integro devono essere basate sui dati. Le metriche e i log dovrebbero essere le funzionalità principali.

Automatizza il provisioning dell'infrastruttura

Crea un'infrastruttura immutabile tramite l'automazione per migliorare la coerenza dei tuoi ambienti e aumentare il successo dei tuoi deployment.

Tratta la tua infrastruttura come codice

Infrastructure as Code (IaC) è una tecnica che ti incoraggia a trattare il provisioning e la configurazione dell'infrastruttura nello stesso modo in cui gestisci il codice dell'applicazione. La logica di provisioning e configurazione è archiviata in controllo del codice sorgente, in modo che sia rilevabile e possa essere sottoposta a controllo delle versioni e controllo. Poiché si trova in un repository di codice, puoi sfruttare le pipeline di integrazione continua e deployment continuo (CI/CD), in modo che qualsiasi modifica alla configurazione possa essere testata e sottoposta a deployment in modo automatico.

Rimuovendo i passaggi manuali dal provisioning dell'infrastruttura, IaC riduce al minimo gli errori umani e migliora la coerenza e la riproducibilità delle app e degli ambienti. In questo modo, l'adozione di IaC aumenta la resilienza delle tue app.

Cloud Deployment Manager consente di automatizzare la creazione e la gestione delle risorse Google Cloud con modelli flessibili. In alternativa, Config Connector consente di gestire le risorse utilizzando tecniche e flussi di lavoro di Kubernetes. Google Cloud integra inoltre il supporto integrato per i più diffusi strumenti IaC di terze parti, tra cui Terraform, Chef e Puppet.

Crea un'infrastruttura immutabile

L'infrastruttura immutabile è una filosofia che si basa sui vantaggi dell'infrastruttura come codice. Un'infrastruttura immutabile impone che le risorse non vengano mai modificate dopo il deployment. Se è necessario aggiornare una macchina virtuale, un cluster Kubernetes o una regola firewall, puoi aggiornare la configurazione per la risorsa nel repository di origine. Dopo aver testato e convalidato le modifiche, esegui nuovamente il deployment della risorsa utilizzando la nuova configurazione. In altre parole, anziché modificare le risorse, devi ricrearle.

La creazione di un'infrastruttura immutabile porta a deployment e rollback più prevedibili. Inoltre, mitiga i problemi comuni nelle infrastrutture modificabili, come la deriva della configurazione e i server Snowflake. In questo modo, l'adozione di un'infrastruttura immutabile migliora ulteriormente la coerenza e l'affidabilità dei tuoi ambienti.

Progetta per l'alta disponibilità

La disponibilità è una misura della frazione di tempo in cui un servizio è utilizzabile. La disponibilità è spesso utilizzata come indicatore chiave dell'integrità complessiva del servizio. Le architetture ad alta disponibilità mirano a massimizzare la disponibilità dei servizi, tipicamente tramite deployment ridondante. In termini più semplici, raggiungere un'alta disponibilità in genere comporta la distribuzione di risorse di calcolo, il bilanciamento del carico e la replica dei dati.

Distribuzione fisica delle risorse

I servizi Google Cloud sono disponibili in varie località in tutto il mondo. Queste località sono suddivise in regioni e zone. Il modo in cui esegui il deployment dell'app in queste regioni e zone influisce su disponibilità, latenza e altre proprietà della tua app. Per saperne di più, consulta le best practice per la scelta della regione per Compute Engine.

La ridondanza è la duplicazione di componenti di un sistema al fine di aumentarne la disponibilità complessiva. In Google Cloud, la ridondanza si ottiene solitamente eseguendo il deployment dell'app o del servizio in più zone o anche in più regioni. Se un servizio è presente in più zone o regioni, può resistere meglio alle interruzioni del servizio in una determinata zona o regione. Sebbene Google Cloud faccia il possibile per evitare queste interruzioni, alcuni eventi sono imprevedibili ed è meglio essere preparati.

Con i gruppi di istanze gestite di Compute Engine, puoi distribuire le istanze di macchine virtuali in più zone all'interno di una regione e gestirle come un'unità logica. Google Cloud offre inoltre dischi permanenti a livello di regione per replicare automaticamente i dati in due zone di una regione.

Allo stesso modo, puoi migliorare la disponibilità e la resilienza delle app di cui è stato eseguito il deployment su GKE creando cluster regionali. Un cluster a livello di regione distribuisce componenti, nodi e pod del piano di controllo GKE in più zone all'interno di una regione. Poiché i componenti del piano di controllo sono distribuiti, puoi continuare ad accedere al piano di controllo del cluster anche durante un'interruzione che interessa una o più zone (ma non tutte).

Preferisci servizi gestiti

Anziché installare, supportare e gestire in modo indipendente tutte le parti dello stack di applicazioni, puoi utilizzare i servizi gestiti per utilizzare parti dello stack di applicazioni come servizi. Ad esempio, anziché installare e gestire un database MySQL su macchine virtuali (VM), puoi utilizzare un database MySQL fornito da Cloud SQL. Riceverai un accordo sul livello del servizio (SLA) sulla disponibilità e potrai affidarti a Google Cloud per gestire la replica dei dati, i backup e l'infrastruttura sottostante. Utilizzando i servizi gestiti, potrai dedicare meno tempo alla gestione dell'infrastruttura e più tempo al miglioramento dell'affidabilità della tua app.

Molti dei servizi gestiti di computing, database e archiviazione di Google Cloud offrono ridondanza integrata, che può aiutarti a raggiungere i tuoi obiettivi di disponibilità. Molti di questi servizi offrono un modello regionale, ovvero l'infrastruttura che esegue la tua app si trova in una regione specifica ed è gestita da Google in modo che sia disponibile in modo ridondante in tutte le zone all'interno di quella regione. Se una zona non è disponibile, l'app o i dati vengono pubblicati automaticamente da un'altra zona dell'area geografica.

Alcuni servizi di database e archiviazione offrono inoltre una disponibilità multiregionale, il che significa che l'infrastruttura che esegue la tua app si trova in diverse regioni. I servizi in più regioni possono tollerare la perdita di un'intera regione, ma in genere a costo di una latenza maggiore.

Bilanciamento del carico a ogni livello

Il bilanciamento del carico consente di distribuire il traffico tra gruppi di risorse. Quando distribuisci il traffico, puoi assicurarti che le singole risorse non diventino sovraccaricate mentre altre sono inattive. La maggior parte dei bilanciatori del carico offre anche funzionalità di controllo di integrità per garantire che il traffico non sia instradato a risorse in stato non integro o non disponibile.

Google Cloud offre diverse opzioni di bilanciamento del carico. Se la tua app viene eseguita su Compute Engine o GKE, puoi scegliere il tipo di bilanciatore del carico più appropriato in base al tipo, all'origine e ad altri aspetti del traffico. Per maggiori informazioni, consulta la panoramica del bilanciamento del carico e la panoramica del networking di GKE.

In alternativa, alcuni servizi gestiti da Google Cloud, come App Engine e Cloud Run, bilanciano automaticamente il carico del traffico.

È pratica comune bilanciare il carico delle richieste ricevute da origini esterne, ad esempio da client web o mobile. Tuttavia, l'utilizzo di bilanciatori del carico tra diversi servizi o livelli all'interno della tua app può anche aumentare resilienza e flessibilità. A questo scopo, Google Cloud fornisce il bilanciamento del carico di livello 4 e 7 interno.

Il seguente diagramma mostra un bilanciatore del carico esterno che distribuisce il traffico globale in due regioni, us-central1 e asia-east1. Mostra inoltre il bilanciamento del carico interno che distribuisce il traffico dal livello web a quello interno all'interno di ogni regione.

La distribuzione del traffico globale tra regioni.

Monitora la tua infrastruttura e le tue app

Prima di poter decidere come migliorare la resilienza e la scalabilità della tua app, devi comprenderne il comportamento. Avere accesso a un insieme completo di metriche e serie temporali pertinenti sulle prestazioni e sull'integrità della tua app può aiutarti a scoprire potenziali problemi prima che causino un'interruzione. Possono anche aiutarti a diagnosticare e risolvere un'interruzione nel caso si verifichi. Il capitolo sul monitoraggio dei sistemi distribuiti nel libro su SRE di Google fornisce una buona panoramica di alcuni approcci al monitoraggio.

Oltre a fornire insight sullo stato della tua app, le metriche possono essere utilizzate anche per controllare il comportamento della scalabilità automatica per i tuoi servizi.

Cloud Monitoring è lo strumento di monitoraggio integrato di Google Cloud. Cloud Monitoring importa eventi, metriche e metadati e fornisce insight tramite dashboard e avvisi. La maggior parte dei servizi Google Cloud invia automaticamente metrics a Cloud Monitoring e Google Cloud supporta anche molte origini di terze parti. Cloud Monitoring può essere utilizzato anche come backend per i più diffusi strumenti di monitoraggio open source, offrendo un "pannello centralizzato" da cui osservare l'app.

Monitoraggio a tutti i livelli

La raccolta di metriche a vari livelli o livelli all'interno dell'architettura fornisce un quadro olistico dell'integrità e del comportamento della tua app.

Monitoraggio dell'infrastruttura

Il monitoraggio a livello di infrastruttura fornisce l'integrità e le prestazioni di base per la tua app. Questo approccio al monitoraggio acquisisce informazioni come il carico della CPU, l'utilizzo della memoria e il numero di byte scritti su disco. Queste metriche possono indicare che una macchina è sovraccarica o non funziona come previsto.

Oltre alle metriche raccolte automaticamente, Cloud Monitoring fornisce un agente che può essere installato per raccogliere informazioni più dettagliate dalle VM di Compute Engine, incluse le app di terze parti in esecuzione su queste macchine.

Monitoraggio delle app

Ti consigliamo di acquisire le metriche a livello di app. Ad esempio, potresti voler misurare quanto tempo è necessario per eseguire una determinata query o quanto tempo occorre per eseguire una sequenza correlata di chiamate ai servizi. Sei tu a definire queste metriche a livello di app. Acquisiscono informazioni che le metriche integrate di Cloud Monitoring non possono ottenere. Le metriche a livello di app sono in grado di acquisire condizioni aggregate che rispecchiano meglio i flussi di lavoro chiave e possono rivelare problemi che le metriche di infrastruttura di basso livello non rilevano.

Ti consigliamo inoltre di utilizzare OpenTelemetry per acquisire le metriche a livello di app. OpenTelemetry offre un unico standard aperto per i dati di telemetria. Usa OpenTelemetry per raccogliere ed esportare i dati dalle applicazioni e dall'infrastruttura cloud-first. Puoi quindi monitorare e analizzare i dati di telemetria esportati.

Monitoraggio dei servizi

Per le app distribuite e basate su microservizi, è importante monitorare le interazioni tra i diversi servizi e componenti delle app. Queste metriche possono aiutarti a diagnosticare problemi come l'aumento del numero di errori o la latenza tra i servizi.

Istio è uno strumento open source che fornisce insight e controllo operativo sulla rete di microservizi. Istio genera telemetria dettagliata per tutte le comunicazioni di servizio e può essere configurato per inviare le metriche a Cloud Monitoring.

Monitoraggio end-to-end

Il monitoraggio end-to-end, chiamato anche monitoraggio black-box, verifica il comportamento visibile all'esterno così come lo vede l'utente. Questo tipo di monitoraggio verifica se un utente è in grado di completare azioni critiche all'interno delle soglie definite. Questo monitoraggio granulare può rivelare errori o latenza che un monitoraggio più granulare potrebbe non rilevare e rivela la disponibilità percepita dall'utente.

Esponi l'integrità delle tue app

Un sistema ad alta disponibilità deve avere un modo per determinare quali parti del sistema sono integri e che funzionano correttamente. Se alcune risorse non risultano integri, il sistema può inviare richieste altrove. In genere i controlli di integrità prevedono il pulling dei dati da un endpoint per determinare lo stato o l'integrità di un servizio.

Il controllo di integrità è una responsabilità fondamentale dei bilanciatori del carico. Quando crei un bilanciatore del carico associato a un gruppo di istanze di macchine virtuali, definisci anche un controllo di integrità. Il controllo di integrità definisce il modo in cui il bilanciatore del carico comunica con le macchine virtuali per valutare se determinate istanze devono continuare a ricevere traffico. I controlli di integrità del bilanciatore del carico possono essere utilizzati anche per riparare automaticamente gruppi di istanze, in modo da ricreare le macchine non integre. Se esegui su GKE ed esegui il bilanciamento del carico del traffico esterno tramite una risorsa in entrata, GKE crea automaticamente i controlli di integrità appropriati per il bilanciatore del carico.

Kubernetes dispone del supporto integrato per i probe di attività e di idoneità. Questi probe aiutano l'orchestratore di Kubernetes a decidere come gestire pod e richieste all'interno del cluster. Se il deployment della tua app è stato eseguito su Kubernetes, è consigliabile esporre l'integrità dell'app a questi probe mediante endpoint appropriati.

Definisci le metriche chiave

Il monitoraggio e il controllo di integrità forniscono metriche sul comportamento e sullo stato della tua app. Il passaggio successivo consiste nell'analizzare queste metriche per determinare quali sono le più descrittive o più efficaci. Le metriche chiave variano a seconda della piattaforma su cui viene eseguito il deployment dell'app e del lavoro svolto.

Non troverai solo una metrica che indica se scalare la tua app o che un determinato servizio non è integro. Spesso si tratta di una combinazione di fattori che insieme indicano un determinato insieme di condizioni. Con Cloud Monitoring, puoi creare metriche personalizzate per acquisire queste condizioni. Il libro Google SRE promuove quattro segnali aurei per il monitoraggio di un sistema rivolto agli utenti: latenza, traffico, errori e saturazione.

Considera anche la tua tolleranza nei confronti dei valori anomali. Utilizzare un valore medio o mediano per misurare lo stato di salute o il rendimento potrebbe non essere la scelta migliore, perché queste misure possono nascondere grossi squilibri. Pertanto, è importante considerare la distribuzione delle metriche; il 99° percentile potrebbe essere una misura più informativa della media.

Definire gli obiettivi del livello di servizio (SLO)

Puoi utilizzare le metriche raccolte dal sistema di monitoraggio per definire gli obiettivi del livello di servizio (SLO). Gli SLO specificano un livello target di prestazioni o affidabilità per il servizio. Gli SLO sono un pilastro fondamentale delle pratiche SRE e sono descritti in dettaglio nel capitolo sugli obiettivi del livello di servizio nel libro SRE e nel capitolo sull'implementazione degli SLO nel foglio di lavoro SRE.

Puoi utilizzare il monitoraggio dei servizi per definire gli SLO in base alle metriche in Cloud Monitoring. Puoi creare criteri di avviso sugli SLO per indicare se corri il rischio di violare uno SLO.

Archivia le metriche

Le metriche del sistema di monitoraggio sono utili nel breve termine per agevolare i controlli di integrità in tempo reale o per analizzare problemi recenti. Cloud Monitoring conserva le tue metriche per diverse settimane per soddisfare al meglio questi casi d'uso.

Tuttavia, l'archiviazione delle metriche di monitoraggio per le analisi a lungo termine è vantaggiosa. L'accesso a un record storico può aiutarti ad adottare un approccio basato sui dati per perfezionare l'architettura dell'app. Puoi utilizzare i dati raccolti durante e dopo un'interruzione per identificare colli di bottiglia e interdipendenze nelle tue app. I dati possono essere utilizzati anche per creare e convalidare test significativi.

I dati storici possono anche aiutarti a verificare che la tua app supporti gli obiettivi commerciali durante periodi chiave. Ad esempio, i dati possono aiutarti ad analizzare la scalabilità della tua app durante gli eventi promozionali a traffico elevato nel corso degli ultimi trimestri o addirittura degli ultimi anni.

Per i dettagli su come esportare e archiviare le metriche, consulta la soluzione di esportazione delle metriche di Cloud Monitoring.

Determina il profilo di scalabilità

Vuoi che la tua app soddisfi gli obiettivi relativi a esperienza utente e prestazioni senza eseguire il provisioning eccessivo delle risorse.

Il seguente diagramma mostra una rappresentazione semplificata del profilo di scalabilità di un'app. L'app mantiene un livello di base di risorse e utilizza la scalabilità automatica per rispondere ai cambiamenti della domanda.

Profilo di scalabilità delle app.

Trova un equilibrio tra costo ed esperienza utente

Decidere se scalare la propria app implica fondamentalmente l'equilibrio tra costi e esperienza utente. Stabilisci il livello minimo accettabile di prestazioni e, potenzialmente, anche dove impostare un tetto. Queste soglie variano da un'app all'altra e potenzialmente tra diversi componenti o servizi all'interno di una singola app.

Ad esempio, un'app web o mobile rivolta ai consumatori potrebbe avere obiettivi di latenza rigorosi. Le ricerche dimostrano che anche piccoli ritardi possono influire negativamente su come gli utenti percepiscono la tua app, causando un minor numero di conversioni e di registrazioni. Di conseguenza, è importante assicurati che la tua app abbia una capacità di pubblicazione sufficiente per rispondere rapidamente alle richieste degli utenti. In questo caso, i costi più elevati per l'esecuzione di più server web potrebbero essere giustificati.

Il rapporto costo/prestazioni potrebbe essere diverso per un'app interna non business-critical, in cui gli utenti sono probabilmente più tollerati a piccoli ritardi. Di conseguenza, il profilo di scalabilità può essere meno aggressivo. In questo caso, mantenere bassi i costi potrebbe essere più importante dell'ottimizzazione dell'esperienza utente.

Imposta risorse di riferimento

Un altro componente chiave del profilo di scalabilità è la scelta di un insieme minimo appropriato di risorse.

In genere, lo scale up delle macchine virtuali di Compute Engine o dei cluster GKE richiede tempo, perché è necessario creare e inizializzare nuovi nodi. Pertanto, potrebbe essere necessario mantenere un insieme minimo di risorse, anche in assenza di traffico. Anche in questo caso, l'estensione delle risorse di riferimento è influenzata dal tipo di app e dal profilo di traffico.

Al contrario, le tecnologie serverless come App Engine, Cloud Functions e Cloud Run sono progettate per scalare fino a zero, nonché per avviare e scalare rapidamente, anche in caso di avvio a freddo. A seconda del tipo di app e del profilo di traffico, queste tecnologie possono garantire efficienze per parti della tua app.

Configura scalabilità automatica

La scalabilità automatica consente di scalare automaticamente le risorse di calcolo utilizzate dalla tua app. Generalmente, la scalabilità automatica si verifica quando vengono superate determinate metriche o vengono soddisfatte determinate condizioni. Ad esempio, se le latenze delle richieste al tuo livello web iniziano a superare un determinato valore, potresti voler aggiungere automaticamente altre macchine per aumentare la capacità di pubblicazione.

Molti prodotti di computing di Google Cloud hanno funzionalità di scalabilità automatica. I servizi gestiti serverless come Cloud Run, Cloud Functions e App Engine sono progettati per la scalabilità rapida. Questi servizi di solito offrono opzioni di configurazione per limitare o influenzare il comportamento della scalabilità automatica, ma in generale, gran parte del comportamento del gestore della scalabilità automatica è nascosto all'operatore.

Compute Engine e GKE forniscono più opzioni per controllare il comportamento della scalabilità. Con Compute Engine, puoi scalare in base a vari input, tra cui le metriche personalizzate di Cloud Monitoring e la capacità di gestione del bilanciatore del carico. Puoi impostare i limiti minimo e massimo per il comportamento di scalabilità e definire un criterio di scalabilità automatica con più indicatori per gestire scenari diversi. Come con GKE, puoi configurare il gestore della scalabilità automatica dei cluster per aggiungere o rimuovere nodi in base alle metrics dei carichi di lavoro o dei pod oppure su metriche esterne al cluster.

Ti consigliamo di configurare il comportamento della scalabilità automatica in base alle metriche chiave delle app, al tuo profilo di costo e al livello minimo richiesto definito di risorse.

Riduci al minimo il tempo di avvio

Affinché la scalabilità sia efficace, deve avvenire abbastanza rapidamente da gestire l'aumento del carico. Ciò è particolarmente vero quando si aggiunge capacità di computing o gestione.

Utilizza immagini precotte

Se la tua app viene eseguita su VM di Compute Engine, probabilmente dovrai installare il software e configurare le istanze per eseguire l'app. Anche se puoi utilizzare gli script di avvio per configurare nuove istanze, un modo più efficiente è creare un'immagine personalizzata. Un'immagine personalizzata è un disco di avvio che hai impostato con il software e la configurazione specifici dell'app.

Per ulteriori informazioni sulla gestione delle immagini, consulta l'articolo Best practice per la gestione delle immagini.

Dopo aver creato l'immagine, puoi definire un modello di istanza. I modelli di istanza combinano l'immagine disco di avvio, il tipo di macchina e altre proprietà dell'istanza. Puoi quindi utilizzare un modello di istanza per creare singole istanze VM o un gruppo di istanze gestite. I modelli di istanza sono un modo conveniente per salvare la configurazione di un'istanza VM in modo da poterla utilizzare in seguito per creare nuove istanze VM identiche.

Sebbene la creazione di immagini e modelli di istanza personalizzati possa aumentare la velocità di deployment, può anche aumentare i costi di manutenzione, perché le immagini potrebbero dover essere aggiornate più spesso. Per ulteriori informazioni, consulta la documentazione relativa al bilanciamento della configurazione delle immagini e della velocità di deployment.

Containerizza la tua app

Un'alternativa alla creazione di istanze VM personalizzate è containerizzare l'app. Un container è un pacchetto di software eseguibile leggero e autonomo che include tutto ciò che serve per eseguire un'app: codice, runtime, strumenti di sistema, librerie di sistema e impostazioni. Queste caratteristiche rendono le app containerizzate più portabili, più facili da implementare e più facili da mantenere su larga scala rispetto alle macchine virtuali. Inoltre, in genere i container si avviano rapidamente, il che li rende adatti ad app scalabili e resilienti.

Google Cloud offre diversi servizi per eseguire i container di app. Cloud Run fornisce una piattaforma di computing gestita e serverless per ospitare i tuoi container stateless. L'ambiente flessibile di App Engine ospita i tuoi container in una piattaforma gestita come Platform as a Service (PaaS). GKE fornisce un ambiente Kubernetes gestito per ospitare e orchestrare le tue app containerizzate. Puoi anche eseguire i container della tua app su Compute Engine quando hai bisogno del controllo completo sull'ambiente dei container.

Ottimizza la tua app per un avvio rapido

Oltre a garantire che il deployment dell'infrastruttura e dell'app possa essere eseguito nel modo più efficiente possibile, è anche importante assicurarsi che l'app sia online rapidamente.

Le ottimizzazioni appropriate per la tua app variano a seconda delle caratteristiche dell'app e della piattaforma di esecuzione. È importante eseguire queste operazioni:

  • Individua ed elimina i colli di bottiglia eseguendo la profilazione delle sezioni critiche della tua app che vengono richiamate all'avvio.
  • Riduci il tempo di avvio iniziale implementando tecniche come l'inizializzazione pigra, in particolare per le risorse costose.
  • Riduci al minimo le dipendenze dell'app che potrebbero dover essere caricate all'avvio.

Favorisci architetture modulari

Puoi aumentare la flessibilità della tua app scegliendo architetture che consentono il deployment, la gestione e la scalabilità dei componenti in modo indipendente. Questo pattern può anche migliorare la resilienza eliminando i single point of failure.

Suddividi la tua app in servizi indipendenti

Se progetti la tua app come un insieme di servizi indipendenti a basso accoppiamento, puoi aumentarne la flessibilità. Se adotti un design a basso accoppiamento, i servizi vengono rilasciati e distribuiti in modo indipendente. Oltre a molti altri vantaggi, questo approccio consente a questi servizi di utilizzare diversi stack tecnici e di essere gestiti da team diversi. Questo approccio a basso accoppiamento è il tema chiave dei pattern di architettura, come microservizi e SOA.

Se pensi a come tracciare i confini intorno ai tuoi servizi, i requisiti di disponibilità e scalabilità sono dimensioni fondamentali. Ad esempio, se un determinato componente ha un requisito di disponibilità o un profilo di scalabilità diverso rispetto agli altri componenti, potrebbe essere un buon candidato per un servizio autonomo.

Punta all'apolidia

Un'app o un servizio stateless non conserva lo stato o i dati permanenti locali. Un modello stateless garantisce che tu possa gestire ogni richiesta o interazione con il servizio indipendentemente dalle richieste precedenti. Questo modello facilita la scalabilità e la recuperabilità, perché significa che il servizio può aumentare, ridurre o essere riavviato senza perdere i dati necessari per gestire le richieste o i processi in corso. L'stateless è particolarmente importante quando utilizzi un gestore della scalabilità automatica, perché le istanze, i nodi o i pod che ospitano il servizio possono essere creati ed eliminati in modo imprevisto.

È possibile che tutti i tuoi servizi non siano stateless. In tal caso, specifica esplicitamente i servizi che richiedono uno stato. Garantendo una separazione chiara tra i servizi stateless e stateful, puoi garantire una scalabilità semplice per i servizi stateless adottando al contempo un approccio più ponderato per i servizi stateful.

Gestire le comunicazioni tra i servizi

Un problema delle architetture di microservizi distribuiti è la gestione della comunicazione tra i servizi. Man mano che la tua rete di servizi cresce, è probabile che crescano anche le interdipendenze. Non vuoi che l'errore di un servizio comporti l'errore di altri servizi, a volte definito errore a cascata.

Puoi contribuire a ridurre il traffico verso un servizio sovraccarico o in errore adottando tecniche come il pattern dell'interruttore di sicurezza, i backoff esponenziali e il degrado graduale. Questi pattern aumentano la resilienza della tua app dando ai servizi di sovraccarico la possibilità di eseguire il ripristino o gestendo in modo controllato gli stati di errore. Per ulteriori informazioni, consulta il capitolo sulla gestione degli errori a cascata nel libro di Google SRE.

L'utilizzo di un mesh di servizi può aiutarti a gestire il traffico tra i tuoi servizi distribuiti. Un mesh di servizi è un software che collega i servizi e aiuta a disaccoppiare la logica di business dal networking. Un mesh di servizi in genere fornisce funzionalità di resilienza come nuovi tentativi di richieste, failover e interruttori di sicurezza.

Usare database e tecnologie di archiviazione appropriati

Alcuni database e tipi di archiviazione sono difficili da scalare e rendere resilienti. Assicurati che le scelte relative al database non limitino la disponibilità e la scalabilità dell'app.

Valuta le esigenze del tuo database

Lo schema di progettazione di un'app come insieme di servizi indipendenti si estende anche ai database e allo spazio di archiviazione. Potrebbe essere appropriato scegliere diversi tipi di archiviazione per parti diverse dell'app, il che si traduce in uno spazio di archiviazione eterogeneo.

Le app convenzionali spesso operano esclusivamente con database relazionali. I database relazionali offrono funzionalità utili come transazioni, elevata coerenza, integrità referenziale e sofisticate query nelle tabelle. Queste funzionalità rendono i database relazionali una buona scelta per molte funzionalità comuni delle app. Tuttavia, i database relazionali hanno anche alcuni vincoli. In genere sono difficili da scalare e richiedono una gestione attenta in una configurazione ad alta disponibilità. Un database relazionale potrebbe non essere la scelta migliore per tutte le esigenze del tuo database.

I database non relazionali, spesso chiamati database NoSQL, adottano un approccio diverso. Anche se i dettagli variano da un prodotto all'altro, i database NoSQL di solito sacrificano alcune funzionalità dei database relazionali a favore di una maggiore disponibilità e di una scalabilità più semplice. In termini di teorema CAP, i database NoSQL spesso preferiscono la disponibilità piuttosto che la coerenza.

L'adeguatezza di un database NoSQL spesso dipende dal grado di coerenza richiesto. Se il tuo modello dei dati per un determinato servizio non richiede tutte le funzionalità di un RDBMS e può essere progettato per essere coerente, la scelta di un database NoSQL potrebbe offrire maggiore disponibilità e scalabilità.

Nell'ambito della gestione dei dati, i database relazionali e non relazionali sono spesso visti come complementari anziché tecnologie concorrenti. Utilizzando entrambi i tipi di database in modo strategico, le organizzazioni possono sfruttare i punti di forza di ciascuno per ottenere risultati ottimali in termini di archiviazione, recupero e analisi dei dati.

Oltre a una gamma di database relazionali e NoSQL, Google Cloud offre anche Spanner, un database a elevata coerenza, ad alta disponibilità e distribuito a livello globale con supporto per SQL. Per informazioni sulla scelta di un database appropriato su Google Cloud, consulta Database Google Cloud.

Implementare la memorizzazione nella cache

Lo scopo principale di una cache è aumentare le prestazioni di recupero dei dati riducendo la necessità di accedere al livello di archiviazione più lento sottostante.

La memorizzazione nella cache supporta una migliore scalabilità riducendo il ricorso all'archiviazione basata su disco. Poiché le richieste possono essere gestite dalla memoria, le latenze delle richieste al livello di archiviazione vengono ridotte, consentendo in genere al tuo servizio di gestire più richieste. Inoltre, la memorizzazione nella cache può ridurre il carico sui servizi a valle dell'app, in particolare i database, consentendo ad altri componenti che interagiscono con il servizio downstream di scalare ulteriormente o del tutto la scalabilità.

La memorizzazione nella cache può anche aumentare la resilienza supportando tecniche come il degrado graduale. Se il livello di archiviazione sottostante è sovraccarico o non disponibile, la cache può continuare a gestire le richieste. Anche se i dati restituiti dalla cache potrebbero essere incompleti o non aggiornati, ciò potrebbe essere accettabile per alcuni scenari.

Memorystore per Redis fornisce un servizio completamente gestito basato sul datastore in memoria Redis. Memorystore for Redis offre accesso a bassa latenza e velocità effettiva elevata per i dati ad accesso intensivo. Può essere implementato in una configurazione ad alta disponibilità che fornisce replica tra zone e failover automatico.

Modernizza la cultura e i processi di sviluppo

DevOps può essere considerato una vasta raccolta di processi, cultura e strumenti che promuovono l'agilità e riducono il time to market per app e funzionalità abbattendo i compartimenti stagni tra sviluppo, operazioni e team correlati. Le tecniche DevOps mirano a migliorare la qualità e l'affidabilità del software.

Una discussione dettagliata su DevOps non rientra nell'ambito di questo documento, ma nelle sezioni seguenti vengono discussi alcuni aspetti chiave relativi al miglioramento dell'affidabilità e della resilienza della tua app. Per ulteriori dettagli, consulta la pagina DevOps di Google Cloud.

Progettare per la testabilità

I test automatici sono un componente fondamentale delle moderne pratiche di distribuzione del software. La capacità di eseguire un insieme completo di test di unità, integrazione e sistema è essenziale per verificare che l'app funzioni come previsto e che possa passare alla fase successiva del ciclo di deployment. La testabilità è un criterio di progettazione fondamentale per la tua app.

Ti consigliamo di utilizzare i test delle unità per la maggior parte dei test, poiché sono rapidi da eseguire e in genere semplici da gestire. Ti consigliamo inoltre di automatizzare i test di sistema e di integrazione di livello superiore. Questi test sono notevolmente semplificati se adotti le tecniche Infrastructure as Code, poiché gli ambienti di test e le risorse dedicati possono essere creati on demand e quindi rimossi una volta completati i test.

Con l'aumento della percentuale del codebase coperta dai test, riduci l'incertezza e la potenziale diminuzione dell'affidabilità a seguito di ogni modifica al codice. Una copertura di test adeguata significa che è possibile apportare più modifiche prima che l'affidabilità scenda al di sotto di un livello accettabile.

I test automatici sono parte integrante dell'integrazione continua. L'esecuzione di un solido set di test automatici su ogni commit del codice fornisce feedback rapidi sulle modifiche, migliorando la qualità e l'affidabilità del software. Gli strumenti nativi di Google Cloud come Cloud Build e strumenti di terze parti come Jenkins possono aiutarti a implementare l'integrazione continua.

Automazione dei deployment

L'integrazione continua e l'automazione completa dei test ti offrono fiducia nella stabilità del tuo software. Una volta implementate, il passaggio successivo consiste nell'automatizzare il deployment dell'app. Il livello di automazione del deployment varia a seconda della maturità della tua organizzazione.

Scegliere una strategia di deployment appropriata è fondamentale per ridurre al minimo i rischi associati al deployment di nuovo software. Con la strategia giusta, puoi aumentare gradualmente l'esposizione delle nuove versioni a segmenti di pubblico più ampi, verificando il comportamento durante il processo. Puoi anche impostare disposizioni chiare per il rollback in caso di problemi.

Adottare pratiche SRE per gestire i fallimenti

Per le app distribuite che operano su larga scala sono comuni i casi di errore di uno o più componenti. Se adotti i pattern descritti in questo documento, la tua app può gestire meglio le interruzioni causate da una release del software difettosa, dalla terminazione imprevista di macchine virtuali o anche da un'interruzione dell'infrastruttura che interessa un'intera zona.

Tuttavia, anche con un'attenta progettazione dell'app, è inevitabile che si verifichino eventi imprevisti che richiedono un intervento umano. Se implementi processi strutturati per gestire questi eventi, puoi ridurne notevolmente l'impatto e risolverli più rapidamente. Inoltre, se esamini le cause e le risposte all'evento, puoi contribuire a proteggere la tua app da eventi simili in futuro.

Processi solidi per la gestione degli incidenti e l'esecuzione di post-mortem senza colpa sono i principi fondamentali di SRE. Anche se l'implementazione di tutte le pratiche di Google SRE potrebbe non essere pratica per la tua organizzazione, se adotti anche solo una minima serie di linee guida, puoi migliorare la resilienza della tua app. Le appendici del libro sull'SRE contengono alcuni modelli che possono aiutarti a dare forma ai tuoi processi.

Convalida e rivedi l'architettura

Man mano che l'app si evolve, il comportamento degli utenti, i profili di traffico e persino le priorità aziendali possono cambiare. Analogamente, possono evolversi altri servizi o infrastruttura da cui dipende la tua app. È quindi importante testare e convalidare periodicamente la resilienza e la scalabilità della tua app.

Testa la tua resilienza

È fondamentale verificare che la tua app risponda agli errori nel modo previsto. Il tema generale è che il modo migliore per evitare i fallimenti è introdurre l'errore e imparare da quest'ultimo.

Simulare e introdurre errori è un'operazione complessa. Oltre a verificare il comportamento della tua app o del tuo servizio, devi anche assicurarti che vengano generati gli avvisi previsti e che vengano generate metriche appropriate. Consigliamo un approccio strutturato, in cui introduci errori semplici e poi esegui la riassegnazione.

Ad esempio, potresti procedere come segue, convalidando e documentando il comportamento in ogni fase:

  • Introduci errori intermittenti.
  • Blocca l'accesso alle dipendenze del servizio.
  • Blocca tutte le comunicazioni di rete.
  • Terminare gli host.

Per maggiori dettagli, guarda il video Breaking your System to make them unbreakable di Google Cloud Next 2019.

Se utilizzi un mesh di servizi come Istio per gestire i servizi delle app, puoi iniettare gli errori a livello dell'applicazione anziché terminare i pod o le macchine oppure puoi inserire pacchetti danneggiati a livello di TCP. Puoi introdurre ritardi per simulare la latenza di rete o un sistema upstream sovraccarico. Puoi anche introdurre le interruzioni, che imitano gli errori nei sistemi upstream.

Testa il comportamento di scalabilità

Ti consigliamo di utilizzare test non funzionanti automatici per verificare che la tua app scala come previsto. Spesso questa verifica è abbinata a test delle prestazioni o del carico. Puoi usare strumenti semplici come hey per inviare il carico a un'app web. Per un esempio più dettagliato che mostra come eseguire il test del carico su un endpoint REST, consulta Test di carico distribuito con Google Kubernetes Engine.

Un approccio comune è garantire che le metriche chiave rimangano entro i livelli previsti per carichi variabili. Ad esempio, se stai testando la scalabilità del tuo livello web, potresti misurare le latenze medie delle richieste per volumi elevati di richieste degli utenti. Analogamente, per una funzionalità di elaborazione di backend, potresti misurare il tempo medio di elaborazione delle attività quando il volume delle attività aumenta improvvisamente.

Inoltre, vuoi che i test misurino che il numero di risorse create per gestire il carico del test rientra nell'intervallo previsto. Ad esempio, i tuoi test potrebbero verificare che il numero di VM create per gestire alcune attività di backend non superi un determinato valore.

È importante anche testare i casi limite. Qual è il comportamento della tua app o del tuo servizio quando vengono raggiunti i limiti massimi di scalabilità? Qual è il comportamento dello scale down del servizio che aumenta improvvisamente di nuovo?

Creare architetture costanti

Il mondo della tecnologia si muove velocemente e questo è particolarmente vero per il cloud. Nuovi prodotti e funzionalità vengono rilasciati di frequente, emergono nuovi modelli e la domanda da parte degli utenti e degli stakeholder interni continua a crescere.

Come stabilito dal post del blog sui principi dell'architettura cloud-native, cerca sempre modi per perfezionare, semplificare e migliorare l'architettura delle tue app. I sistemi software sono esseri viventi e devono adattarsi a riflettere le nuove priorità.

Passaggi successivi