Ottimizza le applicazioni Python per Cloud Run

Questa guida descrive le ottimizzazioni per i servizi Cloud Run scritte nel linguaggio di programmazione Python, insieme alle informazioni di base per comprendere i compromessi in alcune delle ottimizzazioni. Le informazioni contenute in questa pagina integrano i suggerimenti per l'ottimizzazione generale, validi anche per Python.

Molte delle best practice e delle ottimizzazioni in queste applicazioni tradizionali basate sul Web Python si basano su:

  • Gestione delle richieste in parallelo (I/O sia thread che non bloccanti)
  • Riduzione della latenza delle risposte utilizzando il pool di connessioni e le funzionalità batch non critiche, ad esempio l'invio di tracce e metriche alle attività in background.

Ottimizzare l'immagine container

Ottimizzando l'immagine container, puoi ridurre i tempi di caricamento e di avvio. Puoi ottimizzare l'immagine nei seguenti modi:

  • Inserisci nel container solo ciò di cui la tua app ha bisogno per il runtime
  • Ottimizzazione del server WSGI

Inserisci nel container solo ciò di cui la tua app ha bisogno per il runtime

Valuta quali componenti sono inclusi nel container e se sono richiesti per l'esecuzione del servizio. Esistono diversi modi per ridurre al minimo l'immagine del container:

  • Utilizza un'immagine di base più piccola
  • Spostare file di grandi dimensioni all'esterno del container

Utilizza un'immagine di base più piccola

Docker Hub fornisce una serie di immagini di base Python ufficiali che puoi utilizzare se scegli di non installare Python dall'origine all'interno dei tuoi container. che dipendono dal sistema operativo Debian.

Se utilizzi l'immagine python di Docker Hub, valuta l'uso della versione slim. Queste immagini hanno dimensioni più piccole perché non sono dotate di una serie di pacchetti utilizzati per creare ruote, ad esempio che non è necessario utilizzare per l'applicazione. Ad esempio, l'immagine python viene fornita con il compilatore GNU C, il preprocessore e le utilità principali.

Per identificare i dieci pacchetti più grandi in un'immagine di base, puoi eseguire questo 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é il numero di questi pacchetti di basso livello è inferiore, le immagini basate su slim offrono anche una minore superficie di attacco per potenziali vulnerabilità. Queste immagini potrebbero non includere gli elementi necessari per creare le ruote dall'origine.

Puoi aggiungere di nuovo pacchetti specifici aggiungendo una riga RUN apt install al tuo Dockerfile. Scopri di più sull'utilizzo dei pacchetti di sistema in Cloud Run.

Sono disponibili anche opzioni per i container non basati su Debian. L'opzione python:alpine può comportare un container molto più piccolo, ma molti pacchetti Python potrebbero non avere ruote precompilate che supportano i sistemi basati su Alpi. L'assistenza migliora (vedi PEP-656), ma continua a essere variata. Puoi anche utilizzare distroless base image, che non contiene gestori di pacchetti, shell o altri programmi.

Spostare file di grandi dimensioni all'esterno del container

I file di grandi dimensioni, come gli asset multimediali, ecc. non devono essere inclusi nel contenitore di base.

Google Cloud offre diverse opzioni di hosting, come Cloud Storage, per archiviare questi elementi di grandi dimensioni. Spostare asset di grandi dimensioni in questi servizi, quindi farvi riferimento dalla tua applicazione in fase di esecuzione.

Ottimizzare il server WSGI

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

Ottimizza gunicorn

La parte CMD di Dockerfile mostra una chiamata ottimizzata di gunicorn:


# Use the official lightweight Python image.
# https://hub.docker.com/_/python
FROM python:3.10-slim

# Allow statements and log messages to immediately appear in the Knative logs
ENV PYTHONUNBUFFERED True

# Copy local code to the container image.
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . ./

# Install production dependencies.
RUN pip install --no-cache-dir -r requirements.txt

# Run the web service on container startup. Here we use the gunicorn
# webserver, with one worker process and 8 threads.
# For environments with multiple CPU cores, increase the number of workers
# to be equal to the cores available.
# Timeout is set to 0 to disable the timeouts of the workers to allow Cloud Run to handle instance scaling.
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app

Se stai pensando di modificare queste impostazioni, regola il numero di worker e thread in base all'applicazione. Ad esempio, prova a utilizzare un numero di worker uguale ai core disponibili e assicurati che le prestazioni siano migliorate, quindi regola il numero di thread. L'impostazione di troppi worker o thread può avere un impatto negativo, ad esempio una latenza di avvio a freddo più lunga, una memoria più utilizzata, richieste più piccole al secondo e così via.

L'aggiunta dell'impostazione --preload può aiutarti a:

  • Identifica i bug gravi del runtime al momento del deployment
  • Risparmia risorse di memoria

Prima di aggiungere questo componente, dovresti valutare cosa viene caricato preventivamente dalla tua applicazione.

Altri server WSGI

Non è consentito utilizzare gunicorn per eseguire Python in container. Puoi utilizzare qualsiasi server web WSGI o ASGI, purché il container rimanga in ascolto sulla porta HTTP $PORT, in base al contratto di runtime dei container.

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

Ad esempio, se un file denominato main.py contiene l'oggetto app, le seguenti chiamate avvieranno 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

Puoi aggiungerli come riga CMD exec in Dockerfile o come voce web: in Procfile quando utilizzi i buildpack di Google Cloud.

Ottimizza le applicazioni

Nel codice del servizio Cloud Run, puoi anche eseguire l'ottimizzazione per ridurre i tempi di avvio e l'utilizzo della memoria.

Ridurre i thread

Puoi ottimizzare la memoria riducendo il numero di thread, utilizzando strategie reattive non bloccanti ed evitando attività in background. Inoltre, evita di scrivere nel file system, come indicato nella pagina dei suggerimenti generali.

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 le attività in background al di fuori delle richieste e mantenere comunque l'accesso alla CPU.

Riduci le attività di avvio

Le applicazioni basate su Web Python possono avere molte attività da completare durante l'avvio, ad esempio il precaricamento dei dati, il riscaldamento della cache, la creazione di pool di connessioni e così via. Queste attività, se eseguite in sequenza, possono essere lente. Tuttavia, se vuoi che vengano eseguite in parallelo, devi aumentare il numero di core CPU.

Attualmente Cloud Run invia una richiesta di utente reale per attivare un'istanza di avvio completo. Gli utenti che hanno assegnato una richiesta a un'istanza appena avviata potrebbero riscontrare ritardi lunghi. Cloud Run attualmente non ha un controllo di "idoneità" per evitare di inviare richieste ad applicazioni non pronte.

Passaggi successivi

Per altri suggerimenti, consulta