1. 简介
上次更新日期:2022 年 7 月 14 日
应用的可观测性
可观测性和持续性能分析器
可观测性是用于描述系统特性的术语。具有可观测性的系统可让团队主动调试其系统。在这种情况下,可观测性的三大支柱:日志、指标和跟踪记录是系统获取可观测性的基本插桩。
此外,除了可观测性的三大支柱之外,持续剖析也是可观测性的另一个关键组成部分,并且正在扩大业内用户群。Cloud Profiler 是其发起者之一,可让您通过一个简单的界面深入了解应用调用堆栈中的性能指标。
此 Codelab 是该系列的第 2 部分,介绍了如何对连续性能分析器代理进行插桩测试。第 1 部分介绍如何使用 OpenTelemetry 和 Cloud Trace 进行分布式跟踪,您将在第 1 部分中详细了解如何识别微服务的瓶颈。
构建内容
在此 Codelab 中,您将在“莎士比亚应用”的服务器服务中对连续性能分析器代理进行插桩(也称为 Shakesapp)。Shakesapp 的架构如下所述:
- Loadgen 使用 HTTP 将查询字符串发送到客户端
- 客户端将查询从 loadgen 传递到 gRPC 中的服务器
- 服务器接受来自客户端的查询,从 Google Cloud Storage 提取所有 Shakespare 作品(文本格式),搜索包含该查询的行,并返回与客户端匹配的行号
在第 1 部分中,您发现服务器服务中的某个位置存在瓶颈,但无法确定确切原因。
学习内容
- 如何嵌入 Profiler 代理
- 如何在 Cloud Profiler 上调查瓶颈问题
此 Codelab 介绍了如何在您的应用中检测连续性能分析器代理。
所需条件
- Go 基础知识
- Kubernetes 基础知识
2. 设置和要求
自定进度的环境设置
如果您还没有 Google 账号(Gmail 或 Google Apps),则必须创建一个。登录 Google Cloud Platform Console (console.cloud.google.com) 并创建一个新项目。
如果您已经有一个项目,请点击控制台左上方的项目选择下拉菜单:
然后在出现的对话框中点击“新建项目”按钮以创建一个新项目:
如果您还没有项目,则应该看到一个类似这样的对话框来创建您的第一个项目:
随后的项目创建对话框可让您输入新项目的详细信息:
请记住项目 ID,它是所有 Google Cloud 项目中的唯一名称(很抱歉,上述名称已被占用,您无法使用!)。稍后,此 Codelab 将将其称为 PROJECT_ID。
接下来,您需要在 Developers Console 中启用结算功能(如果您尚未这样做),以便使用 Google Cloud 资源并启用 Cloud Trace API。
在此 Codelab 中运行仅花费几美元,但是如果您决定使用更多资源或继续让它们运行,费用可能更高(请参阅本文档末尾的“清理”部分)。官方文档中介绍了 Google Cloud Trace、Google Kubernetes Engine 和 Google Artifact Registry 的价格。
Google Cloud Platform 的新用户均有资格获享 $300 赠金,免费试用此 Codelab。
Google Cloud Shell 设置
虽然可以通过笔记本电脑远程操作 Google Cloud 和 Google Cloud Trace,但在此 Codelab 中,我们将使用 Google Cloud Shell,这是一个在云端运行的命令行环境。
基于 Debian 的这个虚拟机已加载了您需要的所有开发工具。它提供了一个持久的 5GB 主目录,并且在 Google Cloud 中运行,大大增强了网络性能和身份验证。这意味着在本 Codelab 中,您只需要一个浏览器(没错,它适用于 Chromebook)。
如需从 Cloud 控制台激活 Cloud Shell,只需点击“激活 Cloud Shell”图标 即可(预配和连接到环境应该只需要片刻时间)。
在连接到 Cloud Shell 后,您应该会看到自己已通过身份验证,并且相关项目已设置为您的 PROJECT_ID
。
gcloud auth list
命令输出
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
命令输出
[core] project = <PROJECT_ID>
如果出于某种原因未设置项目,只需发出以下命令即可:
gcloud config set project <PROJECT_ID>
正在查找您的 PROJECT_ID
?检查您在设置步骤中使用的 ID,或在 Cloud Console 信息中心查找该 ID:
默认情况下,Cloud Shell 还会设置一些环境变量,这对您日后运行命令可能会很有用。
echo $GOOGLE_CLOUD_PROJECT
命令输出
<PROJECT_ID>
最后,设置默认可用区和项目配置。
gcloud config set compute/zone us-central1-f
您可以选择各种不同的可用区。如需了解详情,请参阅区域和可用区。
Go 语言设置
在此 Codelab 中,我们针对所有源代码使用 Go。在 Cloud Shell 上运行以下命令,并确认 Go 的版本是否为 1.17 及更高版本
go version
命令输出
go version go1.18.3 linux/amd64
设置 Google Kubernetes 集群
在此 Codelab 中,您将在 Google Kubernetes Engine (GKE) 上运行微服务集群。此 Codelab 的流程如下:
- 将基准项目下载到 Cloud Shell 中
- 将微服务构建到容器中
- 将容器上传到 Google Artifact Registry (GAR)
- 将容器部署到 GKE
- 修改轨迹插桩服务的源代码
- 转到第 2 步
启用 Kubernetes Engine
首先,我们设置一个 Kubernetes 集群,在 GKE 上运行 Shakesapp,因此需要启用 GKE。导航到“Kubernetes Engine”菜单并按“启用”按钮。
现在,您可以创建 Kubernetes 集群了。
创建 Kubernetes 集群
在 Cloud Shell 上,运行以下命令以创建 Kubernetes 集群。请确认可用区值在您将用于创建 Artifact Registry 代码库的区域下。如果代码库区域未覆盖区域,请更改区域值 us-central1-f
。
gcloud container clusters create otel-trace-codelab2 \ --zone us-central1-f \ --release-channel rapid \ --preemptible \ --enable-autoscaling \ --max-nodes 8 \ --no-enable-ip-alias \ --scopes cloud-platform
命令输出
Note: Your Pod address range (`--cluster-ipv4-cidr`) can accommodate at most 1008 node(s). Creating cluster otel-trace-codelab2 in us-central1-f... Cluster is being health-checked (master is healthy)...done. Created [https://container.googleapis.com/v1/projects/development-215403/zones/us-central1-f/clusters/otel-trace-codelab2]. To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-f/otel-trace-codelab2?project=development-215403 kubeconfig entry generated for otel-trace-codelab2. NAME: otel-trace-codelab2 LOCATION: us-central1-f MASTER_VERSION: 1.23.6-gke.1501 MASTER_IP: 104.154.76.89 MACHINE_TYPE: e2-medium NODE_VERSION: 1.23.6-gke.1501 NUM_NODES: 3 STATUS: RUNNING
Artifact Registry 和 Skaffold 设置
现在,我们有一个可以进行部署的 Kubernetes 集群了。接下来,我们准备用于推送和部署容器的 Container Registry。对于这些步骤,我们需要设置 Artifact Registry (GAR) 和 Skaffold 才能使用它。
Artifact Registry 设置
前往“Artifact Registry”菜单并按“启用”按钮。
片刻之后,您会看到 GAR 的代码库浏览器。点击“CREATE REPOSITORY”并输入代码库的名称
在此 Codelab 中,我将新代码库命名为 trace-codelab
。工件的格式为“Docker”地理位置类型为“区域”选择靠近您为 Google Compute Engine 默认可用区设置的区域。例如,此示例选择了“us-central1-f”所以我们在这里选择“us-central1(爱荷华)”。然后点击“创建”按钮。
现在可以看到“trace-codelab”。
我们稍后将返回此处来检查注册表路径。
Skaffold 设置
在构建在 Kubernetes 上运行的微服务时,Skaffold 是一个方便的工具。它使用一小组命令来处理构建、推送和部署应用容器的工作流。默认情况下,Skaffold 使用 Docker Registry 作为 Container Registry,因此您需要配置 Skaffold 以在推送容器时识别 GAR。
再次打开 Cloud Shell 并确认是否已安装 Skaffold。(Cloud Shell 默认将 Skaffold 安装到环境中。)运行以下命令并查看 Skaffold 版本。
skaffold version
命令输出
v1.38.0
现在,您可以注册供 Skaffold 使用的默认代码库。如需获取注册表路径,请前往 Artifact Registry 信息中心,然后点击您刚刚在上一步中设置的代码库的名称。
然后,您会在页面顶部看到面包屑导航路径。点击 图标,将注册表路径复制到剪贴板。
点击复制按钮后,浏览器底部会出现一个对话框,其中显示如下消息:
"us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab"已复制
返回 Cloud Shell。使用您刚刚从信息中心复制的值运行 skaffold config set default-repo
命令。
skaffold config set default-repo us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab
命令输出
set value default-repo to us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab for context gke_stackdriver-sandbox-3438851889_us-central1-b_stackdriver-sandbox
此外,您还需要将注册表配置为 Docker 配置。运行以下命令:
gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
命令输出
{ "credHelpers": { "gcr.io": "gcloud", "us.gcr.io": "gcloud", "eu.gcr.io": "gcloud", "asia.gcr.io": "gcloud", "staging-k8s.gcr.io": "gcloud", "marketplace.gcr.io": "gcloud", "us-central1-docker.pkg.dev": "gcloud" } } Adding credentials for: us-central1-docker.pkg.dev
现在,您可以执行下一步,在 GKE 上设置 Kubernetes 容器。
摘要
在此步骤中,您将设置 Codelab 环境:
- 设置 Cloud Shell
- 为 Container Registry 创建了 Artifact Registry 代码库
- 设置 Skaffold 以使用 Container Registry
- 创建一个 Kubernetes 集群并在其中运行 Codelab 微服务
后续步骤
在下一步中,您将在服务器服务中对连续性能分析器代理进行插桩 (instrument)。
3. 构建、推送和部署微服务
下载 Codelab 资料
在上一步中,我们已经为此 Codelab 设置了所有前提条件。现在,您可以在这些微服务的基础上运行整个微服务了。此 Codelab 资料托管在 GitHub 上,因此请使用以下 Git 命令将它们下载到 Cloud Shell 环境。
cd ~ git clone https://github.com/ymotongpoo/opentelemetry-trace-codelab-go.git cd opentelemetry-trace-codelab-go
该项目的目录结构如下所示:
. ├── README.md ├── step0 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src ├── step1 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src ├── step2 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src ├── step3 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src ├── step4 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src ├── step5 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src └── step6 ├── manifests ├── proto ├── skaffold.yaml └── src
- 清单:Kubernetes 清单文件
- proto:客户端与服务器之间通信的 proto 定义
- src:每个服务的源代码的目录
- skaffold.yaml:Skaffold 的配置文件
在此 Codelab 中,您将更新位于 step4
文件夹下的源代码。此外,您还可以参阅 step[1-6]
文件夹中的源代码,了解从头开始所做的更改。(第 1 部分介绍步骤 0 到步骤 4,第 2 部分介绍步骤 5 和 6)
运行 skaffold 命令
最后,您就可以开始构建内容,并将所有内容推送到您刚刚创建的 Kubernetes 集群中。这听起来似乎包含多个步骤,但实际上 Skaffold 会为您完成所有操作。我们使用以下命令尝试一下:
cd step4 skaffold dev
运行此命令后,您会看到 docker build
的日志输出,并且可以确认这些日志输出已成功推送到注册表。
命令输出
... ---> Running in c39b3ea8692b ---> 90932a583ab6 Successfully built 90932a583ab6 Successfully tagged us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice:step1 The push refers to repository [us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice] cc8f5a05df4a: Preparing 5bf719419ee2: Preparing 2901929ad341: Preparing 88d9943798ba: Preparing b0fdf826a39a: Preparing 3c9c1e0b1647: Preparing f3427ce9393d: Preparing 14a1ca976738: Preparing f3427ce9393d: Waiting 14a1ca976738: Waiting 3c9c1e0b1647: Waiting b0fdf826a39a: Layer already exists 88d9943798ba: Layer already exists f3427ce9393d: Layer already exists 3c9c1e0b1647: Layer already exists 14a1ca976738: Layer already exists 2901929ad341: Pushed 5bf719419ee2: Pushed cc8f5a05df4a: Pushed step1: digest: sha256:8acdbe3a453001f120fb22c11c4f6d64c2451347732f4f271d746c2e4d193bbe size: 2001
推送所有服务容器后,Kubernetes 部署会自动启动。
命令输出
sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8 size: 1997 Tags used in deployment: - serverservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice:step4@sha256:8acdbe3a453001f120fb22c11c4f6d64c2451347732f4f271d746c2e4d193bbe - clientservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/clientservice:step4@sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8 - loadgen -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/loadgen:step4@sha256:eea2e5bc8463ecf886f958a86906cab896e9e2e380a0eb143deaeaca40f7888a Starting deploy... - deployment.apps/clientservice created - service/clientservice created - deployment.apps/loadgen created - deployment.apps/serverservice created - service/serverservice created
部署后,您会看到每个容器中发送到 stdout 的实际应用日志,如下所示:
命令输出
[client] 2022/07/14 06:33:15 {"match_count":3040} [loadgen] 2022/07/14 06:33:15 query 'love': matched 3040 [client] 2022/07/14 06:33:15 {"match_count":3040} [loadgen] 2022/07/14 06:33:15 query 'love': matched 3040 [client] 2022/07/14 06:33:16 {"match_count":3040} [loadgen] 2022/07/14 06:33:16 query 'love': matched 3040 [client] 2022/07/14 06:33:19 {"match_count":463} [loadgen] 2022/07/14 06:33:19 query 'tear': matched 463 [loadgen] 2022/07/14 06:33:20 query 'world': matched 728 [client] 2022/07/14 06:33:20 {"match_count":728} [client] 2022/07/14 06:33:22 {"match_count":463} [loadgen] 2022/07/14 06:33:22 query 'tear': matched 463
请注意,此时您想要查看来自服务器的任何邮件。现在,您已准备好开始使用 OpenTelemetry 对应用进行插桩,以便对服务进行分布式跟踪。
在开始对服务进行插桩之前,请使用 Ctrl-C 关闭集群。
命令输出
... [client] 2022/07/14 06:34:57 {"match_count":1} [loadgen] 2022/07/14 06:34:57 query 'what's past is prologue': matched 1 ^CCleaning up... - W0714 06:34:58.464305 28078 gcp.go:120] WARNING: the gcp auth plugin is deprecated in v1.22+, unavailable in v1.25+; use gcloud instead. - To learn more, consult https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke - deployment.apps "clientservice" deleted - service "clientservice" deleted - deployment.apps "loadgen" deleted - deployment.apps "serverservice" deleted - service "serverservice" deleted
摘要
在此步骤中,您已在自己的环境中准备了 Codelab 资料,并确认 Skaffold 按预期运行。
后续步骤
在下一步中,您将修改 loadgen 服务的源代码,以检测轨迹信息。
4. Cloud Profiler 代理插桩
持续分析的概念
在解释持续分析的概念之前,我们需要先了解分析的概念。分析是动态分析应用(动态程序分析)的方法之一,通常在应用开发期间负载测试等过程中执行。这是一个单次活动,用于测量特定时间段内的系统指标,例如 CPU 和内存使用率。收集配置文件数据后,开发者会从代码中对其进行分析。
持续分析是正常分析的扩展方法:它定期对长时间运行的应用运行短窗口分析,并收集大量分析数据。然后,它会根据应用的某个属性(例如版本号、部署可用区、测量时间等)自动生成统计分析。如需详细了解此概念,请参阅我们的文档。
由于目标是正在运行的应用,因此有办法定期收集配置文件数据,并将它们发送到对统计数据进行后处理的某个后端。这是 Cloud Profiler 代理,您很快就会将其嵌入服务器服务。
嵌入 Cloud Profiler 代理
按 Cloud Shell 右上角的 按钮,打开 Cloud Shell Editor。从左侧窗格中的 Explorer 中打开
step4/src/server/main.go
,然后找到 main 函数。
step4/src/server/main.go
func main() { ... // step2. setup OpenTelemetry tp, err := initTracer() if err != nil { log.Fatalf("failed to initialize TracerProvider: %v", err) } defer func() { if err := tp.Shutdown(context.Background()); err != nil { log.Fatalf("error shutting down TracerProvider: %v", err) } }() // step2. end setup svc := NewServerService() // step2: add interceptor interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider()) srv := grpc.NewServer( grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor(interceptorOpt)), grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor(interceptorOpt)), ) // step2: end adding interceptor shakesapp.RegisterShakespeareServiceServer(srv, svc) healthpb.RegisterHealthServer(srv, svc) if err := srv.Serve(lis); err != nil { log.Fatalf("error serving server: %v", err) } }
在 main
函数中,您会看到 OpenTelemetry 和 gRPC 的一些设置代码,这些代码已在 Codelab 第 1 部分中完成。现在,您将在此处为 Cloud Profiler 代理添加插桩。与处理 initTracer()
时一样,您可以编写一个名为 initProfiler()
的函数,以提高可读性。
step4/src/server/main.go
import ( ... "cloud.google.com/go/profiler" // step5. add profiler package "cloud.google.com/go/storage" ... ) // step5: add Profiler initializer func initProfiler() { cfg := profiler.Config{ Service: "server", ServiceVersion: "1.0.0", NoHeapProfiling: true, NoAllocProfiling: true, NoGoroutineProfiling: true, NoCPUProfiling: false, } if err := profiler.Start(cfg); err != nil { log.Fatalf("failed to launch profiler agent: %v", err) } }
下面我们来详细了解一下 profiler.Config{}
对象中指定的选项。
- 服务:您可以选择并开启性能分析器信息中心的服务名称
- ServiceVersion:服务版本名称。您可以根据此值比较个人资料数据集。
- NoHeapProfiling:停用内存消耗分析
- NoAllocProfiling:停用内存分配性能剖析
- NoGoroutineProfiling:停用 goroutine 分析
- NoCPUProfiling:停用 CPU 性能剖析
在此 Codelab 中,我们仅启用 CPU 性能分析。
现在,您只需在 main
函数中调用此函数即可。请务必在导入块中导入 Cloud Profiler 软件包。
step4/src/server/main.go
func main() { ... defer func() { if err := tp.Shutdown(context.Background()); err != nil { log.Fatalf("error shutting down TracerProvider: %v", err) } }() // step2. end setup // step5. start profiler go initProfiler() // step5. end svc := NewServerService() // step2: add interceptor ... }
请注意,您使用 go
关键字调用 initProfiler()
函数。因为 profiler.Start()
会阻塞,因此您需要在另一个 goroutine 中运行它。现在,可以开始构建了。请务必在部署前运行 go mod tidy
。
go mod tidy
现在,使用新的服务器服务部署集群。
skaffold dev
通常需要几分钟才能在 Cloud Profiler 上看到火焰图。输入“profiler”,然后点击 Profiler 的图标。
然后,您会看到以下火焰图。
摘要
在此步骤中,您已将 Cloud Profiler 代理嵌入服务器服务,并确认其会生成火焰图。
后续步骤
在下一步中,您将使用火焰图调查应用中瓶颈的原因。
5. 分析 Cloud Profiler 火焰图
什么是火焰图?
火焰图是直观呈现剖析数据的方法之一。如需了解详细说明,请参阅我们的文档,其简短摘要如下:
- 每个条形表示应用中的方法/函数调用
- 垂直方向是调用堆栈;调用堆栈从上到下增大
- 水平方向表示资源用量;越长,越糟糕。
因此,我们来看看获得的火焰图。
分析火焰图
在上一部分中,您已了解火焰图中的每个条形表示函数/方法调用,其长度表示函数/方法中的资源使用情况。Cloud Profiler 的火焰图按降序或从左至右的长度对条形图排序,您可以先查看图表的左上角。
在本例中,很明显,grpc.(*Server).serveStreams.func1.2
消耗了最多的 CPU 时间,并且通过从上到下查看调用堆栈,这大部分时间都花在 main.(*serverService).GetMatchCount
(服务器服务中的 gRPC 服务器处理程序)中。
在 GetMatchCount 下,您会看到一系列正则表达式函数:regexp.MatchString
和 regexp.Compile
。它们来自标准软件包:也就是说,应该从包括性能在内的多个角度对其进行充分测试。但此处的结果显示,regexp.MatchString
和 regexp.Compile
中的 CPU 时间资源使用率较高。鉴于这些事实,这里假设使用 regexp.MatchString
与性能问题有关。让我们看一下使用该函数的源代码。
step4/src/server/main.go
func (s *serverService) GetMatchCount(ctx context.Context, req *shakesapp.ShakespeareRequest) (*shakesapp.ShakespeareResponse, error) { resp := &shakesapp.ShakespeareResponse{} texts, err := readFiles(ctx, bucketName, bucketPrefix) if err != nil { return resp, fmt.Errorf("fails to read files: %s", err) } for _, text := range texts { for _, line := range strings.Split(text, "\n") { line, query := strings.ToLower(line), strings.ToLower(req.Query) isMatch, err := regexp.MatchString(query, line) if err != nil { return resp, err } if isMatch { resp.MatchCount++ } } } return resp, nil }
这是调用 regexp.MatchString
的位置。通过读取源代码,您可能会注意到,该函数在嵌套的 for 循环内被调用。因此,此函数的用法可能不正确。我们来查找正则表达式的 GoDoc。
根据该文档,regexp.MatchString
会在每次调用中编译正则表达式模式。所以,导致大量资源消耗的原因好像是这样的。
摘要
在此步骤中,您通过分析火焰图假设了资源消耗的原因。
后续步骤
在下一步中,您将更新服务器服务的源代码,并在 1.0.0 版的基础上确认更改。
6. 更新源代码并比较火焰图
更新源代码
在上一步中,您假设 regexp.MatchString
的使用与大量资源消耗有关。让我们解决这个问题。打开代码并对该部分稍作更改。
step4/src/server/main.go
func (s *serverService) GetMatchCount(ctx context.Context, req *shakesapp.ShakespeareRequest) (*shakesapp.ShakespeareResponse, error) { resp := &shakesapp.ShakespeareResponse{} texts, err := readFiles(ctx, bucketName, bucketPrefix) if err != nil { return resp, fmt.Errorf("fails to read files: %s", err) } // step6. considered the process carefully and naively tuned up by extracting // regexp pattern compile process out of for loop. query := strings.ToLower(req.Query) re := regexp.MustCompile(query) for _, text := range texts { for _, line := range strings.Split(text, "\n") { line = strings.ToLower(line) isMatch := re.MatchString(line) // step6. done replacing regexp with strings if isMatch { resp.MatchCount++ } } } return resp, nil }
如您所见,现在正则表达式模式编译过程将从 regexp.MatchString
中提取,并移出了嵌套的 for 循环。
在部署此代码之前,请务必更新 initProfiler()
函数中的版本字符串。
step4/src/server/main.go
func initProfiler() { cfg := profiler.Config{ Service: "server", ServiceVersion: "1.1.0", // step6. update version NoHeapProfiling: true, NoAllocProfiling: true, NoGoroutineProfiling: true, NoCPUProfiling: false, } if err := profiler.Start(cfg); err != nil { log.Fatalf("failed to launch profiler agent: %v", err) } }
现在,我们来看看它的工作原理。使用 skaffold 命令部署集群。
skaffold dev
一段时间后,重新加载 Cloud Profiler 信息中心,看看它是什么样的。
请务必将版本更改为 "1.1.0"
,以便仅显示版本 1.1.0 中的配置文件。如您所知,GetMatchCount 的条形长度变短了,CPU 时间的使用率也随之缩短(即条形变短)。
您不仅可以查看单个版本的火焰图,还可以比较两个版本之间的差异。
更改“比较对象”的值下拉列表然后将“要比较的版本”的值“1.0.0”(原始版本)。
你会看到这种火焰图。图表的形状与 1.1.0 相同,但着色不同。在比较模式中,颜色的含义如下:
- 蓝色:值(资源消耗量)减少
- 橙色:获得的值(资源消耗量)
- 灰色:中性色
让我们详细了解一下图例中的函数。点击要放大的条形,即可查看堆栈内的更多详细信息。请点击 main.(*serverService).GetMatchCount
栏。此外,将鼠标悬停在条形图上,即可查看对比详情。
其中显示总 CPU 时间从 5.26 秒减少到 2.88 秒(总共 10 秒 = 采样窗口)。这是一项巨大的改进!
现在,您可以通过分析配置文件数据来提升应用性能。
摘要
在此步骤中,您在服务器服务中进行了修改,并确认了 Cloud Profiler 比较模式的改进。
后续步骤
在下一步中,您将更新服务器服务的源代码,并在 1.0.0 版的基础上确认更改。
7. 额外步骤:确认 Trace 广告瀑布流中的改进
分布式跟踪记录和持续性能剖析之间的区别
在此 Codelab 的第 1 部分中,您已确认可以找出请求路径的微服务中的瓶颈服务,并且无法确定特定服务中瓶颈的确切原因。在此 Codelab 的第 2 部分中,您已了解持续性能分析可让您通过调用堆栈识别单个服务中的瓶颈。
在此步骤中,我们来回顾一下分布式跟踪记录 (Cloud Trace) 的瀑布图,并了解它与持续分析的区别。
此瀑布图是包含“love”查询的跟踪记录之一。总共需要大约 6.7 秒(6700 毫秒)。
这是对同一查询进行改进之后的结果。如您所知,现在总延迟时间为 1.5 秒(1500 毫秒),与之前的实现相比有了很大的改进。
这里的要点是,在分布式跟踪瀑布图中,除非您对任何位置进行 span 插桩,否则调用堆栈信息不可用。此外,分布式跟踪记录仅关注跨服务的延迟时间,而持续分析关注单个服务的计算机资源(CPU、内存、操作系统线程)。
另一方面,分布式跟踪记录是事件库,连续配置文件是统计学。每条跟踪记录都有不同的延迟时间图,并且您需要不同的格式(如分布)来获取延迟时间变化趋势。
摘要
在此步骤中,您了解了分布式跟踪记录和持续性能剖析之间的区别。
8. 恭喜
您已成功使用 OpenTelemery 创建了分布式跟踪记录,并在 Google Cloud Trace 的微服务中确认了请求延迟时间。
要进行扩展练习,您可以自行尝试以下主题。
- 当前的实现方式会发送健康检查生成的所有 span。(
grpc.health.v1.Health/Check
) 如何从 Cloud Trace 中过滤掉这些 span?请点击此处查看提示。 - 将事件日志与 span 相关联,了解其在 Google Cloud Trace 和 Google Cloud Logging 上的工作原理。请点击此处查看提示。
- 将某项服务替换为其他语言的服务,并尝试使用该语言的 OpenTelemetry 对其进行插桩。
此外,如果您想在此之后了解性能分析器,请继续学习第 2 部分。在这种情况下,您可以跳过下面的清理部分。
清理
完成此 Codelab 后,请停止 Kubernetes 集群并确保删除该项目,以免 Google Kubernetes Engine、Google Cloud Trace 和 Google Artifact Registry 产生意外费用。
首先,删除集群。如果您使用 skaffold dev
运行集群,只需按 Ctrl-C 即可。如果您使用 skaffold run
运行集群,请运行以下命令:
skaffold delete
命令输出
Cleaning up... - deployment.apps "clientservice" deleted - service "clientservice" deleted - deployment.apps "loadgen" deleted - deployment.apps "serverservice" deleted - service "serverservice" deleted
删除集群后,从菜单窗格中选择“IAM &管理员”>“设置”,然后点击“关闭”按钮。
然后在对话框的表单中输入项目 ID(而非项目名称),并确认关闭。