El entorno de ejecución de Python

Descripción general

El entorno de ejecución de Python es la pila de software encargada de instalar el código de tu aplicación y sus dependencias, así como de ejecutar la aplicación. El entorno de ejecución se declara en app.yaml como runtime: python:

runtime: python
env: flex

Los entornos de ejecución en el entorno flexible se compilan con Docker. El entorno de ejecución de Python se basa en Ubuntu 16.04. El código fuente para el entorno de ejecución de Python está disponible de forma pública en GitHub.

Intérprete

El intérprete predeterminado es Python 2.7.9. Puedes especificar si deseas usar Python 2 o 3 en la app.yaml de la aplicación con la configuración runtime_config:

runtime: python
env: flex

runtime_config:
    python_version: 3

Las siguientes versiones de intérpretes son compatibles:

  • Python 2.7.9 especificado por 2.
  • Python 3.7.2 especificado por 3.

Dependencias

El entorno de ejecución busca un archivo requirements.txt en el directorio fuente de tu aplicación y usa pip para instalar cualquier dependencia antes de iniciar la aplicación. Para obtener más información acerca de cómo declarar y administrar los paquetes, consulta Cómo usar las bibliotecas de Python.

Usar bibliotecas C con Python

Para habilitar el uso de paquetes de Python que requieren extensiones C, los encabezados de la versión de Python actual y los siguientes paquetes de Ubuntu vienen preinstalados en el sistema:

Estos paquetes permiten la instalación de las bibliotecas más populares de Python. Si la aplicación requiere otras dependencias del nivel del sistema operativo, deberás usar un entorno de ejecución personalizado basado en este entorno de ejecución para instalar los paquetes adecuados.

Inicio de la aplicación

El entorno de ejecución inicia tu aplicación mediante el entrypoint definido en app.yaml. El punto de entrada debería iniciar un proceso que responda a solicitudes HTTP en el puerto definido por la variable de entorno PORT.

La mayoría de las aplicaciones web usan un servidor WSGI, como Gunicorn, uWSGI o Waitress.

Antes de poder usar uno de estos servidores, debes agregarlos como una dependencia en el requirements.txt de tu aplicación. El entorno de ejecución garantiza que todas las dependencias estén instaladas antes de que se llame al entrypoint.

Flask==0.10.1
gunicorn==19.3.0

Este es un ejemplo de un entrypoint que usa gunicorn para una aplicación Flask:

entrypoint: gunicorn -b :$PORT main:app

Este es un ejemplo de un entrypoint que usa gunicorn para una aplicación Django:

entrypoint: gunicorn -b :$PORT mydjangoapp:wsgi

Gunicorn es el servidor WSGI recomendado, pero es completamente posible utilizar cualquier otro servidor WSGI. Por ejemplo, el siguiente es un entrypoint que usa uWSGI con Flask:

entrypoint: uwsgi --http :$PORT --wsgi-file main.py --callable app

Para las aplicaciones que pueden controlar solicitudes sin un servidor WSGI, puedes simplemente ejecutar una secuencia de comandos de Python:

entrypoint: python main.py

Los ejemplos básicos de entrypoint antes mencionados pretenden ser puntos de partida y pueden funcionar para tus aplicaciones web. Sin embargo, la mayoría de las aplicaciones necesitarán configurar aún más el servidor WSGI. En lugar de especificar todas las configuraciones en el entrypoint, crea un archivo gunicorn.conf.py en el directorio raíz de tu proyecto, donde se encuentra tu app.yaml, y especifícalo en el entrypoint:

entrypoint: gunicorn -c gunicorn.conf.py -b :$PORT main:app

Puedes leer sobre todos los valores de configuración de Gunicorn en la documentación.

Trabajadores

Gunicorn utiliza trabajadores para controlar las solicitudes. De manera predeterminada, Gunicorn utiliza trabajadores de sincronización. Esta clase de trabajadores es compatible con todas las aplicaciones web, pero cada trabajador puede controlar solamente una solicitud a la vez. De manera predeterminada, gunicorn solo usa uno de estos trabajadores. A menudo, esto puede hacer que no se utilicen correctamente tus instancias y aumentar la latencia en aplicaciones con cargas importantes.

Te recomendamos configurar una cantidad de trabajadores equivalente a de 2 a 4 veces la cantidad de núcleos de CPU para tu instancia, más uno. Puedes especificar esto en gunicorn.conf.py de la siguiente manera:

import multiprocessing

workers = multiprocessing.cpu_count() * 2 + 1

Además, algunas aplicaciones web que están mayormente vinculadas a la E/S pueden registrar una mejora en el rendimiento si utilizan una clase diferente de trabajadores. Si tu clase de trabajador requiere dependencias adicionales, como gevent o tornado, esas dependencias deberán declararse en el requirements.txt de tu aplicación.

Proxies HTTPS y de reenvío

App Engine finaliza la conexión HTTPS en el balanceador de cargas y reenvía la solicitud a la aplicación. La mayoría de las aplicaciones no necesitan saber si la solicitud se envió a través de HTTPS o no, pero en aquellas que se necesita esta información, se debe configurar Gunicorn para que confíe en el proxy de App Engine en su gunicorn.conf.py:

forwarded_allow_ips = '*'
secure_scheme_headers = {'X-FORWARDED-PROTO': 'https'}

Gunicorn ahora garantizará que wsgi.url_scheme sea 'https', que la mayoría de los frameworks web usarán como un indicador de que la solicitud es segura. Si tu servidor o marco de trabajo WSGI no es compatible, verifica el valor del encabezado X-Forwarded-Proto de forma manual.

Algunas aplicaciones también necesitan verificar la dirección IP del usuario. Esta está disponible en el encabezado estándar X-Forwarded-For.

Ten en cuenta que la configuración secure_scheme_headers en gunicorn.conf.py debe estar en mayúscula, como X-FORWARDED-PROTO, pero los encabezados que tu código puede leer en mayúsculas y minúsculas, como X-Forwarded-Proto.

Extiende el tiempo de ejecución

El entorno de ejecución estándar de Python puede usarse para crear uno personalizado. Los entornos de ejecución personalizados se configuran a través de Dockerfile. Puedes generar un Dockerfile basado en el entorno de ejecución estándar de Python mediante gen-config:

gcloud beta app gen-config --custom

Luego, puedes personalizar el Dockerfile y el .dockerignore como desees. Por último, deberás especificar runtime: custom en lugar de runtime: python en app.yaml. Consulta Personalizar el entorno de ejecución de Python para obtener más información.

Variables del entorno

Las siguientes variables del entorno se configuran mediante el entorno de ejecución:

Variable del entorno Descripción
GAE_INSTANCE El nombre de la instancia actual.
GAE_MEMORY_MB La cantidad de memoria disponible para el proceso de la aplicación.
GAE_SERVICE El nombre del servicio especificado en el archivo app.yaml de la aplicación; si no se especifica ningún nombre, se configura como default.
GAE_VERSION La etiqueta de versión de la aplicación actual.
GOOGLE_CLOUD_PROJECT El ID del proyecto asociado con la aplicación, que se puede ver en Google Cloud Console.
PORT El puerto que recibirá las solicitudes HTTP.

Puedes definir variables de entorno adicionales con app.yaml.

Servidor de metadatos

Cada instancia de la aplicación puede usar el servidor de metadatos de Compute Engine para consultar información acerca de la instancia, incluidos el nombre del host, la dirección IP externa, el ID de la instancia, los metadatos personalizados y la información de la cuenta de servicio. App Engine no permite establecer metadatos personalizados para cada instancia; sin embargo, puedes establecer metadatos personalizados de todo el proyecto y leerlos desde las instancias de App Engine y Compute Engine.

Esta función de ejemplo usa el servidor de metadatos para obtener la dirección IP externa de la instancia:

METADATA_NETWORK_INTERFACE_URL = \
    ('http://metadata/computeMetadata/v1/instance/network-interfaces/0/'
     'access-configs/0/external-ip')

def get_external_ip():
    """Gets the instance's external IP address from the Compute Engine metadata
    server. If the metadata server is unavailable, it assumes that the
    application is running locally.
    """
    try:
        r = requests.get(
            METADATA_NETWORK_INTERFACE_URL,
            headers={'Metadata-Flavor': 'Google'},
            timeout=2)
        return r.text
    except requests.RequestException:
        logging.info('Metadata server could not be reached, assuming local.')
        return 'localhost'