Jaeger 很容易被误读,因为产品最显眼的部分是 trace UI。开发者搜索一次慢请求,打开瀑布图,看见哪一个服务在等待,然后带着一个有用答案离开。这个工作流真实存在,却遮住了另一层架构:那个答案起初能否被留在系统里,由这一层架构决定。

截至 2026-06-02T05:01:57Z UTC,Jaeger 在 GitHub 上的最新 release 是 v2.18.0,发布于 2026-05-13;项目仓库显示 22,852 stars2,920 forks402 open issues,最近一次 push 时间戳为 2026-06-02T01:46:55Z。[3][4] 这些数字属于一次新鲜度检查,并不构成架构本身。对运营者来说,真正重要的变化在于 Jaeger v2 围绕一只建立在 OpenTelemetry Collector 组件之上的灵活二进制文件设计,角色可以组合成 collector、query、ingester、all-in-one,或近似 agent 的安放方式。[1][3]

这意味着采用问题不应停在“我们要不要用 Jaeger 做 tracing”。更好的问题是:写入流量、读取流量、缓冲、采样控制与存储责任,应该分别安放在哪里?

第一条边界是 collector 对 query

Jaeger 的架构文档把主要角色说得很清楚。collector 接收 trace 数据并写入存储。query service 提供 API 和 UI,用于取回 trace 并做可视化。ingester 从 Kafka 读取 span,再写入存储。all-in-one 模式把 collector 和 query 合在一个进程里。[1] 这张清单看起来简单,却构成了第一项架构决定。

all-in-one 模式适合本地开发、演示和小型概念验证环境。Jaeger 文档明确写到,使用内存存储的 all-in-one 不适合生产,因为重启后数据会消失;使用 Badger 支撑的 all-in-one 可以承载有限规模,但仍然是单实例形态,无法横向扩展。[1] 把这种模式当作生产捷径,会很快把一次成功演示误认为一套可靠 tracing 平台。

collector/query 分离的形态更重要,因为 trace 系统面对的是两种差异很大的负载画像。写入流量来自应用和 collector,常常带着突发性。读取流量来自人、dashboard、事故响应、API 消费者,有时还来自自动化分析。两条路径若共享过多命运,系统会以令人受挫的方式失效:存储尖峰使摄取开始丢数据,或高强度查询窗口让 UI 在事故展开时变慢。

Jaeger 文档直接说明了 collector/query 分离的优势:使用外部存储后,all-in-one 与分离配置都可以横向扩展,但 collector/query 分离允许团队独立扩展读写流量,并施加不同的访问或安全策略。[1] 这就是本文的架构中心。trace UI 并非产品边界。真正的边界在于 trace 写入与 trace 读取能否作为两块不同的操作表面被管理。

直写存储可行,直到存储变成节流点

第二项决定,是让 collector 直接写入存储,还是经过 Kafka。在 direct-to-storage 模式下,collector 接收 span,并把它们直接写进后端。Jaeger 指出,存储必须扛住平均流量和峰值流量;collector 可以使用内存队列抹平短期峰值,但持续性的存储滞后仍会导致数据丢失。[1]

因此,当工作负载可预测、保留周期有边界、存储后端留有足够余量时,直写存储很有吸引力。对许多团队而言,它也是更清楚的第一种生产形态:移动部件更少,需要运维的队列更少,数据究竟在哪里延迟也更容易说清。

它的失效模式同样清楚。只要存储跟不上,摄取就会受损。因此,tracing 不能只按服务数量估算容量。团队需要了解 span 体量、峰值请求形态、服务名和操作名里的基数、保留目标、索引预期,以及事故期间的查询行为。一个发出少量且形状良好的 span 的服务集合,运营起来可以比另一个规模更小、却追踪过多内部细节并把每次请求都变成大图的集合更容易。

Jaeger 的存储页面进一步收紧了这个判断。项目要求在耐久生产使用中配置持久化存储,列出 Cassandra、Elasticsearch 和 OpenSearch 作为主要支持的分布式后端,并说明对大规模生产而言,Jaeger 团队推荐 OpenSearch,优先级高于 Cassandra。[2] 它还支持面向自定义存储后端的 gRPC Remote Storage API v2,另有 memory 与 Badger 用于更窄的场景。[2] 因而,存储从隐藏实现细节转为主要运营契约。

Kafka 是缓冲,并非神奇的可靠性开关

当团队需要把采集与存储解耦时,Kafka 进入 Jaeger 架构。在 Jaeger 的 via-Kafka 部署里,collector 将 span 发布到 Kafka,ingester 从 Kafka 消费并写入存储。文档把这种形态描述为一种防止 collector 与存储之间数据丢失的方式,多个 ingester 还可以在彼此之间分摊摄取负载。[1]

这套形态很有力量,因为它改变了失效窗口。只要 Kafka 容量、保留周期和 ingester 恢复能力被诚实地估算,存储变慢就不会立刻迫使 collector 丢掉同等数量的数据。它也让平台团队有一条更清楚的路径,吸收大型部署中突发式的 trace 生产。

但 Kafka 也改变了 tracing 平台的责任归属。一旦 Kafka 位于中间,tracing 栈就拥有了队列保留、consumer lag、分区、磁盘压力与 replay 语义。团队已经熟练运营 Kafka 时,这可以是合适的交换。团队若连 Jaeger 自身的存储责任都尚未理顺,Kafka 会把一个 trace 后端变成另一套分布式系统,事故解释难度随之上升。

实际规则很朴素:当 trace 数据足够重要、突发性足够强、体量足够高,足以支撑队列所有权的成本时,再使用 Kafka。不要只是因为图里有一层缓冲会让“生产架构”听起来更严肃,就把它放进来。

OpenTelemetry 改变安放位置,边界仍然要存在

Jaeger v2 与 OpenTelemetry 的对齐,是另一项主要架构信号。Jaeger 二进制文件建立在 OpenTelemetry Collector 框架之上,并包含上游组件,例如 OTLP receiver、batch 与 attribute processors;也包含 contrib 组件,例如 Kafka receiver/exporter 与 tail sampling processor;还包含 Jaeger 专属组件,例如 storage exporter 与 query extension。[1]

这一点很重要,因为许多团队已经为了 metrics、logs、enrichment、routing 或 vendor fan-out 运行 OpenTelemetry Collectors。Jaeger 文档说明,运营 Jaeger 不需要再单独配置一套 OpenTelemetry Collector,因为 Jaeger 本身就是一份带有不同角色的 collector 定制发行版。不过,如果 OpenTelemetry Collectors 已经属于 telemetry estate 的一部分,它们可以作为 sidecar、host agent、daemonset 或远程服务集群放在 Jaeger 前面。[1]

这给了团队弹性,也制造了设计陷阱。OpenTelemetry Collector 的安放方式不能滑成含糊的“collector 越多 observability 越多”。sidecar 或 host-agent 安放可以简化 SDK 配置,并把 enrichment 工作分散到靠近应用的位置。远程 collector 集群可以帮助做分片和 tail-based sampling。每一种安放方式也都会增加一次额外的 marshaling 与 unmarshaling 层。[1]

重点不在于避开 OpenTelemetry Collectors,而在于让每一层 collector 都承担明确工作。本地 collector 应该负责本地 enrichment、本地缓冲或 endpoint 简化。远程 collector 应该负责 routing、sampling、fan-out 或 policy。若这些都不存在,额外层级就会变成运营仪式。

采样是一项架构决定

Dapper 论文在这里仍然有用,因为它解释了分布式 tracing 为什么必须保持低开销、拥有足以广泛部署的透明度,并能在大系统中服务开发者和运营者。[6] Dapper 作者强调,采样与通用库中的 instrumentation 是关键设计选择,并非细枝末节的成本控制。[6] Jaeger 以开源形式继承了这一课:trace 后端只有在收集到足够 trace 以解释行为时才有价值,但 trace 数量也不能多到压垮存储与 UI 表面。

Jaeger 架构页面指出,OpenTelemetry Collector 可以支持 Jaeger 的 remote sampling protocol,并提供静态采样配置,或把请求代理到 Jaeger 后端,包括 adaptive sampling 场景。[1] 在实践中,这意味着采样应进入架构评审。它并非账单到达之后才交给某个人调参的后续事项。

对平台团队而言,采样策略在上线前应该回答三个问题。哪些 endpoint 因为错误或合规上下文重要而始终值得看见?哪些 flow 可以按概率采样,因为聚合形状已经足够?哪些高流量或低价值 span 不应占据存储主导位置?这些问题若悬而未决,Jaeger 仍然可以在机械层面运行,但 trace corpus 会变得嘈杂、昂贵或具有误导性。

Jaeger 最适合的位置

当团队需要一套开放 tracing 后端,能够自然进入以 OpenTelemetry 为中心的体系,保有 trace 存储控制权,并随着生产压力增长而分开扩展读写关注点时,Jaeger 最有力量。[1][2][5] 它尤其适合那些已经理解 tracing 并非带着更漂亮屏幕的 logging 的团队。Trace 建模的是跨服务因果路径;价值来自传播、采样、存储与查询形态的共同作用。

采用边界同样清楚。如果团队无法拥有存储后端,无法决定采样策略,或期待 UI 弥补缺乏纪律的 instrumentation,Jaeger 就会显得吃力。如果组织想要完全托管的供应商体验,并且没有兴趣运营 collector、存储、保留周期、升级和查询访问,它也并非强匹配。

CNCF 语境有所帮助,但不应读得过重。Jaeger 于 2017 年被 CNCF 接纳,并于 2019 年毕业;CNCF 项目页面当前把它描述为一套分布式 tracing 平台,拥有健康的项目评分,以及来自许多组织的数千名贡献者。[5] 这是成熟度信号。它不能替代架构工作。

最清楚的 Jaeger 推出路径,是从小处开始,但尽早画出生产边界:在读写流量需要不同扩展方式时分离 collector/query;在存储余量清楚时采用直写存储;只有当缓冲在运营上站得住时才使用 Kafka;为 OpenTelemetry Collector 层指定明确职责;把采样策略当作平台契约。只要这些部分被命名出来,Jaeger 就不只是 trace 的去处。它会成为一套 tracing 后端,事故到来前,其失效模式已经可见。

来源

  1. Jaeger 文档,《Architecture》(v2.18/latest):角色、all-in-one 限制、collector/query 分离、direct-to-storage 与 Kafka 部署路径、OpenTelemetry Collector 安放方式,以及 Jaeger 二进制文件组成。
  2. Jaeger 文档,《Storage Backends》(v2.18/latest):持久化存储要求、主要分布式后端、面向大规模生产的 OpenSearch 推荐、Remote Storage API v2、memory 与 Badger。
  3. GitHub API,jaegertracing/jaeger 仓库元数据,采样于 2026-06-02:stars、forks、open issues、push 时间戳、license、language 与项目 topics。
  4. GitHub release 页面,jaegertracing/jaeger v2.18.0:用于本文新鲜度检查的最新 release tag 与发布日期。
  5. CNCF 项目页,《Jaeger》:CNCF 接纳与毕业日期、项目状态、健康评分、贡献者和组织指标,以及项目定位。
  6. Benjamin H. Sigelman 等,《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》,Google Research,2010:基础 tracing 设计目标、采样理由与运营使用框架。
  7. Wikimedia Commons,《Front of server racks at NERSC.jpg》:Derrick Coetzee 于 2011 年拍摄的真实照片,作为本文题图来源。