Istio 支持通过 Envoy 代理进行分布式追踪,代理自动为其应用程序生成追踪 span,只需要应用程序转发适当的请求上下文即可。Istio 支持很多追踪系统,包括 Zipkin, Jaeger,Lightstep 和 Datadog,其中 Jaeger 目前已经成为 Istio 默认的分布式追踪工具。在我们的容器云平台中,我们采用了Istio + Jaeger的组合,为应用程序提供了低侵入性的链路追踪功能。本文将重点介绍如何通过Envoy代理和Jaeger实现应用程序的分布式追踪。

1、Envoy 分布式追踪 Jaeger 的实现原理

  Envoy 原生支持 Jaeger,追踪所需 x-b3 开头的 Header 和 x-request-id 在不同的服务之间由业务逻辑进行传递,并由 Envoy 上报给 Jaeger,最终 Jaeger 生成完整的追踪信息。
  为了将各种追踪 span 整合在一起以获得完整的追踪图,应用程序必须在传入和传出请求之间传播追踪上下文信息。特别是,Istio 依赖于应用程序传播 b3 追踪 Header 以及由 Envoy 生成的请求 ID,即应用程序服务请求时需携带这些 Header。这些 Header 包括:

  • x-request-id: 这是一个唯一的请求标识符,用于标识单个请求。它有助于追踪请求,但不涉及分布式跟踪。在Istio中,这通常是由Envoy代理生成的,以便在服务之间传递请求标识。
  • x-b3-traceid: 追踪ID,是一个在整个请求链中唯一的标识符。它用于标识一个请求在分布式系统中的路径。
  • x-b3-spanId: 跨度ID,用于标识单个操作或跨度。每个服务处理请求时都会创建一个新的SpanId。它有助于将请求的不同部分关联起来。
  • x-b3-parentspanid: 父跨度ID,指示当前操作的父操作的SpanId。这有助于构建操作之间的层次关系,以便更好地理解请求的调用链。
  • x-b3-sampled:采样标志,用于指示是否对请求进行采样,以决定是否在分布式跟踪系统中记录该请求的信息。如果采样标志为 "1",则对该请求进行采样,如果为 "0",则不采样。
  • x-b3-flags:标志位,用于标识请求的特定属性。在一些系统中,可能会使用这个标志位来传递额外的信息。

  如果请求中没有 B3 HTTP Header,Istio Sidecar 代理(Envoy) 会自动生成初始化的 Headers。

  在 Istio 中,Envoy 和 Jaeger 的关系如下:

  上图中 Front Envoy 指的是第一个接收到请求的 Envoy Sidecar,它会负责创建 Root Span 并追加到请求 Header 内,请求到达不同的服务时,Envoy Sidecar 会将追踪信息进行上报。

2、应用程序通过 Envoy 代理和 Jaeger 进行分布式追踪

2.1 查看通过 Envoy 代理的应用程序会被自动生成初始化哪些 Headers

  在第一章介绍 Envoy 分布式追踪 Jaeger 的实现原理时,我们知道 Envoy 原生支持 Jaeger,追踪所需 x-b3 开头的 Header 和 x-request-id 在不同的服务之间由业务逻辑进行传递,并由 Envoy 上报给 Jaeger,最终 Jaeger 生成完整的追踪信息。如果请求中没有 B3 HTTP Header,Istio Sidecar 代理(Envoy) 会自动生成初始化的 Headers。在这一小节,我们将看一下通过 Envoy 代理的应用程序会被自动生成初始化哪些 Headers。

(1) 构建个简单镜像能够打印 HTTP 请求信息

以下是一个示例,使用 Go 语言来创建一个简单的 HTTP 服务器,并在其中打印请求信息:

1)创建一个名为 main.go 的 Go 源代码文件,内容如下:

package main

import (
"fmt"
"log"
"net/http"
) func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// 打印请求信息
fmt.Println("Request Method:", r.Method)
fmt.Println("Request URL:", r.URL.String())
fmt.Println("Request Headers:")
for header, value := range r.Header {
fmt.Printf(" %s: %v\n", header, value)
}
fmt.Println("Request Body:", r.Body) // 返回响应
w.WriteHeader(http.StatusOK)
w.Write([]byte("Hello, World!"))
}) log.Fatal(http.ListenAndServe(":80", nil))
}

2)创建一个名为 Dockerfile 的文件,内容如下:

# 使用 Golang 官方的 Golang 镜像作为基础镜像
FROM golang:1.17 # 设置工作目录
WORKDIR /app # 将 main.go 文件复制到容器中的 /app 目录下
COPY ./main.go . # 编译 Go 代码
RUN go build -o http_request_printer main.go # 在容器启动时运行 Go 应用
CMD ["./http_request_printer"]

3)使用以下命令构建 Docker 镜像:

docker build -t http_request_printer .

4)构建完成后,可以使用以下命令运行容器:

docker run -p 8080:80 http_request_printer

注意 1:如果宿主机暴露 18080 端口,不要用谷歌浏览器或者谷歌浏览器里面的 Postman 插件进行测试,因为谷歌浏览器默认会禁用 18080 端口。

5)修改镜像 tag ,将镜像推送到镜像仓库,此步骤比较简单,忽略命令。

6)通过 Postman 测试访问此容器:

7)通过容器日志查看请求信息:

可以看到除了自定义 Header 外,谷歌浏览器里面的 Postman 插件帮我们加了一些 Header 信息,但是可以很明确的看到这些 Header 都和链路追踪没有关系。

(2) 验证通过 Envoy 代理的应用程序会被自动生成初始化哪些 Headers

1)定义 http_request_printer 服务声明式配置文件

# http_request_printer_deploy_svc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: tracing
labels:
version: v1
app: http-request-printer
name: http-request-printer-v1
spec:
replicas: 1
selector:
matchLabels:
version: v1
app: http-request-printer
template:
metadata:
labels:
version: v1
app: http-request-printer
annotations:
cloudbases.io/containerSecrets: '{"container-rr19ea":"harbor"}'
spec:
containers:
- name: container-rr19ea
imagePullPolicy: IfNotPresent
image: '10.20.32.201:80/library/http_request_printer'
ports:
- name: http-80
protocol: TCP
containerPort: 80
servicePort: 80
serviceAccount: default
affinity: {}
initContainers: []
volumes: []
imagePullSecrets:
- name: harbor
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
---
apiVersion: v1
kind: Service
metadata:
namespace: tracing
labels:
version: v1
app: http-request-printer
annotations:
cloudbases.io/serviceType: statelessservice
name: http-request-printer
spec:
sessionAffinity: None
selector:
app: http-request-printer
template:
metadata:
labels:
version: v1
app: http-request-printer
ports:
- name: http-80
protocol: TCP
port: 80
targetPort: 80
type: NodePort

2)在 Kubernetes 集群中部署 http_request_printer 服务

kubectl apply -f http_request_printer_deploy_svc.yaml

3)查看服务访问信息,并通过 Postman 访问此服务

[root@master1 ~]# kubectl get svc -n=tracing
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
http-request-printer NodePort 10.234.131.36 <none> 80:32513/TCP 74s

4)通过容器日志查看请求信息:

可以看到除了自定义 Header 外,谷歌浏览器里面的 Postman 插件帮我们加了一些 Header 信息,但是可以很明确的看到这些 Header 都和链路追踪没有关系。

5)修改应用deployment配置让注入边车

    #修改Pod模板标签,添加边车注入标签 sidecar.istio.io/inject: 'true'
   labels:
app: http-request-printer
sidecar.istio.io/inject: 'true'
version: v1

6)Pod重启后,确定容器已注入边车

7)注入边车后,再通过 Postman 访问此服务

查看容器日志可以看到多了5个和链路追踪相关的 Header 数据。

由此证明,如果请求中没有 B3 HTTP Header,Istio Sidecar 代理(Envoy) 会自动生成初始化的 Headers。

8)可以看到 x-b3-sampled 值为1,代表 Jaeger 会记录该请求的信息

3、总结

  Envoy 原生支持 Jaeger,追踪所需 x-b3 开头的 Header 和 x-request-id 在不同的服务之间由业务逻辑进行传递,并由 Envoy 上报给 Jaeger,最终 Jaeger 生成完整的追踪信息。

 为了将各种追踪 span 整合在一起以获得完整的追踪图,应用程序必须在传入和传出请求之间传播追踪上下文信息。特别是,Istio 依赖于应用程序传播 b3 追踪 Header 以及由 Envoy 生成的请求 ID,即应用程序服务请求时需携带这些 Header。

  如果请求中没有 B3 HTTP Header,Istio Sidecar 代理(Envoy) 会自动生成初始化的 Headers。

参考:分布式追踪

应用程序通过 Envoy 代理和 Jaeger 进行分布式追踪(一)的更多相关文章

  1. ASP.NET Core使用Jaeger实现分布式追踪

    前言 最近我们公司的部分.NET Core的项目接入了Jaeger,也算是稍微完善了一下.NET团队的技术栈. 至于为什么选择Jaeger而不是Skywalking,这个问题我只能回答,大佬们说了算. ...

  2. 总结两种动态代理jdk代理和cglib代理

    动态代理 上篇文章讲了什么是代理模式,为什么用代理模式,从静态代理过渡到动态代理. 这里再简单总结一下 什么是代理模式,给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原 ...

  3. 通过一个工具类更深入理解动态代理和Threadlocal

    动态代理和Threadlocal 一个代理类返回指定的接口,将方法调用指定的调用处理程序的代理类的实例.返回的是一个代理类,由指定的类装载器的定义和实现指定接口指定代理实例调用处理程序最近用到一个工具 ...

  4. 爬虫--requests模块高级(代理和cookie操作)

    代理和cookie操作 一.基于requests模块的cookie操作 引言:有些时候,我们在使用爬虫程序去爬取一些用户相关信息的数据(爬取张三“人人网”个人主页数据)时,如果使用之前requests ...

  5. java的静态代理、jdk动态代理和cglib动态代理

    Java的代理就是客户端不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理.使用代理有两个好处,一是可以隐藏委托类的实现:二是可以实现客户与委托类之间的解耦,在不修改委托类代码的情况 ...

  6. JAVA高级架构师基础功:Spring中AOP的两种代理方式:动态代理和CGLIB详解

    在spring框架中使用了两种代理方式: 1.JDK自带的动态代理. 2.Spring框架自己提供的CGLIB的方式. 这两种也是Spring框架核心AOP的基础. 在详细讲解上述提到的动态代理和CG ...

  7. AOP的底层实现-CGLIB动态代理和JDK动态代理

    AOP是目前Spring框架中的核心之一,在应用中具有非常重要的作用,也是Spring其他组件的基础.它是一种面向切面编程的思想.关于AOP的基础知识,相信多数童鞋都已经了如指掌,我们就略过这部分,来 ...

  8. JDK动态代理和CGLIB的区别

    Aspect默认情况下不用实现接口,但对于目标对象,在默认情况下必须实现接口 如果没有实现接口必须引入CGLIB库 我们可以通过Advice中添加一个JoinPoint参数,这个值会由spring自动 ...

  9. JDK动态代理和CGLib动态代理简单演示

    JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...

  10. SpringAOP-JDK 动态代理和 CGLIB 代理

    在 Spring 中 AOP 代理使用 JDK 动态代理和 CGLIB 代理来实现,默认如果目标对象是接口,则使用 JDK 动态代理,否则使用 CGLIB 来生成代理类. 1.JDK 动态代理 那么接 ...

随机推荐

  1. #Python assign赋值,新增列操作

  2. 大家都在用-神奇的Markdown格式

    概述 Markdown 是一种轻量级标记语言,它可以使我们专注于写作内容,而不用过多关注排版,很多博主.作家等都用它来撰写文章~ 本文将给各位小伙伴介绍 Markdown 语法的使用,本篇文章索奇就是 ...

  3. 2022-08-19:以下go语言代码输出什么?A:equal;B:not equal;C:不确定。 package main import ( “fmt“ “reflect“ )

    2022-08-19:以下go语言代码输出什么?A:equal:B:not equal:C:不确定. package main import ( "fmt" "refle ...

  4. 2021-07-12:缺失的第一个正数。给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。比如[3,4,5

    2021-07-12:缺失的第一个正数.给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数.请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案.比如[3,4,5 ...

  5. ABP - 依赖注入(1)

    依赖注入实现了系统之间.模块之间和对象之间依赖关系的解耦,基本上是现代应用程序框架必不可少的一个组成部分. ABP的依赖注入系统是基于Microsoft的依赖注入扩展库(Microsoft.Exten ...

  6. springboot 分析源码欢迎页和图标-> thymeleaf模板引擎常用语法->扩展

    欢迎页: icon: 注意点:  thymeleaf模板引擎 1.使用thymeleaf模板引擎前要导入对应依赖包 2.阅读源码: 根据源码说明我们可以将html文件放置在templates目录下,然 ...

  7. 百度云原生数据库GaiaDB的HTAP与多地多活技术实践

    摘要:云原生数据库在使用存算分离技术后,可以在完全兼容MYSQL协议和语法的情况下,极大提升单实例所能承载的数据规模与吞吐能力上限.但除了对客户端兼容外,对整个数据生态(地域容灾,数据分析,备份恢复) ...

  8. 在EXCEL和WPS表格里实现邮件合并功能

    在EXCEL和WPS表格里实现邮件合并功能 2020/3/21 22:06:09 0人评论 10635次 OFFICE邮件合并:在Office中,先建立两个文档:一个WORD包括所有文件共有内容的主文 ...

  9. js有关dom操作学习

    dom对象就是操作网页的document dom节点: 整个文档是一个文档节点(document对象) 每个 HTML 元素是元素节点(element 对象) HTML 元素内的文本是文本节点(tex ...

  10. 基于SqlSugar的开发框架循序渐进介绍(30)-- 整合客户关系管理系统模块功能

    以前在随笔<Winform开发框架之客户关系管理系统(CRM)的开发总结系列1-界面功能展示 >的几篇随笔中介绍过基于WInform开发框架开发的CRM系统,系统的功能主要也是围绕着客户相 ...