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 se incluyen en el contenedor y si son necesarios para la ejecución del servicio. Existen varias formas de minimizar la imagen del contenedor:
- Usa una imagen base más pequeña
- Cómo mover 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 incluyen una serie de paquetes que se usarían para compilar ruedas, por ejemplo, lo que es posible que 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 hay opciones para contenedores que no están 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 siendo variada. También puedes usar distroless base image
, que no contiene administradores de paquetes, shells ni ningún otro programa.
Cómo mover archivos grandes fuera del contenedor
No es necesario incluir archivos grandes, como recursos multimedia y otros, en el contenedor base.
Google Cloud ofrece varias opciones de hosting, como Cloud Storage, para almacenar estos elementos grandes. Mueve recursos grandes a estos servicios y, luego, haz referencia a ellos desde tu aplicación durante 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.
De forma predeterminada, gunicorn
genera trabajadores y escucha en el puerto especificado cuando se inicia, incluso antes de evaluar el código de la aplicación. En este caso, debes configurar sondeos de inicio personalizados para tu servicio, ya que el sondeo de inicio predeterminado de Cloud Run marca inmediatamente una instancia de contenedor como en buen estado en cuanto comienza a escuchar en $PORT
.
Si deseas cambiar este comportamiento, puedes invocar gunicorn
con la configuración --preload
para evaluar el código de tu aplicación antes de escuchar. Esto puede ser útil para lo siguiente:
- 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 al uso de 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 de entorno de ejecución de contenedores.
Entre las alternativas comunes, se 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: