本指南介绍了对使用 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
如果您考虑更改这些设置,请针对每个应用调整工作器和线程数。例如,尝试使用等于可用核心数量的工作线程,并确保性能有所提高,然后调整线程数。设置过多的工作器或线程会产生负面影响,例如较长的冷启动延迟时间、消耗更多的内存、较小的每秒请求数等。
默认情况下,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 设置为始终分配,以便您可以在请求之外运行后台活动,并且仍拥有 CPU 访问权限。
减少启动任务
Python Web 应用会在启动期间完成许多任务,例如预加载数据、预热缓存、建立连接池等。依次执行这些任务会很慢。但是,如果您希望它们并行执行,则应增加 CPU 核心数。
Cloud Run 目前会发送一个实际用户请求以触发冷启动实例。其请求被分配到新启动实例的用户可能会遇到较长的延迟。Cloud Run 目前没有“就绪”检查来避免向未就绪的应用发送请求。
后续步骤
如需查看更多提示,请参阅以下内容