基于 Jaeger 进行微服务链路追踪

本文涉及的产品
应用实时监控服务-可观测链路OpenTelemetry版,每月50GB免费额度
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: 基于解决不同行业、业务应用的可扩展性、可用性等一系列问题,由此而生的微服务架构得到了各大厂商的、组织以及个人的青睐,随之而来便广泛应用于各种行业场景应用中。然而,随着时间的推移,越来越多的问题慢慢地呈现在大众的视野中。

     基于解决不同行业、业务应用的可扩展性、可用性等一系列问题,由此而生的微服务架构得到了各大厂商的、组织以及个人的青睐,随之而来便广泛应用于各种行业场景应用中。然而,随着时间的推移,越来越多的问题慢慢地呈现在大众的视野中。

     其中,最为核心的问题莫过于微服务分布式性质导致的运行问题,以及带来的 2 个至关重要的挑战:

     1、Monitoring,监控,如何能够全方位监控所有服务及其所涉及的相关指标。

     2、Tracing,追踪,如何能够立体化追踪所有请求并识别我们应用服务中链路调用的瓶颈?

     在本文中,我们将介绍如何将 Jaeger 被分类的跟踪集成到 Spring Boot MicroServices 中。在解析之前,我们先来了解下 Jaeger 链路追踪工作流原理,具体如如下参考示意图所示:

   基于 Jaeger 组件架构原理,我们可以看到:在分布式系统中处理,当一个跟踪完成后,通过 Jaeger-Agent 将数据推送到 Jaeger-Collector。此时,Jaeger-Collector 负责处理四面八方推送来的跟踪信息,然后存储到后端系统库中,例如:可以存储到 ES、数据库等。然后,用户可以借助 Jaeger-UI 图形界面观测到这些被分析出来的跟踪信息。

    关于数据采样率,在实际的业务场景中,链路追踪系统本身也会造成一定的性能低损耗,如果完整记录每次请求,对于生产环境可能会带来极大的性能损耗,因此,我们需要依据当前现状进行采样策略配置。截止目前,当前可支持5种采样率设置,具体如下:
    1、固定采样(sampler.type=const)sampler.param=1 全采样, sampler.param=0 不采样
    2、按百分比采样(sampler.type=probabilistic)sampler.param=0.1 则随机采十分之一的样本
   3、采样速度限制(sampler.type=ratelimiting)sampler.param=2.0 每秒采样两个traces

  4、动态获取采样率 (sampler.type=remote) 此策略为默认配置,可以通过配置从 Agent 中获取采样率的动态设置
     5、自适应采样(Adaptive Sampling)开发计划中

   在实际的业务场景中,为了能够追溯某一请求运行轨迹,通常,在理想情况下,我们需要对整个链路拓扑进行全方位追踪,以便能够在业务出现异常时能够快速响应、快速处理。因此,无论是基于 VM 的 Spring Cloud 微服务还是基于 Container ,其链路追踪体系基本的模型参考示意图如下所示:

    在本文中,我们以 “Demo” 的形式对基于 Jaeger 的分布式链路追踪系统工程进行简要描述。基于上述的模型参考示意图,我们开始进行相关组件的部署。

    为了能够尽可能详尽地解析 Jaeger 组件的基础原理,我们将先以最简单、明了的方式进行 Jaeger 组件部署。基于 Jaeger 的“all in one” 来进行镜像的构建、启动。具体如下所示:


[administrator@JavaLangOutOfMemory ~ ]% docker run -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
  -p 5775:5775/udp \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 14268:14268 \
  -p 14250:14250 \
  -p 9411:9411 \
  jaegertracing/all-in-one:latest

     在本文中,有关端口释义如下所示:

组件
端口
协议 描述
Agent
6831 UDP

应用程序向代理发送跟踪的端口,接受 Jaeger.thrift而不是 Compact thrift协议

Agent 6832 UDP 通过二进制thrift协议接受 Jaeger.thrift ,需要某些不支持压缩的客户端库
Agent
5775 UDP
接收兼容zipkin的协议数据
Agent 5778 HTTP
大数据流量下不建议使用
...
... ... ...
组件
端口
协议
描述
Collector
14250
TCP
Agent 发送 Proto 格式数据
Collector
14267
TCP
Agent 发送 Jaeger.thrift格式数据
Collector 14268
HTTP
从客户端接受 Jaeger.thrift
Collector
14269
HTTP
健康检查

组件
端口
协议
描述
Query
16686
HTTP
HTTP 查询服务于 Jaeger UI
Query
16687
HTTP
健康检查
... ...
...
...

    自 1.17 版本(https://www.jaegertracing.io/docs/1.23/operator/ #当前版本),开始,我们还可以基于 Operator 的方式进行部署,并且其支持如下几种业务方式:

    1、All-In-One Strategy

    2、Production Strategy

    3、Streaming Strategy

    Jaeger Operator:Jaeger Operator for Kubernetes 简化了在 Kubernetes 上的部署和运行 Jaeger。Jaeger Operator 是 Kubernetes Qperator 的实现。从技术层面上讲,Qperator 是打包,部署和管理 Kubernetes 应用程序的一种方法。Jaeger Operator 版本跟踪 Jaeger 组件(查询,收集器,代理)的一种版本。具体部署步骤如下所示:


[administrator@JavaLangOutOfMemory ~ ]% kubectl create namespace jaeger


[administrator@JavaLangOutOfMemory ~ ]% kubectl create -n jaeger -f  https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/crds/jaegertracing.io_jaegers_crd.yaml 
[administrator@JavaLangOutOfMemory ~ ]% kubectl create -n jaeger -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/service_account.yaml
[administrator@JavaLangOutOfMemory ~ ]% kubectl create -n jaeger -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role.yaml
[administrator@JavaLangOutOfMemory ~ ]% kubectl create -n jaeger -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role_binding.yaml
[administrator@JavaLangOutOfMemory ~ ]% kubectl create -n jaeger -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/operator.yaml

     此时,查看其状态,具体命令如下所示:


[administrator@JavaLangOutOfMemory ~ ]% kubectl get all -n jaeger

     然后,进行 Jaeger 实例的创建,创建 jaeger.yaml 文件,配置 ES 集群及资源限制,具体如下所示:Deployment 以及所涉及 demo-prod-collector 容器的 CPU 和内存使用大小。如下示例文件,定义了最大数量可以起 10 个 Pod。


apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
  name: demo-prod
spec:
  strategy: production
  storage:
    type: elasticsearch
    options:
      es:
        server-urls: http://10.172.10.1:9200
        index-prefix: 
  collector:
    maxReplicas: 10
    resources:
      limits:
        cpu: 500m
        memory: 512Mi
[administrator@JavaLangOutOfMemory ~ ]% kubectl apply -f  jaeger.yaml  -n jaeger
jaeger.jaegertracing.io/demo-prod created

     若实际的业务场景中,如果流量过大,我们可以借助接入 Kafka 集群以减轻 ES 存储库压力,故此,修改后的 jaeger.yaml 文件如下所示:


apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
  name: demo-streaming
spec:
  strategy: streaming
  collector:
    options:
      kafka:
        producer:
          topic: jaeger-spans
          brokers: demo-cluster-kafka-brokers.kafka:9092   #修改为kafka地址
  ingester:
    options:
      kafka:
        consumer:
          topic: jaeger-spans
          brokers: demo-cluster-kafka-brokers.kafka:9092  #修改为kafka地址
      ingester:
        deadlockInterval: 5s
  storage:
    type: elasticsearch
    options:
      es:
        server-urls: http://elasticsearch:9200   #修改为ES地址

     针对 Agent 的部署,Agent 官方目前有两种部署方案,一种是基于 DaemonSet 方式,一种是基于 Sidecar 方式。依据官方所述,Jaeger 中的 Agent 组件是作为 Tracer 和 Collector 之间的 buffer, 所以 Agent 应该离 Tracer 越近越好,通常应该是 Tracer 的 Localhost, 基于这样的假定,Tracer 能够直接通过UDP发送 span 到 Agent,达到最好的性能和可靠性之间的平衡。

     DaemonSet 的 Pod 运行在节点(Node)级别,此形式的 Pod 如同每个节点上的守护进程,Kubernetes 确保每个节点有且只有一个 Agent Pod 运行, 如果以 DaemonSet 方式部署,则意味着这个 Agent 会接受节点上所有应用 Pods 发送的数据,对于 Agent 来说所有的 Pods 都是同等对待的。这样确实能够节省一些内存,但是一个 Agent 可能要服务同一个节点上的数百个 Pods。

     Sidecar 是在应用 Pod 中增加其他服务,在 Kubernetes 中服务是以 Pod 为基本单位的,但是一个 Pod 中可以包含多个容器, 这通常可以用来实现嵌入一些基础设施服务, 在 Sidecar 方式部署下,对于 Jaeger Agent 会作为 Pod 中的一个容器和 Tarcer 并存,由于运行在应用级别,不需要额外的权限,每一个应用都可以将数据发送到不同的 Collector 后端,这样能保证更好的服务扩展性。

   综合对比分析,若我们基于私有云环境且信任 Kubernetes 集群上运行的应用,通常建议可以采用 DaemonSet 进行部署,毕竟,此种方式尽可能占用较少的内存资源。反之,若为公有云环境,或者希望获得多租户能力,Sidecar 可能更好一些,由于 Agent 服务当前没有任何安全认证手段,这种方式不需要在 Pod 外暴露 Agent 服务,相比之下更加安全一些,尽管内存占用会稍多一些(每个 Agent 内存占用在20M以内)。

    1、基于 DaemonSet 模式部署


apiVersion: apps/v1
 kind: DaemonSet
 metadata:
   name: jaeger-agent
   labels:
     app: jaeger-agent
 spec:
   selector:
     matchLabels:
       app: jaeger-agent
   template:
     metadata:
       labels:
         app: jaeger-agent
     spec:
       containers:
         - name: jaeger-agent
           image: jaegertracing/jaeger-agent:1.12.0
           env:
             - name: REPORTER_GRPC_HOST_PORT
               value: "jaeger-collector:14250"
           resources: {}
       hostNetwork: true
       dnsPolicy: ClusterFirstWithHostNet
       restartPolicy: Always

    通过 Kubernetes Downward API 将节点的 IP 信息(status.hostIP) 以环境变量的形式注入到应用容器中。

    2、基于 Sidecar 模式部署


apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: myapp
    labels:
      app: myapp
  spec:
    replicas: 1
    selector:
      matchLabels:
        app: myapp
    template:
      metadata:
        labels:
          app: myapp
      spec:
        containers:
          - name: myapp
            image: example/myapp:version
          - name: jaeger-agent
            image: jaegertracing/jaeger-agent:1.12.0
            env:
              - name: REPORTER_GRPC_HOST_PORT
                value: "jaeger-collector:14250"

     至此,Jaeger 服务部署 OK。剩余组件的部署可参考官网。接下来,我们来看一下 Jaeger 接入 Traefik 组件的相关配置。默认情况下,Traefik 使用 Jaeger 来最为追踪系统的后端实现.,具体配置如下所示:


[administrator@JavaLangOutOfMemory ~ ]% cat traefik.toml
[tracing]
  [tracing.jaeger]          # 开启jaeger的追踪支持
    samplingServerURL = "http://localhost:5778/demo"     # 指定jaeger-agent的http采样地址
    samplingType = "const"  # 指定采样类型[const(const|probabilistic|rateLimiting)]
    samplingParam = 1.0      # 采样参数的值[1.0(const:0|1,probabilistic:0-1,rateLimiting:每秒的span数)]
    localAgentHostPort = "127.0.0.1:6831"                    # 本地agent主机和端口(会发送到jaeger-agent)
    gen128Bit = true        # 生成128位的traceId,兼容OpenCensus
    propagation = "jaeger"  # 设置数据传输的header类型[jaeger(jaeger|b3兼容OpenZipkin)]
    traceContextHeaderName = "demo-trace-id"  # 跟踪上下文的header,用于传输跟踪上下文的http头名
  [tracing.jaeger.collector]  # 指定jaeger的collector服务
    endpoint = "http://127.0.0.1:14268/api/traces?format=jaeger.thrift"
    user = "demo-user"          # 向collector提交时的http认证用户[""]
    password = "demo-password"  # 向collector提交时的http认证密码[""]
# cli 配置
--tracing.jaeger=true
--tracing.jaeger.samplingServerURL=http://localhost:5778/demo
--tracing.jaeger.samplingType=const
--tracing.jaeger.samplingParam=1.0
--tracing.jaeger.localAgentHostPort=127.0.0.1:6831
--tracing.jaeger.gen128Bit
--tracing.jaeger.propagation=jaeger
--tracing.jaeger.traceContextHeaderName=uber-trace-id
--tracing.jaeger.collector.endpoint=http://127.0.0.1:14268/api/traces?format=jaeger.thrift
--tracing.jaeger.collector.user=demo-user
--tracing.jaeger.collector.password=demo-password

    针对 Spring Boot 微服务,主要涉及以下步骤,具体如下所示:

    1、整合 Jaeger ,引入 Maven 依赖


<dependency>
    <groupId>io.opentracing.contrib</groupId>
    <artifactId>opentracing-spring-jaeger-web-starter</artifactId>
    <version>3.3.1</version>
</dependency> 

    2、连接属性配置


spring.application.name=demo
opentracing.jaeger.enabled=true
opentracing.jaeger.log-spans=false
# opentracing.jaeger.enable128-bit-traces=true 

    3、其他参数配置,诸如,使用 JaegerAutoConfiguration 进行自动配置,以及 Log back 日志配置文件等,以便能够有效对服务请求链路进行全方位追踪。基于日志配置文件,以及结合官网给出的参考,主要针对自定义参数 traceId、spanId、sampled,当然这些参数也可以在 new MDCScopeManager.Builder() 时指定。具体可参考如下配置所示:


<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
    <Appenders>
        <Console name="console" target="SYSTEM_OUT">
            <PatternLayout
                    pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg% traceId=%X{traceId} spanId=%X{spanId} sampled=%X{sampled}%n" />
        </Console>
    </Appenders>
    <Loggers>
        <Root level="debug" additivity="false">
            <AppenderRef ref="console" />
        </Root>
    </Loggers>
</Configuration>

    基于上述的配置,即可完成 Spring Boot 微服务的部署(Order Service、Payment Service、Account Service、Customer Service以及其他服务),相关服务的部署及源码暂不在本文中描述,后续将呈现。至此,在整个网络架构拓扑中,接入层 Traefik 和 服务层 Spring Boot 已完成 Jaeger 分布式链路追踪系统的接入,具体生成的相关依赖图如下所示:

     此时,我们也可以看到各个服务之间的调用依赖以及接口请求的日志情况,具体如下所示:

     针对服务层下游的组件(缓存层、基础中间件层、数据存储层等等)接入,将在后续的文章中进行各自单独解析。

     综上所述,基于云原生生态领域的链路追踪系统 Jaeger ,在实际的业务场景中对于识别、定位及分析我们应用网络拓扑结构中服务间的链路调用的瓶颈其作用是不言而喻的,具有十分重要的参考意义。基于其所具备的“问题识别”、“信息追溯”等特征,使得我们在梳理服务之间的复杂依赖以及疑难问题分析面前能够迎刃而解。

相关实践学习
基于OpenTelemetry构建全链路追踪与监控
本实验将带领您快速上手可观测链路OpenTelemetry版,包括部署并接入多语言应用、体验TraceId自动注入至日志以实现调用链与日志的关联查询、以及切换调用链透传协议以满足全链路打通的需求。
分布式链路追踪Skywalking
Skywalking是一个基于分布式跟踪的应用程序性能监控系统,用于从服务和云原生等基础设施中收集、分析、聚合以及可视化数据,提供了一种简便的方式来清晰地观测分布式系统,具有分布式追踪、性能指标分析、应用和服务依赖分析等功能。 分布式追踪系统发展很快,种类繁多,给我们带来很大的方便。但在数据采集过程中,有时需要侵入用户代码,并且不同系统的 API 并不兼容,这就导致了如果希望切换追踪系统,往往会带来较大改动。OpenTracing为了解决不同的分布式追踪系统 API 不兼容的问题,诞生了 OpenTracing 规范。OpenTracing 是一个轻量级的标准化层,它位于应用程序/类库和追踪或日志分析程序之间。Skywalking基于OpenTracing规范开发,具有性能好,支持多语言探针,无侵入性等优势,可以帮助我们准确快速的定位到线上故障和性能瓶颈。 在本套课程中,我们将全面的讲解Skywalking相关的知识。从APM系统、分布式调用链等基础概念的学习加深对Skywalking的理解,从0开始搭建一套完整的Skywalking环境,学会对各类应用进行监控,学习Skywalking常用插件。Skywalking原理章节中,将会对Skywalking使用的agent探针技术进行深度剖析,除此之外还会对OpenTracing规范作整体上的介绍。通过对本套课程的学习,不止能学会如何使用Skywalking,还将对其底层原理和分布式架构有更深的理解。本课程由黑马程序员提供。
相关文章
|
6月前
|
Dubbo Java 应用服务中间件
微服务框架(十六)Spring Boot及Dubbo zipkin 链路追踪组件埋点
此系列文章将会描述Java框架Spring Boot、服务治理框架Dubbo、应用容器引擎Docker,及使用Spring Boot集成Dubbo、Mybatis等开源框架,其中穿插着Spring Boot中日志切面等技术的实现,然后通过gitlab-CI以持续集成为Docker镜像。 本文第一部分为调用链、OpenTracing、Zipkin和Jeager的简述;第二部分为Spring Boot及Dubbo zipkin 链路追踪组件埋点
|
3月前
|
存储 监控 Go
带你十天轻松搞定 Go 微服务系列(九、链路追踪)
带你十天轻松搞定 Go 微服务系列(九、链路追踪)
|
4天前
|
存储 JSON 监控
微服务链路追踪原理,一文搞懂!
本文重点讲解微服务链路追踪(Microservices Distributed Tracing),介绍其原理、架构及工作流程。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
微服务链路追踪原理,一文搞懂!
|
17天前
|
存储 NoSQL 关系型数据库
微服务Zipkin链路追踪原理,图解版,一文吃透!
本文重点讲解Zipkin链路追踪的原理与使用,帮助解决微服务架构下的服务响应延迟等问题,提升系统性能与稳定性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
微服务Zipkin链路追踪原理,图解版,一文吃透!
|
3月前
|
Kubernetes 监控 API
微服务从代码到k8s部署应有尽有系列(十二、链路追踪)
微服务从代码到k8s部署应有尽有系列(十二、链路追踪)
|
6月前
|
消息中间件 SpringCloudAlibaba Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
1008 0
|
11月前
|
存储 前端开发 算法
微服务线上问题排查困难?不知道问题出在哪一环?那是你还不会分布式链路追踪
微服务线上问题排查困难?不知道问题出在哪一环?那是你还不会分布式链路追踪
129 1
|
监控 网络协议 Java
分布式链路追踪- SkyWalking使用手册
分布式链路追踪- SkyWalking使用手册
1224 0
分布式链路追踪- SkyWalking使用手册
|
3月前
|
存储 监控 开发者
分布式链路监控系统问题之系统拆分后链路追踪技术的问题如何解决
分布式链路监控系统问题之系统拆分后链路追踪技术的问题如何解决
|
存储 监控 数据可视化
Golang链路追踪:实现高效可靠的分布式系统监控
Golang链路追踪:实现高效可靠的分布式系统监控