针对 Cloud Run 优化 Python 应用

本指南介绍了对使用 Python 编程语言编写的 Cloud Run 服务的优化以及有助于您了解某些优化所涉及的权衡的背景信息。此页面上的信息是对常规优化提示的补充,这些提示同样适用于 Python。

这些传统的 Python Web 应用中的许多最佳实践和优化都围绕着以下内容:

  • 处理并发请求(基于线程的 I/O 和非阻塞 I/O)
  • 通过使用连接池和批处理非关键函数减少响应延迟时间,例如将跟踪记录和指标发送到后台任务。

优化容器映像

通过优化容器映像,您可以缩短加载时间和启动时间。您可以通过以下方式优化映像:

  • 仅将您的应用在运行时需要的内容放入容器中
  • 优化 WSGI 服务器

仅将您的应用在运行时需要的内容放入容器中

考虑容器中包含哪些组件,以及执行服务是否需要这些组件。您可以通过多种方式最大限度地减小容器映像:

  • 使用较小的基础映像
  • 将大文件移出容器

使用较小的基础映像

如果您选择不从容器中的源安装 Python,Docker Hub 提供了许多可供您使用官方 Python 基础映像。这些基于 Debian 操作系统。

如果您使用的是 Docker Hub 的 python 映像,请考虑使用 slim 版本。这些映像比较小,因为它们不包含许多用于构建轮子的软件包,例如,您可能不需要为应用构建轮子。例如,Python 映像附带 GNU C 编译器、预处理器和核心实用程序。

如需确定基础映像中的十个最大的软件包,您可以运行以下命令:

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'

由于这些低级别软件包较少,因此基于 slim 的映像也会为潜在漏洞提供较少的攻击面。请注意,这些映像可能不包含从源代码构建轮子所需的元素。

您可以通过向 Dockerfile 添加 RUN apt install 行来重新添加特定的软件包。详细了解如何在 Cloud Run 中使用系统软件包

此外,还提供了基于非 Debian 的容器的选项。python:alpine 选项可能会导致容器小得多,但许多 Python 软件包可能没有支持基于 Alpine 系统的预编译轮子。支持服务正在改进(请参阅 PEP-656),但会继续发生变化。您还可以考虑使用 distroless base image,它不包含任何软件包管理器、shell 或任何其他程序。

将大文件移出容器

媒体资产等大型文件不需要包含在基本容器中。

Google Cloud 提供了多种托管方案(例如 Cloud Storage)来存储这些大型项。将大型资产移动到这些服务,然后在运行时从应用中引用它们。

优化 WSGI 服务器

Python 通过实现 WSGI 标准 PEP-3333 来实现应用与网络服务器交互的方式标准化。一种较常见的 WSGI 服务器是 gunicorn,示例文档的多个部分都使用了该服务器。

优化 gunicorn

将以下 CMD 添加到 Dockerfile 以优化 gunicorn 的调用:

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

如果您考虑更改这些设置,请针对每个应用调整工作器和线程数。例如,尝试使用等于可用核心数量的工作线程,并确保性能有所提高,然后调整线程数。设置过多的工作器或线程会产生负面影响,例如较长的冷启动延迟时间、消耗更多的内存、较小的每秒请求数等。

添加 --preload 设置有助于:

  • 在部署时识别严重的运行时错误
  • 节省内存资源

在添加此项之前,您应该考虑应用预加载的内容。

其他 WSGI 服务器

您并非只能使用 gunicorn 在容器中运行 Python。您可以使用任何 WSGI 或 ASGI 网络服务器,只要容器根据容器运行时合同侦听 HTTP 端口 $PORT 即可。

常见的替代方案包括 uwsgiuvicornwaitress

例如,如果名为 main.py 的文件包含 app 对象,则以下调用将启动 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

这些调用可以在 Dockerfile 中作为 CMD exec 行添加,或者在使用 Google Cloud 的 buildpack 时作为 Procfile 中的 web: 条目添加。

优化应用

在 Cloud Run 服务代码中,您也可以进行优化以减少启动时间和内存用量。

减少线程

您可以通过使用非阻塞反应式策略和避免后台活动来减少线程数量,从而优化内存。 此外,还要避免写入文件系统,如常规提示页面中所述。

如果您希望在 Cloud Run 服务中支持后台活动,请将 Cloud Run 服务 CPU 设置为始终分配,以便您可以在请求之外运行后台活动,并且仍拥有 CPU 访问权限。

减少启动任务

Python Web 应用会在启动期间完成许多任务,例如预加载数据、预热缓存、建立连接池等。依次执行这些任务会很慢。但是,如果您希望它们并行执行,则应增加 CPU 核心数。

Cloud Run 目前会发送一个实际用户请求以触发冷启动实例。其请求被分配到新启动实例的用户可能会遇到较长的延迟。Cloud Run 目前没有“就绪”检查来避免向未就绪的应用发送请求。

后续步骤

如需查看更多提示,请参阅以下内容