Questa guida fornisce le best practice per progettare, implementare, testare ed eseguire il deployment di un servizio Cloud Run. Per altri suggerimenti, vedi Migrazione di un servizio esistente.
Scrivere servizi efficaci
Questa sezione descrive le best practice generali per la progettazione e l'implementazione di un dal servizio Cloud Run.
Attività in background
Per attività in background si intende tutto ciò che accade dopo che la risposta HTTP è stata disponibili. Per determinare se nel servizio è presente attività in background che non sia immediatamente evidente, controlla nei log la presenza di eventuali registrato dopo la voce della richiesta HTTP.
Configura la CPU da allocare sempre per l'utilizzo di attività in background
Se vuoi supportare le attività in background nel tuo servizio Cloud Run, imposta la CPU del servizio Cloud Run in modo che sia sempre allocata in modo da poter eseguire attività in background al di fuori delle richieste e avere comunque accesso alla CPU.
Evita attività in background se la CPU viene allocata solo durante l'elaborazione delle richieste
Se devi impostare il servizio su alloca la CPU solo durante l'elaborazione delle richieste, quando il servizio Cloud Run termina la gestione di una richiesta, l'accesso dell'istanza alla CPU verrà disabilitato o gravemente limitato. Se utilizzi questo tipo di allocazione della CPU, non devi avviare thread o routine in background che vengono eseguiti al di fuori dell'ambito dei gestori delle richieste.
Esamina il codice per assicurarti che tutte le operazioni asincrone vengano completate prima di inviare la risposta.
L'esecuzione di thread in background con questo tipo di allocazione della CPU può comportare un comportamento imprevisto perché ogni richiesta successiva allo stesso container l'istanza riprende qualsiasi attività in background sospesa.
Eliminare i file temporanei
Nell'ambiente Cloud Run, l'archiviazione su disco è un file system in memoria. I file scritti sul disco consumano memoria altrimenti disponibile per il tuo servizio, e può persistere tra una chiamata e l'altra. Se non li elimini, alla fine potresti riscontrare un errore di esaurimento della memoria e un successivo avvio a freddo.
Segnalare errori
Gestisci tutte le eccezioni e non permettere che il tuo servizio abbia un arresto anomalo in caso di errori. Un arresto anomalo provoca un avvio a freddo mentre il traffico è in coda per un'istanza sostitutiva.
Per informazioni su come segnalare correttamente gli errori, consulta la guida alla segnalazione degli errori.
Ottimizzazione del rendimento
Questa sezione descrive le best practice per ottimizzare il rendimento.
Avvia rapidamente i container
Poiché le istanze vengono scalate in base alle esigenze, il loro tempo di avvio influisce sulla latenza del servizio. Sebbene Cloud Run disaccoppia l'avvio e l'elaborazione delle richieste, può accadere che una richiesta debba attendere l'avvio di una nuova istanza dall'elaborazione dei dati, in particolare quando si scala da zero. Questa operazione è chiamata "avvio a freddo".
La routine di avvio è costituita da:
- Download dell'immagine container (utilizzando la tecnologia di streaming delle immagini container di Cloud Run)
- avvia il container eseguendo l'entrypoint .
- In attesa che il container inizi ad ascoltare sulla porta configurata.
L'ottimizzazione per la velocità di avvio del contenitore riduce al minimo la latenza di elaborazione delle richieste.
Utilizza il boosting della CPU all'avvio per ridurre la latenza di avvio
Puoi attivare il boosting della CPU all'avvio per aumentare temporaneamente l'allocazione della CPU durante l'avvio dell'istanza in modo da ridurre la latenza di avvio.
Utilizza il numero minimo di istanze per ridurre gli avvii a freddo
Puoi configurare le istanze minime e la concorrenza per ridurre al minimo gli avvii a freddo. Ad esempio, l'utilizzo di un numero minimo di istanze pari a 1 indica che il servizio è pronto a ricevere fino al numero di richieste simultanee configurate per il servizio senza dover avviare una nuova istanza.
Tieni presente che una richiesta in attesa dell'avvio di un'istanza verrà mantenuta in attesa in una coda come segue:
- Se vengono avviate nuove istanze, ad esempio durante uno scale out, le richieste almeno il tempo di avvio medio delle istanze di container di questo servizio. Sono inclusi i casi in cui la richiesta avvia uno scale out, ad esempio durante la scalabilità partendo da zero.
- Se il tempo di avvio è inferiore a 10 secondi, le richieste scadranno per un massimo di 10 secondi.
- Se non sono presenti istanze in fase di avvio e la richiesta non avviano uno scale out, le richieste pendono per un massimo di per 10 secondi.
Utilizzare le dipendenze in modo oculato
Se utilizzi un linguaggio dinamico con librerie dipendenti, ad esempio i moduli di importazione in Node.js, il tempo di caricamento di questi moduli si aggiunge alla latenza di avvio.
Riduci la latenza di avvio nei seguenti modi:
- Riduci al minimo il numero e la dimensione delle dipendenze per creare un servizio snello.
- Carica in modo lento il codice che non viene utilizzato di frequente, se la tua lingua lo supporta.
- Utilizza le ottimizzazioni di caricamento del codice, come l'ottimizzazione dell'autoloader di Composer di PHP.
Utilizza le variabili globali
In Cloud Run, non puoi assumere che lo stato del servizio venga mantenuto tra le richieste. Tuttavia, Cloud Run riutilizza le singole istanze per gestire il traffico in corso, quindi puoi dichiarare una variabile a livello globale per consentire il riutilizzo del relativo valore nelle chiamate successive. Non è possibile sapere in anticipo se una singola richiesta beneficia di questo riutilizzo.
Puoi anche memorizzare nella cache gli oggetti se sono costosi da ricreare richiesta di servizio. Se lo sposti dalla logica di richiesta all'ambito globale, il rendimento migliorerà.
Node.js
Python
Vai
Java
Esegui l'inizializzazione lazy delle variabili globali
L'inizializzazione delle variabili globali avviene sempre durante l'avvio, che aumenta il tempo di avvio a freddo. Utilizza l'inizializzazione lazy per gli oggetti usati di rado per posticipare il costo in termini di tempo e ridurre i tempi di avvio a freddo.
Node.js
Python
Vai
Java
Utilizza un ambiente di esecuzione diverso
Potresti riscontrare tempi di avvio più rapidi utilizzando una un ambiente di esecuzione diverso.
Ottimizza la concorrenza
Le istanze Cloud Run possono gestire più richieste contemporaneamente,
"in parallelo", fino a una contemporaneità massima configurabile.
È diverso dalle funzioni Cloud Run, che utilizzano concurrency = 1
.
Cloud Run regola automaticamente la concorrenza fino al valore massimo configurato.
La concorrenza massima predefinita di 80 è adatta per molte immagini container. Tuttavia, devi:
- Riducilo se il container non è in grado di elaborare molte richieste in parallelo.
- Aumentalo se il container è in grado di gestire un grande volume di richieste.
Ottimizza la contemporaneità per il tuo servizio
Il numero di richieste in parallelo che ogni istanza può gestire può essere limitato dall'architettura tecnologica e dall'utilizzo di risorse condivise come variabili e connessioni al database.
Per ottimizzare il tuo servizio per ottenere la massima contemporaneità stabile:
- Ottimizza il rendimento del servizio.
- Imposta il livello di supporto della concorrenza previsto in qualsiasi configurazione della concorrenza a livello di codice. Non tutti gli stack tecnologici richiedono questa impostazione.
- Esegui il deployment del servizio.
- Imposta la concorrenza Cloud Run per il tuo servizio su un valore pari o inferiore a qualsiasi configurazione a livello di codice. Se non esiste una configurazione a livello di codice, utilizza della contemporaneità prevista.
- Utilizzare i test di carico che supportano una contemporaneità configurabile. Devi verificare che il servizio rimanga stabile con il carico e la concorrenza previsti.
- Se il servizio non funziona correttamente, vai al passaggio 1 per migliorarlo o al passaggio 2 per ridurre la concorrenza. Se il servizio funziona correttamente, torna al passaggio 2 e aumenta la concorrenza.
Continua a eseguire l'iterazione finché non trovi la contemporaneità stabile massima.
Abbina la memoria alla contemporaneità
Ogni richiesta gestita dal servizio richiede una certa quantità di memoria aggiuntiva. Perciò, quando regoli la contemporaneità in alto o in basso, assicurati di regolare la memoria limite.
Evita stato globale modificabile
Se vuoi sfruttare lo stato globale modificabile in un contesto simultaneo, prendi in considerazione passaggi nel codice per assicurarti che questa operazione venga eseguita in sicurezza. Ridurre al minimo il conflitto limitando l'inizializzazione una tantum e il riutilizzo delle variabili globali, come descritto sopra Prestazioni.
Se utilizzi variabili globali modificabili in un servizio che gestisce più richieste contemporaneamente, assicurati di usare blocchi o mutex per impedire le racecondition.
Confronto tra velocità effettiva, latenza e compromessi in termini di costo
L'ottimizzazione dell'impostazione del numero massimo di richieste in parallelo può aiutarti a bilanciare il compromesso tra velocità effettiva, latenza e costo del tuo servizio.
In generale, un'impostazione massima per le richieste in parallelo più bassa comporta una latenza inferiore e una velocità effettiva inferiore per istanza. Con un numero massimo di richieste in parallelo più basso, Un numero minore di richieste compete per le risorse all'interno di ogni istanza e richiesta raggiunge prestazioni migliori. Tuttavia, poiché ogni istanza può gestire meno richieste contemporaneamente, il throughput per istanza è inferiore e il servizio richiede più istanze per gestire lo stesso traffico.
Nella direzione opposta, invece, viene impostata un'impostazione massima di richieste in parallelo più alta. di solito comporta una latenza più elevata e una velocità effettiva superiore per istanza. Le richieste potrebbero dover attendere l'accesso a risorse come CPU, GPU e larghezza di banda della memoria all'interno dell'istanza, il che comporta un aumento della latenza. Ma ogni istanza può elaborare più richieste contemporaneamente, in modo che il servizio abbia bisogno di meno istanze nel complesso per elaborare lo stesso traffico.
Considerazioni sui costi
La fatturazione di Cloud Run avviene in base al tempo di istanza. Se la CPU è sempre allocata, la data e l'ora dell'istanza corrispondono alla durata totale di ciascuna istanza. Se la CPU non è sempre allocato, il tempo dell'istanza è il tempo impiegato da ogni istanza per elaborare almeno una richiesta.
L'impatto delle richieste massime in parallelo sulla fatturazione dipende dal tuo schema di traffico. La riduzione del numero massimo di richieste in parallelo può comportare una fattura inferiore se l'impostazione più bassa porta a
- Latenza ridotta
- Istanze che completano il lavoro più velocemente
- Le istanze si arrestano più velocemente anche se è necessario un numero maggiore di istanze
È possibile anche il contrario: la riduzione delle richieste in parallelo massime può aumentare la fatturazione se l'aumento del numero di istanze non è compensato dalla riduzione del tempo di esecuzione di ciascuna istanza, a causa della latenza migliorata.
Il modo migliore per ottimizzare la fatturazione è tramite i test di carico. utilizzando diverse impostazioni per il numero massimo di richieste in parallelo per identificare l'impostazione che comporta il tempo di istanza fatturabile più basso, come indicato nella metrica di monitoraggio container/billable_instance_time.
Sicurezza dei container
Molte pratiche di sicurezza del software per uso generico si applicano alle applicazioni containerizzate. Esistono alcune pratiche specifiche per i container o che si allineano alla filosofia e all'architettura dei container.
Per migliorare la sicurezza del contenitore:
Utilizza immagini di base sicure e sottoposte a manutenzione attiva, come le immagini di base di Google o le immagini ufficiali di Docker Hub.
Applica gli aggiornamenti della sicurezza ai tuoi servizi ricostruendo regolarmente le immagini dei contenitori e reimplementando i servizi.
Includi nel contenitore solo ciò che è necessario per eseguire il servizio. Il codice, i pacchetti e gli strumenti aggiuntivi sono potenziali vulnerabilità di sicurezza. Consulta la sezione precedente per informazioni sull'impatto sul rendimento.
Implementa un processo di compilazione deterministico che includa versioni specifiche di software e librerie. In questo modo, le schede non verificate di includere codice nel tuo container.
Imposta il contenitore in modo che venga eseguito come utente diverso da
root
con il Istruzione DockerfileUSER
. Per alcune immagini contenitore potrebbe essere già configurato un utente specifico.Impedisci l'utilizzo delle funzionalità in anteprima utilizzando i criteri dell'organizzazione personalizzati.
Automatizzare l'analisi della sicurezza
Attiva l'analisi delle vulnerabilità per l'analisi della sicurezza delle immagini container archiviate in Artifact Registry.
Crea immagini container minime
Le immagini container di grandi dimensioni aumentano probabilmente le vulnerabilità di sicurezza perché contengono più elementi di cui il codice ha bisogno.
Grazie alla tecnologia di streaming delle immagini container di Cloud Run, le dimensioni dell'immagine container non influiscono sul tempo di avvio a freddo o di elaborazione delle richieste. Inoltre, le dimensioni dell'immagine container non vengono conteggiate ai fini della memoria disponibile. del container.
Per creare un container minimo, valuta l'utilizzo di un'immagine di base magra come:
Ubuntu ha dimensioni maggiori, ma è un'immagine di base di uso comune con un ambiente server out-of-box più completo.
Se il tuo servizio ha un processo di compilazione che richiede molti strumenti, ti consigliamo di utilizzare le costruzioni a più fasi per mantenere il contenitore leggero in fase di esecuzione.
Queste risorse forniscono ulteriori informazioni sulla creazione di immagini container lean:
- Best practice di Kubernetes: come e perché creare immagini di container di piccole dimensioni
- 7 best practice per la creazione di container