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. Nginx常用基础模块

    Nginx常用基础模块 目录 Nginx常用基础模块 目录索引模块 配置方式 nginx的状态模块 配置方式 nginx访问控制模块 配置方式 nginx的访问限制模块 请求限制重定向 Nginx连接 ...

  2. 基于ORB-SLAM3库搭建SLAM系统

    参考资料 ORB-SLAM3配置及安装教程 ORB-SLAM3配置安装及运行 环境配置 Win 11pro VMware 17Pro Ubuntu 18.04 Eigen3 Pangolin Open ...

  3. LeetCode 周赛 345(2023/05/14)体验一题多解的算法之美

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 往期回顾:LeetCode 双周赛第 104 场 · 流水的动态规划,铁打的结构化思考 周赛概览 T1. 找 ...

  4. 代码随想录算法训练营Day52 动态规划

    代码随想录算法训练营 代码随想录算法训练营Day52 动态规划| 300.最长递增子序列 674. 最长连续递增序列 718. 最长重复子数组 300.最长递增子序列 题目链接:300.最长递增子序列 ...

  5. SqlServer 设置用户只能访问特定表、特定数据库

    设置用户只能访问特定表.特定数据库 一.只能访问特定数据库 1.[安全性]-[登录名]右击用户.打开属性,选择用户映射,勾选特定数据库 2. 如果 服务器角色 勾选了 [查看任意数据库],那么登录后会 ...

  6. JavaWeb编程面试题——Spring Framework

    引言 面试题==知识点,这里所记录的面试题并不针对于面试者,而是将这些面试题作为技能知识点来看待.不以刷题进大厂为目的,而是以学习为目的.这里的知识点会持续更新,目录也会随时进行调整. 关注公众号:编 ...

  7. C++别名的使用

    c++中的别名使用,类似引用,在别名中,"&"的意思不再是取地址,而是建立一个指针,直接指向数据.这是一个小例子: #include <iostream> us ...

  8. 绘图;OSPF 虚连接

    绘图;OSPF 虚连接 原图如下 绘图 实验拓扑 实验需求 按照图示分区域配置OSPF 配置虚连接认证 实验步骤 配置相应接口IP地址及loopback 环回口地址 按照图示分区域配置OSPF AR1 ...

  9. 前端Vue非常简单实用商品分类展示组件 侧边商品分类组件

    前端vue非常简单实用商品分类展示组件 侧边商品分类组件 , 下载完整代码请访问uni-app插件市场址:https://ext.dcloud.net.cn/plugin?id=13084 效果图如下 ...

  10. AR技术的应用与未来

    目录 随着科技的不断进步,增强现实(AR)技术也在不断发展壮大.AR技术是一种通过计算机技术和传感器技术将虚拟信息融合到现实世界中的技术,可以为用户带来一种全新的.交互性更强的体验.本文将探讨AR技术 ...