大数跨境

Grafana 运维看板怎么设计?从资源指标到业务可用性

Grafana 运维看板怎么设计?从资源指标到业务可用性 红帽Linux认证
2026-10-23
2
导读:Grafana 运维看板怎么设计?从资源指标到业务可用性一、问题背景1.1 一个"什么都有"的看板,为什么没人看某公司运维团队花了两个月,做了一个"全链路监控大屏"。

Grafana 运维看板怎么设计?从资源指标到业务可用性

一、问题背景

1.1 一个"什么都有"的看板,为什么没人看

某公司运维团队花了两个月,做了一个"全链路监控大屏"。32 个面板,从 CPU、内存、磁盘、网络,到 JVM 堆、GC 次数、数据库连接数、Redis 命中率,应有尽有。上线第一周大家还新鲜,第二周就没人打开了。

真出故障时,值班同学的第一反应不是打开这个大屏,而是 SSH 上机器敲 top。问原因,回答很直接:

  • 屏幕上 32 条曲线,看不出哪条是"现在有问题";
  • CPU 从 40% 涨到 60%,不知道这算不算异常;
  • 业务方说"下单失败",大屏上没有"下单成功率"这个指标;
  • 定位到某个服务后,还要再开三个看板才能看到那个服务的实例级指标。

这个看板的问题不是"指标不全",而是没有设计。它把所有能采集到的指标平铺在屏幕上,把"判断"这件事留给了人。而人在凌晨三点被叫醒时,最缺的就是判断力。

1.2 看板的三种失败模式

失败模式一:指标堆砌,没有主线。一个面板里塞 20 条曲线,图例被截断,颜色重复。读者要花 30 秒才能找到自己关心的那条,而这个时间足够故障扩大。

失败模式二:只有资源,没有业务。CPU、内存、磁盘都正常,但用户在下单页看到 502。资源指标和业务可用性之间没有桥接,运维看到"一切正常",业务看到"全挂了"。

失败模式三:有数据,没结论。面板显示 P99 延迟 850ms,但没有基线、没有阈值、没有颜色。读者不知道 850ms 是好是坏,只能凭经验猜。

这三种失败模式的根因是同一个:看板被当成"数据展示工具",而不是"决策工具"。好的看板应该让读者在 5 秒内回答三个问题:现在有没有问题?问题在哪一层?下一步看什么?

1.3 本文的环境假设

项目 版本 / 取值
Grafana 13.2.4(2026-10-13 发布),OSS 版
数据源 Prometheus 3.15.1(另有 3.13.5 LTS 环境),Loki 3.x
采集 node_exporter、kube-state-metrics、cAdvisor、DCGM Exporter、blackbox_exporter
部署 Kubernetes,Helm 部署,后端存储 PostgreSQL 16
规模 约 120 个节点、900 个 Pod、40 个业务服务、每日 800 万活跃序列
告警 Grafana 统一告警(Unified Alerting)+ 部分 Prometheus 规则
代码管理 看板 JSON 存 Git,通过 provisioning 下发;部分团队用 Git Sync

Grafana 13 有几个必须知道的破坏性变更,直接影响本文的部署和升级章节:

  • grafana-cli 和 grafana-server 命令被移除,改用 grafana cli 和 grafana server;
  • 旧版 Image Renderer 插件被彻底移除,截图和报表要用新版渲染服务(JWT 认证);
  • API 路径从 /api 迁移到 /apis,旧的数字 id 数据源 API 默认禁用;
  • HTTP 压缩 enable_gzip 默认开启;
  • 从开启了 Git Sync 的 12.x 升级到 13.0.0 存在数据迁移 bug,可能丢看板,必须先升到 13.0.1 并备份数据库。

这些变更在升级章节会展开,先记住一点:Grafana 13 不是一个小版本升级。

二、适用场景与边界

2.1 本文覆盖的看板类型

类型 读者 刷新频率 回答的问题
值班总览(L0) 一线值班、on-call 30s-1min 现在有没有故障?哪块红了?
服务视图(L1) 服务 owner、SRE 1min 这个服务的 RED 指标如何?依赖健康吗?
资源视图(L2) 系统运维 1-5min 节点、磁盘、网络、GPU 有没有瓶颈?
排查视图(L3) 故障处理人 按需 某个实例在某个时间点发生了什么?
业务视图 业务方、管理层 5-15min 订单量、成功率、GMV 是否正常?

这五类看板不是五个独立的 dashboard,而是一套有层级和跳转关系的体系。L0 发现异常,点进去到 L1,再点进 L2/L3,这条路径必须在设计时就打通。

2.2 不在本文范围

  • 日志分析看板(Loki 的 Explore 用法),只在"从指标跳到日志"时提一句;
  • 链路追踪(Tempo/Jaeger)的面板设计,只在 exemplar 部分涉及;
  • 大屏投放(TV 模式、轮播),那是展示需求,不是运维需求;
  • Grafana Cloud 的托管服务特性,本文全部基于自建 OSS 版。

2.3 一个前提:指标本身要靠谱

看板是"指标的上层建筑"。如果指标采集有问题,再好的看板也是错的。三个前置条件:

  1. 采集间隔统一。Prometheus 的 scrape_interval 如果是 15s,看板上所有 rate() 的窗口必须是 15s 的整数倍,否则会出现锯齿。
  2. 标签规范统一。instance、job、service、namespace 这几个标签的语义要全公司一致,否则没法跨服务聚合。
  3. 计数器不重置。进程重启导致的 counter 归零,rate() 能处理,但 increase() 的估算会有偏差,这点在 PromQL 章节细说。

三、核心知识:三层指标体系

3.1 为什么需要分层

监控指标有成百上千个,但人的短期记忆只能同时处理 5-7 个信息。看板设计的第一原则是分层:每一层只回答一个问题,层与层之间有明确的跳转关系。

  
  
  
  
text
第三层:业务指标(订单量、支付成功率、GMV)
   ↑ 业务方关心
第二层:服务指标(RED:请求率、错误率、延迟)
   ↑ 服务 owner 关心
第一层:资源指标(USE:CPU、内存、磁盘、网络)
   ↑ 系统运维关心

故障排查的方向是从上往下:业务指标异常 → 看服务指标定位是哪个服务 → 看资源指标定位是哪台机器/哪个依赖。

但告警的方向应该从下往上:资源打满会引发服务异常,服务异常会导致业务指标下跌。资源告警要早于业务告警,否则等业务报警时已经影响用户了。

3.2 USE 方法:资源层

USE 是 Brendan Gregg 提出的资源监控方法,三个维度:

  • Utilization(使用率):资源有多忙,比如 CPU 使用率 65%;
  • Saturation(饱和度):有多少任务在排队,比如 run queue 长度、磁盘 IO 等待队列;
  • Errors(错误):发生了多少错误,比如网卡丢包、磁盘坏块。

对每一类资源,三个维度都要有指标:

资源 Utilization Saturation Errors
CPU rate(node_cpu_seconds_total{mode!="idle"}) node_load1 / node_pressure_cpu_waiting_seconds_total node_cpu_seconds_total 无错误维度,看 dmesg
内存 1 - MemAvailable/MemTotal node_pressure_memory_waiting_seconds_total、swap 使用 OOM killer 次数
磁盘 rate(node_disk_io_time_seconds_total) node_pressure_io_waiting_seconds_total node_disk_io_errors 类指标
网络 rate(node_network_receive_bytes_total) node_netstat_Tcp_RetransSegs、node_netstat_TcpExt_ListenOverflows node_network_receive_errs_total
文件描述符 node_filefd_allocated / node_filefd_maximum 无 应用日志里的 EMFILE

只做 Utilization 是最常见的错误。CPU 使用率 70% 看起来还好,但如果 run queue 长度是 32,说明任务在严重排队,实际体验已经很差。

3.3 RED 方法:服务层

RED 适合请求驱动的服务(HTTP、gRPC、消息消费):

  • Rate:每秒请求数;
  • Errors:每秒错误数或错误率;
  • Duration:请求耗时分布(P50/P90/P99)。

对应的 PromQL:

  
  
  
  
promql
# Rate
sum by (service) (rate(http_requests_total[5m]))

# Errors
sum by (service) (rate(http_requests_total{status=~"5.."}[5m]))
  / sum by (service) (rate(http_requests_total[5m]))

# Duration P99
histogram_quantile(0.99,
  sum by (le, service) (rate(http_request_duration_seconds_bucket[5m]))
)

RED 的三个指标必须同时看。只有 Rate 没有 Errors,看不出成功率;只有 Errors 没有 Rate,1 个错误和 1000 个错误看起来一样;只有 Duration 没有前两个,不知道延迟升高是不是因为流量涨了。

3.4 四个黄金信号与 SLO

Google SRE 提出的四个黄金信号是 Latency、Traffic、Errors、Saturation,和 RED + USE 有重叠。工程上更实用的是 SLO 体系:

  • SLI(Service Level Indicator):可量化的指标,比如"HTTP 请求成功率";
  • SLO(Service Level Objective):SLI 的目标值,比如"30 天内成功率 ≥ 99.9%";
  • 错误预算(Error Budget):1 - SLO,比如 0.1%,即 30 天允许 43.2 分钟不可用;
  • 燃烧率(Burn Rate):当前错误率相对预算消耗的速度。燃烧率 1 表示按当前速度 30 天刚好用完预算;燃烧率 14.4 表示 1 小时就烧掉 2% 的预算,按这个速度约 50 小时(两天出头)预算见底。

SLO 的价值在于把"服务好不好"变成一个可以计算的数字,而不是靠感觉。看板上放一个"错误预算剩余 62%"的面板,比放 20 条延迟曲线有用得多。

3.5 指标类型与 PromQL 的坑

Prometheus 有四种指标类型,看板设计必须知道它们的区别:

类型 特点 常用函数 典型错误
Counter 单调递增,重启归零 rate、increase 直接画原值,看到一条斜线
Gauge 可增可减 avg_over_time、max_over_time 用 rate,得到无意义的抖动
Histogram 分桶统计 histogram_quantile 忘了 sum by (le)
Summary 客户端算分位数 直接取值 无法跨实例聚合

坑一:rate() 的窗口必须大于采集间隔的 4 倍。scrape_interval 是 15s,用 rate(x[1m]) 只有 4 个采样点,结果抖动很大。Grafana 提供 $__rate_interval 变量,它会自动算出合适的窗口(通常是 4 倍采集间隔),优先用它。

坑二:histogram_quantile 必须按 le 聚合。

  
  
  
  
promql
# 错误:没有 sum by (le),多个实例的桶直接相加,分位数算出来是错的
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))

# 正确
histogram_quantile(0.99,
  sum by (le) (rate(http_request_duration_seconds_bucket[5m]))
)

histogram_quantile 的输入必须是"每个 le 值对应一个累计计数",如果不聚合,同一个 le 会有多条序列,函数行为未定义。

坑三:分位数不能求平均。avg(histogram_quantile(0.99, ...)) 是错的。分位数不是线性可加的,要先把桶聚合再算分位数。

坑四:increase() 在窗口边界会外推。increase(x[1h]) 会把窗口两端不完整的采样按比例外推,结果可能比实际值大或小。对精度要求高的场景,用 sum_over_time 配合 resets() 自己算,或者接受这个误差。

坑五:没有数据时 histogram_quantile 返回 NaN。服务刚上线、或者某个时间段没有请求,面板会显示 NaN 或空白。用 or vector(0) 兜底,或者在面板的 “No value” 设置里填 0。

3.6 Grafana 的数据模型

一个 panel 的渲染流程:

  
  
  
  
text
Data source query → DataFrame → Transformations → Field options → Visualization
  • Query:PromQL 查询,一个 panel 可以有多个 query(A、B、C),用于对比或计算;
  • DataFrame:Grafana 的统一数据格式,每列有类型(time、number、string);
  • Transformations:数据加工,比如 Join by field、Calculate field、Filter data by value;
  • Field options:单位、小数位、阈值、颜色、显示名;
  • Visualization:图形类型。

理解这个流程的意义:很多"看板显示不对"的问题,出在 Transform 或 Field options,而不是查询。比如单位设成了 short 而不是 percent,0.95 会显示成 0.95 而不是 95%。

3.7 Grafana 13 的看板能力变化

Grafana 13 对看板做了几处影响设计的改动:

Dynamic dashboards(GA):新的布局引擎,所有看板打开时自动迁移。面板可以按行/标签做动态分组,配合"区块级变量"(public preview),可以解决"一个 $instance 变量影响所有面板"的老问题。

Filters 变量:原来的 Ad hoc filters 改名为 Filters(schema 里仍是 AdhocVariable)。对多租户看板来说,这是一个比手写变量更省事的下钻方式。

Panel styles(public preview):可以一键把颜色、阈值、显示配置应用到多个面板,适合统一风格。以前要改 20 个面板的阈值,现在改一次。

图例数量上限(GA):海量序列的面板渲染性能提升,但这不应该成为你不做聚合的理由。一个面板 500 条曲线,即使渲染不卡,人也看不懂。

Git Sync(GA):看板可以直接和 GitHub/GitLab 仓库同步,在 UI 里编辑后提交 PR。这让"看板即代码"的门槛降低了很多,但也带来新的风险(见第十七章)。

Grafana Advisor(GA):实例健康检查,会提示数据源健康检查失败、插件过时、SSO 配置错误。默认每周跑一次,建议开启并把结果接入告警。

四、整体思路:五层看板体系

4.1 分层与跳转

  
  
  
  
text
L0 值班总览
├── 顶部:4 个 SLO 数字(可用性、错误预算、P99、错误率)
├── 中部:40 个服务的红绿灯表格(可排序、可下钻)
└── 底部:当前 firing 的告警列表
        │ 点击服务名
        ▼
L1 服务视图(每个服务一个 dashboard,用变量区分)
├── 顶部:本服务的 SLO 与燃烧率
├── 中部:RED 三联图 + 延迟分位数 + 状态码分布
├── 依赖:下游服务的健康状态
└── 底部:实例列表(点击进入 L3)
        │ 点击实例
        ▼
L2 资源视图(节点维度)
├── USE 三维度:CPU、内存、磁盘、网络、FD
├── 容器资源:cgroup 限制与实际使用
└── GPU:利用率、显存、温度、XID
        │ 点击时间点
        ▼
L3 排查视图(Explore 或临时 dashboard)
├── 原始指标 + 日志 + trace
└── 按需组合,不长期保存

关键设计原则:每一层只回答一个问题,跳转链路必须是单向向下的。L0 不展示细节,L3 不承担总览职责。

4.2 命名与目录规范

看板多了以后,找不到比看不懂更痛苦。命名规范:

  
  
  
  
text
[层级] 业务域 / 对象 - 用途

示例:
[L0] 全局 / 值班总览
[L1] 交易 / 订单服务
[L1] 交易 / 支付服务
[L2] 基础设施 / 节点资源
[L2] 基础设施 / GPU 资源
[L3] 交易 / 订单服务 - 实例排查

文件夹结构对应组织架构:

  
  
  
  
text
General/
├── 00-值班总览/
├── 10-业务服务/
│   ├── 交易/
│   ├── 用户/
│   └── 营销/
├── 20-基础设施/
├── 30-中间件/
└── 90-归档/

90-归档 放废弃的看板,不要直接删。删了之后有人问"去年那个看板呢",你还能找回来。

4.3 变量的设计

变量是看板复用性的关键。一个好的看板应该能用一套 JSON 服务所有服务,靠变量切换。

  
  
  
  
text
变量名        类型              查询
$env          query             label_values(up, env)
$service      query             label_values(http_requests_total{env="$env"}, service)
$instance     query (multi)     label_values(up{env="$env", service="$service"}, instance)
$interval     interval          -

使用时的注意事项:

  
  
  
  
promql
# 单值变量,直接引用
sum(rate(http_requests_total{service="$service"}[$__rate_interval]))

# 多值变量,必须用 =~ 和 regex 格式化
sum(rate(http_requests_total{instance=~"${instance:regex}"}[$__rate_interval]))

${instance:regex} 会把 ["a","b"] 格式化成 a|b,这是多值变量的正确用法。写成 =~"$instance" 在 Grafana 里也能工作(它会自动加锚点),但在复杂表达式里容易出错,显式用 :regex 更稳。

变量的 refresh 设置:

  • On dashboard load:只在打开时刷新,适合 $env 这种很少变的;
  • On time range change:时间范围变了就刷新,适合 $instance(实例可能扩缩容);
  • On dashboard load 是默认值,如果实例列表经常变,要改成 On time range change。

4.4 时间范围与刷新

看板 默认时间范围 刷新间隔 理由
L0 总览 最近 1 小时 30s 值班需要近实时
L1 服务 最近 6 小时 1min 看趋势和对比
L2 资源 最近 24 小时 5min 容量趋势
业务视图 最近 7 天 15min 业务方看日/周趋势

刷新间隔不要小于 scrape_interval。采集是 15s,看板 5s 刷新只会看到重复数据,还增加 Prometheus 压力。

绝对时间范围(比如 2026-10-20 14:00 ~ 15:30)用于故障复盘。复盘时要把时间范围固定下来,否则过几天再看,数据已经过期了。

4.5 面板数量的控制

一个 dashboard 的面板数量控制在 12-20 个。超过 20 个,滚动就成了负担;少于 8 个,通常信息不够。

控制手段:

  • 用 row 折叠不常用的部分;
  • 用 Repeat by variable 把一个面板复制成多行,而不是手工加 10 个面板;
  • 把细节移到 L3,L1 只留关键指标。

五、指标盘点:先确认你有什么

5.1 资源层指标(node_exporter)

node_exporter 1.x 的关键指标,按 USE 分类:

维度 指标 说明
CPU 使用率 node_cpu_seconds_total{mode="idle"} 用 1 - idle 比例算
CPU 饱和度 node_load1、node_pressure_cpu_waiting_seconds_total PSI 指标需要内核 4.20+ 且 cgroup v2
内存使用率 node_memory_MemAvailable_bytes 用 Available 而不是 Free
内存饱和度 node_pressure_memory_waiting_seconds_total、node_memory_SwapCached_bytes swap 使用是危险信号
磁盘使用率 node_filesystem_avail_bytes / node_filesystem_size_bytes 注意过滤 tmpfs、overlay
磁盘饱和度 node_disk_io_time_seconds_total、node_pressure_io_waiting_seconds_total io_time 是设备忙碌时间
网络流量 node_network_receive_bytes_total 注意 direction 和 device
网络错误 node_network_receive_errs_total、node_network_receive_drop_total 非 0 就要查
TCP 重传 node_netstat_Tcp_RetransSegs 重传率高说明网络质量差
文件描述符 node_filefd_allocated / node_filefd_maximum 接近 1 会 EMFILE
时间偏移 node_timex_offset_seconds 超过 1s 要查 NTP

node_memory_MemAvailable_bytes 和 node_memory_MemFree_bytes 的区别很重要:Free 是完全没用的内存,Available 是包含可回收的 page cache。用 Free 算使用率,会看到"内存永远 90%",因为 Linux 会用空闲内存做缓存。

5.2 容器与 K8s 指标

cAdvisor(通过 kubelet 的 /metrics/cadvisor)提供容器资源:

  
  
  
  
text
container_cpu_usage_seconds_total
container_memory_working_set_bytes
container_fs_reads_bytes_total
container_network_receive_bytes_total

container_memory_working_set_bytes 是判断容器是否接近 OOM 的正确指标,它排除了可回收的 page cache。用 container_memory_usage_bytes 会高估。

kube-state-metrics 提供对象状态:

  
  
  
  
text
kube_deployment_status_replicas_available
kube_deployment_spec_replicas
kube_pod_container_status_restarts_total
kube_pod_container_status_waiting_reason
kube_node_status_condition
kube_job_status_failed

这些指标是"业务可用性"的桥梁:副本数不足、Pod 反复重启、Job 失败,都能直接反映到看板上。

5.3 应用指标

应用需要暴露什么,取决于看板要回答什么。最小集:

  
  
  
  
text
http_requests_total{service, method, path, status}          # counter
http_request_duration_seconds_bucket{service, path, le}     # histogram
http_requests_in_flight{service}                            # gauge
http_request_size_bytes{service}                            # histogram

标签基数是最大的坑。path 如果是 /order/12345 这种带 ID 的,每个订单一个序列,几天就能把 Prometheus 撑爆。正确做法是做路径模板化:

  
  
  
  
text
/order/{id}          → path="/order/:id"
/user/{id}/profile   → path="/user/:id/profile"

Spring Boot 的 Micrometer 默认会做 uri 标签的模板化(用 @RequestMapping 的模板),Go 的 promhttp 需要自己处理。上线前一定要检查 path 标签的基数:

  
  
  
  
promql
# 基数最高的 10 个标签值
topk(10, count by (path) ({__name__=~"http_requests_total"}))

如果某个 path 的序列数超过 100,基本可以确定没做模板化。

5.4 黑盒探测

有些可用性问题,应用指标看不出来:域名解析失败、证书过期、端口不通、首页白屏。这些要用 blackbox_exporter 从外部探测:

  
  
  
  
yaml
# blackbox.yml
modules:
http_2xx:
    prober:http
    timeout:5s
    http:
      valid_http_versions: ["HTTP/1.1", "HTTP/2.0"]
      valid_status_codes: [200, 301, 302]
      follow_redirects:true
      preferred_ip_protocol:ip4
tcp_connect:
    prober:tcp
    timeout:3s
icmp:
    prober:icmp
    timeout:3s

对应的探测结果指标:

  
  
  
  
text
probe_success{instance, job}                    # 1 成功,0 失败
probe_duration_seconds{instance}                # 探测耗时
probe_ssl_earliest_cert_expiry{instance}        # 证书过期时间戳
probe_http_status_code{instance}                # HTTP 状态码

证书过期是最值得做成看板的一项:

  
  
  
  
promql
# 距离证书过期还有多少天
(probe_ssl_earliest_cert_expiry - time()) / 86400

低于 30 天变黄,低于 7 天变红。这一条能消灭掉大部分"证书过期导致全站不可用"的事故。

六、L0 值班总览:5 秒内判断有没有事

6.1 布局

  
  
  
  
text
┌─────────────────────────────────────────────────────────────┐
│ [可用性 99.94%] [错误预算 62%] [P99 320ms] [错误率 0.06%]   │  4 个 stat
├─────────────────────────────────────────────────────────────┤
│ 服务健康总表(40 行,可排序)                                │  table
│ 服务名 | 可用性 | 错误率 | P99 | QPS | 状态                 │
├─────────────────────────────────────────────────────────────┤
│ 全局 QPS 与错误率趋势                                        │  timeseries
├─────────────────────────────────────────────────────────────┤
│ 容量水位(CPU/内存/磁盘/连接数 四个 gauge)                   │  gauge ×4
├─────────────────────────────────────────────────────────────┤
│ 当前告警(firing + pending)                                 │  alert list
└─────────────────────────────────────────────────────────────┘

6.2 四个核心数字

可用性(30 天滚动):

  
  
  
  
promql
sum(rate(http_requests_total{env="prod", status!~"5.."}[30d]))
  / sum(rate(http_requests_total{env="prod"}[30d]))

单位设为 percentunit(0-1 显示成百分比),阈值:< 0.999 变黄,< 0.995 变红。

注意 [30d] 这种大窗口查询很重,Prometheus 要扫 30 天的数据。正确做法是用 recording rule 预聚合:

  
  
  
  
yaml
groups:
  -name:slo
    interval:1m
    rules:
      -record:job:sli_availability:ratio_rate30d
        expr:|
          sum(rate(http_requests_total{env="prod", status!~"5.."}[30d]))
            / sum(rate(http_requests_total{env="prod"}[30d]))

看板查 job:sli_availability:ratio_rate30d,一条序列,毫秒级返回。

错误预算剩余:

  
  
  
  
promql
1 - (
  (1 - job:sli_availability:ratio_rate30d) / (1 - 0.999)
)

结果 0.62 表示还剩 62% 的预算。这个数字比"可用性 99.94%"更直观:99.94% 看起来很好,但如果 SLO 是 99.99%,预算已经用掉 94% 了。

P99 延迟:

  
  
  
  
promql
histogram_quantile(0.99,
  sum by (le) (rate(http_request_duration_seconds_bucket{env="prod"}[5m]))
)

错误率:

  
  
  
  
promql
sum(rate(http_requests_total{env="prod", status=~"5.."}[5m]))
  / sum(rate(http_requests_total{env="prod"}[5m]))

这四个数字放在最上面,用大字号 stat 面板,带 sparkline(迷你趋势线)。值班同学扫一眼就知道当前状态。

6.3 服务健康总表

这是 L0 最有价值的面板。用 table 展示所有服务的 RED 指标,按状态排序,异常的排在最上面。

实现方式:多个 instant query + Transform。

  
  
  
  
promql
# Query A:错误率
sum by (service) (rate(http_requests_total{env="prod", status=~"5.."}[5m]))
  / sum by (service) (rate(http_requests_total{env="prod"}[5m]))

# Query B:P99
histogram_quantile(0.99,
  sum by (le, service) (rate(http_request_duration_seconds_bucket{env="prod"}[5m]))
)

# Query C:QPS
sum by (service) (rate(http_requests_total{env="prod"}[5m]))

# Query D:副本可用数(kube-state-metrics 的标签是 deployment)
sum by (deployment) (kube_deployment_status_replicas_available)

Query D 这里有个细节:kube-state-metrics 的标签是 deployment,不是 service。要把它和前三列 join 到一起,有两种做法:

  1. 在 Prometheus 的 metric_relabel_configs 里把 deployment 重命名成 service(要求命名规范一致,比如 Deployment 名就是服务名);
  2. 在 Transform 里用 deployment 作为连接键,前提是前三列也能提供同名的 deployment 标签。

命名不统一时,最省事的办法是维护一张"服务名 → Deployment 名"的映射表,用 label_replace 在查询里补标签:

  
  
  
  
promql
label_replace(
  sum by (deployment) (kube_deployment_status_replicas_available),
  "service", "$1", "deployment", "(.*)-deploy"
)

Transform 配置:

  1. Join by field,字段选 service,模式 outer join;
  2. Organize fields,重命名列、隐藏多余的 Time 列;
  3. Sort by 错误率降序。

Field options:

  • 错误率列:unit percentunit,thresholds 0.001 黄、0.01 红,cell display mode Color background;
  • P99 列:unit s,thresholds 按服务基线;
  • 状态列:用 Value mapping,把错误率 > 1% 映射成 “异常”,否则 “正常”。

这样一张表,40 个服务的健康状态一屏看完,异常的还在最上面。

6.4 容量水位

四个 gauge,展示全局水位:

  
  
  
  
promql
# CPU 平均使用率
avg(1 - rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100

# 内存平均使用率
avg(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100

# 磁盘最高使用率
max(1 - node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"}
        / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}) * 100

# 连接数水位(以 Nginx 为例)
max(nginx_connections_active / (nginx_worker_connections * nginx_worker_count)) * 100

最后一个表达式里的 nginx_worker_connections 不是标准指标,需要自己用 textfile collector 或者 recording rule 注入。更简单的做法是直接看绝对值加阈值:

  
  
  
  
promql
max(nginx_connections_active)

gauge 的阈值设置:< 70% 绿,70%-85% 黄,> 85% 红。这些阈值必须结合业务基线,一个常年 80% CPU 的集群,85% 才告警就太晚了。

6.5 告警列表

Grafana 的 Alert list 面板,过滤条件设为 State: firing,按严重程度排序。这个面板的作用是让值班同学不用切换页面就能看到所有 firing 的告警。

如果告警在 Alertmanager 里,可以用 Alertmanager 数据源;如果用 Grafana 统一告警,直接选 Grafana 数据源即可。

七、L1 服务视图:定位到服务之后看什么

7.1 顶部:SLO 与燃烧率

L1 的顶部不要放资源指标,放 SLO:

  
  
  
  
promql
# 当前燃烧率(1 小时窗口)
(1 - (
  sum(rate(http_requests_total{service="$service", status!~"5.."}[1h]))
    / sum(rate(http_requests_total{service="$service"}[1h]))
)) / (1 - 0.999)

燃烧率的解读:

燃烧率 含义 处理
< 1 消耗速度低于预算 正常
1-6 预算会在 5-30 天内耗尽 关注,不需要立即处理
6-14.4 预算会在 2-5 天内耗尽 当天处理
> 14.4 按此速度 2 天内耗尽预算 立即处理,等同故障

7.2 RED 三联图

三个面板并排,共享时间轴:

  
  
  
  
promql
# Rate
sum by (path) (rate(http_requests_total{service="$service"}[$__rate_interval]))

# Errors
sum by (status) (rate(http_requests_total{service="$service", status=~"5.."}[$__rate_interval]))

# Duration(P50/P90/P99 三条线)
histogram_quantile(0.50, sum by (le) (rate(http_request_duration_seconds_bucket{service="$service"}[$__rate_interval])))
histogram_quantile(0.90, sum by (le) (rate(http_request_duration_seconds_bucket{service="$service"}[$__rate_interval])))
histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket{service="$service"}[$__rate_interval])))

延迟面板放三个分位数,是因为它们回答不同问题:

  • P50 上升:所有请求都变慢了,通常是依赖变慢或资源不足;
  • P99 上升但 P50 不变:少量请求变慢,通常是长尾(GC、锁竞争、慢查询、缓存击穿);
  • P99 剧烈抖动:调度问题、网络抖动、上游超时重试。

7.3 状态码分布

堆叠面积图,按状态码分组:

  
  
  
  
promql
sum by (status) (rate(http_requests_total{service="$service"}[$__rate_interval]))

用 Stack 模式,Percent 堆叠更容易看出比例。5xx 用红色系,4xx 用黄色系,2xx 用绿色系。

这里要注意:4xx 不一定正常。401/403 突增可能是鉴权配置错误,429 突增是限流误伤。看板上要把 4xx 单独画一条线,不要和 2xx 混在一起。

7.4 依赖健康

服务的错误往往来自下游。L1 要展示下游依赖的健康状态:

  
  
  
  
promql
# 下游服务的错误率
sum by (target_service) (
  rate(http_client_requests_total{service="$service", status=~"5.."}[$__rate_interval])
) / sum by (target_service) (
  rate(http_client_requests_total{service="$service"}[$__rate_interval])
)

前提是应用暴露了带 target_service 标签的出向请求指标。如果没有,至少要有:

  
  
  
  
promql
# 按下游地址的错误率(从网关或服务网格拿)
sum by (upstream) (rate(nginx_http_requests_total{status=~"5.."}[5m]))

依赖健康面板的价值:当订单服务报错时,能立刻看出是订单服务自己挂了,还是支付服务挂了。

7.5 实例对比

用 table 或 heatmap 展示每个实例的指标,快速找出"某个实例拖后腿":

  
  
  
  
promql
# 每个实例的错误率
sum by (instance) (rate(http_requests_total{service="$service", status=~"5.."}[$__rate_interval]))
  / sum by (instance) (rate(http_requests_total{service="$service"}[$__rate_interval]))

如果 8 个实例里 7 个错误率 0.01%,1 个是 5%,那问题就在这一个实例上,不用看整体。这是"平均指标掩盖问题"的典型场景。

用 heatmap 展示延迟分布也很有效:

  
  
  
  
promql
sum by (le) (rate(http_request_duration_seconds_bucket{service="$service"}[$__rate_interval]))

Heatmap 能看出"延迟分布整体右移"还是"只有一小撮请求变慢",比单条 P99 曲线信息量大。

7.6 部署标注

面板上的竖线标注(annotation)能直接把"延迟升高"和"发布"对应起来:

  
  
  
  
promql
# 查询部署变更
changes(kube_deployment_status_observed_generation{namespace="prod"}[10m]) > 0

在 Dashboard settings → Annotations 里添加,颜色设成灰色或蓝色,标签显示 {{deployment}}。

这个功能的价值在于:故障复盘时,80% 的"原因不明"都能靠标注解释。延迟在 14:32 升高,标注显示 14:31 有一次发布,因果关系一目了然。

八、L2 资源视图:USE 三维度

8.1 布局原则

资源看板不要按"指标类型"分组,要按"资源类型"分组。每一行是一个资源,行内按 USE 排列:

  
  
  
  
text
Row: CPU
├── 使用率(按实例,热力图)
├── 饱和度(load / 核数,折线)
└── PSI 等待时间(折线)

Row: 内存
├── 使用率(按实例)
├── Available 趋势
└── swap 使用 + OOM 事件

Row: 磁盘
├── 使用率(按 mountpoint)
├── 预测满盘时间
└── IO 利用率与 await

Row: 网络
├── 带宽(rx/tx)
├── 丢包与错误
└── TCP 重传

Row: 进程与 FD
├── 文件描述符使用率
└── 进程数

用 Repeat by $instance 可以让每个实例一行,但实例多了会很长。更好的做法是"全局热力图 + 单实例下钻"。

8.2 CPU

  
  
  
  
promql
# 使用率(按实例,1 - idle)
100 * (1 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[$__rate_interval])))

# 归一化负载(load1 / 核数),> 1 表示排队
sum by (instance) (node_load1)
  / count by (instance) (node_cpu_seconds_total{mode="idle"})

# PSI:CPU 因等待而停滞的时间占比
rate(node_pressure_cpu_waiting_seconds_total[$__rate_interval])

node_pressure_cpu_waiting_seconds_total 需要内核 4.20+ 且 node_exporter 开启 PSI 采集。它的值接近 1 表示 CPU 严重不足,比 load 更准确——load 高但 PSI 低,说明是 IO 等待导致的,不是 CPU 不够。

面板选择:

  • 使用率用 heatmap(横轴时间,纵轴实例,颜色表示值),一眼看出哪个实例异常;
  • 归一化负载用 timeseries,加一条 y=1 的阈值线;
  • PSI 用 timeseries,阈值 0.2 黄、0.5 红。

8.3 内存

  
  
  
  
promql
# 使用率
100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)

# swap 使用
100 * (node_memory_SwapTotal_bytes - node_memory_SwapFree_bytes)
      / node_memory_SwapTotal_bytes

# OOM 事件(需要 node_exporter 的 vmstat 采集或从日志转指标)
increase(node_vmstat_oom_kill[$__rate_interval])

node_vmstat_oom_kill 是 node_exporter 从 /proc/vmstat 采集的,值为累计 OOM kill 次数。这个指标非 0 增长,说明有进程被内核杀了,必须查。

内存面板要特别注意单位。node_memory_* 是字节,Grafana 里设 unit 为 bytes(IEC),显示成 GiB。设成 decbytes 会显示成 GB,和 free -h 的数字对不上,容易误导。

8.4 磁盘

磁盘是运维事故的高发区,要三个面板:

  
  
  
  
promql
# 使用率(排除虚拟文件系统)
100 * (1 - node_filesystem_avail_bytes{fstype!~"tmpfs|overlay|squashfs"}
            / node_filesystem_size_bytes{fstype!~"tmpfs|overlay|squashfs"})

# 预测满盘时间(线性回归,6 小时窗口,预测 4 小时后)
predict_linear(node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"}[6h], 4*3600) < 0

# inode 使用率
100 * (1 - node_filesystem_files_free{fstype!~"tmpfs|overlay"}
            / node_filesystem_files{fstype!~"tmpfs|overlay"})

# IO 利用率
100 * rate(node_disk_io_time_seconds_total{device=~"sd.*|nvme.*"}[$__rate_interval])

# 平均读等待(毫秒):读耗时 / 读完成次数
1000 * rate(node_disk_read_time_seconds_total[5m])
     / rate(node_disk_reads_completed_total[5m])

# 平均写等待(毫秒)
1000 * rate(node_disk_write_time_seconds_total[5m])
     / rate(node_disk_writes_completed_total[5m])

predict_linear 是磁盘看板上最有价值的一个函数。它回答的不是"现在用了多少",而是"按当前写入速度,多久会满"。返回负值表示在预测时间点会满:

  
  
  
  
promql
# 4 小时内会满的磁盘
predict_linear(node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"}[6h], 4*3600) < 0

这条查询直接可以做成告警。比"使用率 > 85%"更早发现问题,也更少误报(一个稳定在 88% 的磁盘不会告警,一个从 60% 快速涨到 80% 的会告警)。

inode 耗尽是容易被忽略的故障:磁盘还有空间,但无法创建新文件,报 No space left on device。日志目录、缓存目录、容器层最容易出这个问题。

8.5 网络

  
  
  
  
promql
# 入向带宽(bit/s)
sum by (instance) (rate(node_network_receive_bytes_total{device!~"lo|veth.*|docker.*"}[$__rate_interval])) * 8

# 出向带宽
sum by (instance) (rate(node_network_transmit_bytes_total{device!~"lo|veth.*|docker.*"}[$__rate_interval])) * 8

# 丢包率
sum by (instance) (rate(node_network_receive_drop_total{device!~"lo"}[$__rate_interval]))
  / sum by (instance) (rate(node_network_receive_packets_total{device!~"lo"}[$__rate_interval]))

# TCP 重传率
sum by (instance) (rate(node_netstat_Tcp_RetransSegs[$__rate_interval]))
  / sum by (instance) (rate(node_netstat_Tcp_OutSegs[$__rate_interval]))

* 8 是把字节转成比特,配合 unit bps 显示。如果不乘 8,显示的是 B/s,和网卡标称的 1Gbps/10Gbps 对不上。

重传率超过 0.5% 就说明网络有问题(丢包、拥塞、双工不匹配)。这个指标在"应用变慢但 CPU 内存都正常"时特别有用。

8.6 文件描述符与进程

  
  
  
  
promql
# FD 使用率
100 * node_filefd_allocated / node_filefd_maximum

# 进程数(node_exporter 的 processes collector)
node_processes_pids

# 进程数上限(接近时 fork 会失败)
node_processes_max_processes

FD 使用率超过 80% 要警惕。Nginx、Java 应用、数据库都是 FD 消耗大户,耗尽后表现为 Too many open files,服务直接不可用。

8.7 Kubernetes 层

资源看板之外,还需要一个 K8s 状态看板:

  
  
  
  
promql
# 副本不足的 Deployment
kube_deployment_status_replicas_available
  < kube_deployment_spec_replicas

# 反复重启的 Pod(1 小时内重启超过 3 次)
sum by (namespace, pod) (increase(kube_pod_container_status_restarts_total[1h])) > 3

# Pending 超过 10 分钟的 Pod
count by (namespace) (
  kube_pod_status_phase{phase="Pending"} == 1
) > 0

# 节点 NotReady
kube_node_status_condition{condition="Ready", status="true"} == 0

# Job 失败
kube_job_status_failed > 0

这些指标的价值在于把"业务不可用"翻译成"K8s 对象异常"。用户看到 502,运维看到"3 个 Pod 在 CrashLoopBackOff",因果链就清楚了。

8.8 GPU 资源

如果集群有 GPU,单独做一个 GPU 看板。核心指标(DCGM Exporter):

  
  
  
  
promql
# 显存使用率
100 * DCGM_FI_DEV_FB_USED / (DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE)

# SM 实际活跃度(比 GPU_UTIL 更准)
avg by (gpu, instance) (DCGM_FI_PROF_SM_ACTIVE)

# 显存带宽利用率
avg by (gpu, instance) (DCGM_FI_PROF_DRAM_ACTIVE)

# 温度
DCGM_FI_DEV_GPU_TEMP

# 功耗
DCGM_FI_DEV_POWER_USAGE

# XID 错误
DCGM_FI_DEV_XID_ERRORS

GPU 看板的特殊之处:DCGM_FI_DEV_GPU_UTIL 显示 100% 不代表算力用满,要看 DCGM_FI_PROF_SM_ACTIVE。这一点在 GPU 相关的看板设计里必须单独说明,否则会误导容量决策。

九、业务视图:让业务方也看得懂

9.1 业务指标从哪来

业务指标不能靠埋点"顺便"产生,要有明确的定义和采集链路。三种来源:

  1. 应用埋点:在关键业务动作处打点,比如下单成功、支付成功;
  2. 数据库查询:定时跑 SQL 统计,转成指标(适合对实时性要求不高的);
  3. 日志聚合:从访问日志里提取业务事件,用 Loki 或 Fluent Bit 转指标。

第一种最准,第三种最灵活,第二种最容易做但压力大。生产上通常是"关键指标埋点 + 次要指标日志聚合"。

9.2 业务看板的核心面板

  
  
  
  
promql
# 订单创建速率
sum(rate(business_order_created_total[5m])) * 60

# 下单成功率
sum(rate(business_order_created_total[5m]))
  / sum(rate(business_order_submit_total[5m]))

# 支付成功率
sum(rate(business_payment_success_total[5m]))
  / sum(rate(business_payment_attempt_total[5m]))

# GMV(每分钟)
sum(rate(business_gmv_yuan_total[5m])) * 60

业务看板的设计要点:

  • 用业务语言,不用技术术语。写"下单成功率",不写"order_created / order_submit 比值";
  • 给对比基准。今天的曲线要和昨天、上周同一天对比(Grafana 的 Compare to 功能或 offset 1d);
  • 异常自动标注。大促、发版、故障时间点用 annotation 标出来。

9.3 同比与环比

  
  
  
  
promql
# 今天 vs 昨天同一时刻
sum(rate(business_order_created_total[5m]))
sum(rate(business_order_created_total[5m] offset 1d))

# 今天 vs 上周同一天
sum(rate(business_order_created_total[5m] offset 7d))

offset 是 PromQL 里做时间对比的标准方式。注意 offset 要放在区间选择器后面:metric[5m] offset 1d,不能写成 metric offset 1d [5m]。

9.4 漏斗分析

业务方常问"为什么转化率下降了",这需要漏斗:

  
  
  
  
promql
# 各环节转化率
sum(rate(business_funnel_step_total{step="view"}[5m]))
sum(rate(business_funnel_step_total{step="cart"}[5m]))
sum(rate(business_funnel_step_total{step="order"}[5m]))
sum(rate(business_funnel_step_total{step="pay"}[5m]))

用 Bar gauge 或 Stat 面板展示每个环节的绝对值,用另一个面板展示相邻环节的转化率。漏斗的价值在于定位"哪一步掉了",而不是只看最终结果。

十、PromQL 进阶:让看板查得快、算得对

10.1 Recording rules 的命名与分层

看板上的复杂查询应该固化成 recording rule,原因有三个:

  1. 查询快(预计算好的序列,毫秒级返回);
  2. 口径统一(所有人用同一个表达式,不会各写各的);
  3. Prometheus 压力小(不用每次刷新都扫大量原始数据)。

命名规范(参考 Prometheus 官方建议):

  
  
  
  
text
level:metric:operations

level      → 聚合级别:job、instance、service、node
metric     → 指标名,去掉 _total 后缀
operations → 运算,如 rate5m、ratio、sum

示例:

  
  
  
  
yaml
groups:
  -name:slo-rules
    interval:30s
    rules:
      # 服务级请求速率
      -record:service:http_requests:rate5m
        expr:sumby(service)(rate(http_requests_total{env="prod"}[5m]))

      # 服务级错误率
      -record:service:http_errors:ratio_rate5m
        expr:|
          sum by (service) (rate(http_requests_total{env="prod", status=~"5.."}[5m]))
            / sum by (service) (rate(http_requests_total{env="prod"}[5m]))

      # 服务级 P99
      -record:service:http_request_duration_seconds:p99_5m
        expr:|
          histogram_quantile(0.99,
            sum by (le, service) (rate(http_request_duration_seconds_bucket{env="prod"}[5m]))
          )

      # 30 天可用性(用 1h 的预聚合再聚合,避免扫 30 天原始数据)
      -record:service:sli_availability:ratio_rate30d
        expr:|
          sum by (service) (avg_over_time(service:http_errors:ratio_rate5m[30d]))

最后一条要注意:avg_over_time 对错误率做平均是近似算法,严格来说应该用"总成功数/总请求数"。更准的做法是保留两个 counter 的预聚合:

  
  
  
  
yaml
      -record:service:http_requests:increase1h
        expr:sumby(service)(increase(http_requests_total{env="prod"}[1h]))

      -record:service:http_errors:increase1h
        expr:sumby(service)(increase(http_requests_total{env="prod",status=~"5.."}[1h]))

      -record:service:sli_availability:ratio_rate30d
        expr:|
          1 - (
            sum by (service) (sum_over_time(service:http_errors:increase1h[30d]))
              / sum by (service) (sum_over_time(service:http_requests:increase1h[30d]))
          )

10.2 聚合与基数控制

看板查询慢,90% 是基数问题。三个原则:

原则一:先过滤再聚合。

  
  
  
  
promql
# 差:先算所有服务的速率,再过滤
sum by (service) (rate(http_requests_total[5m])){service="order"}

# 好:先过滤,再聚合(语法上要写在选择器里)
sum by (service) (rate(http_requests_total{service="order"}[5m]))

原则二:用 sum without 去掉高基数标签。

  
  
  
  
promql
# 保留除 instance、pod 之外的所有标签
sum without (instance, pod) (rate(http_requests_total[5m]))

原则三:避免在面板上做 count by (path) 这种高基数聚合。 路径如果有几千个,一条查询就能拖垮 Prometheus。

10.3 几个容易写错的函数

rate vs irate:

  • rate 用窗口内首尾两个点算平均速率,平滑,适合看趋势和告警;
  • irate 用最后两个点算瞬时速率,敏感,适合看瞬时峰值,但曲线抖动大。

看板上的长期趋势用 rate,排查瞬时尖刺用 irate。告警一律用 rate,irate 容易误报。

increase 的外推问题:

  
  
  
  
promql
# 窗口 1h,采集间隔 15s,实际只有 240 个点
increase(http_requests_total[1h])

increase 会把首尾不完整的部分按比例外推。如果窗口边界刚好切在一个点的中间,结果会有几个百分点的误差。对账类场景(比如计费)不能用 increase,要用 sum_over_time 配合 resets()。

histogram_quantile 的插值误差:

分位数是从桶的边界插值算出来的,桶越粗误差越大。默认的 bucket 是 [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],如果实际延迟集中在 0.1-0.25 之间,P99 的精度就很差。要让分位数准,bucket 要在业务关心的区间加密。

holt_winters 已重命名:Prometheus 3.x 里 holt_winters 改名为 double_exponential_smoothing,老看板升级后会报错。

10.4 时间修饰符

  
  
  
  
promql
# 固定时间点(用于复盘对比)
sum(rate(http_requests_total[5m] @ 1760000000))

# 相对偏移
sum(rate(http_requests_total[5m] offset 1h))

# 子查询:先算 1 分钟的 rate,再取 1 小时的最大值
max_over_time(rate(http_requests_total[1m])[1h:1m])

@ 修饰符在故障复盘时特别有用:把"故障时刻"固定住,然后对比"故障前 1 小时"和"故障后 1 小时"。

10.5 空值与缺失

  
  
  
  
promql
# 服务没有流量时,返回 0 而不是空
sum(rate(http_requests_total{service="x"}[5m])) or vector(0)

# 检测目标消失(比 up == 0 更可靠)
absent_over_time(up{job="node"}[5m])

# 某个指标不存在时告警
absent(http_requests_total{service="payment"})

or vector(0) 会让面板在无数据时显示 0,而不是空白。这在"错误率"类面板上很重要:没有请求时错误率应该是 0,不是 NaN。

但要注意:or vector(0) 也会掩盖"采集挂了"的问题。如果指标完全没采集,面板显示 0,看起来一切正常。所以关键指标要用 absent() 单独告警,不能只靠看板。

十一、钻取:从看板到根因的路径

11.1 Data links

在 table 面板的实例列上加 data link,点击跳到 L3:

  
  
  
  
text
URL: /d/instance-detail/instance?var-instance=${__data.fields.instance}&var-env=${env}&from=${__from}&to=${__to}

可用的内置变量:

变量 含义
${__data.fields.instance} 当前行的 instance 字段值
${__from} / ${__to} 当前时间范围(毫秒时间戳)
${__url_time_range} from=...&to=... 形式的参数串
${__value.time} 数据点的时间

${__url_time_range} 是最省事的,直接拼在 URL 后面就能带上时间范围。

11.2 从指标跳到日志

Loki 数据源支持从指标面板跳转:

  
  
  
  
logql
{namespace="prod", pod="$pod"} |= "error"

配合 Grafana 的 “Explore” 链接,或者在 panel 上加 data link:

  
  
  
  
text
/d/explore?left={"datasource":"loki","queries":[{"expr":"{pod=\"${__data.fields.pod}\"} |~ \"ERROR\""}],"range":{"from":"${__from}","to":"${__to}"}}

URL 里的 JSON 需要 encode,手工拼很容易出错。更简单的做法是用 Grafana 的 “Derived fields”:在 Loki 数据源里配置从日志中提取 trace_id,然后在日志面板里直接点 trace_id 跳到 Tempo。

11.3 Exemplars

Exemplar 是"指标数据点上挂的 trace id"。配置后,在延迟面板上点击某个尖刺,能直接跳到那条请求的 trace。

配置步骤:

  1. 应用侧在 histogram 观测时带上 trace_id(OpenTelemetry 的 Exemplar 支持);
  2. Grafana 数据源配置里开启 “Exemplars”,填 trace_id 的标签名;
  3. Tempo 数据源配置好,并在 Prometheus 数据源的 “Exemplar internal link” 里指向 Tempo。

这一步的收益很高:从"P99 升高"到"看具体哪条请求慢",原来要十几分钟,配好 exemplar 后是两次点击。

11.4 Dashboard links

在看板右上角放几个固定链接:

  • 返回 L0 总览;
  • 跳到本服务的日志(Loki Explore,带好 namespace 和 service 过滤);
  • 跳到本服务的告警规则列表;
  • 跳到 Runbook(Wiki 或内部文档)。

Runbook 链接特别值得做。故障时最缺的不是数据,是"下一步该干什么"。把 Runbook 放在看板上,能把平均恢复时间缩短一半。

十二、告警:看板的另一半

12.1 看板和告警的分工

看板是"人主动看",告警是"系统主动叫人"。两者的判断逻辑应该一致,但阈值可以不同:

  • 看板上的红色阈值是"值得关注";
  • 告警阈值是"必须现在处理"。

如果两者用同一个阈值,要么告警太多(狼来了),要么看板上的红色没人理。

12.2 多窗口多燃烧率

单阈值告警的问题是:阈值低了误报,阈值高了漏报。SRE 的做法是用"燃烧率 + 多窗口":

严重度 长窗口 短窗口 燃烧率阈值 含义
page(立即处理) 1h 5m 14.4 1 小时消耗 2% 预算,约 2 天耗尽
page(立即处理) 6h 30m 6 6 小时消耗 5% 预算,约 5 天耗尽
ticket(当天处理) 3d 6h 1 3 天消耗 10% 预算,30 天耗尽

两个窗口同时满足才告警,作用是"快速恢复"和"防抖":短窗口负责快速发现,长窗口负责确认不是瞬时抖动。

PromQL 实现:

  
  
  
  
promql
# 长窗口燃烧率(1h)
(
  1 - (
    sum(rate(http_requests_total{service="order", status!~"5.."}[1h]))
      / sum(rate(http_requests_total{service="order"}[1h]))
  )
) / (1 - 0.999)

# 短窗口燃烧率(5m)
(
  1 - (
    sum(rate(http_requests_total{service="order", status!~"5.."}[5m]))
      / sum(rate(http_requests_total{service="order"}[5m]))
  )
) / (1 - 0.999)

在 Grafana 告警规则里配两个 query(A 和 B),条件用 A > 14.4 && B > 14.4。

12.3 告警规则的组织

按"影响面"而不是"指标类型"分组:

  
  
  
  
text
Alerts/
├── 00-slo/             # SLO 燃烧率(page)
├── 10-availability/    # 服务不可用、探测失败(page)
├── 20-capacity/        # 磁盘、内存、连接数(ticket)
├── 30-infra/           # 节点、网络、GPU(ticket/page 视情况)
└── 90-meta/            # 采集自身健康(ticket)

90-meta 经常被忽略,但它很重要:监控系统自己挂了,必须有告警。

  
  
  
  
promql
# Prometheus 抓不到任何 target
absent(up{job="node"} == 1)

# 采集延迟过高
prometheus_target_scrape_pool_sync_total 的变化率异常

# 规则评估失败
increase(prometheus_rule_evaluation_failures_total[10m]) > 0

# 告警没发出去
rate(alertmanager_notifications_failed_total[5m]) > 0

12.4 告警内容的设计

一条好的告警应该包含五要素:

  
  
  
  
yaml
annotations:
  summary:"订单服务错误率 2.3%,超过 SLO 阈值"
description:"过去 1 小时错误率 2.3%(SLO 0.1%),影响约 1200 次请求。主要错误码 500(占 87%),集中在 order-service-7d9f 实例。"
runbook_url:"https://wiki.example.com/runbook/order-service"
dashboard_url:"https://grafana.example.com/d/order-l1?var-service=order"
impact:"用户下单失败,预计每分钟损失 40 单"

description 里带"主要错误码、集中实例"这种信息,需要告警规则里做聚合。Grafana 的模板变量支持从查询结果里取值:

  
  
  
  
text
{{ with printf "service:http_errors:ratio_rate5m{service=\"order\"}" | query }}
  {{ . | first | value | humanizePercentage }}
{{ end }}

runbook_url 和 dashboard_url 是最有价值的两项。值班同学收到告警后,第一件事是点链接,不是思考。

12.5 静默、抑制与维护窗口

  • 静默(Silence):临时屏蔽某类告警,比如发版期间屏蔽该服务的 5xx 告警。必须设过期时间,最长 24 小时。
  • 抑制(Inhibition):高级别告警触发时,抑制低级别。比如"节点 NotReady"抑制该节点上所有 Pod 的告警,避免一次故障产生 200 条告警。
  • 维护窗口(Mute timing):定期维护(比如每周日凌晨的备份)期间不告警。

三者都容易滥用。静默必须有审批和记录,否则会变成"把告警关掉就没人报故障了"。

12.6 告警与看板的联动

告警通知里带 dashboard 链接,点进去直接定位到出问题的服务和时间段。这要求:

  1. 看板的 URL 支持变量传参(?var-service=order&from=now-1h);
  2. 告警的 dashboard_url 用模板变量拼出来;
  3. 看板的默认时间范围能被 URL 覆盖。

这条链路打通后,从"收到告警"到"看到异常曲线"是 10 秒,而不是 3 分钟。

十三、部署与配置管理

13.1 grafana.ini 关键配置

  
  
  
  
ini
# /etc/grafana/grafana.ini
[server]
protocol = http
http_port = 3000
domain = grafana.internal.example.com
root_url = https://grafana.internal.example.com/
serve_from_sub_path = false
enable_gzip = true

[database]
type = postgres
host = pg.internal.example.com:5432
name = grafana
user = grafana
password = $__env{DB_PASSWORD}
ssl_mode = require
max_open_conns = 50
max_idle_conns = 10

[security]
admin_user = admin
cookie_secure = true
cookie_samesite = lax
disable_gravatar = true

[users]
allow_sign_up = false
auto_assign_org_role = Viewer

[auth.anonymous]
enabled = false

[analytics]
reporting_enabled = false
check_for_updates = false
check_for_plugin_updates = false

[log]
mode = console
level = info

[metrics]
enabled = true
interval_seconds = 30

[unified_alerting]
enabled = true
ha_listen_address = 0.0.0.0:9094
ha_advertise_address = 10.40.1.11:9094
ha_peers = 10.40.1.11:9094,10.40.1.12:9094

几个要点:

  • password = $__env{DB_PASSWORD} 是 Grafana 原生支持的写法,从环境变量读密码,配置文件里不出现明文。注意不要写成 ${DB_PASSWORD}——那种 shell 风格的替换在 grafana.ini 里不生效,只有容器启动脚本才会处理。另一种等价做法是用 GF_DATABASE_PASSWORD 环境变量覆盖(GF_ 前缀 + 段名 + 键名,下划线分隔)。
  • enable_gzip 在 Grafana 13 默认已经是 true,显式写上是为了让配置可读。
  • [unified_alerting] ha_peers 是多实例告警高可用的关键。三个实例都要配同样的 peer 列表,否则告警会重复发送或者漏发。
  • [analytics] 三项关掉,减少外网请求,内网环境更干净。

grafana.ini 的生效方式和其他配置不同,要分清三类:

配置类型 路径 生效方式 验证
主配置(grafana.ini) /etc/grafana/grafana.ini 必须重启进程(systemctl restart grafana-server 或重启 Pod) curl /api/health 看版本与数据库状态
数据源 provisioning /etc/grafana/provisioning/datasources/ 文件变更后约 10 秒自动加载,无需重启 数据源页面点 “Save & test”,或调 /api/datasources
看板 provisioning /etc/grafana/provisioning/dashboards/ 按 updateIntervalSeconds 周期扫描,默认 10 秒 看板列表出现新文件对应的条目

改 grafana.ini 不要用 systemctl reload——Grafana 不支持 reload,命令会静默失败或者报 “Job type reload is not applicable”。改完必须 restart,重启期间看板不可用约 5-15 秒,要安排在低峰。

13.2 数据源 provisioning

  
  
  
  
yaml
# /etc/grafana/provisioning/datasources/prometheus.yaml
apiVersion:1
datasources:
-name:Prometheus
    uid:prom-main
    type:prometheus
    access:proxy
    url:http://prometheus.monitoring.svc:9090
    isDefault:true
    jsonData:
      timeInterval:15s
      httpMethod:POST
      prometheusType:Prometheus
      prometheusVersion:3.15.1
      exemplarTraceIdDestinations:
        -name:trace_id
          datasourceUid:tempo-main
    editable:false

-name:Loki
    uid:loki-main
    type:loki
    access:proxy
    url:http://loki.monitoring.svc:3100
    jsonData:
      maxLines:1000
      derivedFields:
        -name:trace_id
          matcherRegex:"trace_id=(\\w+)"
          datasourceUid:tempo-main

uid 必须显式指定。看板 JSON 里引用数据源用的是 uid,如果 provisioning 时不指定,每次重建都会生成新 uid,所有看板都会报 “Datasource not found”。

timeInterval: 15s 要和 Prometheus 的 scrape_interval 一致,Grafana 用它来计算 $__rate_interval。

13.3 看板 provisioning

  
  
  
  
yaml
# /etc/grafana/provisioning/dashboards/default.yaml
apiVersion:1
providers:
-name:'ops-dashboards'
    orgId:1
    folder:'10-业务服务'
    folderUid:'biz'
    type:file
    disableDeletion:true
    allowUiUpdates:false
    updateIntervalSeconds:30
    options:
      path:/var/lib/grafana/dashboards/biz
      foldersFromFilesStructure:false

三个参数决定行为:

  • disableDeletion: true:不允许在 UI 里删除 provisioning 的看板,防止误删;
  • allowUiUpdates: false:UI 上改了不保存,重启后恢复成文件版本。这强制了"看板即代码";
  • updateIntervalSeconds: 30:文件变化后 30 秒内自动加载,不用重启 Grafana。

allowUiUpdates: false 是双刃剑。它保证了配置一致性,但也意味着业务方想在 UI 上调个阈值必须先提 PR。如果团队还没建立这个习惯,可以先设 true,等流程跑顺了再关。

13.4 看板即代码的两种方式

方式一:JSON 文件 + Git + provisioning(传统方式)

  
  
  
  
bash
# 从 Grafana 导出看板 JSON
curl -s -H "Authorization: Bearer $GRAFANA_TOKEN" \
  "https://grafana.example.com/api/dashboards/uid/order-l1" \
  | jq '.dashboard' > dashboards/biz/order-l1.json

# 提交
git add dashboards/biz/order-l1.json
git commit -m "feat(dashboard): 订单服务增加燃烧率面板"

导出时要注意:JSON 里的 id 字段要去掉(否则导入时会覆盖同 id 的看板),version 字段也要处理,否则会有版本冲突。

方式二:Git Sync(Grafana 13 GA)

在 UI 里连接 GitHub/GitLab/Bitbucket 仓库,看板可以直接在 Grafana 里编辑并提交 PR。优点是门槛低,业务方不用学 Git 命令;缺点是:

  • 同步冲突的处理不透明,两个人在 UI 上同时改同一个看板,可能丢失改动;
  • 从 12.x 升级到 13.0.0 有已知的迁移 bug,可能导致看板和文件夹丢失。

如果要用 Git Sync,必须先升级到 13.0.1 并备份数据库。已经踩过坑的团队反馈,从 13.0.0 升级到 13.0.1 无法恢复丢失的数据,只能还原数据库再升。

13.5 高可用部署

Grafana 本身是无状态的(除了 SQLite 模式),高可用方案:

  
  
  
  
text
                    ┌── Grafana 实例 1 ──┐
LB (HTTPS) ────────>│                    │──── PostgreSQL(共享)
                    └── Grafana 实例 2 ──┘
                             │
                     告警 HA 集群(9094 互连)

要点:

  • 数据库必须用 PostgreSQL 或 MySQL,不能用 SQLite(SQLite 不支持多实例并发写);
  • 两个实例的 [unified_alerting] ha_peers 必须包含彼此;
  • root_url 必须一致,否则告警通知里的链接会指向错误的实例;
  • 会话存储在数据库里(默认),不需要额外的 session 共享。

13.6 权限模型

层级 机制 用途
组织 Org 多租户隔离(一般一个公司一个 Org)
团队 Team 按业务线分组
文件夹 Folder permissions 控制谁能看/改某个业务域的看板
数据源 Data source permissions 企业版功能,OSS 版只能靠 Org 隔离
API Service Account + Token 自动化脚本用,不要用 admin 账号

最小权限实践:

  
  
  
  
bash
# 创建 service account(Grafana 9.1+)
curl -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  https://grafana.example.com/api/serviceaccounts \
  -d '{"name":"dashboard-ci","role":"Editor"}'

# 生成 token
curl -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
  https://grafana.example.com/api/serviceaccounts/1/tokens \
  -d '{"name":"ci-token"}'

注意 Grafana 13 里 API 路径正在从 /api 迁到 /apis,旧的 /api 仍然可用但已标记弃用。新写的自动化脚本建议直接用 /apis,避免下次升级再改。

十四、性能优化

14.1 看板慢的三个原因

原因一:查询本身重。 30 天窗口的 rate()、高基数聚合、没有预聚合的分位数。

判断方法:打开面板的 Inspect → Query,看每个 query 的耗时。超过 2 秒的就要优化。

原因二:面板太多。 一个 dashboard 20 个面板,每个 3 个 query,打开一次就是 60 个请求打向 Prometheus。

原因三:刷新太快。 5 秒刷新 + 20 个面板,等于每秒 4 个查询,值班室里开着 5 个大屏就是每秒 20 个。

14.2 优化手段

  
  
  
  
ini
[dataproxy]
timeout = 60
keep_alive_seconds = 30
logging = false

[database]
max_open_conns = 50

[alerting]
# 告警规则的评估并发,太高会打爆数据源
max_attempts = 3

看板层面:

  • 把复杂查询做成 recording rule;
  • 面板的 Max data points 设为面板宽度的 2 倍(默认是宽度像素数,宽屏下会请求过多点);
  • 用 Min interval 限制最小步长(设为 15s,和采集间隔一致);
  • 不常用的面板放进折叠的 row,Grafana 不会加载折叠 row 的数据(需要开启 “Keep rows collapsed” 的懒加载行为,具体看版本)。

Prometheus 层面:

  
  
  
  
yaml
# prometheus.yml 启动参数
--query.timeout=2m
--query.max-samples=50000000
--query.lookback-delta=5m

--query.max-samples 是硬限制,超过就报错。看板查询报 query processing would load too many samples into memory 就是这个限制触发了,说明查询范围太大或基数太高。

14.3 缓存

Grafana 企业版有查询缓存,OSS 版没有。OSS 的替代方案:

  • 用 recording rule 预计算(最有效);
  • 用 Prometheus 的 --storage.tsdb.retention.time 控制数据量,查询更快;
  • 在前面加一层 Prometheus 的 remote read 缓存(比如 Thanos Query 的缓存);
  • 用 Grafana 的 “Snapshot” 功能把看板定格(只适合汇报,不适合运维)。

十五、故障排查

15.1 看板显示 No data

按顺序排查:

  
  
  
  
bash
# 1. 确认时间范围有数据
curl -s "http://prometheus:9090/api/v1/query?query=up{job='node'}&time=$(date +%s)"

# 2. 确认标签匹配
curl -s "http://prometheus:9090/api/v1/labels" | jq '.data' | head -30

# 3. 确认变量值
# 在看板上把变量切成 "All",看是否有数据

# 4. 确认采集目标在线
curl -s "http://prometheus:9090/api/v1/targets?state=active" | jq '.data.activeTargets[] | select(.health!="up")'

常见原因:

现象 原因 处理
所有面板 No data 数据源配置错误或 Prometheus 挂了 检查数据源健康状态
单个面板 No data 标签写错、指标名拼错 在 Explore 里验证查询
切换变量后 No data 变量值在目标时间范围不存在 改变量 refresh 为 On time range change
时间范围拉长后 No data 数据已过期(retention) 检查 retention 配置

15.2 面板显示 NaN

NaN 通常来自 histogram_quantile 在没有数据时返回 NaN,或者 0/0。

处理:

  • 面板的 “No value” 设为 0;
  • 查询加 or vector(0);
  • 如果是除法,加 > 0 过滤掉分母为 0 的情况。

但要注意:NaN 有时是正确的。服务确实没有请求时,错误率就是未定义,显示 0 会掩盖"服务挂了没流量"的问题。这种情况应该用 absent() 单独告警。

15.3 变量不生效

三个常见原因:

  1. 多值变量没用 =~:instance="$instance" 在多选时会被拼成 a,b,匹配不到任何序列。改成 instance=~"${instance:regex}"。
  2. 变量依赖顺序错了:$instance 的查询里用了 $service,但 $service 排在后面,Grafana 会按顺序加载,导致 $instance 查不到值。在变量设置里调整顺序,或者用 “Show dependants” 显式声明依赖。
  3. All 值的处理:多值变量选 All 时,值是 $__all 或者正则 .*,取决于 “All value” 的设置。用 .* 时要注意它也会匹配空字符串。

15.4 时间不对

三种时间问题:

  • 时区:Grafana 默认用浏览器时区,Prometheus 存的是 UTC。看板上显示 14:00,Prometheus 里是 06:00,这是正常的。团队内部要统一用哪个时区看,建议统一用 UTC+8。
  • 时钟偏移:服务器时间不同步,导致指标时间戳错乱。node_timex_offset_seconds 超过 1 秒就要查 NTP。
  • 时间范围与采集间隔不匹配:选 1 分钟范围、15 秒采集,只有 4 个点,曲线会很奇怪。最小时间范围建议是采集间隔的 10 倍以上。

15.5 Grafana 自身的问题

  
  
  
  
bash
# 看 Grafana 日志
kubectl logs -n monitoring deploy/grafana --tail=200

# 看健康状态
curl -s http://grafana:3000/api/health | jq

# 看指标(需要 [metrics] enabled = true)
curl -s http://grafana:3000/metrics | grep -E "grafana_http_request_duration_seconds|grafana_datasource_request"

常见错误:

日志 原因 处理
failed to connect to database 数据库连接失败 检查连接串、网络、密码
datasource not found 看板引用的 uid 不存在 检查 provisioning 的 uid
context deadline exceeded 查询超时 调 [dataproxy] timeout 或优化查询
too many open connections 数据库连接池不足 调 max_open_conns

如果 Grafana 前面有 Nginx,Live 功能(实时推送)需要 WebSocket 支持:

  
  
  
  
nginx
location / {
    proxy_pass http://grafana_backend;
    proxy_http_version1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_read_timeout3600s;
}

漏了 Upgrade 和 Connection 头,Live 功能会一直重连,控制台报 WebSocket connection failed。

十六、升级风险与回滚

16.1 Grafana 13 的破坏性变更清单

变更 影响 应对
grafana-cli 移除 所有用 grafana-cli 的脚本失效 改成 grafana cli
grafana-server 移除 systemd 单元里的启动命令失效 改成 grafana server
Image Renderer 插件移除 截图、报表、告警图片全部失效 部署新版渲染服务,配 JWT
/api 迁到 /apis 自动化脚本可能失效 逐步迁移,旧路径暂时兼容
数字 id 数据源 API 禁用 用 id 而非 uid 的脚本失效 改用 uid
enable_gzip 默认开 CPU 略增,带宽降 一般无需处理
React 18 → 19 部分第三方面板插件无法渲染 升级插件或替换
文件夹与看板统一存储 升级时迁移数据 备份数据库

16.2 升级步骤

  
  
  
  
bash
# 1. 备份数据库(最重要)
pg_dump -h pg.internal -U grafana -Fc grafana > /backup/grafana-$(date +%F).dump

# 2. 备份 provisioning 和看板 JSON
tar czf /backup/grafana-conf-$(date +%F).tar.gz /etc/grafana /var/lib/grafana/dashboards

# 3. 记录当前版本
grafana server -v

# 4. 在预发环境用生产数据库的副本升级验证
#    (不要直接在生产试)

# 5. 升级(以 Helm 为例)
helm upgrade grafana grafana/grafana -n monitoring \
  --set image.tag=13.2.4 \
  -f values-prod.yaml

# 6. 验证
curl -s https://grafana.example.com/api/health | jq '.version'

16.3 回滚

  
  
  
  
bash
# 1. 回滚镜像
helm rollback grafana -n monitoring

# 2. 如果数据库 schema 已经迁移,必须还原备份
pg_restore -h pg.internal -U grafana -d grafana --clean --if-exists /backup/grafana-2026-10-20.dump

# 3. 重启
kubectl rollout restart -n monitoring deploy/grafana

关键点:Grafana 的数据库迁移是单向的。 升级到 13 之后,数据库 schema 变了,直接回滚镜像到 12 会启动失败。必须先还原数据库备份,再回滚镜像。

所以升级前的数据库备份不是"建议",是"必须"。备份要验证可恢复(在测试库上 restore 一次),不能只看文件大小。

16.4 灰度策略

Grafana 是内部工具,灰度方式简单:

  1. 先升级一个实例(HA 部署下有两个),观察 24 小时;
  2. 确认看板加载、告警评估、数据源查询都正常;
  3. 再升级第二个实例。

如果只有一个实例,就在低峰期升级,并准备好 30 分钟内可回滚。

十七、生产注意事项

17.1 看板的生命周期

看板会腐烂。一个两年前设计的看板,指标可能已经改名、服务可能已经下线、阈值可能已经不适用。管理方式:

  • 每个看板标注 owner 和最后review时间(放在 dashboard 的 description 里);
  • 每季度清理一次,90 天没人访问的看板移到归档文件夹;
  • 用 Grafana 的 usage insights(企业版)或查询日志统计访问量。

17.2 不要把所有东西都放进 Grafana

Grafana 擅长展示时序指标,不擅长:

  • 拓扑图(用专门的工具或 Graphviz 面板);
  • 大表格(几千行的表格在 Grafana 里很卡);
  • 复杂交互(下钻超过 3 层,体验就很差)。

判断标准:如果一个面板需要"点击、筛选、再点击"才能得到答案,它可能应该是一个独立的应用,而不是 Grafana 面板。

17.3 指标治理

看板的问题往往是指标的问题。三个治理动作:

  • 命名规范:<namespace>_<subsystem>_<name>_<unit>,比如 http_request_duration_seconds;
  • 标签规范:service、instance、env、version 必须有,高基数标签(user_id、order_id)禁止;
  • 基数监控:定期看 prometheus_tsdb_head_series,增长过快说明有指标爆炸。
  
  
  
  
promql
# 序列数最多的 10 个指标
topk(10, count by (__name__) ({__name__=~".+"}))

17.4 文档与交接

看板设计要有文档,至少包含:

  • 每个面板回答什么问题;
  • 每个阈值的依据(基线数据、SLO);
  • 异常时的处理步骤(Runbook);
  • 指标的采集链路(从应用到 Prometheus 到 Grafana)。

没有文档的看板,换个人就废了。这是运维团队最常见的知识流失。

十八、上线检查清单

18.1 设计阶段

项 检查内容 通过标准
分层 L0/L1/L2 是否齐全 每层有明确的读者和用途
跳转 层级之间能否下钻 点击链路可用
变量 是否支持环境/服务/实例切换 切换后数据正确
时间范围 默认范围是否合理 与刷新频率匹配
面板数 是否在 12-20 之间 一屏能看完关键信息

18.2 指标阶段

项 检查方式 通过标准
采集 up == 1 所有 target 在线
基数 count by (__name__) 无异常增长的指标
标签 抽样查询 service/instance/env 齐全
精度 与 top、free 对比 误差 < 5%
分桶 检查 histogram bucket 覆盖业务关心的区间

18.3 告警阶段

项 检查方式 通过标准
规则 逐条 review 有 for、有 severity、有 runbook
通知 发测试告警 能收到,内容完整
抑制 模拟节点故障 不产生告警风暴
静默 检查过期时间 无长期静默
自监控 absent(up) 采集挂了能告警

18.4 运维阶段

项 检查方式 通过标准
备份 每周 pg_dump 可恢复(验证过)
版本 grafana server -v 无已知高危漏洞
性能 打开 L0 耗时 < 3 秒
权限 抽查文件夹权限 业务方只能看自己的
文档 Runbook 链接 每个告警都有

十九、总结

Grafana 看板设计的核心不是"画得好看",而是让人在最短时间内做出正确判断。这需要三件事:

第一,分层。 L0 回答"有没有事",L1 回答"哪个服务",L2 回答"哪台机器"。每层只做一件事,层与层之间用链接串起来。一个 32 面板的大屏之所以没人看,就是因为它试图在一屏里回答所有问题。

第二,选对指标。 资源层用 USE(使用率、饱和度、错误),服务层用 RED(速率、错误、延迟),业务层用 SLO(可用性、错误预算、燃烧率)。只有 CPU 曲线没有业务成功率,等于只有体温计没有病历。

第三,把判断固化下来。 阈值、基线、燃烧率、告警规则,这些都是"判断"的固化。看板上的一条红色阈值线,背后应该有一份基线数据支撑;告警里的一条 runbook 链接,背后应该有一次故障复盘。

最后是一份可以直接落地的顺序:

  1. 盘清现有指标,确认采集覆盖和标签规范;
  2. 定义 3-5 个核心 SLO,算出错误预算;
  3. 做 L0 总览:4 个数字 + 服务健康表 + 告警列表;
  4. 做 L1 服务视图:RED 三联图 + 依赖健康 + 实例对比;
  5. 做 L2 资源视图:USE 三维度,重点是磁盘预测和 PSI;
  6. 配 recording rules,把重查询预聚合;
  7. 配多窗口燃烧率告警,每条告警带 runbook;
  8. 打通 L0 → L1 → L2 的跳转和日志链接;
  9. 把看板 JSON 进 Git,用 provisioning 下发;
  10. 每季度 review 一次,清理没人看的看板。

看板是运维团队的"共同语言"。设计得好的看板,能让一个新人在凌晨三点独立判断故障范围;设计得差的看板,只会让所有人继续 SSH 上去敲 top。


【声明】内容源于网络
0
0
红帽Linux认证
社区致力于分享红帽Linux及其他产品最新资讯,RHCSA/RHCE/RHCA认证动态。
内容 182
粉丝 0
红帽Linux认证 社区致力于分享红帽Linux及其他产品最新资讯,RHCSA/RHCE/RHCA认证动态。
总阅读1.3k
粉丝0
内容182