一、问题背景:为啥Jaeger跑在K8s上会丢追踪?
不少开发者把Jaeger(一个专门用来记录程序调用轨迹的工具,比如你点外卖时,系统从接单、派单到配送的全流程记录,就可以用Jaeger追踪)部署到K8s(用来管理一堆程序容器的工具,相当于一个大型的程序“管家”)集群后,都会遇到一个头疼的问题:明明程序正常跑,Jaeger却偶尔甚至经常丢追踪数据——要么某条用户操作的轨迹断了,要么整个微服务链路没被记录。这背后最常见的坑,就是K8s的网络策略(相当于给程序之间的通信加的“门禁”,谁能连谁、连哪个端口都要管)和Jaeger自身的配置没对上。
先给大家说个真实的踩坑场景:去年我帮一个做电商的朋友排查问题,他们的K8s集群里,前端、订单、支付三个微服务,每个服务都加了网络策略,只允许特定服务之间通信。Jaeger是单独部署在一个叫jaeger的命名空间里的,结果上线一周后,运营发现用户从加购物车到下单的链路追踪经常断,有时候甚至连Jaeger界面都看不到任何数据。最后查了三天才发现,是网络策略把微服务连Jaeger的端口给拦了,而且Jaeger的配置里还开了个容易被忽略的“重试限制”,导致丢数据后没补回来。
这篇文章就从这个真实场景出发,一步步讲怎么排查这类问题,帮大家避开网络和配置的坑。
二、核心概念铺垫:先搞懂两个基础东西
要排查问题,得先搞清楚两个最核心的东西,不然看后面的步骤会懵。
2.1 Jaeger的通信逻辑(大白话版)
Jaeger本身是一套分布式追踪系统,它的核心组件分两类:一类是客户端(比如Java的Jaeger客户端库、Python的Jaeger客户端),另一类是服务端(Jaeger Agent、Jaeger Collector、Jaeger UI)。客户端会把程序的追踪数据(比如调用时间、调用的服务名)发给Agent,Agent再把数据传给Collector,最后Collector把数据存起来,UI用来展示。 这里有个关键的点:客户端和Agent之间的通信端口是固定的,默认用的是UDP的6831端口(专门用来发追踪数据),如果用的是Agentless模式(客户端直接连Collector,不用Agent),那用的是HTTP的14268端口。
2.2 K8s网络策略的作用(大白话版)
K8s的网络策略(NetworkPolicy)是用来限制Pod之间通信的规则,相当于给每个Pod加了个“门禁卡”,只有符合规则的请求才能进来。比如你可以设置:只有前端服务的Pod能连订单服务的Pod,其他服务连不了;或者某个Pod只能连特定命名空间里的Pod。 这里有个容易踩的坑:K8s的网络策略是“白名单”模式,也就是说,只要你加了网络策略,默认所有没被允许的请求都会被拦。比如你给微服务加了网络策略,只允许连其他微服务,那微服务连Jaeger的请求就会被拦,除非你专门加规则允许。
三、排查实战步骤:从简单到复杂一步步来
排查问题的思路一定要从简单到复杂,先排除最容易犯的错,再查复杂的配置。
3.1 第一步:先查Jaeger自身的基础配置
很多人丢追踪,不是网络的问题,是Jaeger的配置没弄对,所以先查这个,省得白忙活。 这里给大家一个标准的Jaeger配置示例,技术栈统一用K8s的YAML配置(后面所有示例都用这个技术栈),大家可以对照着看自己的配置有没有问题。
# Jaeger Collector的Deployment配置,技术栈:K8s YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: jaeger-collector
namespace: jaeger # Jaeger所在的命名空间
spec:
replicas: 1
selector:
matchLabels:
app: jaeger-collector
template:
metadata:
labels:
app: jaeger-collector
spec:
containers:
- name: jaeger-collector
image: jaegertracing/jaeger-collector:latest # 用官方最新镜像,避免旧版本bug
ports:
- containerPort: 14268 # Agentless模式下,客户端连Collector的HTTP端口
name: http
- containerPort: 14250 # 连其他组件的端口,这里不用改
name: grpc
env:
# 关键配置:开启追踪数据的持久化,不然重启就丢
- name: SPAN_STORAGE_TYPE
value: elasticsearch # 用Elasticsearch存追踪数据,也可以用Cassandra
# 关键配置:设置客户端连的地址,要和后面的Service对应
- name: COLLECTOR_ZIPKIN_HTTP_PORT
value: "9411"
# 关键配置:设置Agent的端口,要是用Agent模式的话
- name: AGENT_HTTP_PORT
value: "14268"
查配置的时候,重点看这几个地方: 第一,有没有开启追踪数据的持久化?如果没开,Jaeger重启后之前的数据就丢了,会误以为是追踪丢失。 第二,配置的端口对不对?比如Agent模式下,客户端要连Agent的UDP 6831端口,要是配置里把这个端口关了,肯定收不到数据。 第三,有没有设置“重试限制”?比如有些Jaeger配置里,客户端发数据失败后,只重试3次,超过就丢数据,要是网络偶尔卡,就会丢追踪。
3.2 第二步:排查K8s网络策略的问题
这是最常见的坑,很多人加了网络策略,忘了允许微服务连Jaeger。
3.2.1 先看微服务有没有加网络策略
首先要查你的微服务所在的命名空间,有没有加网络策略。用下面的命令查:
# 技术栈:K8s Shell命令,查指定命名空间下的所有网络策略
kubectl get netpol -n <你的微服务命名空间>
如果输出有内容,说明加了网络策略,那就要看策略里有没有允许连Jaeger。
3.2.2 看网络策略的具体规则
用下面的命令看网络策略的具体内容:
# 技术栈:K8s Shell命令,查看指定网络策略的详细配置
kubectl describe netpol <网络策略名称> -n <你的微服务命名空间>
比如我之前那个电商朋友的微服务网络策略,内容是这样的:
# 电商微服务的网络策略,技术栈:K8s YAML
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: microservice-netpol
namespace: e-commerce # 微服务所在的命名空间
spec:
podSelector:
matchLabels:
app: microservice # 匹配所有微服务的Pod
policyTypes:
- Ingress # 允许哪些请求进来
- Egress # 允许哪些请求出去(重点看这个)
egress:
- to:
- podSelector:
matchLabels:
app: frontend # 允许连前端服务
ports:
- protocol: TCP
port: 80
- to:
- podSelector:
matchLabels:
app: order # 允许连订单服务
ports:
- protocol: TCP
port: 8080
这个策略里,只允许微服务连前端和订单服务,根本没提Jaeger,所以微服务连Jaeger的请求会被拦。
3.2.3 修复网络策略的示例
要修复这个问题,就要在网络策略的Egress规则里,加上允许连Jaeger的规则。比如下面这个修复后的示例:
# 修复后的电商微服务网络策略,技术栈:K8s YAML
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: microservice-netpol
namespace: e-commerce
spec:
podSelector:
matchLabels:
app: microservice
policyTypes:
- Ingress
- Egress
egress:
# 原来的规则不变
- to:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 80
- to:
- podSelector:
matchLabels:
app: order
ports:
- protocol: TCP
port: 8080
# 新增的规则:允许连Jaeger的Collector
- to:
- namespaceSelector:
matchLabels:
name: jaeger # 匹配Jaeger所在的命名空间
ports:
- protocol: TCP # Agentless模式用TCP,端口14268
port: 14268
# 如果是Agent模式,加下面的规则
# - protocol: UDP
# port: 6831
这里要注意:如果Jaeger用的是Agent模式,就要允许微服务连Agent的UDP 6831端口;如果是Agentless模式,就要允许连Collector的TCP 14268端口。
3.3 第三步:排查Jaeger和微服务的网络连通性
就算配置对了,有时候还是会有网络问题,比如Jaeger的Service没对,或者DNS解析有问题。
3.3.1 先查Jaeger的Service配置
Jaeger的Service是用来让微服务找到Jaeger的,相当于一个“地址簿”。用下面的命令查Jaeger的Service:
# 技术栈:K8s Shell命令,查Jaeger命名空间下的Service
kubectl get svc -n jaeger
比如输出的结果里,Jaeger Collector的Service应该是这样的:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
jaeger-collector ClusterIP 10.96.123.45 <none> 14268/TCP 7d
这里的Cluster-IP是Jaeger的内部地址,微服务要通过这个地址连Jaeger。
3.3.2 测试微服务和Jaeger的连通性
可以在微服务的Pod里,直接连Jaeger的端口,测试能不能通。比如用下面的命令进入微服务的Pod:
# 技术栈:K8s Shell命令,进入指定Pod的终端
kubectl exec -it <微服务Pod名称> -n <微服务命名空间> -- bash
然后在Pod里用curl命令测试连通性:
# 技术栈:Shell命令,测试连Jaeger的14268端口
curl -v http://jaeger-collector.jaeger:14268/api/traces
如果输出“Connection refused”,说明网络不通,要么是网络策略的问题,要么是Jaeger的Service配置错了。 如果输出“Connection timed out”,说明是网络策略把请求拦了,因为K8s的网络策略默认拦请求的时候,不会返回拒绝,只会超时。
3.4 第四步:排查Jaeger的配置陷阱
除了网络问题,Jaeger自身的配置也有很多容易踩的坑,比如采样率、重试机制、存储配置。
3.4.1 采样率配置的坑
Jaeger的采样率是指,Jaeger会按一定比例收集追踪数据,比如采样率是10%,就只会收集10%的请求数据,剩下的90%会被丢。很多人会把采样率设得很低,以为能省资源,结果误以为是追踪丢失。 查采样率的配置,要看Jaeger客户端的配置,比如Java的客户端配置:
// Jaeger Java客户端配置示例,技术栈:Java
@Configuration
public class JaegerConfig {
@Bean
public Tracer jaegerTracer() {
// 采样率设为100%,也就是收集所有请求的数据
Sampler sampler = new ConstSampler(true);
return new Tracer.Builder("e-commerce-service")
.withSampler(sampler)
.build();
}
}
这里要注意:如果是生产环境,采样率可以根据情况调整,但测试环境一定要设为100%,不然测试的时候会误以为丢追踪。
3.4.2 重试机制的坑
Jaeger的客户端默认有重试机制,但如果网络偶尔卡,重试次数不够,还是会丢数据。比如Java的Jaeger客户端,默认重试次数是3次,超过就丢数据。可以把重试次数调高,比如设为5次:
// Jaeger Java客户端配置示例,技术栈:Java,调整重试次数
@Bean
public Tracer jaegerTracer() {
Sampler sampler = new ConstSampler(true);
return new Tracer.Builder("e-commerce-service")
.withSampler(sampler)
// 设置重试次数为5次
.withReporter(new RemoteReporter.Builder()
.withMaxQueueSize(1000) // 队列大小,防止数据堆积
.withFlushInterval(1000) // 刷新间隔,1秒刷一次
.withMaxRetries(5) // 重试次数
.build())
.build();
}
3.4.3 存储配置的坑
Jaeger的追踪数据是存在存储里的,比如Elasticsearch、Cassandra,如果存储满了,或者存储的配置错了,Jaeger就存不了数据,导致追踪丢失。比如Elasticsearch的索引满了,Jaeger就会报错,存不了数据。 查存储的配置,要看Jaeger的环境变量,比如Elasticsearch的配置:
# Jaeger Collector的环境变量配置,技术栈:K8s YAML
env:
- name: SPAN_STORAGE_TYPE
value: elasticsearch
- name: ES_SERVER_URLS
value: http://elasticsearch:9200 # Elasticsearch的地址
- name: ES_INDEX_PREFIX
value: jaeger # 索引前缀
- name: ES_NUM_REPLICAS
value: 1 # 副本数
如果Elasticsearch的地址错了,或者索引满了,Jaeger就存不了数据,要及时扩容或者清理旧数据。
四、应用场景、优缺点和注意事项
4.1 应用场景
这篇文章讲的排查方法,主要适用于以下场景: 第一,Jaeger部署在K8s集群上,微服务加了网络策略的场景; 第二,Jaeger用Agentless模式或者Agent模式,微服务和Jaeger在不同命名空间的场景; 第三,生产环境或者测试环境,Jaeger偶尔丢追踪数据的场景; 第四,刚把Jaeger部署到K8s上,上线后发现追踪丢失的场景。
4.2 技术优缺点
排查过程中用到的技术,各有优缺点: 第一,K8s网络策略的优点是能限制Pod之间的通信,提高安全性,缺点是配置复杂,容易漏规则; 第二,Jaeger Agent模式的优点是能缓存追踪数据,减少网络请求,缺点是每个微服务都要部署Agent,增加了运维成本; 第三,Jaeger Agentless模式的优点是部署简单,不用每个微服务都部署Agent,缺点是客户端直接连Collector,网络压力大; 第四,Jaeger用Elasticsearch存储的优点是查询速度快,支持复杂查询,缺点是存储成本高,需要维护Elasticsearch集群。
4.3 注意事项
排查和配置的时候,要注意以下几点: 第一,K8s网络策略是白名单模式,加了策略后一定要测试所有需要的通信; 第二,Jaeger的采样率在测试环境一定要设为100%,避免测试结果不准; 第三,Jaeger的配置要和Service对应,避免地址错了连不上; 第四,Jaeger的存储要定期清理旧数据,避免满了存不了新数据; 第五,排查的时候要从简单到复杂,先查配置,再查网络,最后查复杂的陷阱。
五、文章总结
Jaeger部署在K8s集群上丢追踪数据,大部分都是网络策略和配置的坑,只要按照正确的步骤排查,就能很快找到问题。排查的核心思路是:先排除Jaeger自身的基础配置问题,再查K8s的网络策略有没有漏规则,然后测试网络连通性,最后排查Jaeger的配置陷阱。 另外,配置的时候一定要注意细节,比如网络策略的端口要和Jaeger的端口对应,采样率要根据环境调整,重试次数要足够,存储要定期维护。只要避开这些坑,就能让Jaeger正常工作,帮你快速排查微服务的问题。
Comments