Jaeger Remote Storage:用 gRPC 共享单节点存储后端的完整部署指南
【免费下载链接】jaegerCNCF Jaeger, a Distributed Tracing Platform项目地址: https://gitcode.com/GitHub_Trending/ja/jaeger
导读
jaeger-remote-storage是 CNCF Jaeger 项目中一个独立的可执行程序,它把内存(memory)或 Badger 这类单节点存储实现封装成 gRPC 服务,对外暴露 Jaeger Remote Storage gRPC API,让 Jaeger 的采集器、查询器等组件可以通过网络远程使用这些存储后端。本文基于 cmd/remote-storage/README.md 及其对应的 入口代码、服务端实现 与示例配置,系统讲解该二进制的工作原理、完整配置项、多租户开启方式,以及如何与主 Jaeger 进程集成。读完你可以独立搭建一个可对外提供 gRPC 远程存储能力的 Jaeger 存储服务,并理解其内部调用链。
一、Remote Storage 是什么:设计定位与适用场景
jaeger-remote-storage的定位在 main.go 中写得很明确:它"允许共享内存存储或 Badger 等单节点存储实现,并实现 Jaeger Remote Storage gRPC API"。换句话说,它解决的核心问题是存储能力的远程化:
- 默认情况下,
jaeger主进程(v2)通过jaeger_storage扩展在进程内加载存储后端,存储与业务进程强耦合; - 使用
jaeger-remote-storage后,存储被剥离成独立进程,通过标准 gRPC 协议对外服务,实现存储与 Jaeger 各组件的解耦部署; - 单机上的内存存储和 Badger 是进程内库,无法被其他主机上的 Jaeger 组件直接访问,Remote Storage 服务正是为这类后端提供了网络访问入口。
从源码结构看,jaeger-remote-storage复用了与主jaeger二进制完全一致的存储配置格式(app/config.go 中Storage字段的注释明确说明"该配置与主jaeger二进制相同,但只应定义一个后端"),因此它本质上是一个"存储专用化"的轻量服务,只负责暴露存储 API,不承载采集、查询 UI 等职责。
二、构建与启动:从源码到运行
2.1 构建二进制
项目根目录的 Makefile 体系(scripts/makefiles/BuildBinaries.mk)会构建各 cmd 子目录下的二进制。构建完成后,产物即jaeger-remote-storage(在 Docker 场景下,Dockerfile 将remote-storage-linux-$TARGETARCH复制进镜像,并以ENTRYPOINT启动)。
2.2 命令行启动
使用--config-file指定 YAML 配置文件启动:
./jaeger-remote-storage --config-file config.yaml如果不提供任何配置文件,进程也不会拒绝启动。查看 main.go 的loadConfig逻辑:当 Viper 未加载到配置文件时,会回退到 DefaultConfig(),即默认启用memory 存储、监听:17271、max_traces: 1000000,并在日志中提示 "No configuration file provided, using default configuration (memory storage on :17271)"。
main.go中还挂载了一组辅助子命令(main.go),与主jaeger二进制保持一致的使用习惯:
| 子命令 | 作用 |
|---|---|
version | 输出版本信息 |
docs | 输出命令行帮助文档 |
status | 查询服务健康状态(基于管理端口) |
printconfig | 打印当前生效配置 |
featuregate | 查看/切换特性开关 |
2.3 默认端口约定
端口定义集中在 ports/ports.go:
| 端口 | 用途 |
|---|---|
17271(RemoteStorageGRPC) | gRPC 远程存储 API 服务端口 |
17270(RemoteStorageAdminHTTP) | 管理 HTTP 端口(健康检查、指标等),由flags.NewService(ports.RemoteStorageAdminHTTP)创建 |
三、YAML 配置详解
3.1 配置文件总体结构
README 给出的最小配置骨架如下,对应的完整示例见仓库内 cmd/remote-storage/config.yaml:
# Server configuration grpc: endpoint: :17271 # gRPC endpoint for remote storage API # Storage configuration storage: backends: default-storage: memory: max_traces: 100000 # Multi-tenancy configuration (optional) multi_tenancy: enabled: false该配置映射到 app/config.go 中的Config结构体:
type Config struct { GRPC configgrpc.ServerConfig `mapstructure:"grpc"` Tenancy tenancy.Options `mapstructure:"multi_tenancy"` Storage storageconfig.Config `mapstructure:"storage"` }三个顶层段落分别对应:
grpc:gRPC 服务端配置(来自 OTel Collector 的configgrpc.ServerConfig),支持endpoint、TLS、keepalive 等标准字段;storage.backends:存储后端定义,格式与主 Jaeger 的jaeger_storage扩展完全一致;multi_tenancy:可选的多租户开关。
3.2 校验规则:只允许一个后端
与主jaeger二进制不同,Remote Storage 服务只支持一个存储后端。这是配置校验的硬性约束,见 app/config.go:
func (c *Config) Validate() error { // Validate storage configuration if err := c.Storage.Validate(); err != nil { return err } // Ensure only one backend is defined for remote-storage if len(c.Storage.TraceBackends) > 1 { return fmt.Errorf("remote-storage only supports a single storage backend, but %d were configured", len(c.Storage.TraceBackends)) } return nil }对应的测试用例覆盖了各类边界(app/config_test.go):
storage.backends: {}(空 map)→ 报错 "at least one storage backend is required";- 后端配置为空对象
empty-storage: {}→ 同样报错; - 同时配置两个后端 → 报错 "remote-storage only supports a single storage backend"。
如果配置合法,GetStorageName()(app/config.go)会取出第一个后端名称作为实际使用的存储;若取不到任何后端,main.go 会直接logger.Fatal("No storage backend configured")。
3.3 存储后端:与主 Jaeger v2 完全一致的格式
Remote Storage 的存储配置格式与 Jaeger v2 的jaeger_storage扩展完全相同,因此所有官方后端(memory、Badger、gRPC 等)均可直接复用。README 给出了三种典型配置:
Memory 存储
storage: backends: memory-storage: memory: max_traces: 100000max_traces控制内存中最多保留的 trace 数量,超过上限的旧数据会被淘汰。默认配置(未提供配置文件时)同样使用 memory,上限为1_000_000(app/config.go)。
Badger 存储
storage: backends: badger-storage: badger: directories: keys: /tmp/jaeger/badger/keys values: /tmp/jaeger/badger/values ephemeral: false ttl: spans: 168h # 7 days仓库自带的 cmd/remote-storage/config-badger.yaml 还补充了维护与指标相关的可选参数:
storage: backends: badger-storage: badger: directories: keys: /tmp/jaeger/badger/keys values: /tmp/jaeger/badger/values ephemeral: false maintenance_interval: 5m # Badger 后台维护(GC、值日志清理)间隔 metrics_update_interval: 10s # 存储指标刷新间隔 ttl: spans: 168h # trace 数据存活时间(TTL)参数说明:
directories.keys/directories.values:Badger 键值日志的存储目录,分别写入键和值;ephemeral: false:关闭临时模式,数据持久化到磁盘;若为true则使用内存临时存储,进程退出即丢失;maintenance_interval:Badger 值日志(value log)的压缩/清理周期;metrics_update_interval:内部指标更新的时间间隔;ttl.spans:span 数据的生存期,168h即 7 天,到期数据自动过期删除。
gRPC 存储(级联转发)
storage: backends: grpc-storage: grpc: endpoint: remote-server:17271 tls: insecure: trueRemote Storage 服务同样可以把另一个远程 gRPC 存储当作后端(例如做代理/网关),此时endpoint指向目标 gRPC 存储地址,tls.insecure: true表示明文连接。
3.4 多租户(Multi-tenancy)
开启多租户只需将multi_tenancy.enabled置为true,并指定租户识别方式:
grpc: host-port: :17271 multi_tenancy: enabled: true header: x-tenant tenants: - tenant1 - tenant2 storage: backends: default-storage: memory: max_traces: 100000注意 README 中此处grpc段落使用了host-port键名(与前面示例的endpoint等价,二者均可用于指定监听地址)。开启后:
- 服务端在 createGRPCServer 中追加十项拦截器(
tenancy.NewGuardingUnaryInterceptor/tenancy.NewGuardingStreamInterceptor),对未携带合法租户信息的请求进行拦截; - 客户端需在请求中携带
x-tenant请求头(或 gRPC metadata)标明所属租户,且租户必须位于tenants白名单内; - app/config_test.go 的用例验证了
enabled、header、tenants三个字段能被正确解析。
四、服务端实现原理
4.1 从存储工厂到 gRPC Handler 的启动链路
jaeger-remote-storage的启动流程(main.go)大致为:
- 从配置中取出唯一存储后端名称(
GetStorageName); - 调用
storageconfig.CreateTraceStorageFactory创建存储工厂(tracestore.Factory)。注意此处传入的认证解析器为nil——Remote Storage 服务自身不做远端认证解析; - 断言工厂同时实现
depstore.Factory(依赖关系存储接口),否则启动失败并提示 "Storage does not implement dependency store"; - 将工厂交给
app.NewServer创建 gRPC 服务并Start; - 主进程通过
svc.RunAndThen注册关闭钩子,退出时依次关闭 gRPC 服务器与存储工厂(main.go)。
4.2 gRPC 服务内部构造
在 server.go 的NewServer中,存储工厂被拆分为三个能力组件:
reader, err := ts.CreateTraceReader() // trace 读取 writer, err := ts.CreateTraceWriter() // trace 写入 depReader, err := ds.CreateDependencyReader() // 依赖关系读取随后grpcstorage.NewHandler(reader, writer, depReader)组装出完整的 v2 存储处理器,注册到 gRPC 服务器上(server.go 中同时注册了grpc.health.v1.Health健康服务并启用 gRPC reflection)。从测试代码 server_test.go 可以看到,一个可用的 Remote Storage 服务对外暴露的服务集合为:
| 服务 | 作用 |
|---|---|
jaeger.storage.v2.TraceReader | 读取/查询 trace |
jaeger.storage.v2.DependencyReader | 读取服务依赖关系 |
opentelemetry.proto.collector.trace.v1.TraceService | 接收 OTLP 写入的 trace 数据 |
grpc.health.v1.Health | 健康检查 |
拦截器链上还挂载了bearertoken.NewUnaryServerInterceptor/bearertoken.NewStreamServerInterceptor(server.go),使服务端支持 Bearer Token 认证。
4.3 TLS 能力
grpc配置段支持 OTel Collector 标准的 TLS 服务端配置。测试 server_test.go 系统地覆盖了 7 种 TLS 场景,包括:明文连接、客户端不信任服务器证书、主机名不匹配、双向 TLS(mTLS)以及客户端证书签发 CA 不匹配等,可作为配置 mTLS 时的行为参考。
五、与 Jaeger 主进程集成
要让主jaeger(v2)进程通过 gRPC 使用 Remote Storage 服务,需在其配置的jaeger_storage扩展中把存储后端声明为grpc类型:
extensions: jaeger_storage: backends: some-storage: grpc: endpoint: localhost:17271 tls: insecure: true仓库提供了完整的集成示例 cmd/jaeger/config-remote-storage.yaml,其中还展示了读写分离的配置技巧——查询走 17271,写入走独立的 OTLP 端口:
extensions: jaeger_storage: backends: some-storage: grpc: endpoint: "${env:REMOTE_STORAGE_ENDPOINT:-localhost:17271}" tls: insecure: true writer: endpoint: ${env:REMOTE_STORAGE_WRITER_ENDPOINT:-0.0.0.0:4316} tls: insecure: true exporters: jaeger_storage_exporter: trace_storage: some-storage要点说明:
endpoint指向jaeger-remote-storage的 gRPC 端口,Jaeger 从这里读取trace 与依赖关系;writer.endpoint可指向 OTLP TraceService 端口,用于写入trace 数据,允许读写走不同端口/地址;- 两处地址均支持环境变量占位并带默认值,便于在不同环境间复用同一份配置。
关于客户端侧 gRPC 存储后端的更多细节(服务契约、集成测试认证方法等),可继续阅读 internal/storage/v2/grpc/README.md。
六、验证与测试
6.1 使用 gRPC reflection 验证
服务端默认启用 gRPC reflection,你可以直接用grpcurl列出服务,快速确认 Remote Storage 已正常就绪:
grpcurl -plaintext localhost:17271 list # 预期输出中包含: # jaeger.storage.v2.DependencyReader # jaeger.storage.v2.TraceReader # opentelemetry.proto.collector.trace.v1.TraceService # grpc.health.v1.Health这与 server_test.go 中validateGRPCServer的断言集合一致。
6.2 单元测试
仓库为 Remote Storage 提供了完整的单元测试:
- app/config_test.go:覆盖配置加载、默认配置、非法后端、多后端拒绝、多租户解析等场景;
- app/server_test.go:覆盖存储工厂创建失败、端口监听失败、TLS 握手各分支、端口
:0随机分配及 gRPC 服务注册正确性; - app/package_test.go:通过
testutils.VerifyGoLeaks校验无 goroutine 泄漏。
6.3 定制存储的合规认证
如果你的目标是自己实现一个自定义 gRPC 存储后端并接入 Jaeger,可以运行官方集成测试套件进行合规认证(详见 internal/storage/v2/grpc/README.md):
STORAGE=grpc \ CUSTOM_STORAGE=true \ REMOTE_STORAGE_ENDPOINT=${MY_REMOTE_STORAGE_ENDPOINT} \ REMOTE_STORAGE_WRITER_ENDPOINT=${MY_REMOTE_STORAGE_WRITER_ENDPOINT} \ PURGER_ENDPOINT=${MY_PURGER_ENDPOINT} \ make jaeger-v2-storage-integration-test其中PURGER_ENDPOINT是测试要求后端额外提供的一个 HTTP 清理接口,用于在每次测试前重置存储状态。
七、完整部署示例
7.1 内存后端(零配置即可运行)
# 不传配置:自动使用内存存储,监听 :17271,max_traces=1000000 ./jaeger-remote-storage或显式使用 cmd/remote-storage/config.yaml:
./jaeger-remote-storage --config-file config.yaml7.2 Badger 持久化后端
./jaeger-remote-storage --config-file config-badger.yaml对应配置见 cmd/remote-storage/config-badger.yaml,数据持久化在/tmp/jaeger/badger/下,span 数据保留 7 天。
7.3 端到端:Remote Storage + Jaeger 查询
- 启动 Remote Storage(内存或 Badger 后端,监听
:17271); - 启动主
jaeger,配置指向cmd/jaeger/config-remote-storage.yaml,使其jaeger_storage扩展通过 gRPC 连接 Remote Storage; - 向主 Jaeger 的 OTLP 端口发送 trace 数据,写入经独立 writer 端口落入 Remote Storage;
- 通过 Jaeger UI(默认 16686)即可查询到写入的 trace——此时存储已经完全运行在独立进程中,主 Jaeger 进程内不再持有存储实例。
总结
jaeger-remote-storage把 Jaeger 的存储能力从进程内解耦为独立 gRPC 服务:它以与主二进制完全一致的storage.backends配置格式支持 memory、Badger 等后端,通过jaeger.storage.v2系列 API + OTLP TraceService 对外提供服务,并内置健康检查、gRPC reflection、Bearer Token 认证、多租户与完整 TLS 支持。无论是想在多机环境中共享单机存储、把存储独立扩容,还是为自定义 gRPC 存储后端做合规接入,它都是一个轻量且标准的落地方案。
【免费下载链接】jaegerCNCF Jaeger, a Distributed Tracing Platform项目地址: https://gitcode.com/GitHub_Trending/ja/jaeger
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考