第 86 期 - B 站监控 2.0 架构的设计与实践
摘要
B 站基于 Prometheus + Thanos 的监控架构面临稳定性差、用户查询体验差和云上监控数据质量差等痛点,于是设计了监控 2.0 架构,从采集存储分离、存算分离、时序数据库选型、单元化容灾等方面进行改进,并在数据来源、采集、存储、查询等环节有新的方案,还涉及 grafana 版本升级、云监控方案改进和未来规划等内容
背景
B 站之前基于 Prometheus + Thanos 构建了统一监控平台。随着业务发展,指标数据量级暴增,原架构面临诸多痛点:
- 稳定性差:告警计算基于 Prometheus 本地计算,大应用发布时元数据指标 label 变化会导致重新构建 timeseries 索引占用大量内存,引起 Prometheus oom。
- 用户查询体验差:oom 告警导致数据断点,Prometheus + Thanos 查询性能有限,出现面板查询慢/超时、open api 查询失败和误告警等问题。
- 云上监控数据质量差:云主机情况复杂,网络拓扑复杂导致监控数据缺失,且多个云监控数据源让用户难以选择。
2.0 架构设计
设计思路
- 采集存储分离:Prometheus 采集存储一体,导致 target 监控实例调度分发时弹性扩缩困难,新架构要实现采集存储分离,让 target 实例可动态调度,采集器能弹性扩缩。
- 存算分离:Prometheus 存算一体,业务发展时指标数据量级和计算需求非线性关系,按存算一体采购服务器资源会造成浪费,所以要实现存算分离,在写入、存储和查询都能弹性扩缩。
- 时序数据库选型:VictoriaMetrics(VM)被很多公司用作时序数据库,经调研,其写入&查询性能、分布式架构、运维效率满足需求。
- 单元化容灾:之前调度规则不标准,现按照 zone 维度调度,实现同 zone 下全链路单元内容灾。
功能架构概览
Metrics 指标数据应用场景广泛,2.0 架构落地面临监控系统自身稳定性、数据可用性、查询性能、故障爆炸半径等挑战。
各环节具体方案
数据来源
- 原方式问题:之前基于 pull 方式发现 target 监控实例,存在发现延迟(有 30s 同步周期)和运维成本大(业务方接口有问题时难以及时感知)的问题。
- 改进方式:将 pull 方式改成 push 方式,业务方主动 push target 实例。同时在指标平台提供按集成任务申请监控接入,实时显示 target 实例采集状态等信息。
数据采集
调度层
- 一级调度(Master):从数据库获取全量采集 job 配置和 target 监控实例,根据 zone 维度调度构建二级调度所需采集配置。为保证高可用,访问数据库异常时内存快照数据不更新,还做了其他保护策略(如拦截大操作的误操作),通过多种手段将全量调度时间从 50s 降到 10s 内。
- 二级调度(Contractor):根据采集集群名称 + 版本号定时从 Master 获取本机房采集配置,设计多套防止故障爆炸半径。根据 Collector 心跳获取 health 采集节点,按实例和容量维度调度,将采集配置分给对应节点。更新时会触发新调度,且已分配的配置下次调度优先分配。Contractor 重启时通过维护全局 state 变量保证指标数据无断点或抖动。
采集器
采集器 Collector 基于 vmagent 封装。有定时上报心跳给 Contractor 和触发 vmagent 采集的功能。灰度后发现 vmagent 内存高,开启流式采集后内存降低 20%。vmagent 有随机平滑 load 机制,为避免 Collector 扩/缩容时指标断点,设计了退出机制(监听到退出信号后不立即退出,继续采集一个周期)。
数据存储
通过 vmstorage 进行指标存储,这里主要介绍索引结构存储。
- 核心类型
- MetricName:存储 label kv 变量,写入时由 vminsert 序列化实际指标数据中的 label 发起写入请求,查询时结果集的 label 信息也由其序列化存储,类似 tv 的内容元信息,其落盘序列化结构有特定格式。
- MetricId:纳秒时间戳,一簇 ts 的唯一键,入库时生成,8 字节。
- TSID:也是一簇 ts 的唯一键,由租户、指标名 id、job instance labelvalue id 和 MetricId 组成,其落盘序列化结构包含更多标识信息,通过字典序排序后同租户同名指标分布连续,是 vmstorage 中 data 目录下的 key,索引查询核心是根据条件得到 TSID。
- 抽象索引结构:形成了 MetricName -> TSID、MetricId -> MetricName、MetricId -> TSID 等抽象索引结构,还有核心的倒排索引结构。这些索引结构结合可得到查询流程,vm 通过基于前缀的压缩存储减少磁盘占用,相比 Prometheus 磁盘存储成本降低约 40%。vmstorage 性能优秀,以某集群为例,单台 48c 256g 的 vm stroage 能支撑一定的写入和查询量,通过调整 gogc 可优化资源使用。
数据查询
promql 自动替换增强
- 问题提出:通过 grafana 访问 victoriametrics 查询指标时,部分 panel 返回数据慢或失败,以查询 grpc 接口调用 p99 耗时的 promql 语句为例,查询数据量可能很大,影响数据源稳定性。常规预聚合方式存在资源浪费或不能解决问题的情况。
- 执行流程分析:该语句在 vmselect 执行时解析成执行树,按深度优先方式执行各节点,包括指标数据查询、rate 函数执行、sum 聚合函数执行、histogram_quantile 函数执行。执行过程中第一步往往是性能瓶颈,第二步未根本解决性能问题。
- 解决方案:针对第三步输出结果预聚合,将预聚合结果转化为指标 test_metric_app_A,但在 grafana 中直接替换会导致体验不佳。通过构建映射关系并考虑查询条件等因素,对映射关系进行调整,最终在语义无损的前提下完成 promql 的自动替换,还可增加聚合维度,用户无感知,对有性能瓶颈的查询语句进行优化。
基于 promql 的 flink 指标预聚合
- 原预聚合问题:B 站原预聚合通过定时批量查询,对 vmstroage 和 vmselect 压力大,存在内存浪费,查询可能成为慢查询且数据聚合有延迟。
- Flink 预聚合流程
- 数据过滤阶段:类比 vm 查询指标,根据执行树叶子节点构建 key 初步筛选实时 kafka 流中的指标数据,再根据剩余 label 过滤条件进一步过滤得到原始数据。
- 数据分区阶段:以特定的 promql 语句为例,分析执行过程中的内存压力,通过复用 sum 节点的 group by 键作为分区键,对 label 进行优化处理(丢弃不必要的 label),减少内存消耗。
- 时间窗口执行阶段:完成数据筛选和分区后进入此阶段,对执行树进行深度优先调用。由于 flink 时间窗口限制,部分函数实现与 vm 有区别,flink 参考 prometheus 的设计来实现相关函数,且由于之前丢弃了部分 label,部分函数存在不支持的场景。
- 数据上报阶段:前一阶段得到的预聚合指标数据通过 remotewrite 协议写到 vminsert 上。通过此设计,基于 promql 的 flink 预聚合可快速配置生效,减少预聚合资源需求。
- 查询优化收益:查询自动优化和 flink 专项预聚合可治理优化特定 promql,减少慢查询、查询数据源资源,带来高收益。
grafana 版本升级
- 版本升级挑战:从 v6.7.x 升级到 v9.2.x 存在多个 break change,老版本 grafana 部署成本高(物理机部署且黑盒,还有额外的认证服务增加成本)。
- 应对措施:升级前做大量测试和验证,用脚本修复数据不兼容,替换数据源插件。在部署方式上,用 git 仓库管理部署编译脚本,构建 all - in - one 镜像实现容器化部署。新版本 grafana 在特定条件下查询性能提升。升级后,面板加载性能和用户查询体验大幅提升。
云监控方案
- 痛点:云环境下网络不通导致采集失败、存在多个云监控数据源用户难以选择。
- 方案:将 Prometheus 采集的云上数据通过 remote write 回源到 idc 存储集群。与 idc 采集方案相比,Contractor 支持从公网 pull 本 zone 所需采集配置;使用 Prometheus 采集是因为改造成本低且本地可存 1d 数据;引入 vm - auth 组件对回源流量做租户认证和流量调度。
- 收益:云上数据按 zone 调度后质量提升,oncall 降低 90%以上,统一数据源查询,20 + 云上数据源收敛到 1 个。
未来规划
- 支持更长时间 Metrics 指标数据存储,用于业务分析等场景。
- 支持更细粒度的指标埋点,助力业务可观测和排障。
- 增强自监控能力,覆盖更多自监控场景和运维 SOP。
- 迭代指标平台,增加写入/查询封禁、白名单等能力,借助大模型实现 text2promql 等功能。
扩展阅读
Made by 捣鼓键盘的小麦 / © 2025 Front Talk 版权所有