1. 简介

上次更新日期:2021 年 3 月 5 日
应用的可观测性
可观测性和 OpenTelemetry
可观测性是用于描述系统属性的术语。具有可观测性的系统可帮助团队有效调试其系统。在这种情况下,可观测性的三大支柱(日志、指标和轨迹)是系统获得可观测性的基本检测工具。
OpenTelemetry 是一组规范和 SDK,可加速遥测数据(日志、指标和跟踪记录)的插桩和导出,而可观测性需要这些数据。OpenTelemetry 是 CNCF 下的开放标准和社区驱动型项目。通过利用项目及其生态系统提供的库,开发者能够以与供应商无关的方式针对多种架构检测其应用。
分布式轨迹
在日志、指标和轨迹中,轨迹是一种遥测数据,用于指示系统中流程特定部分的延迟时间。尤其是在微服务时代,分布式跟踪是找出整个分布式系统中的延迟瓶颈的强大驱动因素。
在分析分布式轨迹时,轨迹数据可视化是快速了解整体系统延迟的关键。在分布式跟踪中,我们处理一组调用,以处理对系统入口点的单个请求,该请求的形式为包含多个 Span 的 Trace。
Span 表示在分布式系统中完成的单个工作单元,记录开始时间和停止时间。Span 之间通常存在层次关系 - 在下图中,所有较小的 span 都是大型 /messages span 的子 span,并组合成一个显示系统工作路径的轨迹。

Google Cloud Trace 是分布式跟踪后端的一种选择,并且与 Google Cloud 中的其他产品集成得很好。
构建内容
在此 Codelab 中,您将检测在 Google Kubernetes Engine 上运行的 Kubernetes 集群上运行的名为“Shakesapp”的服务中的跟踪信息。Shakesapp 的架构如下所述:

- 客户端向服务器发送查询字符串
- 服务器接受来自客户端的查询,从 Google Cloud Storage 中以文本格式提取所有莎士比亚作品,搜索包含查询的行,并将匹配的行号返回给客户端。
您将跨请求检测跟踪信息。
学习内容
- 如何在 Python 项目中开始使用 OpenTelemetry Trace 库
- 如何使用库创建 span
- 如何在应用组件之间通过网络传播 span 上下文
- 如何将跟踪数据发送到 Google Cloud Trace
- 如何在 Google Cloud Trace 上分析跟踪记录
本 Codelab 介绍了如何对微服务进行插桩。为便于理解,此示例仅包含 3 个组件(负载生成器、客户端和服务器),但您可以将此 Codelab 中介绍的相同流程应用于更复杂、更大的系统。
所需条件
- 了解 Python 3
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 Observability 的价格
- 价格 | Kubernetes Engine 文档
- Artifact Registry 价格 | 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
您可以选择各种不同的可用区。如需了解详情,请参阅区域和可用区。
Python 设置
在此 Codelab 中,我们使用“poetry”来严格管理软件包版本。在 Cloud Shell 中运行以下命令:
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python3 - source $HOME/.poetry/env
设置 Google Kubernetes 集群
在此 Codelab 中,您将在 Google Kubernetes Engine (GKE) 上运行微服务集群。本 Codelab 的流程如下:
- 将基准项目下载到 Cloud Shell 中
- 将微服务构建到容器中
- 将容器上传到 Google Artifact Registry (GAR)
- 将容器部署到 GKE 上
- 修改服务源代码以进行轨迹插桩
- 前往第 2 步
启用 Kubernetes Engine
首先,我们设置一个 Kubernetes 集群,其中 Shakesapp 在 GKE 上运行,因此我们需要启用 GKE。前往“Kubernetes Engine”菜单,然后按“启用”按钮。

现在,您可以创建 Kubernetes 集群了。
创建 Kubernetes 集群
在 Cloud Shell 中,运行以下命令以创建 Kubernetes 集群。请确认区域值位于您用于创建 Artifact Registry 制品库的区域下。如果您的代码库区域未涵盖相应可用区,请更改可用区值 us-central1-f。
gcloud container clusters create otel-trace-codelab --zone us-central1-f \ --num-nodes 1 \ --machine-type e2-highcpu-4
命令输出
Creating cluster otel-trace-codelab in us-central1-f... Cluster is being health-checked (master is healthy)...done. Created [https://container.googleapis.com/v1/projects/psychic-order-307806/zones/us-central1-f/clusters/otel-trace-codelab]. To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-f/otel-trace-codelab?project=psychic-order-307806 kubeconfig entry generated for otel-trace-codelab. NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS otel-trace-codelab us-central1-f 1.18.12-gke.1210 104.154.162.176 e2-medium 1.18.12-gke.1210 3 RUNNING
Artifact Registry 和 skaffold 设置
现在,我们已准备好可供部署的 Kubernetes 集群。接下来,我们将准备一个容器注册表,用于推送和部署容器。对于这些步骤,我们需要设置 GAR 和 skaffold 以使用它。
设置 Artifact Registry
前往“Artifact Registry”的菜单,然后按“启用”按钮。

稍等片刻,您将看到 GAR 的代码库浏览器。点击“创建代码库”按钮,然后输入代码库的名称。

在此 Codelab 中,我将新代码库命名为 trace-codelab。制品格式为“Docker”,位置类型为“区域”。选择与您为 Google Compute Engine 默认可用区设置的区域相近的区域。例如,上面的示例选择了“us-central1-f”,因此这里我们选择“us-central1 (Iowa)”。然后点击“创建”按钮。

现在,您会在代码库浏览器中看到“trace-codelab”。

我们稍后会回到此处检查注册表路径。
Skaffold 设置
如果您要构建在 Kubernetes 上运行的微服务,Skaffold 是一款非常实用的工具。它只需少量命令即可处理构建、推送和部署应用容器的工作流程。Skaffold 默认使用 Docker Registry 作为容器注册表,因此您需要配置 Skaffold,以便在将容器推送到 GAR 时识别 GAR。
再次打开 Cloud Shell,确认是否已安装 Skaffold。(Cloud Shell 默认将 Skaffold 安装到环境中。)运行以下命令,查看 Skaffold 版本。
skaffold version
命令输出
v1.20.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
- 为容器注册表创建了 Artifact Registry 代码库
- 设置 Skaffold 以使用容器注册表
- 创建了运行 Codelab 微服务的 Kubernetes 集群
后续步骤
在下一步中,您将构建、推送微服务并将其部署到集群中
3. 构建、推送和部署微服务
下载 Codelab 材料
在上一步中,我们已为此 Codelab 设置了所有前提条件。现在,您可以在这些平台上运行整个微服务了。Codelab 材料托管在 GitHub 上,因此请使用以下 git 命令将其下载到 Cloud Shell 环境中。
cd ~ git clone https://github.com/GoogleCloudPlatform/opentelemetry-trace-codelab-python.git
项目的目录结构如下:
shakesapp-python
├── LICENSE
├── manifests
│ ├── client.yaml
│ ├── loadgen.yaml
│ └── server.yaml
├── proto
│ └── shakesapp.proto
├── skaffold.yaml
└── src
├── client
├── loadgen
└── server
- manifests:Kubernetes 清单文件
- proto:客户端与服务器之间通信的 proto 定义
- src:每个服务的源代码目录
- skaffold.yaml:Skaffold 的配置文件
运行 skaffold 命令
最后,您已准备好构建、推送和部署整个内容到刚刚创建的 Kubernetes 集群。这听起来包含多个步骤,但实际上,Skaffold 会为您完成所有操作。我们来尝试一下,运行以下命令:
cd shakesapp-python skaffold run --tail
运行命令后,您会立即看到 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
注意:如果您收到“No push access to specified image repository”之类的错误,请检查 skaffold 命令是否尝试将映像推送到 Docker Hub (docker.io),而不管您在 skaffold 中对默认代码库的配置如何。在这种情况下,请尝试向“skaffold run”添加“--default-repo”选项,如下所示。
$ skaffold run –tail –default-repo=us-central1-docker.pkg.dev/[项目 ID]/[代码库名称]
部署后,您会看到每个容器中实际应用日志输出到 stdout 的情况,如下所示:
命令输出
[server] {"event": "starting server: 0.0.0.0:5050", "severity": "info", "timestamp": "2021-03-17T05:25:56.758575Z"}
[client] [2021-03-17 05:25:54 +0000] [1] [INFO] Starting gunicorn 20.0.4
[client] [2021-03-17 05:25:54 +0000] [1] [INFO] Listening at: http://0.0.0.0:8080 (1)
[client] [2021-03-17 05:25:54 +0000] [1] [INFO] Using worker: threads
[client] [2021-03-17 05:25:54 +0000] [7] [INFO] Booting worker with pid: 7
[client] {"event": "server address is serverservice:5050", "severity": "info", "timestamp": "2021-03-17T05:25:54.888627Z"}
[client] {"event": "request to server with query: world", "severity": "info", "timestamp": "2021-03-17T05:26:11.550923Z"}
[server] {"event": "query: world", "severity": "info", "timestamp": "2021-03-17T05:26:11.567048Z"}
[loadgen] {"event": "check connectivity: http://clientservice:8080/_healthz", "severity": "info", "timestamp": "2021-03-17T05:26:11.533605Z"}
[loadgen] {"event": "/_healthz response: ok", "severity": "info", "timestamp": "2021-03-17T05:26:11.544267Z"}
[loadgen] {"event": "confirmed connection ot clientservice", "severity": "info", "timestamp": "2021-03-17T05:26:11.544527Z"}
最后,您就可以开始使用 OpenTelemetry 对应用进行插桩处理,以对服务进行分布式跟踪。
摘要
在此步骤中,您已在环境中准备好 Codelab 材料,并确认 Skaffold 运行正常。
后续步骤
在下一步中,您将修改 loadgen 服务的源代码,以插桩跟踪信息。
4. HTTP 的插桩
跟踪插桩和传播的概念
在修改源代码之前,我先用一个简单的图表简要说明分布式轨迹的工作原理。

在此示例中,我们对代码进行插桩,以将跟踪记录和 span 信息导出到 Cloud Trace,并在从 loadgen 服务到服务器服务的请求中传播跟踪上下文。
应用需要发送跟踪元数据(例如跟踪 ID 和 span ID),以便 Cloud Trace 将具有相同跟踪 ID 的所有 span 组装成一条跟踪记录。此外,应用还需要在请求下游服务时传播跟踪上下文(父 span 的跟踪 ID 和 span ID 的组合),以便下游服务了解它们正在处理哪个跟踪上下文。
OpenTelemetry 可帮助您:
- 生成唯一的跟踪记录 ID 和 span ID
- 将 Trace ID 和 Span ID 导出到后端
- 将跟踪上下文传播到其他服务
插桩第一个 span
检测负载生成器服务
按 Cloud Shell 右上角的按钮
打开 Cloud Shell 编辑器。从左侧窗格中的探索器打开 src/loadgen/loadgen.py,然后找到 main 函数。
src/loadgen/loadgen.py
def main():
...
# start request loop to client service
logger.info("start client request loop")
addr = f"http://{target}"
while True:
logger.info("start request to client")
call_client(addr)
logger.info("end request to client")
time.sleep(2.0)
在 main 函数中,您会看到循环调用其中的函数 call_client。在当前实现中,该部分包含 2 个记录函数调用开始和结束的日志行。现在,我们来对 Span 信息进行插桩,以跟踪函数调用的延迟时间。
首先,您需要创建一个具有唯一跟踪 ID 和 span ID 的 span。OpenTelemetry 为此提供了一个便捷的库。添加以下行,将 OpenTelemetry 库导入到您的代码中。
import structlog
+from opentelemetry import propagate, trace
+from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.instrumentation.requests import RequestsInstrumentor
+from opentelemetry.sdk.trace.export import SimpleSpanProcessor
+from opentelemetry.propagators.cloud_trace_propagator import CloudTraceFormatPropagator
由于负载生成器通过 requests 模块在 HTTP 中调用客户端应用,因此我们使用 requests 的扩展软件包并启用插桩。
from opentelemetry.propagators.cloud_trace_propagator import CloudTraceFormatPropagator
+
+RequestsInstrumentor().instrument()
然后,设置处理跟踪上下文和导出器设置的 Tracer 实例
target = os.environ.get("CLIENT_ADDR", "0.0.0.0:8080")
+ exporter = CloudTraceSpanExporter()
+ trace.get_tracer_provider().add_span_processor(SimpleSpanProcessor(exporter))
+ tracer = trace.get_tracer(__name__)
+ propagate.set_global_textmap(CloudTraceFormatPropagator())
+ trace.set_tracer_provider(TracerProvider())
+
# connectivity check to client service
healthz = f"http://{target}/_healthz"
logger.info(f"check connectivity: {healthz}")
请注意,由于本 Codelab 旨在帮助您了解跟踪插桩的工作原理,因此我们将 Tracer 配置为记录每个请求并将其发送到后端。(SimpleSpanProcessor()) 这不适合生产环境,因此请务必在对生产应用进行插桩时更改此部分。
现在,您可以使用 Tracer 检测 Span。重点在于,您需要做的就是明确生成一个 Span,仅此而已!虽然有两行代码可将事件元数据添加到 Span 中,但您无需手动生成唯一的 Trace ID 和 Span ID 并将其嵌入到 Span 中。
logger.info("start client request loop")
addr = f"http://{target}"
while True:
- logger.info("start request to client")
- call_client(addr)
- logger.info("end request to client")
+ with tracer.start_as_current_span("loadgen") as root_span:
+ root_span.add_event(name="request_start")
+ logger.info("start request to client")
+ call_client(addr)
+ root_span.add_event(name="request_end")
+ logger.info("end request to client")
time.sleep(2.0)
为了让 Docker build 提取所需的 OpenTelemetry 软件包,请运行以下命令:
poetry add "opentelemetry-exporter-gcp-trace=^1.0.0rc0" poetry add "opentelemetry-propagator-gcp=^1.0.0rc0" poetry add "opentelemetry-instrumentation-requests=^0.20b0"
您可以确认相应的依赖项说明已写入 pyproject.toml。
器械客户服务
在上一部分中,我们对下图中的红色矩形框内的部分进行了插桩。我们在负载生成器服务中实现了 span 信息。与负载生成器服务类似,现在我们需要对客户端服务进行插桩。与负载生成器服务的不同之处在于,客户端服务需要提取从 HTTP 标头中的负载生成器服务传播的跟踪 ID 信息,并使用该 ID 生成 Span。

打开 Cloud Shell 编辑器,然后添加所需的模块,就像我们为负载生成器服务所做的那样。
src/client/client.py
import flask
import grpc
import structlog
+from opentelemetry import propagate, trace
+from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
+from opentelemetry.instrumentation.flask import FlaskInstrumentor
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import SimpleSpanProcessor
+from opentelemetry.propagators.cloud_trace_propagator import \
+ CloudTraceFormatPropagator
import shakesapp_pb2
import shakesapp_pb2_grpc
您会注意到,您刚刚导入了 FlaskInstrumentor,该模块可代表用户为 Flask 应用启用自动插桩,以提取 HTTP 标头,从而通过一行代码获取跟踪上下文。OpenTelemetry 社区还提供了与其他主要库的类似实用集成。如需了解详情,您可以参阅官方文档。
app = flask.Flask(__name__)
+FlaskInstrumentor().instrument_app(app)
在开始插桩之前,您需要再次准备 Tracer 实例,这与我们在负载生成器服务中所做的类似。
logger.info(f"server address is {SERVER_ADDR}")
+exporter = CloudTraceSpanExporter()
+trace.get_tracer_provider().add_span_processor(SimpleSpanProcessor(exporter))
+propagate.set_global_textmap(CloudTraceFormatPropagator())
+trace.set_tracer_provider(TracerProvider())
@app.route("/")
def main_handler():
....
现在,您可以在处理程序中添加插桩了。找到 main_handler() 并修改向服务器服务抛出 gRPC 请求的部分。
@app.route("/")
def main_handler():
q, count = random.choice(list(queries.items()))
# get Tracer
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("client") as cur_span:
channel = grpc.insecure_channel(SERVER_ADDR)
stub = shakesapp_pb2_grpc.ShakespeareServiceStub(channel)
logger.info(f"request to server with query: {q}")
cur_span.add_event("server_call_start")
resp = stub.GetMatchCount(shakesapp_pb2.ShakespeareRequest(query=q))
cur_span.add_event("server_call_end")
if count != resp.match_count:
raise UnexpectedResultError(
f"The expected count for '{q}' was {count}, but result was {resp.match_count } obtained"
)
result = str(resp.match_count)
logger.info(f"matched count for '{q}' is {result}")
return result
与负载生成器服务类似,通过以下命令将所需软件包添加到 pyproject.toml 中。
poetry add "opentelemetry-exporter-gcp-trace=^1.0.0rc0" poetry add "opentelemetry-propagator-gcp=^1.0.0rc0" poetry add "opentelemetry-instrumentation-flask=^0.20b0"
然后,尝试通过 skaffold run 命令启动应用,并查看 Cloud Trace 信息中心显示的内容:
skaffold run --tail
在看到一些 build、push 和 deploy 消息后,您将看到 JSON 格式的应用日志。自行前往 Cloud Trace > 跟踪记录列表,检查是否收到了跟踪记录信息。由于负载生成器服务会定期向客户端服务发送请求,并且您已为所有请求启用跟踪,因此您会在跟踪列表中看到很多点。

点击其中一个,您会看到一个瀑布图,如下所示,其中说明了请求和响应过程中每个部分的延迟时间。找到“显示事件”旁边的复选框,然后您会在瀑布图中看到注释。这些注释是您通过 span.add_event() 方法在代码中插桩的注释。

您可能会发现,您看不到来自服务器服务的 span。此答案正确,因为我们根本没有在服务器服务中检测 Span。
摘要
在此步骤中,您已对负载生成器服务和客户端服务进行了插桩,并确认您能够成功跨服务传播跟踪上下文,以及将这两个服务的 span 信息导出到 Cloud Trace。
后续步骤
在下一步中,您将对客户端服务和服务器服务进行插桩,以确认如何通过 gRPC 传播跟踪上下文。
5. gRPC 的插桩
在上一步中,我们检测了此微服务中的前半部分请求。在此步骤中,我们尝试对客户端服务和服务器服务之间的 gRPC 通信进行插桩。(下图中的绿色和紫色矩形)

针对 gRPC 客户端的自动插桩
OpenTelemetry 的生态系统提供了许多实用的库,可帮助开发者对应用进行插桩。在上一步中,我们为“requests”模块使用了自动插桩。在此步骤中,由于我们尝试通过 gRPC 传播跟踪记录上下文,因此我们使用该库。
src/client/client.py
import flask
import grpc
import structlog
from opentelemetry import propagate, trace
from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
from opentelemetry.instrumentation.flask import FlaskInstrumentor
+from opentelemetry.instrumentation.grpc import GrpcInstrumentorClient
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.propagators.cloud_trace_propagator import \
CloudTraceFormatPropagator
import shakesapp_pb2
import shakesapp_pb2_grpc
app = flask.Flask(__name__)
FlaskInstrumentor().instrument_app(app)
+GrpcInstrumentorClient().instrument()
对于客户端服务,我们需要为插桩做的工作非常少。我们希望通过 gRPC 传播跟踪记录上下文,即当前 span 的跟踪记录 ID 和 span ID 的组合。因此,我们调用 GrpcInstrumentatorClient.instrument(),以便处理程序函数中的 gRPC 客户端可以将跟踪上下文嵌入到下面的 HTTP 标头中。
请务必使用 poetry add 命令将新依赖项添加到 pyproject.toml:
poetry add "opentelemetry-instrumentation-grpc=^0.20b0"
针对 gRPC 服务器的自动插桩
与我们为 gRPC 客户端所做的操作类似,我们为 gRPC 服务器调用了自动插桩。添加如下所示的导入,并在文件顶部调用 GrpcInstrumentationServer().instrument()。
注意:请务必拨打
GrpcInstrumentationServe()
在此步骤中,
GrpcInstrumentationClient()
。
src/server/server.py
import grpc
import structlog
from google.cloud import storage
from grpc_health.v1 import health_pb2, health_pb2_grpc
+from opentelemetry import propagate, trace
+from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
+from opentelemetry.instrumentation.grpc import GrpcInstrumentorServer
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import SimpleSpanProcessor
+from opentelemetry.propagators.cloud_trace_propagator import CloudTraceFormatPropagator
import shakesapp_pb2
import shakesapp_pb2_grpc
BUCKET_NAME = "dataflow-samples"
BUCKET_PREFIX = "shakespeare/"
+# enable auto gRPC server trace instrumentation
+GrpcInstrumentorServer().instrument()
+
接下来,您将添加导出器,以将跟踪信息发送到 Cloud Trace 后端。在 serve() 函数中添加以下代码。
def serve():
+ # start trace exporter
+ trace.set_tracer_provider(TracerProvider())
+ trace.get_tracer_provider().add_span_processor(
+ SimpleSpanProcessor(CloudTraceSpanExporter())
+ )
+ propagators.set_global_textmap(CloudTraceFormatPropagator())
+
+ # add gRPC services to server
server = grpc.server(futures.ThreadPoolExecutor(max_workers=4))
service = ShakesappService()
shakesapp_pb2_grpc.add_ShakespeareServiceServicer_to_server(service, server)
health_pb2_grpc.add_HealthServicer_to_server(service, server)
请务必在服务器服务中添加新添加的软件包。
poetry add "opentelemetry-exporter-gcp-trace=^1.0.0rc0" poetry add "opentelemetry-instrumentation-grpc=^0.20b0" poetry add "opentelemetry-propagator-gcp=^1.0.0rc0" poetry add "opentelemetry-instrumentation=^0.20b0"
运行微服务并确认轨迹
然后,使用 skaffold 命令运行修改后的代码。
skaffold run --tail
现在,您会在 Cloud Trace 的“跟踪记录列表”页面中再次看到许多跟踪记录。点击其中一个轨迹,您会发现跨度涵盖了从负载生成器服务到服务器服务的请求。

摘要
在此步骤中,您在 OpenTelemetry 生态系统库的支持下,对基于 gRPC 的通信进行了插桩。此外,您还确认了在负载生成器服务中生成的跟踪上下文已成功传递到服务器服务。
6. 恭喜
您已使用 OpenTelemetry 成功创建分布式跟踪记录,并已在 Google Cloud Trace 中确认微服务之间的请求延迟时间。
如需进行扩展练习,您可以自行尝试以下主题。
- 当前实现会发送健康检查生成的所有 span。如何从 Cloud Trace 中滤除这些 span?提示位于此处。
- 将事件日志与 span 相关联,并了解其在 Google Cloud Trace 和 Google Cloud Logging 中的运作方式。提示位于此处。
- 将某些服务替换为其他语言的服务,并尝试使用相应语言的 OpenTelemetry 对其进行插桩
注意:Google Kubernetes Engine 和 Google Artifact Registry 会持续消耗资源。
清理
完成本 Codelab 后,请停止 Kubernetes 集群并务必删除项目,以免在 Google Kubernetes Engine、Google Cloud Trace 和 Google Artifact Registry 上产生意外费用。
首先,使用以下命令删除集群:
skaffold delete
命令输出
Cleaning up... - deployment.apps "clientservice" deleted - service "clientservice" deleted - deployment.apps "loadgen" deleted - deployment.apps "serverservice" deleted - service "serverservice" deleted
删除集群后,在菜单窗格中依次选择“IAM 和管理”>“设置”,然后点击“关停”按钮。

然后在对话框中的表单中输入项目 ID(而非项目名称),并确认关停。