概述
Jaeger 在设计之初,就保证了能够以弹性的方式来接收大量的数据。为了能够最大化的利用资源,尽可能的避免存储和网络延迟,Jaeger会进行缓冲和批处理操作。
当生成的 span 超过 Jaeger 能够正常处理的数量时,span 数据可能会被丢弃。
然而,默认的限制值可能并不适合所有场景:例如,作为 sidecar 运行的 agnet 可能比在裸机中作为守护程序运行的agent有更严格的内存限制。
部署注意事项
尽管对各个组件进行性能调优很重要,但 Jaeger 的部署方式对于获得最佳性能更为重要。
Collector扩缩容
Collector可以支持水平扩展,即可以按需进行实例的添加和删除,因此,我们可以合理应用PaaS平台的自动扩缩容的功能。
判断Collector扩缩容的一个好的判断依据可以是检查 jaeger_collector_queue_length 指标:当length长时间大于阈值的50%时,就可以考虑进行实例扩容了。 另一个可以考虑的指标是 jaeger_collector_in_queue_latency_bucket,它是一个直方图,表明在worker处理它之前,span在queue中等待了多长时间。当任务延迟随着时间的推移变得更长时,就说明需要对collector实例进行扩充了。
当您的PaaS平台提供自动扩缩容功能时,或者当启动/停止Collector实例比更改现有的正在运行的实例更容易时,建议添加Collector实例数。另外当CPU资源已经即将打满整机时,也说明需要进行水平扩展了。
确保后端存储不是瓶颈
每个span数据都会由Collector使用一个worker程序写入后端存储服务,而在写入后端存储服务时,其实是一个同步阻塞的过程。 当后端存储服务处理过慢时,被存储阻塞的worker数量可能会很多,导致spans被丢弃。
为了帮助发现这种情况,可以分析直方图 jaeger_collector_save_latency_bucket 指标。理想情况下,延迟应随时间保持不变。当直方图显示随着时间的推移,大多数span花费的时间越来越长,这说明你的后端存储服务可能需要扩容或者水平扩展了。
让Agent与你的应用尽可能接近
Agent应用在与注入Opentracing的应用程序部署在同一主机上,以避免网络上的 UDP 数据包丢失。
传统应用程序通常是在裸机上部署一个Agent来实现的。
Kubernetes 等容器环境中,则是通过Sidecar来实现,Sidecar有助于分散Agent处理的负载,并具备允许根据应用程序的需求和重要性单独调整每个Agent的功能。
可用kafka作为中间缓冲区
Jaeger 可以使用 Apache Kafka 作为Collector和实际后端存储(Elasticsearch、Apache Cassandra)之间的缓冲区。这对于流量不平稳,经常会出现流量高峰的场景而言是很有效的。
为此,应将Collector中的 SPAN_STORAGE_TYPE 环境变量设置为 kafka,并且可以使用 Jaeger Ingester 组件,从 Kafka 读取数据并将其写入后端存储。
除了性能方面,将 span 数据写入 Kafka 可用于构建实时的数据管道以进行聚合和从trace中提取特征。
Client配置
Jaeger 客户端应该对应用程序的影响尽可能的小。
因此,它的默认值设置会非常的保守,对于某些场景可能并不适用。
请注意,Jaeger 客户端可以通过编程方式或通过环境变量进行配置。
调整采样配置
JAEGER_SAMPLER_TYPE 和 JAEGER_SAMPLER_PARAM 一起用于设置“采样”trace的频率。
对于生成大量span的应用程序,将采样类型设置为probabilistic并将值设置为 0.001(默认值)将导致以 1/1000 的概率来进行trace采样。 请注意,采样决策是在根span处做出的,并向下传播到所有子span。
对于中低流量的应用程序,将采样类型设置为 const 并将值设置为 1 将会上报所有的span数据。
同样,可以通过将值设置为 0 来禁用trace采样,而上下文传播功能还能继续工作。
一些客户端支持设置 JAEGER_DISABLED 以完全禁用 Jaeger Tracer。仅当 Tracer 的行为方式导致被检测的应用程序出现问题时才建议这样做。
Ps: 我们建议您将Client设置为使用remote采样策略,以便管理员可以集中设置每个服务的具体采样策略。
增加内存中允许的队列大小
大多数 Jaeger 客户端,例如 Java、Go 和 C# 客户端,在将它们发送到 Jaeger Agent/Collector 之前会在内存中缓冲span数据。
此缓冲区的最大大小由环境变量 JAEGER_REPORTER_MAX_QUEUE_SIZE 定义(默认值:约 100 个跨度):大小越大,潜在的内存消耗越高。
当检测的应用程序生成大量span时,队列可能已满,这会导致客户端丢弃新的span数据(监控指标为: jaeger_tracer_reporter_spans_total{result=”dropped”,})。
在大多数常见情况下,队列将接近为空(指标:jaeger_tracer_reporter_queue_length),因为span会定期或者在达到特定大小的批次时刷新到agent或collector。
Thrift 客户端也会向Agent报告他们丢弃的span信息。然后这些指标由Agent本身发布为监控指标: jaeger_agent_client_stats_spans_dropped_total{cause=”full-queue|send-failure|too-large”,}。如果客户端指标由于某些原因无法查询时,则当前指标会很有用。
修改Batch Span处理间隔
Java、Go、NodeJS、Python 和 C# 客户端允许自定义reporter使用的数据推送间隔(默认值:1000 毫秒或 1 秒),例如 RemoteReporter,发送所有内存中span数据到Agent或Collector。 推送间隔设置得越低,推送操作发生的频率就越高。大多数Reporter默认会等待队列中有足够的数据再进行数据推送,该配置可以强制定期执行数据推送操作,以便将span数据及时发送到后端。
当检测的应用程序生成大量span并且Agent/Collector靠近应用程序时,网络开销可能较低,因此推送的间隔可以设置的比较小。当正在使用 HttpSender 并且Collector与应用程序不够接近时,网络开销可能太高,推送间隔则不应该设置的过小。
Agent配置
Jaeger Agent 从Client接收数据并将它们分批发送到Collector。
如果配置不当,即使主机有足够的资源,它也可能最终丢弃数据。
调整服务队列大小
“服务队列大小”相关的一系列配置(processor.jaeger-binary.server-queue-size、processor.jaeger-compact.server-queue-size、processor.zipkin-compact.server-queue-size)表示Agent可以在内存中可以存储的最多span的数量。
可以假设 jaeger-compact 是您的 Agent 主要处理的数据类型,因为它是大多数客户端(例如 Java 和 Go 客户端)中唯一可用的数据类型。
每个队列的默认值为 1000 组span。而每一组span可以包含高达 64KiB 的span,因此,默认每个队列最多可容纳 64MiB 的span。
在典型情况下,队列应该接近空(指标 jaeger_agent_thrift_udp_server_queue_size),因为应该由worker快速读取并处理对应的span数据。
但是,Client提交的span数量可能会突然激增,从而导致有span数据排队。当队列已满时,较旧的span数据将被覆盖,导致span数据丢失(监控指标: jaeger_agent_thrift_udp_server_packets_dropped_total)。
调整worker数量
“worker”相关的一系列配置(processor.jaeger-binary.workers、processor.jaeger-compact.workers、processor.zipkin-compact.workers)指定了并行处理span的worker的数量。
每个 worker 类型的默认大小为 10。通常,一组 span 一旦被放入服务队列就会被处理,并且会阻塞一个 worker,直到整个数据包被发送到Collector。
对于处理来自多个Client的数据的Agent,应该增加Worker的数量。鉴于每个 worker 的成本很低,一个好的经验法则是每个Client则分配 10 个 worker。考虑到每组 span 可能包含高达 64KiB 的 span,这意味着 10 个 worker 能够同时发送大约 640KiB 给Collector。
Collector配置
Collector 从Client和Agent接收数据。
如果配置不当,它的处理能力可能还赶不上单个Agent的处理速度,或者可能会因消耗过多内存而使主机过载。
调整服务队列大小
与Agent类似,Collector能够接收span并将它们放在内部队列中进行处理。其中,Collector会立即返回Agent/Client响应,而不是同步等待span数据进入后端存储。
设置 collector.queue-size(默认值:2000)表示队列支持多少跨度在内存中存储。
在典型情况下,该队列应该接近为空,因为应该有足够的worker从队列中提取span数据并将它们发送到后端。当队列中的span数量(度量 jaeger_collector_queue_length)一直很高时,这表明应该增加worker的数量或后端存储跟不上它接收的数据量。当队列已满时,队列中较旧的span数据将被覆盖,从而导致span数据被丢弃(监控指标: jaeger_collector_spans_dropped_total)。
Ps: Ageng的队列大小大约是一组span,而Collector的队列大小是针对的单个span。
鉴于大多数时间队列大小应接近空,此设置应与collector的可用内存一样大,以最大程度地防止突然的流量高峰。但是,如果您的存储配置不足并且存储更多的数据时,即使是队列大小配置的再大,也会很快填满内存并开始丢弃数据。
实验性功能:从 Jaeger 1.17 开始,Jaeger Collector 可以根据内存需求和平均span大小自动调整队列大小。将配置 collector.queue-size-memory 设置为Collector 可以使用的最大内存大小(以 MiB 为单位),Jaeger 将根据它所看到的平均span大小定期计算理想的队列大小。出于安全原因,最大队列大小硬编码为 100 万条记录。
调整worker数量
Collector 中的 worker 用户从 span 队列中取出数据并进行处理。每个worker从队列中选择一个span并将其持久化到后端存储中。
可以通过设置collector.num-workers(默认值:50)指定worker的数量,并且应该尽可能保持队列接近于零。
一般规则是:后端存储的速度越快,需要的worker数量就越少。
鉴于worker消耗的资源相对较少,这个数字可以随意增加。作为一般规则,当存储速度很快时,队列中每 50 个记录分配一个worker就足够了。如果collector.queue-size 为2000,那么大约有40 个worker 就足够了。
对于较慢的后端存储服务而言,应该相应地调整这个比率,每个队列中需要有更多的Worker。