Optimiser les applications Python pour Cloud Run

Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

Ce guide décrit des optimisations pour les services Cloud Run écrits dans le langage de programmation Python. Il présente également des informations générales pour vous aider à comprendre les compromis requis par certaines des optimisations. Les informations contenues sur cette page viennent en complément des conseils généraux d'optimisation, qui s'appliquent également à Python.

Un grand nombre de bonnes pratiques et d'optimisations de ces applications Web Python traditionnelles reposent sur les aspects suivants :

  • La gestion des requêtes simultanées (E/S non bloquantes et basées sur un thread)
  • La réduction de la latence de réponse à l'aide de fonctions non critiques de pooling des connexions et de traitement par lots, par exemple l'envoi de traces et de métriques à des tâches en arrière-plan

Optimiser l'image de conteneur

Optimiser l'image du conteneur peut vous aider à réduire les temps de chargement et de démarrage. Différentes actions vous permettent d'optimiser l'image :

  • Ne placer dans votre conteneur que ce dont votre application a besoin au moment de l'exécution
  • Optimiser le serveur WSGI

Ne placez dans votre conteneur que ce dont l'application a besoin au moment de l'exécution

Déterminez les composants inclus dans le conteneur et s'ils sont nécessaires à l'exécution du service. Il existe plusieurs façons de réduire l'image de conteneur :

  • Utiliser une image de base plus petite
  • Déplacer les fichiers volumineux en dehors du conteneur

Utiliser une image de base plus petite

Docker Hub fournit un certain nombre d'images de base officielles Python que vous pouvez utiliser si vous choisissez de ne pas installer Python à partir de la source dans vos conteneurs. Elles sont basées sur le système d'exploitation Debian.

Si vous utilisez l'image python de Docker Hub, envisagez d'utiliser la version slim. Ces images sont plus petites, car elles ne sont pas fournies avec un certain nombre de packages qui serviraient à construire des roues, par exemple, dont vous n'aurez peut-être pas besoin pour votre application. Par exemple, l'image Python est fournie avec le compilateur, le préprocesseur et les utilitaires de base GNU C.

Pour identifier les dix packages les plus volumineux d'une image de base, vous pouvez exécuter la commande suivante :

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'

Comme ces packages de bas niveau sont moins nombreux, les images basées sur slim offrent également moins de surface d'attaque en cas de failles. Notez que ces images peuvent ne pas inclure les éléments requis pour créer des roues à partir de la source.

Vous pouvez ajouter des packages spécifiques en ajoutant une ligne RUN apt install à votre fichier Dockerfile. Découvrez comment utiliser les packages système dans Cloud Run.

Il existe également des options pour les conteneurs non basés sur Debian. L'option python:alpine peut entraîner un conteneur beaucoup plus petit, mais de nombreux packages Python peuvent ne pas comporter de roues précompilées compatibles avec les systèmes basés sur Alpine. Le service d'assistance est en cours d'amélioration (voir PEP-656), mais il varie toujours. Vous pouvez également envisager d'utiliser le fichier distroless base image, qui ne contient aucun gestionnaire de packages, interface système ni aucun autre programme.

Déplacer les fichiers volumineux en dehors du conteneur

Les fichiers volumineux, tels que les éléments multimédias, n'ont pas besoin d'être inclus dans le conteneur de base.

Google Cloud propose plusieurs options d'hébergement, telles que Cloud Storage, pour stocker ces éléments volumineux. Déplacez les éléments volumineux vers ces services, puis référencez-les à partir de votre application au moment de l'exécution.

Optimiser le serveur WSGI

Python a standardisé la manière dont les applications peuvent interagir avec les serveurs Web via la mise en œuvre de la norme WSGI, PEP-3333. L'un des serveurs WSGI les plus courants est gunicorn, qui est utilisé dans une grande partie de l'exemple de documentation.

Optimiser Gunicorn

La partie CMD de Dockerfile illustre un appel optimisé de 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

Si vous envisagez de modifier ces paramètres, ajustez le nombre de nœuds de calcul et de threads pour chaque application. Par exemple, essayez d'utiliser un nombre de nœuds de calcul égal aux cœurs disponibles et assurez-vous qu'il y a une amélioration des performances, puis ajustez le nombre de threads. Définir un trop grand nombre de nœuds de calcul ou de threads peut avoir un impact négatif, comme une latence de démarrage à froid plus longue, une plus grande consommation de mémoire, des requêtes plus petites par seconde, etc.

Dans certains cas, l'ajout du paramètre --preload permet d'économiser des ressources mémoire, mais tenez compte du contenu de votre application avant de l'ajouter.

Autres serveurs WSGI

Vous n'êtes pas obligé d'utiliser gunicorn pour exécuter Python dans des conteneurs. Vous pouvez utiliser n'importe quel serveur Web WSGI ou ASGI, à condition que le conteneur écoute sur le port HTTP $PORT, conformément au contrat d'exécution du conteneur.

Les alternatives courantes incluent uwsgi, uvicorn et waitress.

Par exemple, avec un fichier nommé main.py contenant l'objet app, les appels suivants démarrent un serveur 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

Ces fichiers peuvent être ajoutés en tant que ligne CMD exec dans un fichier Dockerfile ou en tant qu'entrée web: dans Procfile lorsque vous utilisez des packs de création Google Cloud.

Optimiser les applications

Dans votre code de service Cloud Run, vous pouvez également optimiser le délai de démarrage et l'utilisation de la mémoire.

Réduire les threads

Vous pouvez optimiser la mémoire en réduisant le nombre de threads, en faisant appel à des stratégies réactives non bloquantes et en évitant les activités d'arrière-plan. Vous devez également éviter d'écrire dans le système de fichiers, comme indiqué dans la page de conseils généraux.

Si vous souhaitez prendre en charge les activités d'arrière-plan dans votre service Cloud Run, configurez le processeur de votre service Cloud Run pour qu'il soit toujours alloué afin de pouvoir exécuter des activités d'arrière-plan en dehors des requêtes tout en conservant l'accès au processeur.

Limiter les tâches de démarrage

Les applications Web Python peuvent avoir de nombreuses tâches à exécuter au démarrage (préchargement des données, préchauffage du cache, établissement de pools de connexions, etc.). Lorsqu'elles sont exécutées de manière séquentielle, ces tâches peuvent se révéler lentes. Toutefois, si vous souhaitez qu'elles s'exécutent en parallèle, vous devez augmenter le nombre de cœurs de processeur.

Actuellement, Cloud Run envoie une requête utilisateur réelle pour déclencher le démarrage à froid d'une instance. Les utilisateurs dont la requête est attribuée à une instance tout juste démarrée peuvent être confrontés à des délais importants. À l'heure actuelle, Cloud Run ne dispose pas d'une "vérification d'aptitude" qui permettrait d'éviter l'envoi de requêtes à des applications qui ne sont pas prêtes.

Étape suivante

Pour accéder à d'autres conseils, consultez les pages suivantes :