Optimiza las aplicaciones de Python para Cloud Run

En esta guía, se describen las optimizaciones para los servicios de Cloud Run escritos en el lenguaje de programación Python, junto con información general para ayudarte a comprender las ventajas y desventajas de algunas de las optimizaciones. La información de esta página complementa las sugerencias de optimización generales que también se aplican a Python.

Muchas de las prácticas recomendadas y optimizaciones de esta aplicación tradicional de Python basada en la Web giran en torno a lo siguiente:

  • Cómo manejar solicitudes simultáneas (de E/S basadas en subprocesos y sin bloqueo)
  • Cómo reducir la latencia de respuesta mediante la agrupación de conexiones y el procesamiento por lotes de funciones no críticas, por ejemplo, el envío de seguimientos y métricas a tareas en segundo plano.

Optimiza la imagen del contenedor

Al optimizar la imagen del contenedor, puedes reducir los tiempos de carga y de inicio. Puedes optimizar la imagen mediante los siguientes métodos:

  • Solo pon en tu contenedor lo que tu app necesita en el entorno de ejecución
  • Optimiza el servidor WSGI

Solo pon en tu contenedor lo que tu app necesita en el entorno de ejecución

Considera qué componentes están incluidos en el contenedor y si son necesarios para la ejecución del servicio. Existen varias formas de minimizar la imagen de contenedor:

  • Usa una imagen base más pequeña
  • Mueve archivos grandes fuera del contenedor

Usa una imagen base más pequeña

Docker Hub proporciona una serie de imágenes base oficiales de Python que puedes usar, si eliges no instalar Python desde la fuente dentro de los contenedores. Estas se basan en el sistema operativo Debian.

Si usas la imagen python de Docker Hub, considera usar la versión slim. Estas imágenes son más pequeñas porque no vienen con una serie de paquetes que se usarían para construir ruedas, por ejemplo, que quizás no necesites hacer para tu aplicación. Por ejemplo, la imagen de Python viene con el compilador de C de GNU, el preprocesador y las utilidades del núcleo.

Para identificar los diez paquetes más grandes en una imagen base, puedes ejecutar el siguiente 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'

Debido a que hay menos de estos paquetes de bajo nivel, las imágenes basadas en slim también ofrecen menos superficie de ataque para vulnerabilidades potenciales. Ten en cuenta que es posible que estas imágenes no incluyan los elementos necesarios para construir ruedas desde la fuente.

Puedes volver a agregar paquetes específicos si agregas una línea RUN apt install a tu Dockerfile. Obtén más información sobre el uso de Paquetes de sistema en Cloud Run.

También existen opciones para contenedores no basados en Debian. La opción python:alpine puede dar como resultado un contenedor mucho más pequeño, pero es posible que muchos paquetes de Python no tengan ruedas precompiladas que admitan sistemas basados en Alpine. La compatibilidad está mejorando (consulta PEP-656), pero sigue cambiando. También puedes considerar usar distroless base image, que no contiene administradores de paquetes, shell o cualquier otro programa.

Mueve archivos grandes fuera del contenedor

No es necesario incluir archivos grandes, como elementos multimedia, etc. en el contenedor base.

Google Cloud ofrece varias opciones de hosting, como Cloud Storage, para almacenar estos elementos grandes. Traslada los recursos grandes a estos servicios y, luego, haz referencia a ellos desde tu aplicación en el tiempo de ejecución.

Optimiza el servidor WSGI

Python estandarizó la forma en que las aplicaciones pueden interactuar con los servidores web mediante la implementación del estándar WSGI, PEP-3333. Uno de los servidores WSGI más comunes es gunicorn, que se usa en gran parte de la documentación de muestra.

Optimiza gunicorn

Agrega el siguiente CMD a Dockerfile para optimizar la invocación de gunicorn:

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

Si consideras cambiar esta configuración, ajusta la cantidad de trabajadores y subprocesos por aplicación. Por ejemplo, intenta usar una cantidad de trabajadores equivalente a los núcleos disponibles y asegúrate de que haya una mejora en el rendimiento y, luego, ajusta la cantidad de subprocesos. Configurar demasiados trabajadores o subprocesos puede tener un impacto negativo, como una latencia de inicio en frío más larga, una memoria más consumida, solicitudes más pequeñas por segundo, etcétera.

Agregar la configuración --preload puede ayudar a hacer a:

  • Identificar errores graves del entorno de ejecución en el momento de la implementación
  • Ahorrar recursos de memoria

Debes considerar lo que tu aplicación está precargando antes de agregar esto.

Otros servidores WSGI

No estás restringido a usar gunicorn para ejecutar Python en contenedores. Puedes usar cualquier servidor web WSGI o ASGI, siempre que el contenedor escuche en el puerto HTTP $PORT, según el contrato del entorno de ejecución de contenedores.

Las alternativas comunes incluyen uwsgi, uvicorn y waitress.

Por ejemplo, con un archivo llamado main.py que contiene el objeto app, las siguientes invocaciones iniciarían un servidor 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

Se pueden agregar como una línea CMD exec en un Dockerfile o como una entrada web: en Procfile cuando se usan los paquetes de compilación de Google Cloud.

Optimiza aplicaciones

En el código de servicio de Cloud Run, también puedes optimizar el tiempo de inicio y el uso de memoria más rápidos.

Reduce subprocesos

Para optimizar la memoria, puedes reducir la cantidad de subprocesos mediante el uso de estrategias de reactivación sin bloqueo y al evitar actividades en segundo plano. También evita escribir en el sistema de archivos, como se menciona en la página de sugerencias generales.

Si deseas admitir actividades en segundo plano en tu servicio de Cloud Run, configura tu CPU de servicio de Cloud Run para que siempre se asigne a fin de que puedas ejecutar actividades en segundo plano fuera de las solicitudes. tienen acceso a la CPU.

Reduce las tareas de inicio

Las aplicaciones tradicionales basadas en la Web de Python pueden tener muchas tareas para completar durante el inicio, p. ej., la precarga de datos, la preparación de la caché, el establecimiento de grupos de conexión, etc. Cuando estas tareas se ejecutan de forma secuencial, pueden ser lentas. Sin embargo, si deseas que se ejecuten en paralelo, debes aumentar la cantidad de núcleos de CPU.

Por el momento, Cloud Run envía una solicitud de usuario real para activar una instancia de inicio en frío. Los usuarios que tienen una solicitud asignada a una instancia recién iniciada pueden experimentar demoras prolongadas. En la actualidad, Cloud Run no tiene una verificación de “preparación” para evitar enviar solicitudes a aplicaciones que no están listas.

¿Qué sigue?

Para obtener más sugerencias, consulta estos artículos: