Ottimizzare le applicazioni Python per Cloud Run

Questa guida descrive le ottimizzazioni per i servizi Cloud Run scritti nel linguaggio di programmazione Python, insieme a informazioni di base per aiutarti a comprendere i compromessi implicati in alcune di queste ottimizzazioni. Le informazioni contenute in questa pagina integrano i suggerimenti generali di ottimizzazione, che si applicano anche a Python.

Molte delle migliori pratiche e ottimizzazioni nelle comuni applicazioni web basate su Python ruotano attorno a:

  • Gestione delle richieste simultanee (I/O basato su thread e non bloccante)
  • Riduzione della latenza di risposta tramite il connection pooling e il raggruppamento delle funzioni non critiche, ad esempio l'invio di tracce e metriche ad attività in background.

Ottimizza l'immagine container

Ottimizza l'immagine container per ridurre i tempi di caricamento e di avvio, utilizzando questi metodi:

  • Riduci a icona i file che vengono caricati all'avvio
  • Ottimizzare il server WSGI

Riduci a icona i file che vengono caricati all'avvio

Per ottimizzare i tempi di avvio, carica solo i file necessari e riducine le dimensioni. Per i file di grandi dimensioni, valuta le seguenti opzioni:

  • Archivia file di grandi dimensioni, come i modelli di AI, nel container per un accesso più rapido. Valuta la possibilità di caricare questi file dopo l'avvio o in fase di runtime.

  • Valuta la possibilità di configurare i montaggi dei volumi Cloud Storage per i file di grandi dimensioni non critici all'avvio, come gli asset multimediali.

  • Importa solo i sottomoduli richiesti da eventuali dipendenze pesanti o importa i moduli quando sono richiesti nel codice, anziché caricarli all'avvio dell'applicazione.

Ottimizza il server WSGI

Python ha standardizzato il modo in cui le applicazioni possono interagire con i server web attraverso l'implementazione dello standard WSGI, PEP-3333. Uno dei server WSGI più comuni è gunicorn, che viene utilizzato in gran parte della documentazione di esempio.

Gunicorn ottimizzato

Aggiungere quanto segueCMD per ilDockerfile per ottimizzare l'invocazione digunicorn:

CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app

Se si intende modificare queste impostazioni, è necessario regolare il numero di worker e thread per ogni singola applicazione. Ad esempio, prova a utilizzare un numero di worker pari al numero di core disponibili e verifica se si riscontra un miglioramento delle prestazioni, quindi regola il numero di thread. L'impostazione di un numero eccessivo di worker o thread può avere un impatto negativo, ad esempio una latenza di avvio a freddo più lunga, un maggiore consumo di memoria, un numero inferiore di richieste al secondo e così via.

Per impostazione predefinita, gunicorn avvia i worker e si mette in ascolto sulla porta specificata all'avvio, ancor prima di valutare il codice dell'applicazione. In questo caso, devi configurare probe di avvio personalizzati per il tuo servizio, poiché il probe di avvio predefinito di Cloud Run contrassegna immediatamente un'istanza container come integra non appena inizia ad ascoltare su $PORT.

Se vuoi modificare questo comportamento, puoi invocaregunicorn con il--preload collocamento per valutare il codice dell'applicazione prima di rimanere in ascolto. Questo può essere utile per:

  • Identificare i bug di runtime più gravi al momento della distribuzione.
  • Risparmiare risorse di memoria

Prima di aggiungere questo elemento, dovresti valutare cosa precarica la tua applicazione.

Altri server WSGI

Non sei obbligato a utilizzare gunicorn per eseguire Python nei container. È possibile utilizzare qualsiasi server web WSGI o ASGI, purché il container sia in ascolto sulla porta HTTP $PORT, come previsto dal contratto di runtime del container .

Le alternative più comuni includono uwsgi, uvicorn e waitress.

Ad esempio, dato un file denominato main.py contenente l'oggetto app, le seguenti invocazioni avvierebbero un server WSGI:

# uwsgi: pip install pyuwsgi
uwsgi --http :$PORT -s /tmp/app.sock --manage-script-name --mount /app=main:app

# uvicorn: pip install uvicorn
uvicorn --port $PORT --host 0.0.0.0 main:app

# waitress: pip install waitress
waitress-serve --port $PORT main:app

Questi possono essere aggiunti comeCMD exec linea in unaDockerfile, o come unweb: ingresso inProcfile quando si utilizzaBuildpack di Google Cloud.

Applicazioni di ottimizzazione

Nel codice del servizio Cloud Run, è inoltre possibile ottimizzare i tempi di avvio e la memoria utilizzata.

Ridurre i fili

È possibile ottimizzare la memoria riducendo il numero di thread, utilizzando strategie reattive non bloccanti ed evitando attività in background. Evita inoltre di scrivere sul file system, come indicato nella pagina dei suggerimenti generali .

Se desideri supportare le attività in background nel tuo servizio Cloud Run, imposta il servizio Cloud Run su fatturazione basata sull'istanza in modo da poter eseguire attività in background al di fuori delle richieste e avere comunque accesso alla CPU.

Riduci le attività di avvio

Le applicazioni web basate su Python possono avere molte attività da completare durante l'avvio, come il precaricamento dei dati, il riscaldamento della cache e la creazione di pool di connessioni. Se eseguite in sequenza, queste operazioni possono risultare lente. Tuttavia, se vuoi che vengano eseguiti in parallelo, aumenta il numero di core della CPU.

Cloud Run invia una richiesta utente reale per attivare un'istanza di avvio a freddo. Gli utenti che hanno una richiesta assegnata a un'istanza appena avviata potrebbero riscontrare lunghi ritardi.

Migliora la sicurezza con immagini di base più sottili

Per migliorare la sicurezza della tua applicazione, utilizza un'immagine di base slimline con meno pacchetti e librerie.

Se scegli di non installare Python dal codice sorgente all'interno dei tuoi container, usa unimmagine base generale di Python da Docker Hub. Queste immagini sono basate sul sistema operativo Debian.

Se utilizzi l'immagine python di Docker Hub, valuta la possibilità di utilizzare la versione slim. Queste immagini sono più piccole perché non includono una serie di pacchetti che verrebbero utilizzati per costruire le ruote, operazione che potrebbe non essere necessaria per la tua applicazione. L'immagine python include il compilatore GNU C, il preprocessore e le utilità principali.

Per identificare i dieci pacchetti più grandi in un'immagine di base, eseguire il seguente comando:

DOCKER_IMAGE=python # or python:slim
docker run --rm ${DOCKER_IMAGE} dpkg-query -Wf '${Installed-Size}\t${Package}\t${Description}\n' | sort -n | tail -n10 | column -t -s $'\t'

Poiché questi pacchetti di basso livello sono meno numerosi, le immagini basate su slim offrono anche una superficie di attacco minore per potenziali vulnerabilità. Alcune di queste immagini potrebbero non includere gli elementi necessari per costruire le ruote partendo dal modello originale.

È possibile aggiungere nuovamente pacchetti specifici aggiungendo una riga RUN apt install al Dockerfile. Per ulteriori informazioni, vedere Utilizzo dei pacchetti di sistema in Cloud Run.

Sono disponibili anche opzioni per container non basati su Debian. L'opzione python:alpine potrebbe generare un container molto più piccolo, ma molti pacchetti Python potrebbero non avere ruote precompilate che supportano i sistemi basati su Alpine. Il supporto sta migliorando (vedi PEP-656), ma continua a variare. Considera anche l'utilizzo deldistroless base image che non contiene gestori di pacchetti, shell o altri programmi.

Utilizzare la variabile di ambiente PYTHONUNBUFFERED per la registrazione

Per visualizzare i log non bufferizzati della tua applicazione Python, imposta la variabile d'ambiente PYTHONUNBUFFERED. Quando imposti questa variabile, i dati stdout e stderr sono immediatamente visibili nei log del contenitore, invece di essere mantenuti in un buffer fino a quando non si è accumulata una certa quantità di dati o fino alla chiusura del flusso.

Passaggi successivi

Per altri suggerimenti, vedi