本指南介绍了对使用 Python 编程语言编写的 Cloud Run 服务的优化以及有助于您了解某些优化所涉及的权衡的背景信息。此页面上的信息是对常规优化提示的补充,这些提示同样适用于 Python。
许多常见的 Python 基于 Web 的应用中的最佳实践和优化都围绕着以下内容:
- 处理并发请求(基于线程的 I/O 和非阻塞 I/O)
- 通过使用连接池和批处理非关键函数减少响应延迟时间,例如将跟踪记录和指标发送到后台任务。
优化容器映像
使用以下方法优化容器映像,以缩短加载时间和启动时间:
- 最大限度地减少启动时加载的文件
- 优化 WSGI 服务器
最大限度地减少启动时加载的文件
为了优化启动时间,请在启动时仅加载所需的文件,并减小这些文件的大小。对于大型文件,请考虑以下选项:
将大型文件(例如 AI 模型)存储在容器中,以便更快地访问。考虑在启动后或运行时加载这些文件。
考虑为启动时不重要的大型文件(例如媒体资产)配置 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
如果您考虑更改这些设置,请针对每个应用调整工作器和线程数。例如,尝试使用等于可用核心数量的工作线程,并确保性能有所提高,然后调整线程数。设置过多的工作器或线程会产生负面影响,例如较长的冷启动延迟时间、消耗更多的内存、较小的每秒请求数等。
默认情况下,gunicorn
会在启动时生成工作器并监听指定端口,甚至在评估应用代码之前也会这样做。在这种情况下,您应为服务设置自定义启动探测,因为 Cloud Run 默认启动探测会在开始监听 $PORT
后立即将容器实例标记为健康状况良好。
如果要更改此行为,可以使用 --preload
设置调用 gunicorn
,以在监听之前评估应用代码。这有助于:
- 在部署时识别严重的运行时错误
- 节省内存资源
在添加此项之前,您应该考虑应用预加载的内容。
其他 WSGI 服务器
您并非只能使用 gunicorn
在容器中运行 Python。您可以使用任何 WSGI 或 ASGI 网络服务器,只要容器根据容器运行时合同侦听 HTTP 端口 $PORT
即可。
常见的替代方案包括 uwsgi
、uvicorn
和 waitress
。
例如,如果名为 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 访问权限。
减少启动任务
Python 基于 Web 的应用在启动期间可能需要完成许多任务,例如预加载数据、预热缓存和建立连接池。如果依序执行,这些任务可能会很慢。但是,如果您希望它们并行执行,请增加 CPU 核心数。
Cloud Run 会发送一个实际用户请求以触发冷启动实例。 其请求分配到新启动实例的用户可能会遇到较长的延迟。
使用精简的基础映像提高安全性
为了提高应用的安全性,请使用包含较少软件包和库的精简版基础映像。
如果您选择不从容器中的源代码安装 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 或任何其他程序。
使用 PYTHONUNBUFFERED
环境变量进行日志记录
如需查看 Python 应用的非缓冲日志,请设置环境变量 PYTHONUNBUFFERED
。设置此变量后,stdout
和 stderr
数据会立即显示在容器日志中,而不是保留在缓冲区中,直到累积到一定量的数据或数据流关闭为止。
后续步骤
如需查看更多提示,请参阅以下内容