土法炼钢兴趣小组的算法知识备份

【可观测性工程】可观测性 vs 监控:从 Zabbix/Nagios 到 OpenTelemetry 的二十年

文章导航

分类入口
architectureobservability
标签入口
#monitoring#observability#nagios#zabbix#prometheus#opentelemetry#graphite#statsd#grafana#push-pull

目录

可观测性 vs 监控:从 Zabbix/Nagios 到 OpenTelemetry 的二十年

时间线

在很多团队的工程语境里,“监控”和”可观测性”被当作同义词使用:有同事把 Grafana 仪表盘叫做”监控大盘”,把 Prometheus 告警叫做”监控告警”,把 Jaeger 里的 Trace 视图也叫做”监控链路”。这种用法在日常沟通中无伤大雅,但它掩盖了一个在架构层面非常关键的事实——监控(Monitoring)和可观测性(Observability)不是一个东西的两个名字,它们来自完全不同的认知模型,对应完全不同的数据组织方式,解决完全不同的问题。

把这两个概念混用的代价,会在系统规模上去、故障类型变得”离奇”之后显现出来。一个典型的症状是:监控面板上所有指标全部绿色,但用户已经在投诉;告警没有响,但 SLO 已经破了;值班同事盯着十几个仪表盘逐个切换,却找不到故障根因。这时候人们才会意识到,自己搭的其实是一套”监控系统”,而不是一套”可观测性系统”。

本文尝试把这段历史捋清楚。从 1999 年 Nagios 1.0 发布,到 2019 年 OpenTelemetry 合并成立,再到 2023 年 OpenTelemetry Metrics 与 Logs 正式 GA,整整二十四年时间里,行业在这件事上反复试错了至少五轮。每一轮的设计选择——推(push)还是拉(pull)、层级命名还是标签化、文本协议还是 gRPC、单一数据模型还是三支柱统一——都有非常具体的历史原因,也都留下了非常具体的工程坑点。理解这段历史,比盲目追新工具更重要。

一、监控的定义与局限

1.1 监控的核心范式

监控的核心范式可以用一个最小环路来概括:预设问题 → 收集指标 → 阈值触发 → 人工响应。这个环路的起点是”预设问题”,也就是说,在系统上线之前,运维工程师必须已经能够列出所有他关心的指标,才能针对每个指标配置采集脚本和告警阈值。

举个最经典的例子:一个 Web 服务器,运维会预设如下几个问题:

每一个问题都对应一个脚本,每一个脚本的输出都对应一个数值,每一个数值都对应一个阈值。只要系统在预设范围内运行,监控就能工作得很好。

1.2 Nagios 插件模型

这套范式最成熟的实现是 Nagios。Nagios 1.0 由 Ethan Galstad 于 1999 年发布,它把”预设问题”变成了工程意义上的”插件”。Nagios 的核心只做三件事:调度检查、记录状态、发送通知。真正的检查逻辑全部由外部插件实现,每个插件就是一个可执行文件,约定输入参数、约定退出码(0=OK、1=WARNING、2=CRITICAL、3=UNKNOWN)、约定标准输出格式。

典型的 Nagios 插件包括:

这种”一切皆插件”的设计让 Nagios 在社区里繁荣了十多年,Monitoring Plugins 项目(原 Nagios Plugins)至今仍在维护数百个官方插件,覆盖几乎所有常见服务。

对于无法直接从 Nagios 主机探测的场景,Nagios 提供了两种方案:一种是 NRPE(Nagios Remote Plugin Executor),它在被监控机器上运行一个守护进程,Nagios 主机通过 TLS 连接过去远程触发插件执行;另一种是 NSCA(Nagios Service Check Acceptor),被监控主机定期把结果”推”到 Nagios 主机,常用于防火墙隔离场景。

一份极简的 Nagios 主配置大致长这样:

# nagios.cfg(节选)
log_file=/var/log/nagios/nagios.log
cfg_dir=/etc/nagios/objects
status_file=/var/log/nagios/status.dat
command_check_interval=-1
service_check_timeout=60
host_check_timeout=30
enable_notifications=1
execute_service_checks=1
accept_passive_service_checks=1
use_large_installation_tweaks=1

对应的服务定义:

# services.cfg
define service {
    use                     generic-service
    host_name               web01.example.com
    service_description     HTTP
    check_command           check_http!-H www.example.com -u / -s "200 OK" -w 3 -c 5
    max_check_attempts      3
    check_interval          1
    retry_interval          1
    check_period            24x7
    notification_interval   30
    notification_period     24x7
    contact_groups          web-admins
}

define service {
    use                     generic-service
    host_name               web01.example.com
    service_description     Disk /var
    check_command           check_nrpe!check_disk_var
    max_check_attempts      3
    check_interval          5
}

以及 NRPE 在远端的配置:

# /etc/nagios/nrpe.cfg
allowed_hosts=10.0.0.1
dont_blame_nrpe=0
command[check_disk_var]=/usr/lib/nagios/plugins/check_disk -w 20% -c 10% -p /var
command[check_load]=/usr/lib/nagios/plugins/check_load -w 5,4,3 -c 10,8,6
command[check_procs_nginx]=/usr/lib/nagios/plugins/check_procs -C nginx -c 1:

这份配置本质上是在做一件事:把每一个”我关心的问题”翻译成一次周期性的命令执行和一次阈值比较

1.3 两类告警:存活检测与阈值告警

传统监控的告警基本可以归为两大类:

存活检测(Liveness Check):只关心”在不在”。端口能不能连上?进程是不是还存在?HTTP 能不能返回 2xx?这类检查的输出是布尔型的,只有 UP / DOWN 两种状态。它处理的是最基础的可用性问题——当一台机器宕机、一个进程崩溃、一个网络分区形成时,存活检测能在分钟级别发现并通知。

阈值告警(Threshold Alert):关心”量”。CPU 超过 80% 告警、磁盘剩余少于 10% 告警、每分钟请求量少于 100 告警。阈值告警的本质是把一个连续变量(指标值)离散化成几个状态(OK / WARNING / CRITICAL)。

两类告警合起来能覆盖绝大多数”系统层”问题。在单体应用时代、物理机或小规模虚拟机时代,这套机制工作得相当好。

1.4 已知未知 vs 未知未知

但是这种范式有一个根本性的限制。Donald Rumsfeld 在 2002 年留下过一段现在被反复引用的话:

“There are known knowns; there are things we know we know. We also know there are known unknowns; that is to say, we know there are some things we do not know. But there are also unknown unknowns—the ones we don’t know we don’t know.”

翻译成中文大致是:

传统监控能覆盖的范围是”已知的未知”——我知道 CPU 会影响性能(这是”已知”),所以我监控 CPU 使用率(用一个指标填充”未知”的取值)。当我想监控 CPU 时,我的认知里必须已经存在”CPU 使用率会影响系统行为”这个假设。

问题在于:在真实的生产故障里,“未知的未知”占了绝对大多数。比如:

这些故障的共同特征是:事前没有人会为它们配置监控,因为没有人会想到要配置。监控系统天然只能回答”你已经想到的问题”——这不是工具的 bug,是架构层面的约束。

1.5 监控的认知上限

换句话说,监控的认知上限等于设计者在系统上线前能想到的问题集合。这个集合是有限的、静态的,而生产系统的故障空间是无限的、动态的。当系统复杂到一定程度(微服务、分布式、多租户、异构),你事前能枚举的问题和实际会发生的问题之间的差距会呈指数级拉大。

这就是为什么在 2010 年代后期,随着微服务架构普及,传统监控开始变得越来越力不从心。工程师发现自己配了成百上千条告警规则,但一出事还是只能靠猜。

二、可观测性的定义

2.1 来自控制论的原始定义

“可观测性”这个词并不是云原生时代发明的。它来自 Rudolf Kálmán(卡尔曼滤波器的那个 Kálmán)1960 年在控制论中给出的严格定义:

一个系统是可观测的(observable),当且仅当可以通过其输出(output)的有限次观测推断出其内部状态(internal state)。

在控制论语境下,这是一个线性代数问题——看观测矩阵的秩。拿到软件工程领域后,Charity Majors 给出了工程化的翻译:

可观测性是指你能够回答关于系统内部状态的任意问题,而无需事先知道你要问什么问题,也无需发布新代码来收集额外的遥测数据。

这个定义里有两个关键限定:

  1. 任意问题:不是一组预设问题,而是任意问题
  2. 无需发布新代码:意味着所需信息必须已经被事先收集

2.2 从”更多监控”到”任意问题可回答”

很多团队把”可观测性”理解成”更多的监控”——上更多的仪表盘、配更多的告警规则、接入更多的指标。这种理解是错的。

可观测性的核心不是”量”,是”维度”。如果你采集了一百个指标,但每个指标只有”服务名”一个标签,那么无论这一百个指标的数量有多大,你能回答的问题仍然是静态的一组——你永远无法用这些数据回答”某个特定用户的请求为什么变慢了”。

可观测性需要的是:在系统的每一个关键事件上,记录足够多的上下文维度。当你记录的维度足够多时,事后可以用任意维度的组合去切分和聚合数据。这才是”任意问题可回答”的真正含义。

2.3 高基数和高维度

这就引出了两个工程上的关键概念:

高基数(High Cardinality):一个字段的取值空间很大。user_id 是典型的高基数字段——几千万用户每人一个值。request_id 基数更高,每个请求一个唯一值。

高维度(High Dimensionality):事件上附带的字段数量多。一个 HTTP 请求的事件,可能带有 user_id、region、device、app_version、route、status_code、response_bytes、upstream、ab_bucket、…… 共几十个字段。

传统监控系统(Nagios、Zabbix、Graphite)都是反高基数的,因为它们的存储模型本质上是”每个标签组合一条时间序列”,基数高了以后序列数量爆炸,索引和查询都会崩掉。Prometheus 部分缓解了这个问题,但也有著名的”label cardinality 爆炸”踩坑史。

真正高基数友好的是事件型存储(Event-oriented storage),比如 Honeycomb 的 columnar event store,或者基于日志/追踪的宽事件(wide event)方案。

2.4 为什么 p99 延迟比 avg 延迟更能发现问题

再举一个例子说明维度的重要性:

假设一个服务总共 10000 QPS,平均延迟 50 ms。看起来很健康。但如果你把数据按 user_id 切开,发现其中 10 QPS(万分之一)的平均延迟是 5000 ms,这 10 QPS 全部来自某个大客户,而这个大客户刚刚签了八位数的合同——你的”平均延迟 50 ms”实际上已经是一个严重事故。

如果你只记录”平均延迟”这一个标量,永远发现不了这种问题。如果你记录了分布(p50/p95/p99/p999),能发现”有人慢”;如果你记录了分布 + user_id 维度,才能发现”是谁慢”。

所以可观测性的工程落地,本质上是在回答一个问题:如何在可承受的存储和查询成本内,保留足够多的事件维度。这是整个领域过去十年的主线,也是 OpenTelemetry 设计的主要驱动力。

三、二十年演进时间线

下面按时间段梳理整个演进过程。每一段都有它自己的时代背景,也都有解决和未解决的问题。

3.1 1999–2006:Nagios 时代

1999 年,Ethan Galstad 在自己的车库里写出了 NetSaint,后来因为商标原因改名为 Nagios(“Nagios Ain’t Gonna Insist On Sainthood”,一个递归缩写)。这是服务器监控工具的事实标准的起点。

当时的时代背景是:

Nagios 的设计完美契合这个时代:

2001 年,Alexei Vladishev 在拉脱维亚发布了 Zabbix。Zabbix 和 Nagios 解决的问题类似,但路线不同——Zabbix 更强调”一站式”,自带数据库(MySQL/PostgreSQL)、自带 Web 界面、自带图表,而 Nagios 的生态是靠一堆插件拼起来的(比如 NagVis、PNP4Nagios、Nagiosgraph、Thruk)。

Zabbix 的另一个核心设计是”Zabbix Agent”——每台被监控主机装一个 agent,主动采集本地指标,通过 TCP 上报给 Zabbix Server。Zabbix 从一开始就支持主动模式(active)和被动模式(passive):

一段典型的 Zabbix Agent 配置:

# zabbix_agentd.conf
Server=10.0.0.1
ServerActive=10.0.0.1
Hostname=web01.example.com
StartAgents=3
HostMetadataItem=system.uname
ListenPort=10050
LogFile=/var/log/zabbix/zabbix_agentd.log
LogFileSize=10
EnableRemoteCommands=0
UnsafeUserParameters=0
UserParameter=mysql.ping,mysqladmin ping | grep -c alive
UserParameter=nginx.conns[*],curl -s http://localhost/nginx_status | awk 'NR==3{print $$3}'

Zabbix 的强大之处在于它的”模板(Template)“体系。一个模板把一组 item、trigger、graph、discovery rule 封装起来,应用到一组主机上。模板可以从 XML 导入导出,社区有大量现成模板(Linux、Nginx、MySQL、PostgreSQL、Redis、MongoDB、Java JMX、SNMP 设备等等)。

一段极简的 Zabbix 模板 XML 片段(示意):

<zabbix_export>
  <version>5.0</version>
  <templates>
    <template>
      <template>Template App Nginx</template>
      <name>Template App Nginx</name>
      <items>
        <item>
          <name>Nginx active connections</name>
          <key>nginx.conns[active]</key>
          <type>0</type>
          <value_type>3</value_type>
          <delay>30s</delay>
          <history>7d</history>
          <trends>365d</trends>
        </item>
      </items>
      <triggers>
        <trigger>
          <expression>{Template App Nginx:nginx.conns[active].last()}&gt;5000</expression>
          <name>Too many active connections on {HOST.NAME}</name>
          <priority>3</priority>
        </trigger>
      </triggers>
    </template>
  </templates>
</zabbix_export>

在中国,Zabbix 从 2010 年前后开始被大量互联网公司采用,成为运维部门的事实标准。早期新浪、搜狐、京东、去哪儿等公司的 OP 团队基本都搭过 Zabbix。当时的典型架构是 Zabbix Server + MySQL + Zabbix Proxy,规模大的公司一台 Server 带几万到十几万的监控项(Item),超过这个数字通常要做分区或者分成多套。

这个时代的局限现在看得很清楚:

3.2 2006–2012:时序指标时代

2006 年,Chris Davis 在 Orbitz 写了 Graphite,后来捐献给开源社区。Graphite 是第一个被广泛使用的”纯时序指标”系统。它的核心组件是:

Graphite 的指标命名是层级(hierarchical)的,用点号分隔:

servers.us-east-1.web01.cpu.idle
servers.us-east-1.web01.cpu.user
servers.us-east-1.web01.load.one_minute
services.checkout.qps
services.checkout.latency.p99

Carbon 接收协议之简单以至于你可以用一行 Python 或者甚至 netcat 推数据:

echo "servers.web01.cpu.idle 42.3 $(date +%s)" | nc -q0 graphite.example.com 2003

2011 年,Etsy 的 Ian Malpass 在一篇博客里介绍了他们内部开发的 StatsD。StatsD 是运行在应用旁边的 daemon,通过 UDP 接收应用发送的原始事件,聚合后推给 Graphite。

StatsD 的 UDP 协议格式是人类可读的:

# Counter:每次 +1
page.views:1|c

# Gauge:当前值
active_users:127|g

# Timer:一次耗时(毫秒)
login.latency:237|ms

# Set:去重计数
unique_ips:10.0.0.1|s

# Sampling:采样上报
api.calls:1|c|@0.1

StatsD 在应用侧聚合、批量 flush(通常 10 秒一次)给 Graphite。客户端侧因为是 UDP,“发完就忘”,对应用延迟影响几乎为零,即使 StatsD daemon 挂了应用也不会受影响。

这个时代还有几个值得一提的工具:

这个时代已经跟 Nagios 时代的思路完全不同了。Nagios 的核心是”状态(OK/WARNING/CRITICAL)“,时序指标时代的核心是”数值的历史”。告警不再是”一次检查的退出码”,而是”一段时间序列超过阈值”。

但时序指标时代的主要问题出在命名系统上。层级命名的根本缺陷是:维度写死在名字里。servers.us-east-1.web01.cpu.idle 里面包含了”us-east-1”、“web01”、“cpu.idle”三个维度信息,但它们只能从左到右展开,聚合困难。比如想问”所有机房的 cpu.idle 平均值”,在 Graphite 里需要用通配符函数:

averageSeries(servers.*.*.cpu.idle)

只要你想做稍微复杂一点的切片,比如”按机房聚合再取每个机房 95 分位”,Graphite 就很难表达。

3.3 2012–2016:Prometheus 革命

Prometheus 的诞生直接针对上述问题。2012 年,Matt T. Proud 和 Julius Volz 在 SoundCloud(德国柏林的一家音乐流媒体公司)内部受 Google Borgmon 启发开始写 Prometheus。SoundCloud 当时面临的问题是:他们在从单体向微服务迁移,现有的 Graphite + StatsD 架构无法应付服务实例数量的爆炸。

Prometheus 的几个核心设计选择:

  1. 标签系统(Labels)替代层级命名。每个时间序列由 metric name + labels 唯一标识:
http_requests_total{method="GET", handler="/api/v1/users", status="200", instance="10.0.0.1:8080"}
  1. 拉取模型(Pull-based)。Prometheus Server 主动从目标抓取(scrape),而不是让目标推送过来。

  2. 服务发现(Service Discovery)。Prometheus 原生支持 Consul、Kubernetes、EC2、DNS SRV 等服务发现机制,目标列表动态变化。

  3. 自研查询语言 PromQL。表达聚合、过滤、函数运算非常自然。

  4. 本地 TSDB。不依赖外部数据库,每个 Prometheus 实例自己存 15 天的数据。

一份最小的 Prometheus 配置:

# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s
  external_labels:
    cluster: prod-east-1

rule_files:
  - /etc/prometheus/rules/*.yml

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager.example.com:9093']

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'node'
    kubernetes_sd_configs:
      - role: node
    relabel_configs:
      - source_labels: [__address__]
        regex: '(.+):(.+)'
        target_label: __address__
        replacement: '${1}:9100'
      - source_labels: [__meta_kubernetes_node_name]
        target_label: instance

  - job_name: 'kubernetes-pods'
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)
      - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
        action: replace
        regex: ([^:]+)(?::\d+)?;(\d+)
        replacement: $1:$2
        target_label: __address__
      - action: labelmap
        regex: __meta_kubernetes_pod_label_(.+)
      - source_labels: [__meta_kubernetes_namespace]
        action: replace
        target_label: namespace
      - source_labels: [__meta_kubernetes_pod_name]
        action: replace
        target_label: pod

Prometheus 2015 年 1 月正式发布 0.x 版本,2016 年加入 CNCF 成为第二个项目(第一个是 Kubernetes),2018 年 8 月毕业。

PromQL 的表达力是 Prometheus 最大的卖点之一。几个典型查询:

# 过去 5 分钟 5xx 错误率
sum by (service) (
  rate(http_requests_total{status=~"5.."}[5m])
)
/
sum by (service) (
  rate(http_requests_total[5m])
)

# 按实例和路径取 p99 延迟
histogram_quantile(0.99,
  sum by (le, instance, handler) (
    rate(http_request_duration_seconds_bucket[5m])
  )
)

# 每个 Pod 的内存使用率
sum by (pod, namespace) (container_memory_working_set_bytes{container!="POD"})
/
sum by (pod, namespace) (kube_pod_container_resource_limits{resource="memory"})

# 告警:API 错误率超过 1%
(sum by (service) (rate(http_requests_total{status=~"5.."}[5m]))
 /
 sum by (service) (rate(http_requests_total[5m]))) > 0.01

对应的告警规则:

# rules/api.yml
groups:
  - name: api.rules
    interval: 30s
    rules:
      - alert: ApiHighErrorRate
        expr: |
          sum by (service) (rate(http_requests_total{status=~"5.."}[5m]))
          /
          sum by (service) (rate(http_requests_total[5m]))
          > 0.01
        for: 10m
        labels:
          severity: page
          team: platform
        annotations:
          summary: "Service {{ $labels.service }} error rate {{ $value | humanizePercentage }}"
          runbook: https://wiki.example.com/runbooks/api-high-error-rate

      - alert: ApiHighLatency
        expr: |
          histogram_quantile(0.99,
            sum by (le, service) (rate(http_request_duration_seconds_bucket[5m]))
          ) > 1
        for: 10m
        labels:
          severity: ticket

Alertmanager 负责告警的去重、分组、路由、静默:

# alertmanager.yml
route:
  group_by: [alertname, cluster]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  receiver: default
  routes:
    - match:
        severity: page
      receiver: pagerduty
    - match:
        team: platform
      receiver: platform-team

receivers:
  - name: default
    email_configs:
      - to: oncall@example.com
  - name: pagerduty
    pagerduty_configs:
      - routing_key: <key>
  - name: platform-team
    webhook_configs:
      - url: https://hooks.slack.com/services/xxx/yyy/zzz

inhibit_rules:
  - source_match:
      severity: critical
    target_match:
      severity: warning
    equal: [alertname, cluster]

Prometheus 生态在 2016 年之后爆炸性增长。Exporter 成为行业标准——几乎任何你能想到的软件,都有对应的 exporter:node_exporter、mysqld_exporter、redis_exporter、blackbox_exporter、jmx_exporter、kafka_exporter、snmp_exporter、nginx-prometheus-exporter 等等。

2016 年同年,Grafana 2.x 开始支持 Prometheus 作为数据源,“Prometheus + Grafana”成为新的默认组合,取代了”Graphite + Graphite-Web”。

3.4 2016–2019:分布式追踪标准化

指标解决了”聚合状态”的问题,但对跨服务的单次请求链路追溯无能为力。这个空缺由分布式追踪(Distributed Tracing)填补。

历史上分布式追踪的源头是 Google 2010 年发表的 Dapper 论文。Twitter 受此启发开源了 Zipkin(2012);Uber 基于 Zipkin 的思想做了更大规模的 Jaeger(2017)。

但是每家公司的 SDK 都是自己的,埋点代码没法在不同系统间迁移,这促成了 OpenTracing 项目的诞生。OpenTracing 由 Ben Sigelman(Dapper 作者之一)于 2016 年发起,定义了一套语言无关的 API 规范:

典型代码(Python):

import opentracing
from opentracing.ext import tags
from opentracing.propagation import Format

tracer = opentracing.global_tracer()

def handle_request(request):
    span_ctx = tracer.extract(Format.HTTP_HEADERS, request.headers)
    with tracer.start_active_span('handle_request', child_of=span_ctx) as scope:
        span = scope.span
        span.set_tag(tags.HTTP_METHOD, request.method)
        span.set_tag(tags.HTTP_URL, request.url)
        span.set_tag('user.id', request.user.id)
        result = do_work()
        span.set_tag(tags.HTTP_STATUS_CODE, result.status)
        return result

OpenTracing 只是规范,不是实现。真正的 Tracer 由 Jaeger、Zipkin 等项目各自提供兼容的实现。

2018 年,Google 开源了 OpenCensus,定位类似——但 OpenCensus 野心更大,它同时覆盖了 Metrics 和 Traces,而且是一个”库”而不仅是”规范”,开箱即用。OpenCensus 来自 Google 内部的 Census 库。

于是行业里出现了”双雄分裂”的局面:

维度 OpenTracing OpenCensus
发起方 LightStep 等 Google
覆盖范围 仅 Traces Traces + Metrics
定位 API 规范 API + SDK
加入 CNCF 2016 2019(孵化)

两个项目的用户基数都不小,但谁也吃不掉对方,中间件作者要做选择题:到底实现哪个接口?用户也头疼:我用了 A,换库的时候发现中间件只支持 B。

3.5 2019 至今:OpenTelemetry 统一

2019 年 5 月,在 KubeCon Barcelona 上,两个项目的 Steering Committee 共同宣布合并为 OpenTelemetry(OTel)。OpenTelemetry 成为 CNCF 项目(现为活跃程度仅次于 Kubernetes 的第二大项目)。

OpenTelemetry 的组成:

  1. API 规范:定义各语言的 API 接口
  2. SDK 实现:提供默认实现(Go、Java、Python、JS、C++、.NET、Rust、PHP、Ruby、Swift、Erlang 等)
  3. OTLP(OpenTelemetry Protocol):基于 gRPC / HTTP + Protobuf 的统一传输协议
  4. OpenTelemetry Collector:vendor-neutral 的数据管道,接收、处理、导出遥测数据
  5. Semantic Conventions:统一语义约定(比如 HTTP 属性、DB 属性、RPC 属性的标准命名)
  6. Instrumentation libraries:开箱即用的自动埋点库(比如 Java Agent、Python opentelemetry-instrument)

一份 Collector 配置长这样:

# otel-collector.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
  prometheus:
    config:
      scrape_configs:
        - job_name: 'kube-state-metrics'
          kubernetes_sd_configs:
            - role: pod
          relabel_configs:
            - source_labels: [__meta_kubernetes_pod_label_app]
              regex: kube-state-metrics
              action: keep
  jaeger:
    protocols:
      thrift_http:
        endpoint: 0.0.0.0:14268
  zipkin:
    endpoint: 0.0.0.0:9411

processors:
  batch:
    timeout: 10s
    send_batch_size: 8192
  memory_limiter:
    check_interval: 1s
    limit_mib: 4000
    spike_limit_mib: 800
  resource:
    attributes:
      - key: deployment.environment
        value: production
        action: upsert
  attributes:
    actions:
      - key: http.user_agent
        action: delete
  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: 500
      - name: baseline
        type: probabilistic
        probabilistic:
          sampling_percentage: 5

exporters:
  otlphttp/tempo:
    endpoint: http://tempo.observability:4318
  prometheusremotewrite:
    endpoint: http://mimir.observability/api/v1/push
    external_labels:
      cluster: prod-east-1
  loki:
    endpoint: http://loki.observability:3100/loki/api/v1/push
  debug:
    verbosity: basic

service:
  pipelines:
    traces:
      receivers: [otlp, jaeger, zipkin]
      processors: [memory_limiter, resource, attributes, tail_sampling, batch]
      exporters: [otlphttp/tempo]
    metrics:
      receivers: [otlp, prometheus]
      processors: [memory_limiter, resource, batch]
      exporters: [prometheusremotewrite]
    logs:
      receivers: [otlp]
      processors: [memory_limiter, resource, batch]
      exporters: [loki]

OTel 的关键里程碑:

可以说,到 2023 年底,“三支柱 + 一协议 + 一 Collector”的图景基本稳定。剩下的主要工作是完善各语言 SDK、扩大自动埋点覆盖、在各家云厂商和 APM 产品里落地 OTLP 接入。

四、Zabbix / Nagios / Prometheus / Grafana Agent 数据模型差异

下面这张表格是从实际使用角度对比四类代表性工具:

维度 Nagios Zabbix Prometheus Grafana Agent (Alloy)
主数据模型 状态(OK/WARN/CRIT/UNK)+ 可选 performance data Item(数值、文本、日志) 标签化时间序列 标签化时间序列 + OTLP
标识符 host + service host + item key metric + labels metric + labels
时间精度 毫秒 毫秒
Push / Pull Pull(主机 exec 插件) 双向(主动/被动) Pull 为主,Pushgateway 辅 双向(scrape + OTLP push)
服务发现 手写配置 低级自动发现(LLD) Kubernetes/Consul/EC2/DNS 复用 Prometheus SD
告警 外置(notifications.cfg) 内置(trigger) 内置(rules)+ Alertmanager 通常交给 Mimir / Prometheus
配置方式 静态 .cfg 文件 Web UI + API + 模板 XML YAML HCL (Flow / Alloy Config)
存储 状态文件 + 外部 RRD/PNP4Nagios MySQL/PostgreSQL/TimescaleDB 本地 TSDB;远程 Mimir/VictoriaMetrics 无状态,转发
典型部署规模 百~千主机 千~万主机 万~百万 series 单实例 Sidecar / DaemonSet

4.1 Nagios 数据模型

Nagios 的基本单位是 check 结果——每次 check 返回一个状态和一行文本描述,可选附带 performance data。Performance data 是 Nagios 1.x 开始就有的扩展,格式是:

'label'=value[UOM];[warn];[crit];[min];[max]

比如:

OK - load average: 0.42, 0.35, 0.30|load1=0.420;5.0;10.0;0; load5=0.350;4.0;8.0;0; load15=0.300;3.0;6.0;0;

Nagios 本身不存时序数据,performance data 通过 process_performance_data 配置项导出给 RRDtool 或其他工具:

process_performance_data=1
service_perfdata_file=/var/lib/nagios/service-perfdata
service_perfdata_file_template=DATATYPE::SERVICEPERFDATA\tTIMET::$TIMET$\tHOSTNAME::$HOSTNAME$\tSERVICEDESC::$SERVICEDESC$\tSERVICEPERFDATA::$SERVICEPERFDATA$\tSERVICECHECKCOMMAND::$SERVICECHECKCOMMAND$\tHOSTSTATE::$HOSTSTATE$\tHOSTSTATETYPE::$HOSTSTATETYPE$\tSERVICESTATE::$SERVICESTATE$\tSERVICESTATETYPE::$SERVICESTATETYPE$
service_perfdata_file_mode=a
service_perfdata_file_processing_interval=15
service_perfdata_file_processing_command=process-service-perfdata

这种数据模型在指标维度上非常弱——你想按”机房”聚合 load1?Nagios 原生不支持。

4.2 Zabbix 数据模型

Zabbix 的 Item 有更丰富的类型:

Item 的 Key 是一个带参数的函数签名:system.cpu.util[,idle,avg1] 表示 CPU 在过去 1 分钟空闲率的均值。

Zabbix 的聚合主要靠”应用(Application)“和”主机组(Host Group)“两个维度,标签化是 4.x 版本之后才加入的,并且不像 Prometheus 那样是数据模型的第一等公民。

4.3 Prometheus 数据模型

Prometheus 的一个时间序列由 metric name + labels 唯一标识。所有聚合、过滤、关联都围绕标签进行。Prometheus 定义了四种 metric 类型:

样例暴露格式(/metrics endpoint):

# HELP http_requests_total Total HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="GET",handler="/api/users",status="200"} 12453
http_requests_total{method="POST",handler="/api/users",status="201"} 389

# HELP http_request_duration_seconds HTTP latency
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{handler="/api/users",le="0.005"} 8800
http_request_duration_seconds_bucket{handler="/api/users",le="0.01"} 10200
http_request_duration_seconds_bucket{handler="/api/users",le="0.05"} 12100
http_request_duration_seconds_bucket{handler="/api/users",le="0.1"} 12400
http_request_duration_seconds_bucket{handler="/api/users",le="+Inf"} 12453
http_request_duration_seconds_count{handler="/api/users"} 12453
http_request_duration_seconds_sum{handler="/api/users"} 381.22

# HELP node_memory_MemAvailable_bytes Memory available
# TYPE node_memory_MemAvailable_bytes gauge
node_memory_MemAvailable_bytes 8.382074880e+09

4.4 Grafana Agent / Alloy 数据模型

Grafana Agent(后改名 Alloy)本身不是存储,是采集和转发层。它的数据模型就是它承载的协议的数据模型:

Alloy 配置用的是 HCL 风格的流式组件:

# config.alloy
prometheus.remote_write "mimir" {
  endpoint {
    url = "http://mimir.observability/api/v1/push"
  }
}

prometheus.scrape "node" {
  targets = [
    {"__address__" = "localhost:9100"},
  ]
  forward_to = [prometheus.remote_write.mimir.receiver]
}

otelcol.receiver.otlp "default" {
  grpc {}
  http {}
  output {
    metrics = [otelcol.processor.batch.default.input]
    logs    = [otelcol.processor.batch.default.input]
    traces  = [otelcol.processor.batch.default.input]
  }
}

otelcol.processor.batch "default" {
  output {
    metrics = [otelcol.exporter.prometheus.mimir.input]
    logs    = [otelcol.exporter.loki.default.input]
    traces  = [otelcol.exporter.otlp.tempo.input]
  }
}

五、推送(Push)vs 拉取(Pull)模型

5.1 Pull 的优势——Prometheus 的选择

Prometheus 作者们对 pull 模型的偏爱有明确理由(官方 FAQ 有专门一节),核心几点:

  1. 天然的健康检查:scrape 失败本身就是一个信号。如果 pull 不到,你立刻知道目标掉线了。Push 模式下,应用挂了,监控侧可能只会看到”没数据”,不知道是挂了还是没流量。
  2. 统一控制采集频率:采集间隔由 Prometheus 决定,不同环境可以不同。Push 模式里如果客户端频率乱改,服务端只能接着。
  3. 服务发现与自动目标列表:Prometheus 通过 SD 自动发现目标,目标变动不用动配置。
  4. 客户端简单:应用只需要暴露一个 HTTP endpoint,不需要维护连接。
  5. 易于调试curl http://pod:8080/metrics 就能看到当前数据。

5.2 Push 的场景

Pull 不是万能的。以下场景 Push 反而更合适:

Prometheus 为短生命周期 job 提供了 Pushgateway。应用把指标 push 到 Pushgateway,Pushgateway 暴露 /metrics 供 Prometheus 抓。示例:

# Shell Job 结束时上报
cat <<EOF | curl --data-binary @- http://pushgateway.example.com:9091/metrics/job/backup/instance/db01
# TYPE backup_last_success_unixtime gauge
backup_last_success_unixtime $(date +%s)
# TYPE backup_duration_seconds gauge
backup_duration_seconds 3742
# TYPE backup_size_bytes gauge
backup_size_bytes 84521938432
EOF

但是 Pushgateway 有著名的坑,后面工程坑点一节会详细说。

5.3 StatsD 的 UDP Push:极简但无确认

StatsD 是另一种极端——纯 UDP Push,无 ACK。应用侧的 SDK 通常长这样:

import statsd
c = statsd.StatsClient('localhost', 8125, prefix='myapp')

c.incr('api.login.attempt')
with c.timer('api.login.latency'):
    do_login()
c.gauge('queue.length', current_queue_size())

UDP 的好处是发送 0 成本(不等响应),即使 StatsD daemon 挂了应用也不会卡。代价是数据可能丢失(内核 socket buffer 满、网络丢包),对于需要”每一个事件都不能丢”的场景不合适。

5.4 OTel 的立场:OTLP 是 Push,Collector 可以 Scrape

OpenTelemetry 的默认协议 OTLP 是 push-based——SDK 把遥测数据主动推送给 Collector(或直接推送给后端)。但 OTel Collector 的 Prometheus receiver 可以反过来 scrape 现有的 /metrics endpoint,把 pull 转成内部 push 管道。

推荐组合:

5.5 Grafana Agent Flow:两者兼得

Grafana Agent Flow / Alloy 最大的卖点就是”同时支持 push 和 pull”——它内部可以定义一个 prometheus.scrape 组件主动拉,也可以定义 otelcol.receiver.otlp 组件接收推送,最后统一导出到后端。这让它在”混合 legacy + 新系统”的迁移场景特别好用。

六、国内从 Zabbix 到 Prometheus 再到 OTel 的典型迁移路径

这一节结合国内常见的演进路径给一个时间轴。大公司和中小公司的演进节奏并不完全同步,但大致可以分成三个阶段。

6.1 阶段一:Zabbix 时代(约 2010–2015)

这个阶段的主要特征:

典型架构:

[Zabbix Agent × N台主机]
         ↓ 10051
[Zabbix Proxy × 几个机房]
         ↓
[Zabbix Server + MySQL]
         ↓
[Grafana(可选,用 Zabbix datasource)]

痛点:

6.2 阶段二:Prometheus + Grafana 时代(约 2015–2020)

这个阶段的背景是 Kubernetes、Docker、Service Mesh 的普及。老 Zabbix 对短生命周期的容器监控非常吃力,而 Prometheus 天生为容器化设计。

典型架构:

[App Pod(暴露 /metrics)]
         ↑ scrape
[Prometheus + kube-state-metrics + node-exporter]
         ↓ remote_write
[Thanos / VictoriaMetrics / Cortex]
         ↓
[Grafana + Alertmanager]

这个阶段的关键变化:

国内公开资料里,这个阶段有几个值得一提的公司实践:

6.3 阶段三:OTel + 自建平台时代(2020 至今)

随着微服务数量进一步膨胀、多语言栈(Go/Java/Python/Node/Rust/C++)并存、追踪与日志需求涌现,团队逐渐意识到单靠 Prometheus 不够。新阶段的特征:

典型架构:

[App with OTel SDK]
      ↓ OTLP/gRPC
[OTel Collector (DaemonSet)]
      ↓ OTLP/gRPC
[OTel Collector Gateway (StatefulSet)]
      ↓ 分流
  ├→ Mimir (Metrics)
  ├→ Tempo (Traces)
  ├→ Loki (Logs)
  └→ Pyroscope (Profiles)
           ↓
         [Grafana]

6.4 迁移挑战

典型挑战:

  1. 历史告警规则迁移:Zabbix trigger 语法和 PromQL 完全不同,需要逐条重写
  2. 双栈并行维护:Zabbix 和 Prometheus 同时告警,需要严格分工(哪些指标归谁)
  3. 数据对齐:Zabbix 秒级 vs Prometheus 毫秒级,跨系统做 join 困难
  4. 埋点规范:从不统一的 metric 命名(有的用 qps.api.login,有的用 api_login_count)统一到 OTel Semantic Conventions
  5. 人的迁移:OP 团队的 Zabbix 技能栈如何过渡到开发为主的 Prometheus/OTel 技能栈

七、Tag / Label 对比

标签(Label / Tag)是现代监控的心脏。不同工具对它的处理差异极大。

7.1 Graphite 层级命名

Graphite 的命名把所有维度编码进字符串:

datacenter.us-east-1.cluster.web.host.web01.cpu.idle
datacenter.us-east-1.cluster.web.host.web01.cpu.user
datacenter.us-east-1.cluster.web.host.web02.cpu.idle
datacenter.us-west-2.cluster.web.host.web11.cpu.idle

要聚合”所有 us-east-1 的 cpu.idle 均值”:

averageSeries(datacenter.us-east-1.cluster.web.host.*.cpu.idle)

要按 datacenter 分组聚合,Graphite 需要 groupByNode

averageSeries(groupByNode(datacenter.*.cluster.web.host.*.cpu.idle, 1, "sumSeries"))

能用,但需要知道维度在命名里的位置(第 1 节?第 3 节?),不灵活。

7.2 Prometheus Label

Prometheus 把维度放进独立的 label 空间:

cpu_idle{datacenter="us-east-1", cluster="web", host="web01"}
cpu_idle{datacenter="us-east-1", cluster="web", host="web02"}
cpu_idle{datacenter="us-west-2", cluster="web", host="web11"}

所有聚合都是一等公民:

# 按 datacenter 聚合
avg by (datacenter) (cpu_idle)

# 过滤特定条件
avg by (datacenter) (cpu_idle{cluster="web"})

# 多维度组合
avg by (datacenter, cluster) (cpu_idle)

# 排名
topk(5, cpu_idle)

# 时间窗口内最大值的按 cluster 排序
bottomk(3, max by (cluster) (cpu_idle[1h]))

这种表达力差距是根本性的,也是 Prometheus 取代 Graphite 的核心原因。

7.3 InfluxDB 的 Tag vs Field

InfluxDB 把列分两类:

一次写入:

cpu,host=web01,dc=us-east-1 idle=42.3,user=12.1,system=3.4 1700000000000000000
   ^measurement    ^tags              ^fields                 ^timestamp ns

InfluxDB 的好处是能一次插入多个 field(CPU 的 idle/user/system),Prometheus 需要多个 metric 名字。但它的 tag 索引在高基数下同样会爆。

7.4 OTel Attributes 与 Prometheus Labels 的映射

OpenTelemetry 不叫 label,叫 Attributes。OTel Attributes 是键值对,值可以是 string、bool、int、double、array。

当 OTel 指标导出到 Prometheus 时,attribute 映射为 label,但有规则:

OTel Collector 的 prometheus exporter 配置里可以控制这些:

exporters:
  prometheus:
    endpoint: 0.0.0.0:8889
    namespace: myapp
    const_labels:
      cluster: prod
    send_timestamps: true
    metric_expiration: 5m
    resource_to_telemetry_conversion:
      enabled: true

八、工程坑点

下面每一条都是真实环境里会掉进去的坑。

8.1 双栈并行期:告警重复

从 Zabbix 向 Prometheus 迁移时,团队通常会有 3–12 个月的双栈并行期。两套系统对同一组主机、同一组服务都有监控,同一个事件(比如磁盘满)会同时触发两套告警。On-call 在凌晨收到两条内容几乎一样但来自不同系统的短信,非常容易误判”是不是出了两件不相关的事”。

解决办法:

8.2 标签维度爆炸

Prometheus 的每个 label 组合产生一条独立时间序列。如果一个指标有 5 个 label,每个 label 平均 10 个取值,总序列数是 10^5 = 10 万。如果其中一个 label 突然变成 user_id(千万级),总序列数瞬间到 10^12 级别,Prometheus 会 OOM、rule 评估变慢到几百秒。

典型的”杀手 label”:

排查:

# Prometheus TSDB 里当前活跃 series 最多的 metric
topk(10, count by (__name__)({__name__=~".+"}))

# 某个 metric 按 label 看基数
count(count by (user_id) (http_requests_total))

防御:

真正需要高基数维度的场景,应该用 Trace 或 Event,不要用 Metric。

8.3 Pull 模型在 NAT 后面失效

Prometheus pull 的前提是能连上目标的 IP:Port。在以下场景失效:

应对:

8.4 Pushgateway 残留数据

Pushgateway 设计用于短生命周期 job,把最近一次的指标永久保存(直到下一次同 job 同 instance 的 push 覆盖它)。

问题:如果 job 不再运行了(被下线了、挂了),Pushgateway 会永远保留这次 push 的最后值。于是:

解决:

8.5 时间戳精度不一致

Zabbix 存储精度到秒,Prometheus 精度到毫秒。如果你做跨系统关联分析——比如想把 Zabbix 的主机指标和 Prometheus 的应用指标放在同一张 Grafana 面板上按时间对齐——会发现 Zabbix 数据总是”整秒”,Prometheus 数据有毫秒抖动,join 时要么丢数据要么要做时间取整。

同样的问题在 Graphite(秒)和 Prometheus 之间也存在。

8.6 告警重复(与 8.1 不同的维度)

即使已经规划好责任边界,告警重复仍然会以其他方式出现:

Alertmanager 去重规则的配置非常重要:

route:
  group_by: [alertname, cluster, service]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h

8.7 服务发现失效

Kubernetes SD 是 Prometheus 最常用的服务发现机制。典型的失效场景:

kubernetes_sd_configs:
  - role: pod
    namespaces:
      names:
        - production    # <-- 新上的 namespace 叫 prod-east-1,不在列表里

结果:新 namespace 的 Pod 完全不被发现,“静默漏监控”——Prometheus 上没有任何错误,Pod 暴露的 /metrics 仍然正常,但就是没人 scrape。

防御:

8.8 OTel Collector 上线没配 memory_limiter

Collector 默认没有内存限制。流量突增时 Collector OOM 崩溃,所有遥测数据同时中断(Metrics、Logs、Traces 都跟着一起挂)。

防御:永远在 pipeline 最前面配 memory_limiter processor:

processors:
  memory_limiter:
    check_interval: 1s
    limit_mib: 4000
    spike_limit_mib: 800

8.9 采样率不一致

如果 OTel Trace 在 SDK 侧做了 1% 的头采样(head-based sampling),Collector 里又加了 tail sampling policy,那么最终真正到达后端的 trace 比例可能远小于预期(1% × tail policy 命中率)。

规则是:头采样和尾采样二选一。头采样适合流量大、对完整性要求低的场景;尾采样适合要捕获所有错误 trace 的场景,但 Collector 需要缓存 decision_wait 时间内的所有 span,内存开销不小。

九、选型建议

基于上面所有分析,给出一组场景化建议。

9.1 纯新建系统

首选组合:OpenTelemetry + Prometheus(或 Mimir/VictoriaMetrics)+ Loki(或 ClickHouse)+ Tempo(或 Jaeger)+ Grafana。

理由:

9.2 存量 Zabbix 系统

渐进式迁移

  1. 保留 Zabbix 做主机和网络监控(它在这件事上仍然很强)
  2. 新业务直接上 Prometheus + Grafana
  3. 老业务上 Prometheus exporter,逐步替换 Zabbix 的 application 监控
  4. 应用层 Trace 新加 OTel
  5. 告警规则逐步迁移,迁移期间严格分责

9.3 存量 Nagios

Nagios 的核心价值是”外部可用性探测”(黑盒探测)。这部分可以用 Prometheus Blackbox Exporter 直接替代,功能重合度 > 90%:

# blackbox.yml
modules:
  http_2xx:
    prober: http
    timeout: 5s
    http:
      preferred_ip_protocol: ip4
      valid_status_codes: [200, 301, 302]
      fail_if_ssl: false
      fail_if_not_ssl: true
      fail_if_body_matches_regexp:
        - "(?i)error"
  icmp:
    prober: icmp
    icmp:
      preferred_ip_protocol: ip4
  tcp_connect:
    prober: tcp

配合 Prometheus scrape:

- job_name: 'blackbox'
  metrics_path: /probe
  params:
    module: [http_2xx]
  static_configs:
    - targets:
      - https://www.example.com
      - https://api.example.com/health
  relabel_configs:
    - source_labels: [__address__]
      target_label: __param_target
    - source_labels: [__param_target]
      target_label: instance
    - target_label: __address__
      replacement: blackbox-exporter:9115

9.4 中小团队(< 50 人)

推荐 Grafana Cloud 免费额度:10k metrics series、50 GB logs、50 GB traces,对小团队够用几年。人力成本比运维开销更重要。

如果数据出海受限,自建一套 Prometheus + Loki + Tempo + Grafana 在单台中等规格机器上也能跑起来。

9.5 大团队(> 500 人)

自建:VictoriaMetrics(或 Mimir)+ Loki + Tempo + Grafana + OTel Collector Gateway 集群。

理由:

9.6 决策表

场景 Metrics Logs Traces SDK
小团队,新建 Grafana Cloud Grafana Cloud Grafana Cloud OTel
小团队,数据不出海 Prometheus Loki Tempo OTel
大团队,新建 Mimir / VictoriaMetrics Loki / ClickHouse Tempo / Jaeger OTel
存量 Zabbix,渐进迁移 Zabbix + Prometheus 并行 Loki 新建 Tempo / Jaeger 新建 OTel
存量 CAT / Skywalking 保留 APM 保留 + 新 Loki 保留 APM,新 OTel 逐步替换 OTel
SaaS 监控客户内网 Pushgateway / OTLP push Loki Agent push OTLP push OTel
Serverless / FaaS OTLP push OTLP push OTLP push OTel

十、小结

从 1999 年到 2024 年,监控这件事大致走过了五个时代:Nagios/Zabbix 的主机存活时代、Graphite/StatsD 的时序指标时代、Prometheus 的标签化拉取时代、OpenTracing/Jaeger 的分布式追踪时代、OpenTelemetry 的三支柱统一时代

每一代都不是前一代的否定,而是对前一代局限的工程回应。Nagios 回答了”系统还活着吗”;Graphite 回答了”指标怎么变的”;Prometheus 回答了”任意维度切分的指标怎么变的”;Jaeger 回答了”一次请求在系统内走了哪些节点”;OpenTelemetry 试图回答”怎么把上面所有的东西用同一套数据模型和 SDK 串起来”。

再往下一代大概率会是可观测性即数据平台——所有 telemetry 数据进入统一列存(ClickHouse、Apache Iceberg 之类),Metrics / Logs / Traces / Events / Profiles 在同一份数据上可查,通过查询语言(而不是预聚合)动态生成所有仪表盘和告警。这种架构已经在几家大公司内部试点,Grafana Loki/Tempo 和 Honeycomb 都在往这个方向走。

但是”更好的工具”永远不等于”更好的可观测性”。工具只能提供能力,真正决定可观测性水平的是:团队有没有把”每一个可疑事件都记录足够多上下文维度”这件事做成肌肉记忆。回到本文开头那句话——监控只能回答你已经想到的问题,可观测性的门票是承认你想不完所有问题。

参考资料

  1. Julius Volz, “Prometheus: Monitoring at SoundCloud”, SoundCloud Blog, 2012
  2. OpenTelemetry Project, “History of OpenTelemetry”, opentelemetry.io
  3. Ethan Galstad, Nagios Core Documentation, nagios.org
  4. Brendan Burns et al., Site Reliability Engineering, Google, 2016
  5. Charity Majors et al., Observability Engineering, O’Reilly, 2022
  6. 美团技术团队,《统一监控平台的架构演进》,美团技术博客,2019
  7. 腾讯云技术团队,《Prometheus 在腾讯云的落地实践》,腾讯云开发者社区,2020
  8. PromQL 官方文档,https://prometheus.io/docs/prometheus/latest/querying/basics/
  9. OpenTelemetry Collector 文档,https://opentelemetry.io/docs/collector/
  10. Graphite 官方文档,https://graphite.readthedocs.io/

上一篇可观测性全景:Metrics、Logs、Traces、Profiles、Events 五大支柱

下一篇指标体系设计:USE、RED、Golden Signals 与业务 KPI

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-22 · architecture / observability

可观测性工程

从 Metrics、Logs、Traces 到 Profiling、eBPF、OpenTelemetry 与 SLO 治理,面向中国工程团队的可观测性系统化手册。

2026-04-22 · architecture / observability

【可观测性工程】指标体系设计:USE、RED、Golden Signals 与业务 KPI

USE 方法论适用于资源,RED 方法论适用于请求,Golden Signals 适用于服务——三套方法论各有其适用对象。本文从 Brendan Gregg、Tom Wilkie、Google SRE 的原始定义出发,构建覆盖资源→服务→业务的完整指标体系,并给出 Prometheus 命名规范、基数治理策略与可抄的指标清单。


By .