剖析 Go 应用的性能

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

Go 的性能剖析文件类型:

  • CPU 时间
  • 分配的堆
  • 争用 (Go mutex)
  • 线程 (Go goroutine)

支持的 Go 语言版本:

  • 除非另有说明,否则所有已正式维护的 Go 版本。 如需了解详情,请参阅 Go 语言发布政策

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

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

支持的操作系统:

  • Linux。Linux 内核支持剖析 Go 应用的性能,其标准 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

在所有受支持的环境中,若要使用 Profiler,请在应用中导入软件包,然后尽早在应用中初始化 Profiler。

MutexProfiling 配置选项设置为 true 即可启用互斥争用性能剖析(界面中的“争用”)。

如需详细了解 Profiler API 及所有配置选项,请参阅公共 API 文档

Compute Engine

对于 Compute Engine,请在 profiler.Config 中将 Service 设置为要进行性能剖析的服务的名称,并视需要将 ServiceVersion 设置为服务版本:


// snippets is an example of starting cloud.google.com/go/profiler.
package main

import (
	"cloud.google.com/go/profiler"
)

func main() {
	cfg := profiler.Config{
		Service:        "myservice",
		ServiceVersion: "1.0.0",
		// ProjectID must be set if not running on GCP.
		// ProjectID: "my-project",

		// For OpenCensus users:
		// To see Profiler agent spans in APM backend,
		// set EnableOCTelemetry to true
		// EnableOCTelemetry: true,
	}

	// Profiler initialization, best done as early as possible.
	if err := profiler.Start(cfg); err != nil {
		// TODO: Handle error.
	}
}

如果源代码中的依赖项是手动提取的,那么您可能需要将以下内容添加到构建脚本或 Dockerfile 中:

go get cloud.google.com/go/profiler

GKE

对于 GKE,请在 profiler.Config 中将 Service 设置为要进行性能剖析的服务的名称,并视需要将 ServiceVersion 设置为服务版本:


// snippets is an example of starting cloud.google.com/go/profiler.
package main

import (
	"cloud.google.com/go/profiler"
)

func main() {
	cfg := profiler.Config{
		Service:        "myservice",
		ServiceVersion: "1.0.0",
		// ProjectID must be set if not running on GCP.
		// ProjectID: "my-project",

		// For OpenCensus users:
		// To see Profiler agent spans in APM backend,
		// set EnableOCTelemetry to true
		// EnableOCTelemetry: true,
	}

	// Profiler initialization, best done as early as possible.
	if err := profiler.Start(cfg); err != nil {
		// TODO: Handle error.
	}
}

如果源代码中的依赖项是手动提取的,那么您可能需要将以下内容添加到构建脚本或 Dockerfile 中:

go get cloud.google.com/go/profiler

App Engine

App Engine 柔性环境和 App Engine 标准环境的代码添加操作几乎与 Compute Engine 和 GKE 环境相同。 但有一种例外情况。在这两种 App Engine 环境中,ServiceServiceVersion 参数由环境生成,因此,您不必指定它们。


// appengine is an example of starting cloud.google.com/go/profiler on
// App Engine.
package main

import (
	"cloud.google.com/go/profiler"
)

func main() {
	// Profiler initialization, best done as early as possible.
	if err := profiler.Start(profiler.Config{
		// Service and ServiceVersion can be automatically inferred when running
		// on App Engine.
		// ProjectID must be set if not running on GCP.
		// ProjectID: "my-project",
	}); err != nil {
		// TODO: Handle error.
	}
}

在本地运行应用时,请在 profiler.Config 中设置 ProjectID(您的 Google Cloud 项目 ID)和 Service 参数,因为它们无法由本地环境生成。您无需设置 ServiceVersion

如果您使用的是 App Engine 标准环境,请参阅将应用迁移到 Go 1.11,详细了解您可能需要对应用进行的更改。此外,您必须使用 Google Cloud CLI 226.0.0 或更高版本。如需更新 Google Cloud CLI,请运行以下命令:

    gcloud components update

要运行应用,请执行以下操作:

  1. 更新依赖项:

    go get cloud.google.com/go/profiler
    
  2. 将应用部署到 App Engine 柔性环境或 App Engine 标准环境:

    gcloud app deploy [DEPLOYMENT]
    

    其中,DEPLOYMENT 是配置文件的路径。例如,DEPLOYMENT 可能是 main/app.yaml

分析数据

在 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

代理日志记录

性能剖析代理可以在其日志中报告调试信息。默认情况下,代理日志记录处于停用状态。

如需启用代理日志记录,请在启动代理时将 DebugLogging 选项设置为 true

profiler.Start(profiler.Config{..., DebugLogging: true});

问题排查

本部分列出了剖析 Go 应用性能所特有的问题。有关常见问题的帮助,请参阅问题排查

行为 原因 解决方案
不收集使用 -buildmode=c-archive 构建的应用的 CPU 时间性能剖析文件。堆、争用和线程性能剖析文件会被收集。GitHub 问题 默认情况下,当 -buildmode 标志为 c-archivec-shared 时,不会为 Go 应用启用 CPU 性能剖析。 先添加对
signal.Notify(make(
chan os.Signal), syscall.SIGPROF)
的调用,然后再调用 profiler.Start
对 GitHub 问题的响应。

使用 Linux Alpine 运行

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

身份验证错误

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

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

请注意,您必须启用代理日志记录才能看到该错误。 默认情况下,Go 代理不会输出任何日志消息。

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

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

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

后续步骤