Questa guida fornisce le best practice per la progettazione, l'implementazione, il test e il deployment di un servizio Cloud Run. Per ulteriori suggerimenti, consulta la sezione Migrazione di un servizio esistente.
Scrivere servizi efficaci
Questa sezione descrive le best practice generali per la progettazione e l'implementazione di un servizio Cloud Run.
Attività in background
L'attività in background è tutto ciò che accade dopo che la risposta HTTP è stata inviata. Per determinare se nel tuo servizio esiste un'attività in background che non è subito evidente, controlla nei log se viene registrato qualsiasi cosa viene registrato dopo la voce della richiesta HTTP.
Configura la CPU da allocare sempre per l'uso delle attività in background
Se vuoi supportare le attività in background nel 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 senza rinunciare all'accesso alla CPU.
Evita attività in background se la CPU viene allocata solo durante l'elaborazione della richiesta
Se devi impostare il servizio in modo da allocare la CPU solo durante l'elaborazione delle richieste, quando il servizio Cloud Run termina di gestire una richiesta, l'accesso dell'istanza alla CPU verrà disabilitato o limitato in modo grave. 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 terminino prima di inviare la risposta.
L'esecuzione di thread in background con questo tipo di allocazione della CPU può causare comportamenti imprevisti, perché qualsiasi richiesta successiva alla stessa istanza di container 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 su disco utilizzano memoria altrimenti disponibile per il tuo servizio e possono rimanere in sospeso tra una chiamata e l'altra. La mancata eliminazione di questi file potrebbe causare un errore di esaurimento della memoria e un successivo avvio a freddo.
Segnala errori
Gestisci tutte le eccezioni e non consentire l'arresto anomalo del servizio in caso di errori. Un arresto anomalo porta a 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.
Avvio rapido dei container
Poiché le istanze vengono scalate secondo necessità, il loro tempo di avvio ha un impatto sulla latenza del servizio. Mentre Cloud Run disaccoppia l'elaborazione delle richieste e l'avvio, può accadere che una richiesta attenda prima che la nuova istanza venga avviata per l'elaborazione, in particolare quando viene scalata da zero. Questo processo è chiamato "avvio a freddo".
La routine di avvio è costituita da:
- Download dell'immagine container (utilizzando la tecnologia di flussi di dati delle immagini container di Cloud Run)
- Avvia il container eseguendo il comando entrypoint.
- In attesa che il container inizi ad ascoltare sulla porta configurata.
L'ottimizzazione della velocità di avvio dei container riduce al minimo la latenza di elaborazione delle richieste.
Usa il booster della CPU all'avvio per ridurre la latenza di avvio
Puoi abilitare il booster 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 un numero minimo di istanze e la contemporaneità per ridurre al minimo gli avvii a freddo. Ad esempio, utilizzare un numero minimo di istanze pari a 1 significa che il servizio è pronto a ricevere fino al numero di richieste in parallelo 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 indicato di seguito:
- Se vengono avviate nuove istanze, ad esempio durante uno scale out, le richieste cadranno almeno per il tempo di avvio medio delle istanze di container di questo servizio. Questo include quando la richiesta avvia uno scale out, ad esempio quando viene scalato da zero.
- Se il tempo di avvio è inferiore a 10 secondi, le richieste verranno sospese per un massimo di 10 secondi.
- Se non sono presenti istanze in fase di avvio e la richiesta non avvia uno scale out, le richieste verranno sospese per un massimo di 10 secondi.
Usa le dipendenze con oculatezza
Se utilizzi un linguaggio dinamico con librerie dipendenti, ad esempio importando moduli 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 sottile.
- Carica lentamente il codice usato raramente, se supportato dal tuo linguaggio.
- Usa ottimizzazioni del caricamento del codice come l'ottimizzazione del caricatore automatico dei composer di PHP.
Utilizzare le variabili globali
In Cloud Run, non puoi presumere che lo stato del servizio venga conservato tra le richieste. Tuttavia, Cloud Run riutilizza singole istanze per gestire il traffico in corso, quindi puoi dichiarare una variabile in ambito globale per consentire il riutilizzo del relativo valore nelle chiamate successive. Non è possibile sapere in anticipo se ogni singola richiesta riceva il vantaggio di questo riutilizzo.
Puoi anche memorizzare nella cache gli oggetti che sono costosi da ricreare per ogni richiesta di servizio. Lo spostamento dalla logica di richiesta all'ambito globale consente di ottenere prestazioni migliori.
Node.js
Python
Go
Java
Esegui l'inizializzazione lazy delle variabili globali
L'inizializzazione delle variabili globali avviene sempre durante l'avvio, aumentando il tempo di avvio a freddo. Usa l'inizializzazione lazy per gli oggetti usati raramente, al fine di rinviare il costo in termini di tempo e ridurre i tempi di avvio a freddo.
Node.js
Python
Go
Java
Utilizza un ambiente di esecuzione diverso
Potresti riscontrare tempi di avvio più rapidi utilizzando un ambiente di esecuzione diverso.
Ottimizza la contemporaneità
Le istanze Cloud Run possono gestire più richieste contemporaneamente, "contemporaneamente", fino a una contemporaneità massima configurabile.
È diverso da Cloud Functions, che utilizza concurrency = 1
.
Cloud Run regola automaticamente la contemporaneità fino al massimo configurato.
La contemporaneità massima predefinita di 80 è ideale per molte immagini container. Tuttavia, devi:
- Riduci questo valore se il container non è in grado di elaborare molte richieste in parallelo.
- Aumentalo se il container è in grado di gestire un volume elevato di richieste.
Ottimizza la contemporaneità per il tuo servizio
Il numero di richieste in parallelo che ogni istanza può essere gestita da ogni istanza può essere limitato dallo stack tecnologico e dall'uso di risorse condivise come variabili e connessioni ai database.
Per ottimizzare il servizio per la massima contemporaneità stabile:
- Ottimizza le prestazioni dei servizi.
- Imposta il livello previsto di supporto della contemporaneità in qualsiasi configurazione di contemporaneità a livello di codice. Non tutti gli stack tecnologici richiedono un'impostazione di questo tipo.
- Esegui il deployment del servizio.
- Imposta la contemporaneità di Cloud Run per il tuo servizio su un valore uguale o inferiore a quello di qualsiasi configurazione a livello di codice. In assenza di una configurazione a livello di codice, utilizza la contemporaneità prevista.
- Utilizza strumenti di test del carico che supportano una contemporaneità configurabile. Devi confermare che il tuo servizio rimanga stabile, nel rispetto del carico e della contemporaneità previsti.
- Se il servizio funziona in modo scadente, vai al passaggio 1 per migliorare il servizio o al passaggio 2 per ridurre la contemporaneità. Se il servizio funziona, torna al passaggio 2 e aumenta la contemporaneità.
Continua a ripetere l'iterazione fino a trovare il massimo della contemporaneità stabile.
Abbina la memoria alla contemporaneità
Ogni richiesta gestita dal tuo servizio richiede una certa quantità di memoria aggiuntiva. Pertanto, quando aumenti o riduci la contemporaneità, assicurati di regolare anche il limite di memoria.
Evita stato globale modificabile
Se vuoi utilizzare lo stato globale modificabile in un contesto simultaneo, esegui passaggi aggiuntivi nel codice per assicurarti che l'operazione venga eseguita in modo sicuro. Riduci al minimo la contesa limitando le variabili globali all'inizializzazione e al riutilizzo una tantum, come descritto sopra nella sezione Prestazioni.
Se utilizzi variabili globali modificabili in un servizio che gestisce più richieste contemporaneamente, assicurati di utilizzare blocchi o mutex per evitare racecondition.
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 in linea con la filosofia e l'architettura dei container.
Per migliorare la sicurezza dei container:
Utilizza immagini di base gestite attivamente e sicure come immagini di base di Google o immagini ufficiali di Docker Hub.
Applica gli aggiornamenti della sicurezza ai tuoi servizi ricreando regolarmente le immagini dei container ed eseguendo nuovamente il deployment dei servizi.
Includi nel container solo ciò che è necessario per eseguire il tuo servizio. Codice, pacchetti e strumenti aggiuntivi sono potenziali vulnerabilità per la sicurezza. Vedi sopra l'impatto sul rendimento correlato.
Implementare un processo di compilazione deterministico che includa versioni specifiche di software e librerie. In questo modo il codice non verificato viene incluso nel container.
Imposta il container in modo che venga eseguito come utente diverso da
root
con l'istruzione DockerfileUSER
. Per alcune immagini container potrebbe essere già configurato un utente specifico.
Automatizza l'analisi della sicurezza
Attiva l'analisi delle vulnerabilità per l'analisi di 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ù di ciò di cui il codice ha bisogno.
A causa della tecnologia di inserimento di flussi di immagini container di Cloud Run, le dimensioni dell'immagine container non influiscono sull'avvio a freddo o sui tempi 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 la possibilità di lavorare da un'immagine di base sottile come:
Ubuntu ha dimensioni maggiori, ma è un'immagine di base di uso comune con un ambiente server pronto all'uso più completo.
Se il tuo servizio ha un processo di compilazione che richiede molti strumenti, valuta la possibilità di utilizzare build multifase per mantenere il container leggero in fase di esecuzione.
Queste risorse forniscono ulteriori informazioni sulla creazione di immagini container sottili:
- Best practice per la creazione di container
- Best practice per Kubernetes: come e perché creare immagini container di piccole dimensioni
- 7 best practice per la creazione dei container