1. 简介
上次更新日期:2021 年 3 月 5 日
应用的可观测性
可观测性和 OpenTelemetry
可观测性是用于描述系统特性的术语。具有可观测性的系统可让团队主动调试其系统。在这种情况下,可观测性的三大支柱:日志、指标和跟踪记录是系统获取可观测性的基本插桩。
OpenTelemetry 是一组规范和 SDK,可加快对可观测性所需的遥测数据(日志、指标和跟踪记录)的插桩和导出。OpenTelemetry 是 CNCF 下的开放标准和社区驱动项目。利用项目及其生态系统提供的库,开发者能够以与供应商无关的方式针对多种架构检测其应用。
分布式跟踪
在日志、指标和跟踪记录中,跟踪记录是一种遥测方法,用于了解系统中进程特定部分的延迟时间。特别是在微服务时代,分布式跟踪记录是找出整个分布式系统中延迟瓶颈的有力驱动因素。
在分析分布式跟踪记录时,跟踪记录数据可视化是一目了然地了解整体系统延迟时间的关键。在分布式跟踪中,我们会处理一组调用,以采用包含多个 Span 的 Trace 形式处理对系统入口点的单个请求。
Span 表示在分布式系统中完成的单个工作单元,记录开始和结束时间。span 通常彼此之间存在分层关系,在下图中,所有较小的 span 都是较大 /messages span 的子 span,并组合成一个跟踪记录,显示系统的工作路径。
Google Cloud Trace 是分布式跟踪后端的可选方案之一,与 Google Cloud 中的其他产品紧密集成。
构建内容
在此 Codelab 中,您将在名为“Shakesapp”的服务中对轨迹信息进行插桩 (instrument)Google Kubernetes Engine 上运行的 Kubernetes 集群。Shakesapp 的架构如下所述:
- 客户端向服务器发送查询字符串
- 服务器接受来自客户端的查询,从 Google Cloud Storage 提取所有 Shakespare 作品(文本格式),搜索包含该查询的行,并返回与客户端匹配的行号。
您将在整个请求中检测跟踪信息。
学习内容
- 如何在 Python 项目中开始使用 OpenTelemetry Trace 库
- 如何使用库创建 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 Artifacat 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 中,我们使用“诗歌”作为严格管理软件包版本在 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 集群,在 GKE 上运行 Shakesapp,因此需要启用 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 集群了。接下来,我们准备用于推送和部署容器的 Container Registry。在此步骤中,我们需要设置 GAR 和 Skaffold 以使用它。
Artifact Registry 设置
前往“Artifact Registry”菜单并按“启用”按钮。
片刻之后,您会看到 GAR 的代码库浏览器。点击“创建代码库”按钮并输入代码库的名称
在此 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.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
- 为 Container Registry 创建了 Artifact Registy 代码库
- 设置 Skaffold 以使用 Container Registry
- 创建一个 Kubernetes 集群并在其中运行 Codelab 微服务
后续步骤
在下一步中,您将构建、推送微服务并将其部署到集群
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
- 清单: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 中默认代码库的配置如何。在这种情况下,请尝试添加“–default-repo”“skaffold run”选项如下所示。
$ 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 的插桩
跟踪记录插桩和传播的概念
在修改源代码之前,我先用简单的图表简要说明分布式跟踪记录的工作原理。
在此示例中,我们将对代码进行插桩 (instrument),以将 Trace 和 Span 信息导出到 Cloud Trace,并在从 loadgen 服务发出的请求中将轨迹上下文传播到服务器服务。
应用需要发送跟踪记录元数据(如跟踪记录 ID 和 Span ID),以便 Cloud Trace 将具有相同跟踪记录 ID 的所有 span 整合到一个跟踪记录中。此外,应用需要在请求下游服务时传播跟踪上下文(跟踪 ID 和父 span 的 Span ID 的组合),以便应用了解其正在处理的跟踪上下文。
OpenTelemetry 可帮助您:
- 生成唯一的跟踪记录 ID 和 Span ID
- 将跟踪记录 ID 和 Span ID 导出到后端
- 将跟踪记录上下文传播到其他服务
乐器第一个跨度
插桩加载生成器服务
按 Cloud Shell 右上角的 按钮,打开 Cloud Shell Editor。从左侧窗格中的 Explorer 中打开 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()
然后设置用于处理跟踪内容和导出器设置的跟踪程序实例
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,我们将跟踪器配置为记录每个请求并将其发送到后端。(SimpleSpanProcessor()
) 这不适合生产环境,因此在对生产应用进行插桩时,请务必更改此部分。
现在,您可以使用跟踪器对 Span 进行插桩。这里的要点是,您需要做的是显式生成 Span,就是这么简单!虽然有两行将事件元数据添加到 Span,但您无需手动生成唯一的跟踪记录 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 构建能够提取所需的 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 Editor 并添加所需的模块,就像我们处理加载生成器服务一样。
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
在看到一些构建、推送和部署消息后,您将看到 JSON 格式的应用日志。转到 Cloud Trace >跟踪列表,用于检查您是否获取了跟踪信息。由于负载生成器服务会定期向客户端服务发送请求,并且您为所有请求启用了跟踪记录,因此您会开始在跟踪记录列表中看到大量的点。
点击其中一个,您会看到如下所示的瀑布图,其中说明了请求和响应过程中各部分的延迟时间。找到“Show Events”旁边的复选框,然后您就可以在广告瀑布流图表中看到相应的注释。这些注解是您通过 span.add_event()
方法在代码中插桩的注解。
您可能会注意到,您没有看到服务器服务的 span。它是正确的,因为我们未在服务器服务中对 Span 进行插桩。
摘要
在此步骤中,您已对负载生成器服务和客户端服务进行了插桩测试,并确认可以成功跨服务传播 Trace Context,并将 Span 信息从这两项服务导出到 Cloud Trace。
后续步骤
在下一步中,您将对客户端服务和服务器服务进行插桩 (instrument),以确认如何通过 gRPC 传播跟踪上下文。
5. 适用于 gRPC 的插桩
在上一步中,我们在此微服务中对请求的前半部分进行了插桩测试。在此步骤中,我们将尝试对客户端服务和服务器服务之间的 gRPC 通信进行插桩。(下图中的绿色和紫色矩形框)
针对 gRPC 客户端的自动插桩
OpenTelemetry 的生态系统提供了许多方便开发者检测应用的库。在上一步,我们针对“请求”使用了自动插桩模块。在此步骤中,由于我们要尝试通过 gRPC 传播 Trace Context,因此需要使用该库。
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 实现的,跟踪 ID 和当前 Span 的 Span ID 的组合。因此,我们调用 GrpcInstrumentatorClient.instrument()
,以便 Hander 函数中的 gRPC 客户端能够将 Trace Context 嵌入到下面的 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 的“Trace 列表”页面中看到许多跟踪记录。点击其中一个跟踪记录,您现在会发现该请求跨越了从加载生成器服务到服务器服务的请求。
摘要
在此步骤中,您通过 OpenTelemetry 生态系统库提供的支持对基于 gRPC 的通信进行了插桩测试。此外,您还确认负载生成器服务中生成的跟踪上下文已成功传送到服务器服务。
6. 恭喜
您已成功使用 OpenTelemery 创建了分布式跟踪记录,并在 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(而非项目名称),并确认关闭。