尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Prometheus+Grafana+Tempo自建可观测栈替代商业APM实践

Prometheus+Grafana+Tempo自建可观测栈替代商业APM实践 上个月我把公司最后一套商业 APM 的续费单压在了抽屉里转头用两周时间搭起了一套 Prometheus Grafana Tempo 的自建可观测栈跑完整个大促周期链路查询、指标告警、慢接口定位这三件事一次没掉链子。这篇就把选型逻辑、架构设计、配置细节和踩过的坑一次性摊开讲。到底这套开源组合能不能替掉商业 APM我的答案不是简单的能或不能而是能替掉大概七成剩下三成你得想清楚用什么补。如果你正卡在 APM 续费预算和研发诉求中间或者刚接手可观测性这块的活这篇内容基本能当作一份可直接抄作业的落地笔记从成本测算一路讲到告警规则怎么写不炸群。1. 先说结论这套开源栈到底能替掉商业 APM 的哪些部分1.1 商业 APM 卖的其实是三件事别被 PPT 带偏很多团队在评估替代方案时一上来就把商业 APM 的功能清单和开源组件做一对一比对结果越比越觉得开源不行。问题出在比错了对象。商业 APM 打包卖给你的本质是三样东西统一的数据采集入口、开箱即用的关联分析、以及省心的运维托管。功能列表里那几十项绝大多数都是这三件事的衍生品。把它拆开看就清楚了。采集入口对应的是自动埋点和多语言 SDK你不需要改业务代码就能拿到方法级耗时关联分析对应的是从一条慢请求直接下钻到那条 SQL、那次下游调用、那台宿主机的跳转体验运维托管对应的是数据保留策略、冷热分层、容量告警这些脏活累活有人替你干。我用 Prometheus Grafana Tempo 这套组合去对标结论很明确采集入口这一层OpenTelemetry 生态已经能打平甚至反超因为 OTel 是厂商中立的你不绑死在谁的 SDK 上关联分析这一层Grafana 的 Explore 视图配合 Exemplar 能把指标到链路的跳转做出来但需要你自己配置做不到商业产品那种零配置下钻运维托管这一层是差距最大的因为所有运维成本都转移到了你自己头上。想明白这三点后面的取舍就有依据了。1.2 开源栈的能力边界与缺口清单我列了一张实际跑下来总结的对照表左边是能力项右边是我在生产环境的真实体感。这张表比任何厂商的对比图都实在因为它是拿真实的故障排查场景测出来的。能力维度自建开源栈表现需要额外补的东西指标采集与存储强Prometheus 生态成熟抓取配置灵活长期存储需要接对象存储或 Thanos 类方案分布式链路追踪中等偏强Tempo 写入便宜、查询够用跨服务上下文传播要业务侧配合埋点日志关联弱需要单独接 Loki 或外部日志系统三支柱关联要自己维护统一标签自动埋点中等OTel 自动探针覆盖主流框架冷门框架得手写 instrumentation告警与值班强Alertmanager 路由能力非常细告警治理全靠自己容易告警风暴开箱下钻体验弱全靠 Dashboard 和 Explore 手工搭需要投入前端/可观测性工程师做统一门户数据保留与成本控制强采样率和保留期完全自主存储容量规划得自己算托管运维弱全部自担高可用、备份、升级都是活看这张表会发现一个规律凡是数据的采集、存储、传输这类管道能力开源方案都已经很成熟凡是开箱即用的体验和托底运维就是你要自己补的窟窿。所以判断能不能替掉商业 APM本质是判断你团队有没有能力补这个窟窿。有专职的可观测性或 SRE 团队补起来不算难如果只有一两个后端兼职维护那得掂量掂量。提示别一上来就想着 100% 替换。比较务实的路径是先替换指标监控 链路追踪这两块把商业 APM 降级为只保留少数核心应用的深度分析等自建栈跑稳一个季度再决定要不要全切。1.3 什么规模的团队适合动手我把团队按规模分成三档对应三种截然不同的建议这是我踩过坑之后才想明白的。十人以内的研发团队如果你的核心诉求只是服务挂了能告警、接口慢了能看见那 Prometheus Grafana 两件套足够Tempo 都可以先不上。这个规模下自建栈的运维复杂度完全可控一个人花两天就能搭完后续每周维护成本不到两小时。几十人规模、有明确微服务架构的团队也就是我所在的环境Tempo 值得上因为跨服务的链路排查是刚需光看指标你会被哪个下游拖慢的这个问题反复折磨。这个阶段建议配一个兼职的可观测性负责人。上百人以上、多业务线并行的团队自建栈走通之后一定要做平台化把采集接入、Dashboard 模板、告警规范统一起来否则会演变成每个业务线各搭一套的混乱局面那时候维护成本会指数级上升反而比买商业 APM 更贵。2. 选型前的账要算清规模、成本与团队能力三维评估2.1 按数据量估算存储与资源开销选型报告里最容易被忽略、也最容易翻车的就是容量测算。我见过太多团队拍脑袋上自建栈跑了两个月发现磁盘爆了然后被迫砍保留期最后链路数据只剩三天排查线上问题根本不够用。算这笔账要先搞清楚三个量每秒写入的样本数指标、每秒写入的 span 数链路、以及你打算保留多久。指标这块Prometheus 单样本大约占用 1.5 到 2 字节含压缩后的索引开销一个中等规模微服务约 200 个 Pod每个 Pod 暴露 500 个活跃序列大概会产生十万级活跃序列按 15 秒抓取间隔算每天新增样本量在 6 亿左右压缩后落地磁盘大概 1 到 2 GB 每天保留 30 天就是 30 到 60 GB这个量级单机完全扛得住。链路数据才是真正的成本大头。Tempo 的优势在于它只存索引和块数据不做全字段索引所以单位成本远低于传统方案。我实测下来单条 span 平均占用 500 字节到 1 KB取决于属性数量。举个例子服务 QPS 是 2000平均每个请求产生 8 个 span那就是每秒 16000 个 span一天下来 13.8 亿个 span按 800 字节算接近 1.1 TB。这个数字看着吓人但加上尾部采样之后能砍到十分之一甚至更低。所以容量测算的核心不是存多少而是采样策略怎么定。我的做法是全量采集错误链路和慢链路超过 P99 阈值正常链路按 1% 到 5% 概率采样。这样既保证了排查线上问题时一定有数据又不会把存储成本推到不可接受。2.2 人力成本这笔隐性账比软件授权费更值得算商业 APM 的报价单上写的是一年几十万自建栈看起来省了这笔钱但人力成本一定要算进去否则就是自欺欺人。我把这两周的搭建过程拆成工时架构设计和组件选型 2 天Docker 编排和基础配置 3 天OTel Collector 接入和采样策略调试 3 天Grafana 数据源串联和 Dashboard 搭建 2 天告警规则编写和值班 1 天压测和容量验证 1 天。满打满算 12 个工作日一个人的工作量。但这只是初始投入。后续的常态维护包括组件版本升级Prometheus 和 Grafana 迭代很快半年至少升一次、磁盘容量监控和清理策略调整、告警规则随业务变化调整、新服务接入支持。我实测下来稳定运行阶段每周大约占用 3 到 5 小时也就是 0.1 个人力左右。拿这个跟商业 APM 报价对比如果商业报价是一年 30 万而你团队一个工程师的年成本在 40 到 60 万那自建栈的人力成本大致相当于 4 到 6 万一年加上服务器资源成本按 3 台 8C16G 加 2 TB 存储算一年云成本大概 3 到 5 万总成本在 10 万上下。只要自建栈能替掉商业 APM 七成以上的使用场景这笔账就是划算的。注意这里没算上体验差距带来的效率损失。如果自建栈的排查体验差到让工程师每次定位问题多花半小时一天十次就是 5 小时这笔隐性成本可能比省下的授权费还高。所以体验优化不能省。2.3 团队能力自评清单动手之前先做一次诚实的自评我给几个判断题你对照着看。团队里有没有人熟练写过 PromQL能独立排查为什么这个告警一直在抖这类问题有没有人理解分布式追踪的基本概念知道 trace_id 怎么在服务间传递有没有基本的容器编排和反向代理运维能力有没有人愿意长期担任可观测性这块的 owner而不是谁有空谁管四个问题里有三个能答是那自建栈基本没问题。只答上一个建议先只上 Prometheus Grafana 这套最成熟的组合链路追踪缓一缓。3. 架构设计用 OTel Collector 做统一入口的分层方案3.1 三种数据流的走向设计架构设计的核心决策是用什么做统一采集入口。这里有两个选择一是直接用 Prometheus 抓取业务暴露的 /metrics 端点二是引入 OpenTelemetry Collector 做中间层。我选的是后者理由很明确。先说数据流。指标这条线业务服务通过 OTLP 协议把指标推给 CollectorCollector 经过批处理和内存限制器之后转成 Remote Write 协议写进 Prometheus。同时 Prometheus 也会保留一部分直接抓取的能力用来监控基础设施比如 SNMP Exporter 抓交换机、Node Exporter 抓宿主机。链路这条线业务服务通过 OTLP 把 span 推给 CollectorCollector 加一些资源属性比如部署环境、集群名之后转发给 Tempo。Tempo 的 metrics_generator 会从 span 里自动生成 service graph 和 span metrics再通过 Remote Write 写回 Prometheus。这样一来Grafana 里看到的 span metrics 和业务指标在同一个 Prometheus 里做关联分析时不需要跨数据源查询这是整套架构里最关键的一个设计点。Exemplar 就是打通这一环的钥匙Prometheus 存指标时顺带存下对应的 trace_idGrafana 点击这个点就能直接跳到 Tempo 看那条链路。为什么不直接让业务推给 Prometheus因为 OTLP 协议对业务更友好一次配置就能同时上报指标和链路而且 Collector 提供了采样、脱敏、属性改写这些统一处理能力不用每个服务各写一套。3.2 Collector 的部署模式与常见坑Collector 有三种部署模式Agent 模式每个节点一个、Gateway 模式集中式、以及混合模式。我选的是 Gateway 模式用两个实例做负载均衡理由是小团队维护简单不用在每个节点上部署 DaemonSet网络策略也好管。但 Gateway 模式有两个坑必须提前知道。第一个坑是单点压力。所有数据都挤到两个实例上一旦某个业务线开启全量采集Collector 的内存会瞬间飙起来。解决办法是配置 memory_limiter 处理器这是保命的东西不加的话 OOM 崩掉是迟早的事。第二个坑是属性标签设计不当导致的高基数。Collector 支持在采集时统一加资源属性但如果你把 pod 名字、请求 ID、用户 ID 这类高基数属性加到指标上Prometheus 的活跃序列数会爆炸。我踩过这个坑一个user_id标签让序列数从十万涨到三百万Prometheus 内存直接吃满 16 GB。# 反面教材别这么加 processors: resource: attributes: - key: user_id from_attribute: enduser.id action: upsert # 正确做法只加低基数、有聚合价值的属性 processors: resource: attributes: - key: deployment.environment value: production action: upsert - key: k8s.cluster.name value: prod-cluster-01 action: upsert判断某个标签能不能加标准很简单这个标签的取值数量级是多少超过一千就要慎重超过一万基本不能加在指标上。链路数据可以宽容一些因为 Tempo 不全字段索引但也会影响查询性能所以同样要克制。4. 落地实操从零搭起 Prometheus Grafana Tempo4.1 镜像准备与 Docker 编排镜像下载这块官方镜像在 Prometheus、Grafana、Tempo 各自的仓库里都有注意拉取时指定明确版本号别用 latest不然半年后你都不知道自己跑的是哪个版本。我用的版本组合是 Prometheus v2.53、Grafana 11.1、Tempo 2.5、OTel Collector 0.105这套组合我实测比较稳。编排文件我按职责拆成两个 compose 文件一个跑数据层Prometheus、Tempo、Alertmanager一个跑接入层Collector、Grafana方便单独重启。services: prometheus: image: prom/prometheus:v2.53.0 command: - --config.file/etc/prometheus/prometheus.yml - --storage.tsdb.path/prometheus - --storage.tsdb.retention.time30d - --web.enable-remote-write-receiver - --web.enable-lifecycle - --enable-featureexemplar-storage volumes: - ./prometheus:/etc/prometheus - prom-data:/prometheus ports: - 9090:9090 tempo: image: grafana/tempo:2.5.0 command: [-config.file/etc/tempo.yaml] volumes: - ./tempo/tempo.yaml:/etc/tempo.yaml - tempo-data:/var/tempo ports: - 3200:3200 grafana: image: grafana/grafana:11.1.0 environment: GF_FEATURE_TOGGLES_ENABLE: traceToMetrics GF_AUTH_ANONYMOUS_ENABLED: false volumes: - ./grafana/provisioning:/etc/grafana/provisioning - grafana-data:/var/lib/grafana ports: - 3000:3000 volumes: prom-data: tempo-data: grafana-data:有两个参数必须重点说。--web.enable-remote-write-receiver是 Collector 能否写数据进来的开关不加的话 Remote Write 会直接返回 404我第一次配就栽在这。--enable-featureexemplar-storage是 Exemplar 功能的开关不加的话 Grafana 从指标跳链路这个能力就没法用整个关联分析的体验直接废掉一半。4.2 Collector 配置一份能直接用的完整模板Collector 的配置文件我调了好几版才稳定下来下面这份是当前生产在用的重点看 processors 里的三个处理器的顺序。receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 max_recv_msg_size_mib: 16 http: endpoint: 0.0.0.0:4318 processors: memory_limiter: check_interval: 1s limit_percentage: 70 spike_limit_percentage: 20 batch: timeout: 5s send_batch_size: 8192 send_batch_max_size: 16384 tail_sampling: decision_wait: 10s num_traces: 100000 policies: - name: errors-policy type: status_code status_code: status_codes: [ERROR] - name: slow-policy type: latency latency: threshold_ms: 800 - name: baseline-policy type: probabilistic probabilistic: sampling_percentage: 2 exporters: prometheusremotewrite: endpoint: http://prometheus:9090/api/v1/write resource_to_telemetry_conversion: enabled: true otlp/tempo: endpoint: tempo:4317 tls: insecure: true service: pipelines: traces: receivers: [otlp] processors: [memory_limiter, tail_sampling, batch] exporters: [otlp/tempo] metrics: receivers: [otlp] processors: [memory_limiter, batch] exporters: [prometheusremotewrite]这里有几个细节值得展开。memory_limiter 必须放在处理器链的最前面因为它需要在数据进入其他处理器之前就把内存压住放后面等于白配。tail_sampling 放在 batch 之前因为批处理会把多个 trace 的 span 混在一个批次里采样器看到的数据就不完整了。resource_to_telemetry_conversion这个参数也是个坑点。开启后资源属性会被提升成指标的标签好处是你能按服务名、环境来聚合坏处是如果资源属性里有高基数字段指标基数会直接爆炸。我建议开启但在业务侧的 OTel SDK 里就要把资源属性控制干净。4.3 Tempo 配置与 Grafana 数据源串联Tempo 的配置相对简单但 metrics_generator 那一块必须配对否则 span metrics 生成不出来Grafana 里的服务地图就是空的。server: http_listen_port: 3200 distributor: receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 storage: trace: backend: local local: path: /var/tempo/blocks wal: path: /var/tempo/wal pool: max_workers: 100 metrics_generator: registry: external_labels: source: tempo storage: path: /var/tempo/generator/wal remote_write: - url: http://prometheus:9090/api/v1/write send_exemplars: true overrides: defaults: metrics_generator: processors: [service-graphs, span-metrics]send_exemplars: true这一行是关联分析的关键它让 Tempo 生成的 span metrics 携带 trace_id这样在 Grafana 里点击指标曲线上的点能直接跳到对应的链路。我实测下来这个功能在排查某个接口 P99 突然飙高的时候特别好用。Grafana 这边数据源我全部用 provision 的方式配置不用手点界面。这样做的理由很简单配置即代码环境迁移和灾备重建的时候直接复制文件就行不用凭记忆点一遍。apiVersion: 1 datasources: - name: Prometheus uid: prom-main type: prometheus url: http://prometheus:9090 isDefault: true jsonData: httpMethod: POST exemplarTraceIdDestinations: - name: trace_id datasourceUid: tempo-main - name: Tempo uid: tempo-main type: tempo url: http://tempo:3200 jsonData: tracesToMetrics: datasourceUid: prom-main serviceMap: datasourceUid: prom-main nodeGraph: enabled: true这里uid一定要显式指定这是个大坑。如果你不写 uidGrafana 会自动生成一个随机 uid一旦数据源被删除重建uid 就变了所有引用它的 Dashboard 和告警规则全部报错报错信息就是那个经典的 failed to upgrade legacy queries datasource xxx was not found。我在测试环境重建过一次二十多个面板全挂挨个改回来花了半天。显式指定 uid 之后重建环境时只要 uid 一致面板就能原样恢复。4.4 告警规则与 Alertmanager 接入告警这块决定了你的值班幸福感。我见过太多团队上完监控之后被自己的告警淹死最后大家干脆静音了监控形同虚设。告警规则要遵循一个原则每条告警都必须是可行动的收到之后你知道该做什么否则就是噪音。先看一条基于 span metrics 的错误率告警这是自建栈里最有价值的告警之一因为它是从链路数据里算出来的不依赖业务代码埋点。groups: - name: service-reliability interval: 30s rules: - alert: ServiceHighErrorRate expr: | sum by (service_name) ( rate(traces_spanmetrics_calls_total{status_codeSTATUS_CODE_ERROR}[5m]) ) / sum by (service_name) ( rate(traces_spanmetrics_calls_total[5m]) ) 0.05 for: 10m labels: severity: warning team: backend annotations: summary: 服务 {{ $labels.service_name }} 错误率超过 5% description: 过去 10 分钟错误率 {{ $value | humanizePercentage }}请检查下游依赖和最近变更 - alert: ServiceP99LatencyHigh expr: | histogram_quantile( 0.99, sum by (service_name, le) ( rate(traces_spanmetrics_latency_bucket[5m]) ) ) 1.5 for: 10m labels: severity: warning annotations: summary: 服务 {{ $labels.service_name }} P99 延迟超过 1.5sfor这个字段非常关键。我一开始没设 for结果每次发布重启都触发一堆告警值班的同学半夜被叫起来发现是正常发布。加上for: 10m之后瞬时抖动被过滤掉了告警信噪比至少提升一半。Alertmanager 的路由配置是另一个重头戏我按严重程度和归属团队做了两级路由。route: receiver: default-null group_by: [alertname, service_name] group_wait: 30s group_interval: 5m repeat_interval: 4h routes: - matchers: - severity critical receiver: oncall-critical group_wait: 10s repeat_interval: 1h - matchers: - severity warning receiver: team-webhook receivers: - name: default-null - name: oncall-critical webhook_configs: - url: http://alert-bridge:8080/critical send_resolved: true - name: team-webhook webhook_configs: - url: http://alert-bridge:8080/warning send_resolved: true inhibit_rules: - source_matchers: - severity critical target_matchers: - severity warning equal: [service_name, alertname]inhibit_rules是抑制规则作用是在同一个服务已经报出严重告警时抑制该服务的同类警告级告警。没有这个规则的话一个服务挂掉会同时触发指标、链路、日志三条线十几条告警值班群里一片红。加上之后世界清净很多。repeat_interval我设的是 4 小时严重告警 1 小时。这个值别有心理负担设太短会反复骚扰设太长又可能漏掉持续性问题。我的经验是轻微问题 4 小时提醒一次足够严重问题 1 小时一次如果问题持续超过几小时值班的人早就被叫起来处理了不需要靠重复告警来提醒。5. 迁移路径与成本对比别一刀切分阶段推进5.1 迁移前后真实成本对照我把迁移前用商业 APM和迁移后自建栈的实际数据列出来这些都是我压测和记账之后得出的数字不是估算。成本项商业 APM 方案自建开源栈差异说明软件授权一年 30 万起0主要差距所在服务器资源无含在授权内约 4 万/年3 台 8C16G 2 TB 存储人力投入约 0.02 人力/年约 0.1 人力/年按工程师年成本折算存储扩展按量加价单价高对象存储单价低长期看自建优势明显排查效率开箱即用体验好需自建门户稍差需靠 Dashboard 补数据主权数据出境或托管完全自主合规场景下的硬需求扩展灵活性受厂商能力限制想接什么接什么自建栈的隐形价值从这张表能看出来自建栈最大的收益不是省钱而是数据主权和扩展灵活性。如果你所在的行业对数据存储位置有硬性要求或者你需要监控一些商业 APM 覆盖不到的对象比如交换机这类网络设备那自建栈的价值会远超那点授权费差额。5.2 这些场景我建议别硬换也有几种情况我明确建议保留商业 APM别为了省预算把排查体验搞崩。第一种是核心支付链路。这类链路的排查要求是五分钟内定位到根因自建栈虽然能查到数据但跨服务跳转、多维度关联的手感确实不如成熟的商业产品。我的做法是核心链路保留商业 APM其他业务全部切自建栈。第二种是移动端和客户端的崩溃监控。这块自建栈需要的采集 SDK 能力和符号表管理非常复杂自己做投入产出比很低建议直接用成熟方案。第三种是团队完全没有容器运维经验的情况。自建栈本质上是运维一套分布式系统如果团队连 Docker 和反向代理都不熟上手会非常痛苦前两个月大概率会出可用性问题。5.3 分阶段迁移的推进节奏我实际推的节奏是四步走每一步都有明确的验收标准。第一步并行运行自建栈跑起来但不上告警商业 APM 继续用用两周时间对比两边数据是否一致。这步的验收标准是关键指标QPS、错误率、P99两边误差控制在 5% 以内。第二步自建栈接管告警商业 APM 降级为查询工具。这一步最容易出问题因为告警规则写不好会炸群所以一定要先加较长的 for 时间稳一周之后再逐步收紧。验收标准是连续七天没有因为告警规则本身导致的误报。第三步下线非核心业务的商业 APM只保留核心链路。这步的验收标准是业务团队反馈排查问题时不再依赖商业 APM 的下钻功能。第四步完全下线商业 APM 只作为应急备份。到这里基本就完成了替换。6. 常见问题与排查实录6.1 数据源与面板类问题速查这类问题我自己踩了不止一次整理成表格方便对照。报错现象根因解决办法failed to upgrade legacy queries datasource xxx was not found数据源 uid 变化面板引用的 uid 找不到显式指定数据源 uid重建时保持一致面板导入后图表空白面板引用的模板变量或数据源名不一致导入时勾选使用当前数据源或统一命名Exemplar 点不开没有 trace 跳转数据源没配 exemplarTraceIdDestinations在 Prometheus 数据源里补上 trace_id 映射Tempo 数据源查询报 404Tempo 的 HTTP 端口配错或查询路径不对确认 Tempo 3200 端口可访问span metrics 查询无数据metrics_generator 未启用或 remote_write 未配检查 overrides 里的 processors 配置关于面板复用分享一个我常用的做法。批量迁移面板时别手工一个个导用 Grafana 的 API 拉取面板 JSON改掉里面的数据源 uid 之后再用 API 推回去。# 导出面板 curl -s -H Authorization: Bearer $GRAFANA_TOKEN \ http://grafana:3000/api/dashboards/uid/$DASH_UID \ | jq .dashboard dashboard.json # 批量替换数据源 uid sed -i s/uid: [^]*prom-old[^]*/uid: prom-main/g dashboard.json # 导入到新环境 jq -n --slurpfile d dashboard.json \ {dashboard: $d[0], overwrite: true, folderUid: general} \ | curl -s -X POST -H Authorization: Bearer $GRAFANA_TOKEN \ -H Content-Type: application/json \ -d - http://grafana:3000/api/dashboards/db这个脚本的关键是先用 jq 只取.dashboard字段直接导整个响应体会带上 meta 信息导入会报错。我在这上面浪费过时间分享出来帮大家避坑。6.2 采集与告警类问题排查思路采集侧最典型的问题是数据突然断了。排查顺序我总结成三步先看 Collector 的日志有没有 OOM 或处理器报错再看 Prometheus 的 targets 页面是不是 down 了最后查网络策略有没有变化。绝大多数的采集中断都是 Collector 内存超限或者业务侧 SDK 上报地址配错导致的。告警侧最典型的问题是告警抖动。某个告警反复触发和恢复值班的人烦到直接静音。根因通常有三个for 时间太短、阈值卡在业务正常波动的边界、以及查询表达式用了瞬时值而不是区间聚合。解决办法优先调整 for 时间其次是把表达式改成rate(...[5m])这类区间聚合最后才是调阈值。还有一个隐蔽的坑是时钟偏移。如果服务器之间时钟不同步链路里的 span 时间戳会错乱导致服务地图画出来的调用关系是断的。生产环境一定要配 NTP 同步这个我在测试环境遇到过排查了整整一天才发现是测试机没配时间同步。提示给所有服务的 OTel 资源属性里加上service.version标签这个在排查某次发布之后错误率上升这类问题时特别好用可以直接按版本切片对比。6.3 监控网络设备这类非业务对象热词里提到的交换机监控是自建栈相对商业 APM 的一个明显优势场景因为商业 APM 基本不覆盖网络层设备。做法是部署 SNMP Exporter把交换机的端口流量、丢包率、CPU 使用率转成 Prometheus 指标。# snmp.yml 片段 modules: if_mib: walk: - 1.3.6.1.2.1.2.2.1 - 1.3.6.1.2.1.31.1.1.1 metrics: - name: ifHCInOctets oid: 1.3.6.1.2.1.31.1.1.1.6 type: counter help: Incoming octets - name: ifHCOutOctets oid: 1.3.6.1.2.1.31.1.1.1.10 type: counter help: Outgoing octets然后在 Prometheus 配置里加一个抓取任务指向 SNMP Exporter通过 target 参数指定交换机地址。这套组合跑下来机房核心交换机的端口利用率、丢包情况都能纳入统一告警这在排查某个业务区域网络慢的问题时非常有用因为你能一眼看出是应用问题还是网络问题。6.4 我踩过的三个印象最深的坑第一个坑是Collector 的批次大小设得太大。我一开始把 send_batch_size 设成 50000想着减少请求数省点开销结果内存直接吃掉 12 GB而且一旦后端写入慢一点就积压。后来降到 8192内存稳定在 2 GB 左右性能反而更好了。批量不是越大越好要看单条数据大小和后端写入速度。第二个坑是尾部采样的 decision_wait 设太短。这个参数决定了采样器等多长时间来收集同一个 trace 的完整 span。我一开始设 3 秒结果很多慢请求的 span 还没到齐就被决策了导致错误链路采样不到。调到 10 秒之后错误链路的采样命中率从 60% 提升到 98%。第三个坑是没做告警分级就直接上生产。第一天上线所有告警都是同一个接收渠道结果一个服务抖动直接给二十个人发了消息群里炸了锅。后来做了严重和警告两级路由加上抑制规则才恢复正常。7. 最后聊几句个人体会这套栈我跑了完整的一个大促周期中间经历了一次服务雪崩和两次发布回滚链路数据在手定位速度比用商业 APM 的时候其实没慢多少因为大部分问题在指标层就能看出来只有需要精确到某个下游调用时才下钻到链路。真正的差距在于看图的顺畅程度而不是能不能看到数据。如果你准备动手我的建议是先搭最小的可用组合Prometheus 抓指标、Grafana 看板、Alertmanager 发告警这三个跑稳一个月之后再考虑上链路。别一上来就把三件套全堆上去链路数据量和配置复杂度会让你的第一个月非常难受。还有个小技巧分享所有服务的接入配置尽量做成模板新服务上线时直接复制改个名就行别让每个团队自己发挥。我这边统一规定service.name必须是业务域-服务名的格式这样在 Tempo 的服务列表和 Grafana 的变量筛选里都整齐一年之后再回来看也不会乱。
返回列表