对于在 Kubernetes 上运行的服务,使用 Istio 等服务网格可实现对服务到服务流量的分布式跟踪,而无需专用插桩 (instrumentation)。但是,您可能希望更好地控制跟踪记录,可能需要在跟踪信息中捕获应用内部资料,或者可能需要跟踪未在 Kubernetes 上运行的代码。OpenCensus 是一个开源库,可用于对分布式微服务应用进行插桩,以跨各种语言、平台和环境收集跟踪记录和指标。
本教程面向希望了解分布式跟踪基础知识并将其应用于服务以提高服务可观测性的开发者、SRE 和 DevOps 工程师。
本教程假定您熟悉以下内容:
- Go 编程语言
- Google Kubernetes Engine (GKE)
DevOps 现状报告确定了可提高软件交付方面表现的功能。本教程将帮助您使用以下功能:
目标
- 创建 GKE 集群并部署示例应用。
- 查看 OpenCensus 插桩代码。
- 查看插桩生成的跟踪记录和日志。
参考架构
下图展示了您在本教程中部署的架构。
如上图所示,您创建了两个 GKE 集群,并将应用部署到每个集群。用户流量将发送到前端集群上的前端应用。前端集群上的前端 pod 会与后端集群上的后端 pod 通信。后端 pod 调用外部 API 端点。
您可以使用 Cloud Build(一个全代管式持续集成、交付和部署平台)根据示例代码构建容器映像并将其存储在 Container Registry 中。GKE 集群在部署时从 Container Registry 中拉取映像。
示例应用
本教程中的示例应用由两个使用 Go 编写的微服务组成。
前端服务接受对 /
网址的 HTTP 请求并调用后端服务。后端服务的地址由 BACKEND
环境变量定义。该变量的值是在您创建的 ConfigMap 对象中设置。
后端服务接受对 /
网址的 HTTP 请求,并对 DESTINATION_URL
环境变量中定义的外部网址进行拨出调用。变量的值通过 ConfigMap
对象设置。外部调用完成后,backend
服务会将 HTTP 状态调用(例如 200,
)返回给调用者。
了解跟踪记录、span 和上下文
如需了解分布式跟踪概念的最贴切描述,请参阅 Google 发布的 Dapper 研究论文。在这篇论文中,下图显示了一个跟踪记录中的五个 span。
该图显示了发出两个后端请求的单个前端请求。 第二个后端调用需要两个帮助程序调用才能完成。每个调用都标有其 span ID 和父级 span 的 ID。
跟踪记录是描述分布式系统如何响应用户请求的信息汇总。跟踪记录由 span 组成,其中每个 span 表示为处理用户请求而涉及的特定请求和响应对。父级 span 描述最终用户观察到的延迟时间。每个子级 span 描述如何调用和响应分布式系统中的特定服务,并记录针对每个服务捕获的延迟信息。
在分布式系统中进行跟踪的挑战是,对各种后端服务发出后续请求时,原始前端请求的信息不会自动传递或者这些信息本身不会传递。在 Go 中,您可以使用上下文(集群 IP 地址和凭据)发出请求,这意味着在 HTTP 标头中加载其他信息。上下文概念通过 OpenCensus 进行扩展,以包含 span 上下文,您可以在其中包含每个后续请求的父级 span 信息。然后,您可以附加子级 span 以编写总体跟踪记录,以便查看用户请求如何遍历系统并最终被提供给用户。
上下文不是 Go 特有的。如需详细了解 OpenCensus 支持 SpanContext
功能的语言,请参阅 OpenCensus 功能矩阵。
费用
本教程使用 Google Cloud 的以下收费组件:
您可使用价格计算器根据您的预计使用量来估算费用。 Google Cloud 新用户可能有资格申请免费试用。
完成本教程后,您可以删除所创建的资源以避免继续计费。如需了解详情,请参阅清理。
准备工作
-
登录您的 Google 帐号。
如果您还没有 Google 帐号,请注册新帐号。
-
在 Google Cloud Console 的项目选择器页面上,选择或创建一个 Google Cloud 项目。
-
确保您的 Cloud 项目已启用结算功能。 了解如何确认您的项目是否已启用结算功能。
- 启用 GKE, Cloud Trace, Cloud Build, Cloud Storage, and Container Registry API。
设置环境
在本部分中,您将使用本教程中使用的工具来设置环境。您将通过 Cloud Shell 运行本教程中的所有终端命令。
-
在 Cloud Console 中,激活 Cloud Shell。
Cloud Shell 会话随即会在 Cloud Console 的底部启动,并显示命令行提示符。Cloud Shell 是一个已安装 Cloud SDK 的 Shell 环境,其中包括
gcloud
命令行工具以及已为当前项目设置的值。该会话可能需要几秒钟时间来完成初始化。 - 设置环境变量:
export PROJECT_ID=$(gcloud config list --format 'value(core.project)' 2>/dev/null)
- 克隆以下 Git 代码库,下载本教程所需的文件:
git clone https://github.com/GoogleCloudPlatform/gke-observability-tutorials.git cd $HOME/gke-observability-tutorials/gke-opencensus-stackdriver/go/http WORKDIR=$(pwd)
您可以将代码库文件夹设为
$WORKDIR
,然后从中执行与本教程相关的所有任务,这样您就可以在完成本教程时删除该文件夹。
安装工具
在 Cloud Shell 中,安装
kubectx
和kubens
。git clone https://github.com/ahmetb/kubectx $WORKDIR/kubectx export PATH=$PATH:$WORKDIR/kubectx
您可以使用这些工具来处理多个 Kubernetes 集群、上下文和命名空间。
在 Cloud Shell 中,安装开源负载生成工具 Apache Bench:
sudo apt-get install apache2-utils
创建 GKE 集群
在本部分中,您将创建两个 GKE 集群,然后在其中部署示例应用。默认情况下,创建的 GKE 集群对 Cloud Trace API 拥有只写权限,因此您在创建集群时无需定义访问权限。
在 Cloud Shell 中,创建集群:
gcloud container clusters create backend-cluster \ --zone=us-west1-a --enable-stackdriver-kubernetes \ --verbosity=none --async gcloud container clusters create frontend-cluster \ --zone=us-west1-a --enable-stackdriver-kubernetes \ --verbosity=none
在本教程中,集群位于
us-west1-a
地区中。如需了解详情,请参阅地理位置和区域。获取集群凭据并将其存储在本地:
gcloud container clusters get-credentials backend-cluster --zone=us-west1-a gcloud container clusters get-credentials frontend-cluster --zone=us-west1-a
重命名集群的上下文,以便在本教程后面的部分中可以更轻松地访问这些上下文:
kubectx backend=gke_${PROJECT_ID}_us-west1-a_backend-cluster kubectx frontend=gke_${PROJECT_ID}_us-west1-a_frontend-cluster
查看 OpenCensus 插桩
在以下部分中,您将查看示例应用的代码,以了解如何使用上下文传播来允许将多个请求的 span 附加到单个父级跟踪记录。
查看前端代码
前端服务代码中有三个导入项:
go.opencensus.io/plugin/ochttp
和go.opencensus.io/plugin/ochttp/propagation/tracecontext
导入项包含ochttp
插件,用于在请求之间传播上下文。上下文会携带后续 Span 附加到的跟踪记录的相关信息。contrib.go.opencensus.io/exporter/stackdriver
导入项会将跟踪记录导出到 Trace。如需查看 OpenCensus 支持的后端列表,请参阅导出工具。github.com/gorilla/mux
导入项是示例应用用于处理请求的库。
main()
函数设置将跟踪记录导出到 Trace 这一流程,并使用 mux 路由器来处理向/
网址发出的请求:- 处理程序使用 HTTP 传播为请求添加上下文。
- 在本教程中,采样设置为
AlwaysSample
。默认情况下,OpenCensus 会以预定的速率对跟踪记录进行采样,您可以使用此参数控制该速率。
查看
mainHandler()
函数:为了捕获总体请求延迟时间,此函数会创建根 span。将后续 span 附加到根 span。
// create root span ctx, rootspan := trace.StartSpan(context.Background(), "incoming call") defer rootspan.End()
为了设置
backend
调用的时间,该函数会创建一个子级 span。// create child span for backend call _, childspan := trace.StartSpan(ctx, "call to backend") defer childspan.End()
函数创建对后端的请求。
// create request for backend call req, err := http.NewRequest("GET", backendAddr, nil) if err != nil { log.Fatalf("%v", err) } childCtx, cancel := context.WithTimeout(req.Context(), 1000*time.Millisecond) defer cancel() req = req.WithContext(childCtx)
函数将 span 上下文添加到该请求。
// add span context to backend call and make request format := &tracecontext.HTTPFormat{} format.SpanContextToRequest(rootspan.SpanContext(), req)
查看后端代码
后端代码的
main()
函数类似于前端服务的main()
函数 - 后端代码会设置 Trace 导出工具并使用 mux 路由器来处理用mainHandler()
函数向/
发出的请求。mainHandler()
函数使用传入请求来获取 Go 提供的 HTTP 上下文和 OpenCensus 提供的跟踪上下文。然后,该函数会创建子级 span 并将其附加到跟踪上下文。最后,它会调用callRemoteEndpoint()
函数以发出需要捕获其延迟时间的另一个调用。callRemoteEndpoint()
函数使用 HTTP 库进行远程端点调用。由于对此调用的跟踪是在父级函数中处理,因此不需要使用跟踪上下文,也不需要创建更多子级 span。
部署应用
在本部分中,您将使用 Cloud Build 为后端和前端服务构建容器映像,并将其部署到 GKE 集群。
构建后端服务
在 Cloud Shell 中,切换到
backend
目录:cd $WORKDIR/backend/
提交构建:
gcloud builds submit . --tag=gcr.io/$PROJECT_ID/backend:latest
确认容器映像已成功创建且可在 Container Registry 中使用:
gcloud container images list
如果输出内容类似如下,则表明容器映像已成功创建,其中
project-id
是您的 Cloud 项目 ID:NAME gcr.io/project-id/backend
部署后端服务
在 Cloud Shell 中,将
kubectx
上下文设置为backend
集群:kubectx backend
部署
configmap
文件:export PROJECT_ID=$(gcloud info --format='value(config.project)') envsubst < backend-configmap.yaml | kubectl apply -f -
创建
backend
部署文件:envsubst < backend-deployment.yaml | kubectl apply -f -
确认 pod 正在运行。
kubectl get pods
输出显示的
Status
为Running
:NAME READY STATUS RESTARTS AGE backend-645859d95b-7mx95 1/1 Running 0 52s backend-645859d95b-qfdnc 1/1 Running 0 52s backend-645859d95b-zsj5m 1/1 Running 0 52s
使用负载平衡器公开
backend
部署:kubectl expose deployment backend --type=LoadBalancer
获取
backend
服务的 IP 地址:kubectl get services backend
输出内容类似如下:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE backend LoadBalancer 10.11.247.58 34.83.88.143 8080:30714/TCP 70s
重复此命令,直到服务的
EXTERNAL-IP
字段从<pending>
更改为 IP 地址。捕获上一步中的 IP 地址作为变量:
export BACKEND_IP=$(kubectl get svc backend -ojson | jq -r '.status.loadBalancer.ingress[].ip')
构建前端服务
在 Cloud Shell 中,切换到
frontend
目录:cd $WORKDIR/frontend/
提交构建:
gcloud builds submit . --tag=gcr.io/$PROJECT_ID/frontend:latest
确认容器映像已成功创建且可在 Container Registry 中使用:
gcloud container images list
如果输出内容类似如下,则表明已成功创建容器映像:
NAME gcr.io/qwiklabs-gcp-47a7dcba55b334f7/backend gcr.io/qwiklabs-gcp-47a7dcba55b334f7/frontend
部署前端服务
在 Cloud Shell 中,将
kubectx
上下文设置为后端集群:kubectx frontend
部署
configmap
文件:export PROJECT_ID=$(gcloud info --format='value(config.project)') envsubst < frontend-configmap.yaml | kubectl apply -f -
创建
frontend
部署文件:envsubst < frontend-deployment.yaml | kubectl apply -f -
确认 pod 正在运行。
kubectl get pods
输出显示的
Status
为Running
:NAME READY STATUS RESTARTS AGE frontend-747b445499-v7x2w 1/1 Running 0 57s frontend-747b445499-vwtmg 1/1 Running 0 57s frontend-747b445499-w47pf 1/1 Running 0 57s
使用负载平衡器公开
frontend
部署:kubectl expose deployment frontend --type=LoadBalancer
获取
frontend
服务的 IP 地址:kubectl get services frontend
输出内容类似如下:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE frontend LoadBalancer 10.27.241.93 34.83.111.232 8081:31382/TCP 70s
重复此命令,直到服务的
EXTERNAL-IP
字段从<pending>
更改为 IP 地址。捕获上一步中的 IP 地址作为变量:
export FRONTEND_IP=$(kubectl get svc frontend -ojson | jq -r '.status.loadBalancer.ingress[].ip')
加载应用并查看跟踪记录
在本部分中,您将使用 Apache Bench 实用程序为您的应用创建请求,并在 Trace 中查看生成的跟踪记录。
在 Cloud Shell 中,使用 Apache Bench 通过 3 个并发线程来生成 1000 个请求:
ab -c 3 -n 1000 http://${FRONTEND_IP}:8081/
在 Cloud Console 中,转到跟踪记录列表页面。
如需查看时间轴,请点击其中一个标有
incoming call
的 URI。此跟踪记录包含三个名称如下的 span:
incoming call
span 捕获客户端观察到的端到端延迟时间。call to backend
span 捕获由前端观察到的后端调用的延迟时间。call remote endpoint
span 捕获由后端观察到的外部端点调用的延迟时间。
清理
若要避免产生费用,最简单的方法是删除您为本教程创建的 Cloud 项目。或者,您也可以删除各个资源。删除项目
- 在 Cloud Console 中,转到管理资源页面。
- 在项目列表中,选择要删除的项目,然后点击删除。
- 在对话框中输入项目 ID,然后点击关闭以删除项目。
后续步骤
- 了解 OpenCensus。
- 详细了解如何使用 OpenCensus 进行分布式跟踪。
- 详细了解 Go 中的 HTTP 上下文传播。
- 试用其他 Google Cloud 功能。查阅我们的教程。
- 阅读我们关于 DevOps 的资源。
- 详细了解与本教程相关的 DevOps 功能:
- 进行 DevOps 快速检查,了解您与业界其他公司相比所处的位置。