Fission 统一 OpenTelemetry 可观测性落地:RFC-0019 指标迁移、OTLP 原生推送与依赖裁剪实战
云原生后端【免费下载链接】fissionFast and Simple Serverless Functions for Kubernetes项目地址https://gitcode.com/gh_mirrors/fi/fission点击查看免费下载导读Fission 的 tracing 与 logging 早已原生使用 OpenTelemetry唯独指标metrics是三段信号中的缺口——全部 39 个指标都是基于 Prometheusclient_golangAPI 手工构建进程内没有任何 OTel metrics SDK。本文以 RFC-0019: Unified OpenTelemetry Observability 为骨架讲解 Fission 如何借助 OTel→Prometheus 桥接导出器bridge exporter把指标插桩层迁移到 OpenTelemetry Metrics API同时保证/metrics端点、指标名、标签、类型与直方图桶位与迁移前完全兼容现有 Grafana 看板、告警、ServiceMonitor 无需改动在此之上还实现了可选的 OTLP 原生指标推送、请求路径直方图的 trace exemplars、以及autoprop/semconv层面的依赖与运行时裁剪。读完本文你将掌握这一迁移的完整设计、三支柱实施方案、配置面变化与风险处置并能直接在 Fission 仓库中定位到每一处落地代码。背景Fission 可观测性的三分之二 OTelRFC-0019 的开篇结论一句话概括Fission 的可观测性是三分之二 OpenTelemetry 原生三分之一不是。Traces成熟pkg/utils/otel/provider.go构建了一个 OTLP/gRPC tracer配合RFC-0015 引入的 error-biased sampler失败调用永远被记录与errorExportProcessor兜底强制导出otelhttp包裹每个 server 与 client 跳点并为 Fission CRD 提供了 span 属性辅助函数。Logs成熟、可选开启zap →otelzap桥接在OTEL_LOGS_ENABLED置位时把控制面记录以 OTLP 推送并携带 trace ID见 RFC-0016 与 loggerfactory 实现。Metrics缺口39 个指标全部是prometheus.NewCounterVec/NewGaugeVec/NewHistogramVec注册进共享的prometheus.Registryregistry.go进程内零 OTel metrics SDK——指标侧的 OTLP 统一只存在于采集层外部 collector 抓/metrics后再以 OTLP 重新发出。RFC 的三大支柱由此展开① 指标迁移到 OTel Metrics API经桥接导出器保持 scrape 兼容并附加 OTLP 推送与 exemplar② 激进的依赖/运行时裁剪去掉autoprop及其拖入的四个 propagator 模块semconv从 v1.4.0 升级到 v1.41.0③ 单一统一 Provider一次初始化同时立起 tracer、meter、logger 三个 provider返回一个同时 flush 三者的 shutdown 闭包。动机为什么非迁不可RFC-0019 在 Motivation 一节给出了五条驱动全部可以在仓库源码中得到印证指标不是 OpenTelemetry。平台对外宣称支持 OpenTelemetry但唯一被 SRE 依赖做 SLO 的信号metrics从未迁移。旧定义散落在各包的init()里注册client_golangcollector例如改造前的 pkg/router/metrics.go、pkg/executor/metrics/metrics.go导致这一信号无法从进程原生 OTLP 推送。指标与 trace 无法关联。Router 已经以 OpenMetrics 格式提供服务server.go 中promhttp.HandlerOpts{EnableOpenMetrics: true}OpenMetrics 正是能携带 exemplar 的格式——但client_golang只有在调用方手工传入时才会附加 exemplar而 Fission 从未这么做。fission_function_overhead_seconds的延迟尖刺无法点进去看到对应的 exemplar trace。一套平台两套指标习语。应用指标用client_golang采集层用 OTLP一个统一走 OTLP 管线的运维者必须为 Fission 单独加一段 Prometheus-receiver scrape 跳点而其他信号都是直连 OTLP。依赖与运行时负担。provider.go 中原来的autoprop.NewTextMapPropagator()为了让运维者从环境变量选 propagator把contrib/propagators/{aws,b3,jaeger,ot}四个模块拉进构建——而 chart 默认值且几乎永远是tracecontext,baggage。semconv曾被钉死在 v1.4.0已升级到 v1.41.0落后当前稳定约定多个版本。导出器引导逻辑手工编写且在信号间重复getTraceExporter/getLogExporter各自重新解析 endpoint/insecure 标志。早先的决定值得重审。团队曾为避免破坏看板而决定指标留在 Prometheus。这个推理对朴素重写是成立的但它没有把 OTel→Prometheus 桥接导出器考虑进来——桥接导出器在保持暴露格式不变的同时移动了插桩 API所以看板翻车这个反对理由不再成立。现状盘点39 个指标与它们的定义文件RFC 用两张表精确盘点现状。信号状态如下信号状态位置Traces成熟OTLP/gRPCerror-biased sampler每跳otelhttppkg/utils/otel/{provider,errorsampler,handler,attributes}.goLogs成熟经otelzap桥接的可选 OTLP 推送pkg/utils/loggerfactory、pkg/utils/otel/provider.goMetrics仅 Prometheusclient_golang无 OTel metrics SDK下表 9 个文件39 个指标及其定义文件代表名示例文件数量代表指标pkg/router/metrics.go10fission_function_calls_total、fission_function_overhead_secondsDefBuckets、fission_router_routes、fission_invocation_failures_totalpkg/router/endpointcache/metrics.go7fission_router_endpointcache_hits_total、..._modegauge、..._sizegauge funcpkg/executor/metrics/metrics.go6fission_function_cold_starts_total、fission_function_running_secondsExponentialBuckets(1,2,16)pkg/executor/client/client.go2fission_router_tap_flush_errors_total、..._notfound_totalpkg/storagesvc/metrics.go3fission_archives、fission_archive_memory_bytesgaugespkg/buildermgr/metrics.go1fission_buildermgr_oci_publish_totalpkg/mqtrigger/metrics.go5fission_mqt_subscriptions、fission_mqt_message_laggaugespkg/utils/metrics/http_metrics.go3http_requests_total、http_requests_duration_secondsDefBucketspkg/utils/otel/errorsampler.go2fission_error_span_export_failures_total、..._drops_total这些文件都通过init()注册进共享的metrics.RegistryServeMetricsserver.go把该 registry 组合进 controller-runtime 的metrics.Registry并在 8080 端口以 OpenMetrics 方式服务/metrics。Pillar 1指标迁移到 OpenTelemetry Metrics APIProvider同一个 resource立起 MeterProvider在pkg/utils/otel的统一初始化里新增一个MeterProvider与 tracer、logger 共用同一个resource。实际落地见 meterprovider.go 的NewMeterProvider它把 OTel Prometheus 导出器以otelprom.WithRegisterer(reg)绑定到现有 registry而不是新建一个promExporter, err : otelprom.New( otelprom.WithRegisterer(metrics.Registry), // 与 ServeMetrics 服务的是同一个 registry otelprom.WithTranslationStrategy(otlptranslator.UnderscoreEscapingWithoutSuffixes), // 不加 unit/_total 后缀名字原样 otelprom.WithoutScopeInfo(), // 不产生 otel_scope_* 标签 otelprom.WithoutTargetInfo(), // 不产生 target_info 序列 ) mp : sdkmetric.NewMeterProvider( sdkmetric.WithResource(res), sdkmetric.WithReader(promExporter), ) otel.SetMeterProvider(mp)注意scrape 兼容的确切含义指标名、标签键/值、类型、直方图桶边界被完整保留——这是看板和告警所查询的东西但不保证逐字节一致的文本OpenMetrics 的_created行与 HELP 排序可能不同。该导出器实现了prometheus.Collector注册到我们递给它的 registry 后OTel 指标就出现在同一个/metrics:8080端点上——不需要新端口不需要改 scrape 配置ServiceMonitor 与 PodMonitor 全部不受影响。仪器映射薄封装让调用点保持简洁一个内部 meter 辅助层扩展 pkg/utils/metrics/meter.go封装Meter让调用点读起来和以前一样Prometheus 仪器OTel 仪器说明CounterVec名字以_total结尾Int64Counter保留含_total的字面名without-suffixes 策略防止出现_total_totalGauge/GaugeVecinc/decInt64UpDownCounter由 Prometheus 导出器暴露为 gauge天然收支平衡今天没有Delete/ResetGauge/GaugeVecset 值Int64Gauge同步SDK v1.28 起可用.Set()的直接映射GaugeFunc如..._endpointcache_sizeInt64ObservableGauge 回调每次采集由回调读取实时值HistogramVecFloat64HistogramWithExplicitBucketBoundaries桶位必须钉死——见下面的最锋利的边标签一对一映射为同键同值的属性如function_namespace、function_name、path、method、code。因为 OTel API 是上下文优先counter.Add(ctx, 1, metric.WithAttributes(...))Router 与 executor 热路径里已贯穿的ctx正是携带活动 span 的载体——这正是 exemplar 能自动工作的原因。以 router/metrics.go 的collectFunctionMetric为例ctx : req.Context()直接传给functionCalls.Add、functionCallErrors.Add与functionCallOverhead.Record。为兼顾热路径性能router 侧还实现了functionCallAttrsCachemetrics.go按(namespace,name,version,path,method,code)六元组以可比较数组为键缓存metric.MeasurementOption热路径只做一次 map 查找、零分配避免每次请求都构建并排序一个 6 属性 Set。pkg/utils/metrics/http_metrics.go的pmAttrs/pmcAttrs是同样的思路。直方图桶位迁移最锋利的边OTel 默认的直方图聚合是指数/base-2 布局不是 Prometheus 桶位。naive 迁移会静默改变桶边界直接打爆histogram_quantile()看板。因此每个直方图在创建时用metric.WithExplicitBucketBoundaries(...)钉死相同的边界封装见 meter.go 的Float64Histogram构造器fission_function_overhead_seconds与http_requests_duration_seconds→prometheus.DefBuckets边界。fission_function_running_seconds→ExponentialBuckets(1, 2, 16)边界1s … 约 9h见 executor/metrics.go。仪器选项比 reader View 更简单且能正确透过全局 provider 的委托回放parity 测试会断言最终的_bucketle序列完全一致。原生 OTLP 推送增量而非替代在同一 meter provider 上附加第二个 reader——sdkmetric.NewPeriodicReader(otlpmetricgrpc-exporter)——由指标导出器配置门控默认仅prometheusotlp可选开启或两者并存。具体实现是 metricexporter.go 的getMetricReader只有当 OTLP endpoint 已配置且OTEL_METRICS_EXPORTER选中otlp时才返回 reader否则返回 nil保持默认安装的 scrape-only 行为。而 Prometheus 桥接 reader 无论是否配置 OTLP 都常驻所以开启 OTLP 是新增一条并行管线而非替换 scrape。一套插桩同时喂给/metricsscrape 与 OTLP 推送后纯 OTLP 管线的运维者就不再需要test/integration/otel/metrics-collector.reference.yaml里的 Prometheus-receiver scrape 跳点。仓库内 test/integration/otel/README.md 记录的是 RFC-0016 的 CI-only OTLP 日志栈Collector LokiRFC 的 Follow-up 部分明确要把该目录扩展为直接演练 OTLP 指标推送。Exemplar以 trace 导出为门控OTel metrics SDK 的默认 trace-based exemplar filter 会在采样 span 内的测量发生时记录 exemplar带trace_id/span_idPrometheus 导出器在 Fission 已经服务的 OpenMetrics 输出中渲染它们。于是请求路径上的直方图无需任何逐调用点埋点就获得了 trace exemplar。陷阱在于SDK 会为每个序列分配一个 per-series exemplar reservoir——这是真实的路由器堆开销随指标基数放大CI 一段上通过 prom-dump/pprof 对比实测约9 MB router 堆而没导出 trace 时这笔开销完全浪费。因此 meterprovider.go 只在配置了 trace 导出器时provider.go 传入cfg.endpoint ! 保留 exemplar 存储否则安装exemplar.AlwaysOffFilter零成本把堆开销挡在默认的 scrape-only 安装之外。注册表冲突警告registry.go 注释明确把 Fission registry 组合进 controller-runtime registry 是原子操作一旦 collector 重复会静默丢弃全部 Fission 指标。OTel 导出器以单个 collector 注册迁移必须在加入 meter 仪器的同一变更里移除对应的MustRegister调用按子系统进行保证任何指标在迁移期间绝不被注册两次。Pillar 2激进的依赖与运行时裁剪去掉autoprop改用显式 W3C 组合已完成。autoprop.NewTextMapPropagator()替换为propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})见 provider.go 与注释。这一替换把contrib/propagators/{aws,b3,jaeger,ot}从模块图里移除。权衡在 Backward compatibility 中记录运维者失去通过OTEL_PROPAGATORS选择 b3/jaeger 的能力默认值不变。若某个部署确实需要非 W3C propagator可以按明确、窄范围的可选方式重新引入而不是默认拖入全部四个模块。semconvv1.4.0 → v1.41.0已完成。刷新资源约定semconv.ServiceNameKey未变所以这里是一次纯导入替换provider.go。autoexport引导整合推迟。本意是把getTraceExporter/getLogExporter/metric reader 收敛为autoexport.NewSpanExporter/NewMetricReader及其日志等价物由环境变量OTEL_*_EXPORTER、OTEL_EXPORTER_OTLP_PROTOCOL驱动。推迟的原因是autoexport在OTEL_TRACES_EXPORTER未设置时默认使用 OTLP 导出器会改变今天无 endpoint 无 tracing的行为——手工构造器保留到该默认值被妥善处理为止。otel/log/logtest保持直接 require。它被loggerfactory_test.go导入Go 没有独立的 test-require 区段被测试导入的模块合理属于直接 require无需改动。关于footprint的诚实说明指标方向的选定增加了 OTel metrics SDK 与 Prometheus/OTLP 指标导出器纯 Prometheus 现状不携带这些同时去掉autoprop及其四个 propagator 模块。净模块数大致持平真正的收益是更少的默认 propagator 模块、更少的自定义 propagator 代码以及平台此前没有的可选原生 OTLP 指标通路。Pillar 3单一统一 ProviderInitProviderprovider.go成为一次性的可观测性初始化它构建一个resourceservice name 标准 detectors为三种信号选择导出器/readertrace/log 经getTraceExporter/getLogExportermetric 经getMetricReader设置全局 tracer、meter、logger providerotel.SetTracerProvider、otel.SetMeterProvider、otellogglobal.SetLoggerProvider安装显式 W3C propagator保留 RFC-0015 的 error-biased sampler 与 error-export processortrace 侧返回一个 shutdown 闭包依次 flush/关闭 tracer provider、meter provider、trace exporter 与 logger provider此前已覆盖 tracer logger这次新增 meter。各子系统的Start函数不变——它们本就接收 logger 并继承全局 provider。值得注意的细节当没有 trace exporter 时InitProvider显式安装sdktrace.NeverSample()provider.go。原因无显式 sampler 时 SDK 默认ParentBased(AlwaysSample)会在每个请求上记录一个永远不会被导出的 serverclient span带属性和消息事件——纯属数据面热路径的浪费。NeverSample仍然提取并传播入站的 W3C trace ID只抑制本地记录且otelhttp指标与采样无关所以/metricsscrape 不受影响。配置面Helm 值全部增量Helm 的openTelemetry.*值保持不变新键均为增量且默认值维持现状见 values.yamlKey默认值效果openTelemetry.metricsExporterprometheusprometheus今天的行为、otlp、或prometheus,otlpopenTelemetry.logsExporter不变默认关闭除非logsEnabled映射到OTEL_LOGS_EXPORTER既有otlpCollectorEndpoint、otlpInsecure、tracesSampler、tracesSamplingRate、propagators、logsEnabled、otlpHeaders不变同今天在values.yaml中可以看到配套的环境变量契约OTEL_METRICS_EXPORTER遵循 OpenTelemetry 约定逗号分隔的导出器名parseOtelConfigprovider.go做精确 token 匹配otlp才开启推送otlphttp/notlp不会误开启propagators键被保留用于 values-schema 兼容但注释明确非 W3C 选项不再生效values.yaml。每个新键都用默认值时现有安装的行为与今天完全一致/metrics上的 Prometheus scrape、设置 endpoint 时的 OTLP trace、可选的 logs。向后兼容整个方案的压舱石这正是让早先留在 Prometheus的决定可以安全反转的原因也是 RFC-0019 反复强调的 linchpin/metricsscrape 兼容同一端点、端口、名字、标签键/值、类型、桶位与 HELP 文本。导出器选项WithTranslationStrategy(UnderscoreEscapingWithoutSuffixes)、WithoutScopeInfo、WithoutTargetInfo 显式桶边界 字面仪器名共同保证parity 测试将其锁死——gather 迁移后的 registry断言每个指标的名字、类型、标签与桶边界仓库内多处测试用testMetricsReg建临时 registry 再Gather()断言如 executor/client/metrics_test.go、statestore/scoped_test.go、utils/metrics/metricstest/metricstest.go。这正是早先决策所担心的缺失的安全网。看板、告警、ServiceMonitor、PodMonitor 全部不变scrape 路径与序列身份没有任何变化现有 Grafana boards 与 Prometheus 规则无需编辑。一个文档化的例外——零值序列惰性出现OTel SDK 只在首次记录后才导出 counter/gauge 序列而client_golang从进程启动就把注册的指标暴露为0。因此某窗口内从未被触碰的指标会缺席于/metrics而不是以0存在——CI 中观察为无 warm-path 流量的腿上的fission_router_endpointcache_hits_total没有序列。rate()/increase()以及任何确实被记录的序列不受影响只有从启动就断言原始序列存在的告警/面板需要metric or vector(0)。Helm 值只增不改默认值复现当前行为没有任何现有键改变含义。文档化的可观察变化仅 trace、面向运维者、非稳定 APIsemconv升级让部分 span 属性键切到当前约定去掉autoprop移除了环境变量选择非 W3C propagator 的能力W3C 默认不变。两者都只影响 trace 消费方绝不影响指标或 scrape 契约。client_golang保留。替换 Prometheus的诚实表述是插桩 API迁到 OpenTelemetry暴露格式仍是 Prometheus/OpenMetrics该库无论何种情况都是 controller-runtime 的传递依赖。分阶段实施每阶段可独立交付各阶段独立可发布、各自 CI 绿符合仓库惯例Phase 0 — meter-provider 脚手架完成加入绑定现有 registry 的 Prometheus 桥接导出器的MeterProvider由InitProvider安装spike 迁移了http_metrics验证了全局 provider 委托能把包初始化期创建的仪器接线到位。Phase 1 — 第一个子系统验证模式完成Router 迁移到 meter 辅助层parity 测试断言名字/类型/桶位fission_function_overhead_seconds直方图用请求上下文记录从而携带 exemplar。Phase 2 — 迁移其余完成Executorclient、storagesvc、mqtrigger、buildermgr、endpointcache、通用http_requests_*、error-sampler 指标。Phase 3 — 裁剪部分完成去掉autoprop换显式 W3C 组合、升级semconvautoexport引导整合推迟见 Pillar 2。Phase 4 — 原生 OTLP 指标推送完成OTLP periodic reader 挂在OTEL_METRICS_EXPORTER/openTelemetry.metricsExporter之后与绝不替代Prometheus scrape 并存。Follow-upautoexport整合各子系统 golden/metrics抓取测试扩展test/integration/otel/演练直连 OTLP 指标推送。风险与对策清单RFC-0019 的 Risks 部分与实现一一对应全部可落到仓库证据导出器默认加后缀导致的指标名漂移导出器在默认配置下会追加 unit 与_total后缀并新增target_info/otel_scope_*。对策上述显式导出器选项 parity 测试兜底。直方图桶位分叉OTel 默认聚合不是 Prometheus 桶位。对策三个直方图强制显式桶边界parity 测试断言_bucket边界。迁移期双重注册一个指标同时在两个 API 上插桩会在原子 registry 组合上冲突并静默丢弃整套 Fission 指标。对策每个子系统在同一变更里同时移除MustRegister与加入 meter 仪器。Exemplar reservoir 堆 / OpenMetrics 协商OTel SDK 按序列分配 exemplar reservoirrouter 指标堆随基数增长的速率约为旧client_golang路径的 ~2 倍CI 上约 9 MB。对策以是否配置 trace 导出器门控 exemplar 存储——未配置时用exemplar.AlwaysOffFilter默认 scrape-only 安装零开销。exemplar 搭乘现有 OpenMetrics 暴露不请求 OpenMetrics 的抓取器自然收不到 exemplar。trace 属性与 propagator 变化semconv升级与autoprop移除改变 trace 侧行为已在文档与 release notes 中记录绝不影响指标契约。总结RFC-0019 用一句话可以收束Fission 的指标插桩迁移到了 OpenTelemetry Metrics API暴露格式仍是 Prometheus/OpenMetricsscrape 契约被 parity 测试锁死同时获得了可选的 OTLP 原生指标推送、请求路径直方图的 trace exemplar、以及更瘦的 OTel 依赖面。对纯 Prometheus 管线的运维者升级是无感的——默认 Helm 值复现现状/metrics上的一切照旧对 OTLP 统一管线的运维者Fission 的三个信号终于都能从进程原生抵达 collector无需再为指标单独保留 scrape 跳点。赞分享云原生后端【免费下载链接】fissionFast and Simple Serverless Functions for Kubernetes项目地址https://gitcode.com/gh_mirrors/fi/fission点击查看免费下载相关推荐Beads 可观测性实战指南通过 OpenTelemetryOTLP导出 bd 指标与追踪Beads 可观测性实战指南通过 OpenTelemetryOTLP导出 bd 指标与追踪 Beads 内置了基于 OpenTelemetryOTelAI 应用Agent 记忆CLIMCP 服务项目管理人工智能Daft OpenTelemetry 可观测性实战指南OTLP 遥测指标采集、配置与源码解读Daft OpenTelemetry 可观测性实战指南OTLP 遥测指标采集、配置与源码解读 Daft 是面向 AI 与多模态负载的高性能数据引擎其查询执行大数据数据分析数据工程AI 应用Python Observability 实战指南基于 Structlog、Prometheus 与 OpenTelemetry 的生产级可观测性落地Python Observability 实战指南基于 Structlog、Prometheus 与 OpenTelemetry 的生产级可观测性落地 在生产AI 插件AI 技能开发工具上一篇Azure Kinect数据处理秘籍从校准到点云生成的完整技术路线下一篇Blackbone安全特性解析Windows内存保护绕过与进程防护技术终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考