Python 运行时

概览

Python 运行时是负责安装应用代码及其依赖项并运行应用的软件堆栈。标准运行时在 app.yaml 中声明为 runtime: python

runtime: python
env: flex

柔性环境中的运行时使用 Docker 构建。 Python 运行时基于 Ubuntu 18.04,根据每个构建脚本而不同。GitHub 上公开提供了此脚本,以及所有 Python 运行时构建的源代码。

解析器

您可以使用 runtime_config 指令和 python_version 设置,在应用的 app.yaml 文件中指定要使用 Python 2 还是 Python 3:

runtime: python
env: flex

runtime_config:
    python_version: 3

如果省略 runtime_configpython_version,则默认解释器为 Python 2.7.12。为每个 python_version 设置部署的解释器如下表所示:

python_version 设置 部署的解释器
2(默认) 2.7.12
3.4 3.4.8
3.5 3.5.9
33.6 3.6.10
3.7 3.7.9

python_version 唯一支持的值包括 233.43.53.63.7,这些值在构建脚本中公开指定。如果上面未列出所需的 Python 版本,可通过以下几种方式将其列出:

  1. App Engine 柔性环境:创建自定义运行时,并选择包含您所需的 Python 版本的有效基础映像。
  2. App Engine 标准环境:支持 Python 3.7、3.8 和 3.9。
  3. Cloud Functions:支持 Python 3.7、3.8 和 3.9。
  4. Cloud Run:根据所需的 Python 版本的容器映像将应用容器化(请参阅其 Python 快速入门)。由于 Python 3.10 映像已经发布,因此您现在可以部署该版本。

对于 App Engine 柔性环境或 Cloud Run,请参阅 Google Cloud 提供的基础映像的构建自定义运行时,或当前可用的 Python 映像(包括 Python 2 映像信息)的Docker Python 基础映像

如需进一步了解适用于 Cloud Run 的 App Engine 应用容器化,请参阅介绍使用 Docker不使用 Docker 进行容器化的相关 Codelab 和视频内容。请注意,此内容目前仅涵盖 App Engine 标准环境到 Cloud Run 的迁移。

依赖项

在启动应用之前,运行时会在应用的源目录中查找 requirements.txt 文件,并使用 pip 安装所有依赖项。如需详细了解如何声明和管理软件包,请参阅使用 Python 库

搭配 Python 使用 C 库

为了支持使用需要 C 扩展程序的 Python 软件包,系统中已预先安装当前 Python 版本的头文件和以下 Ubuntu 软件包:

这些软件包支持安装最热门的 Python 扩展程序。如果您的应用需要其他操作系统级依赖项,您将需要使用基于此运行时的自定义运行时来安装相应的软件包。

应用启动

运行时使用 app.yaml 文件中定义的 entrypoint 来启动应用。entrypoint 应启动一个进程,以响应环境变量 PORT 所定义端口上的 HTTP 请求。

大多数 Web 应用都使用 WSGI 服务器,例如 GunicornuWSGIWaitress

如需使用其中一个服务器,您必须将其作为依赖项添加到应用的 requirements.txt 文件中。运行时会确保所有依赖项均在 entrypoint 调用之前安装完毕。

Flask==2.0.2
gunicorn==20.1.0

在 Flask 应用中使用 gunicorn 时对应的 entrypoint 示例:

entrypoint: gunicorn -b :$PORT main:app

在 Django 应用中使用 gunicorn 时对应的 entrypoint 示例:

entrypoint: gunicorn -b :$PORT mydjangoapp:wsgi

建议您使用 Gunicorn 作为 WSGI 服务器,但您完全可以使用任何其他 WSGI 服务器。下面提供了一个将 uWSGI 与 Flask 搭配使用的 entrypoint 示例:

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

对于可以在没有 WSGI 服务器的情况下处理请求的应用,您只需执行 python 脚本:

entrypoint: python main.py

上面所示的基本 entrypoint 示例只是个学习起点,可能适用于您的 Web 应用,但是,大多数应用将需要进一步配置 WSGI 服务器。在项目的 app.yaml 所在的根目录中创建 gunicorn.conf.py文件(而不是在入口点指定所有设置),并在入口点中指定它:

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

如需了解 Gunicorn 的所有配置值,您可以阅读其文档

工作器

Gunicorn 使用工作器来处理请求。默认情况下,Gunicorn 使用同步工作器。此类工作器与所有 Web 应用都兼容,但每个工作器一次只能处理一个请求。默认情况下,gunicorn 只使用其中一个工作器。这通常会导致您的实例得不到充分利用,并且可能会增加高负载应用的延迟时间。

我们建议您将工作器数量设置为实例的 CPU 核心数的 2 至 4 倍再加一。您可以在 gunicorn.conf.py 中将此字段指定为:

import multiprocessing

workers = multiprocessing.cpu_count() * 2 + 1

另外,一些主要受 I/O 约束的 Web 应用可以通过使用不同的工作器类别来实现性能的提升。 如果您的工作器类需要其他依赖项(例如 gevent 或 tornado),则需要在应用的 requirements.txt 中声明这些依赖项。

HTTPS 和转发代理

App Engine 在负载平衡器上终止 HTTPS 连接,并将请求转发给应用。大多数应用不需要知道请求是否通过 HTTPS 发送,但需要此信息的应用应将 Gunicorn 配置为信任其 gunicorn.conf.py 中的 App Engine 代理:

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

Gunicorn 现在将确保 wsgi.url_scheme'https'(大多数网络框架将使用该标志来指示请求是安全的)。如果您的 WSGI 服务器或框架不支持此功能,只需手动检查X-Forwarded-Proto标头的值即可。

某些应用还需要确定用户的 IP 地址。该地址可在 X-Forwarded-For 标头中获取。

请注意,gunicorn.conf.py 中的 secure_scheme_headers 设置应为大写(如X-FORWARDED-PROTO),但您的代码可以读取的标头将为混合大小写(如 X-Forwarded-Proto)。

扩展运行时

柔性环境 Python 运行时可用于创建自定义运行时。如需了解详情,请参阅自定义 Python

环境变量

以下环境变量由运行时环境设置:

环境变量 说明
GAE_INSTANCE 当前实例的名称。
GAE_MEMORY_MB 可供应用进程使用的内存量。
GAE_SERVICE 在应用的 app.yaml 文件中指定的服务名称,如果未指定服务名称,则设置为 default
GAE_VERSION 当前应用的版本标签。
GOOGLE_CLOUD_PROJECT 与您的应用关联的项目 ID,可在 Google Cloud Console 中查看
PORT 将接收 HTTP 请求的端口。

您可以使用 app.yaml 设置其他环境变量。

元数据服务器

应用的每个实例都可以使用 Compute Engine 元数据服务器来查询实例的相关信息,包括实例的主机名、外部 IP 地址、实例 ID、自定义元数据和服务帐号信息。App Engine 不支持设置每个实例的自定义元数据,但您可以设置项目级的自定义元数据,并从 App Engine 和 Compute Engine 实例中读取这些数据。

此示例函数使用元数据服务器来获取实例的外部 IP 地址:

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'