Ottimizza le applicazioni Python per Cloud Run

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

Questa guida descrive le ottimizzazioni per i servizi Cloud Run scritte nel linguaggio di programmazione Python e le informazioni di base per aiutarti a comprendere i compromessi previsti per alcune ottimizzazioni. Le informazioni contenute in questa pagina integrano i suggerimenti generali per l'ottimizzazione, applicabili anche a Python.

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

  • Gestione delle richieste in parallelo (per I/O basati su thread e non di blocco)
  • Ridurre la latenza delle risposte utilizzando il pool di connessioni e le operazioni in 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:

  • Inserire nel container solo ciò di cui la tua app ha bisogno per il runtime
  • Ottimizzare il 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 necessari per l'esecuzione del servizio. Esistono diversi modi per ridurre al minimo l'immagine 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. Si basano sul sistema operativo Debian.

Se utilizzi l'immagine python di Docker Hub, potresti utilizzare la versione slim. Queste immagini hanno dimensioni più piccole perché non includono una serie di pacchetti che verrebbero utilizzati per creare le ruote, ad esempio per la tua 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 il comando seguente:

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 superficie di attacco inferiore per potenziali vulnerabilità. Tieni presente che 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.

Ci sono anche opzioni per i container non Debian. L'opzione python:alpine può comportare un container molto più piccolo, ma molti pacchetti Python potrebbero non avere ruote precompilate che supportano sistemi basati sulle Alpi. Il supporto è in fase di miglioramento (vedi PEP-656), ma continua a variare. Puoi anche utilizzare la 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 asset multimediali e così via, 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. Sposta asset di grandi dimensioni in questi servizi, quindi fai riferimento a essi dall'applicazione in fase di esecuzione.

Ottimizzare il server WSGI

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

Ottimizza gunicor

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, modifica il numero di worker e di thread in base all'applicazione. Ad esempio, prova a utilizzare un numero di worker uguale ai core disponibili, 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 completo 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 in fase di deployment
  • Risparmia risorse di memoria

Prima di aggiungere questo tipo di elementi, devi valutare cosa viene caricato dalla tua applicazione.

Altri server WSGI

Non è consentito utilizzare gunicorn per eseguire Python nei 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 del container.

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

Ad esempio, per un dato file main.py contenente 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

Possono essere aggiunti come riga CMD exec in una Dockerfile o come voce web: in Procfile quando si utilizzano Google Cloud Buildpacks.

Ottimizzare le applicazioni

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

Ridurre i thread

Puoi ottimizzare la memoria riducendo il numero di thread, utilizzando strategie reattive non bloccanti ed evitando le attività in background. Inoltre, evita di scrivere nel file system, come indicato nella pagina di 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 attività in background al di fuori delle richieste e mantenere sempre l'accesso alla CPU.

Ridurre le attività di avvio

Le applicazioni basate su Web Python possono essere 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 eseguirli in parallelo, devi aumentare il numero di core CPU.

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

Passaggi successivi

Per ulteriori suggerimenti, consulta