剖析 Python 应用的性能

本页面介绍如何修改 Python 应用以捕获性能剖析数据,并将该数据发送到您的 Google Cloud 项目。如需了解性能剖析的常规信息,请参阅性能剖析相关概念

Python 的性能剖析文件类型:

  • CPU 时间
  • 实际用时(主线程)

支持的 Python 语言版本:

  • Python 3.6 或更高版本。

支持的性能剖析代理版本:

  • 支持代理的最新版本。通常,不支持发布超过一年的版本。我们建议您使用最近发布的代理版本。

支持的操作系统:

  • Linux。Linux 内核支持剖析 Python 应用的性能,其标准 C 库是使用 glibcmusl 实现的。如需了解针对 Linux Alpine 内核的特定配置信息,请参阅在 Linux Alpine 上运行

支持的环境:

启用 Profiler API

在使用性能剖析代理之前,请确保已启用底层 Profiler API。您可以使用 Google Cloud CLI 或 Google Cloud 控制台查看该 API 的状态以及在必要时启用该 API:

gcloud CLI

  1. 如果您尚未在工作站上安装 Google Cloud CLI,请参阅 Google Cloud CLI 文档

  2. 运行以下命令:

    gcloud services enable cloudprofiler.googleapis.com
    

如需了解详情,请参阅 gcloud services

Google Cloud 控制台

  1. 在 Google Cloud 控制台的导航面板中,选择 API 和服务,点击启用 API 和服务,然后启用 Cloud Profiler API:

    前往 Profiler API 设置

  2. 如果系统显示 API 已启用,则表示此 API 已经启用。如未显示,请点击启用按钮。

使用 Cloud Profiler

如需了解使用 Python 的最佳做法,请转至设置 Python 开发环境

Compute Engine

对于 Compute Engine,请执行以下操作:

  1. 安装 C/C++ 编译器和开发工具:

    sudo apt-get install -y build-essential
    
  2. 安装 pip:

    sudo apt-get install -y python3-pip
    
  3. 安装 Profiler 软件包:

    pip3 install google-cloud-profiler
    
  4. 导入 googlecloudprofiler 模块,并尽早在初始化代码中调用 googlecloudprofiler.start 函数:

    import googlecloudprofiler
    
    def main():
        # Profiler initialization. It starts a daemon thread which continuously
        # collects and uploads profiles. Best done as early as possible.
        try:
            googlecloudprofiler.start(
                service="hello-profiler",
                service_version="1.0.1",
                # verbose is the logging level. 0-error, 1-warning, 2-info,
                # 3-debug. It defaults to 0 (error) if not set.
                verbose=3,
                # project_id must be set if not running on GCP.
                # project_id='my-project-id',
            )
        except (ValueError, NotImplementedError) as exc:
            print(exc)  # Handle errors here

    您必须在 start 函数中指定 service 参数。如需在 Profiler 界面中按应用版本进行过滤,请指定 service_version 参数。如需了解问题排查和异常信息,请参阅问题排查

GKE

对于 GKE,请执行以下操作:

  1. 修改 Dockerfile 以安装 Profiler 软件包:

    FROM python:3
    ...
    RUN apt-get update && apt-get install -y build-essential python3-pip
    RUN pip3 install google-cloud-profiler
    
  2. 导入 googlecloudprofiler 模块,并尽早在初始化代码中调用 googlecloudprofiler.start 函数:

    import googlecloudprofiler
    
    def main():
        # Profiler initialization. It starts a daemon thread which continuously
        # collects and uploads profiles. Best done as early as possible.
        try:
            googlecloudprofiler.start(
                service="hello-profiler",
                service_version="1.0.1",
                # verbose is the logging level. 0-error, 1-warning, 2-info,
                # 3-debug. It defaults to 0 (error) if not set.
                verbose=3,
                # project_id must be set if not running on GCP.
                # project_id='my-project-id',
            )
        except (ValueError, NotImplementedError) as exc:
            print(exc)  # Handle errors here

    您必须在 start 函数中指定 service 参数。如需在 Profiler 界面中按应用版本进行过滤,请指定 service_version 参数。如需了解问题排查和异常信息,请参阅问题排查

柔性环境

对于 App Engine 柔性环境,请执行以下操作:

  1. google-cloud-profiler 添加到 requirements.txt 文件中。

  2. 导入 googlecloudprofiler 模块,并尽早在初始化代码中调用 googlecloudprofiler.start 函数。

对于 App Engine,serviceservice_version 由您的操作环境生成。如需了解问题排查和异常信息,请参阅问题排查

标准环境

对于 App Engine 标准环境(需要使用 Python 3 运行时环境),请执行以下操作:

  1. google-cloud-profiler 添加到 requirements.txt 文件中。

  2. 导入 googlecloudprofiler 模块,并尽早在初始化代码中调用 googlecloudprofiler.start 函数。

对于 App Engine,serviceservice_version 由您的操作环境生成。如需了解问题排查和异常信息,请参阅问题排查

start 函数

googlecloudprofiler.start 函数会创建一个守护进程线程,用于持续收集和上传性能剖析文件。您应在应用中尽早调用一次 start

参数 说明
service1 (必需)待执行性能剖析的服务的名称。如需了解服务名称的限制,请参阅服务名称和版本参数
service_version1 (可选)待执行性能剖析的服务的版本。如需了解服务版本的限制,请参阅服务名称和版本参数
verbose (可选)日志记录级别。如需详细了解日志记录级别,请参阅代理日志记录

默认值为 0 (Error)
project_id2 (可选)您的 Google Cloud 项目 ID。
disable_cpu_profiling (可选)如需停用 CPU 时间性能剖析,请设置 disable_cpu_profiling=True

只有 Python 3.2 及更高版本支持此参数。对于其他所有 Python 版本,CPU 时间性能剖析均不受支持,此参数将被忽略。

默认值为 False
disable_wall_profiling (可选)如需停用实际用时性能剖析,请设置 disable_wall_profiling=True

Python 3.6 及更高版本支持此参数。对于其他所有 Python 版本,实际用时性能剖析不受支持,此参数将被忽略。

如需了解启用实际用时性能剖析后对 start 函数的限制,请参阅限制

默认值为 False

1 仅适用于 Compute Engine 和 GKE。对于 App Engine,该值由环境生成。
2 对于 Google Cloud,该值由环境生成。对于非 Google Cloud 环境,您必须提供一个值。如需了解相关信息,请参阅剖析在 Google Cloud 外部运行的应用的性能

分析数据

在 Profiler 收集数据后,您可以使用 Profiler 界面查看和分析这些数据。

在 Google Cloud 控制台的导航面板中,选择 Profiler

前往 Profiler

服务名称和版本参数

加载 Profiler 代理时,请指定服务名称参数和可选的服务版本参数,以对其进行配置。

凭借服务名称,Profiler 可以收集该服务所有副本的性能剖析数据。对于每个组合服务版本和区域中的每个服务名称,Profiler 服务将保证平均每分钟生成一个性能剖析文件的收集速率。

例如,如果您的服务有两个版本在三个区域中的副本上运行,那么 Profiler 平均每分钟将为该服务创建 6 个性能剖析文件。

如果您为副本使用不同的服务名称,那么对服务执行性能剖析的频率将超出必要频率,相应的开销也会更高。

选择服务名称时,应注意以下几点:

  • 选择的名称应能清楚地表示应用架构中的服务。如果您只运行单个服务或应用,那么服务名称的选择没那么重要;但如果您的应用作为一组微服务运行,则服务名称的选择较为重要。

  • 切勿在服务名称字符串中使用任何进程专用的值,例如进程 ID。

  • 服务名称字符串必须与如下正则表达式相符:

    ^[a-z0-9]([-a-z0-9_.]{0,253}[a-z0-9])?$

建议使用 imageproc-service 这样的静态字符串作为服务名称。

服务版本是可选的。如果您指定服务版本,则 Profiler 可以汇总来自多个实例的性能剖析信息并将其正确显示出来。服务版本可用于在部署服务时标记不同的版本。Profiler 界面支持您按服务版本过滤数据,这样一来,您就可以比较新旧版本代码的性能。

服务版本参数的值是一个使用自由格式的字符串,但此参数的值通常看起来像版本号,例如 1.0.02.1.2

代理日志记录

默认情况下,性能剖析代理会记录严重级别为 error 的消息。要将代理配置为记录严重级别较低的消息,请在启动代理时指定 verbose 参数。verbose 有四个受支持的值:

  • 0:Error
  • 1:Warning
  • 2:Informational
  • 3:Debug

如果您在调用 start 时将 verbose 参数设置为 1,则将记录严重级别为 WarningError 的消息,而忽略 InformationalDebug 消息。

如需记录所有消息,请在启动代理时将 verbose设置为 3

googlecloudprofiler.start(service='service_name', verbose=3)

问题排查

本部分列出了剖析 Python 应用的性能所特有的限制、例外情况和已知问题。有关常见问题的帮助,请参阅问题排查

限制

性能剖析类型 限制和局限
实际用时
  • 仅限主线程性能剖析。
  • 必须从主线程调用性能剖析文件 start 函数。
  • Profiler 信号处理程序仅在主线程上执行。如果主线程无法执行,则不会捕获任何性能剖析数据。

异常

错误 原因 解决方案
start 期间抛出 NotImplementedError 应用在非 Linux 环境中执行。
  • 在 Linux 环境中执行您的应用。
start 期间抛出 ValueError start 函数的参数无效,无法通过环境变量和参数或性能剖析(如果同时停用 CPU 性能剖析和实际用时性能剖析)确定必要的信息。
  • 确保服务名称和版本符合服务名称和版本参数中指定的要求。
  • 如果启用了实际用时性能剖析,请确保从主线程调用 start
  • 确保您使用的是受支持的 Python 版本,并且已启用 CPU 时间性能剖析或实际用时性能剖析。如需了解详情,请参阅 start 函数
  • 如果您在 Google Cloud 之外运行,请检查是否已为 start 指定 project_id 参数。如需了解详情,请参阅 start 函数

已知问题

行为 原因 解决方案
您没有任何性能剖析数据,或者您启用了新的性能剖析类型,但缺少性能剖析数据。 常见原因与配置有关。 请参阅问题排查
您使用的是 uWSGI,但没有所有进程的 CPU 时间和实际用时性能剖析数据。

当 uWSGI 使用多个工作器处理请求时,默认行为是仅在主进程中执行应用初始化。派生进程不执行初始化序列。

如果您在应用的初始化序列中(例如,在 Django 应用的 AppConfig.ready() 中)配置性能剖析代理,则结果是没有为派生进程配置性能剖析代理。

如需在所有工作器进程中执行应用初始化,请将标记 lazy-apps 设置为 true

请参阅此表中的下一个主题,了解相关问题。

您使用的是 uWSGI,且您没有实际用时性能剖析数据,但您确实有 CPU 时间性能剖析数据。

实际用时性能剖析器依赖于 Python 信号模块。 使用线程支持编译 Python 解释器时,默认配置会停用对派生进程的自定义信号处理。

对于 uWSGI 应用,请将标记 py-call-osafterfork 设置为 true,以启用自定义信号处理。

请参阅此表中的上一主题,了解相关问题。

启用性能剖析器后,错误日志将包含新条目:

BlockingIOError: [Errno 11] Resource temporarily unavailable Exception ignored when trying to write to the signal wakeup fd

GitHub 问题

您的应用已注册信号唤醒文件描述符 signal.set_wakeup_fd。默认情况下,如果文件描述符的缓冲区填满,则系统会将一条警告记录到 stderr。

在 Cloud Profiler 收集性能剖析文件时,它会触发高频率信号。此行为可能会导致文件描述符的缓冲区变满。

如果您的应用在信号中断时能够安全运行,则可以使用 Cloud Profiler。如果您使用的是 Python 3.7 或更高版本,并且想要停用警告消息,则将 warn_on_full_buffer=False 作为参数传递给 signal.set_wakeup_fd

如果您的应用在信号中断时无法安全运行,我们建议您停止使用 Cloud Profiler。继续使用可能会导致错误日志中信号数丢失且条目过多。

使用 Linux Alpine 运行

只有 Google Kubernetes Engine 配置支持适用于 Linux Alpine 的 Python 性能剖析代理。

如需构建 Python 性能剖析代理,您必须安装软件包 build-base。如需在 Alpine 上使用 Python 性能剖析代理,而无需在最终的 Alpine 映像上安装其他依赖项,您可以使用两阶段式构建并在第一阶段编译 Python 性能剖析代理。 例如,以下 Docker 映像使用多阶段式构建来编译和安装 Python 性能剖析代理:

FROM python:3.7-alpine as builder

# Install build-base to allow for compilation of the profiling agent.
RUN apk add --update --no-cache build-base

# Compile the profiling agent, generating wheels for it.
RUN pip3 wheel --wheel-dir=/tmp/wheels google-cloud-profiler

FROM python:3.7-alpine

# Copy over the directory containing wheels for the profiling agent.
COPY --from=builder /tmp/wheels /tmp/wheels

# Install the profiling agent.
RUN pip3 install --no-index --find-links=/tmp/wheels google-cloud-profiler

# Install any other required modules or dependencies, and copy an app which
# enables the profiler as described in "Enable the profiler in your
# application".
COPY ./bench.py .

# Run the application when the docker image is run, using either CMD (as is done
# here) or ENTRYPOINT.
CMD python3 -u bench.py

身份验证错误

如果您的 Docker 映像使用 Linux Alpine(如 golang:alpine 或者只是 alpine)运行,您可能会看到以下身份验证错误:

connection error: desc = "transport: authentication handshake failed: x509: failed to load system roots and no roots provided"

请注意,您必须启用代理日志记录才能看到该错误。

该错误表示使用 Linux Alpine 的 Docker 映像没有默认安装 SSL 根证书。这些证书为性能剖析代理与性能剖析器 API 进行通信所必需。如需解决此错误,请将以下 apk 命令添加到您的 Dockerfile 中:

FROM alpine
...
RUN apk add --no-cache ca-certificates

然后,您需要重新构建并重新部署应用。

后续步骤