Grafana 是云原生时代最主流的可视化平台,但"能用"和"好用"之间隔着一套设计方法论。一个混乱的仪表盘会让值班人员在海量面板中迷失,而一个设计良好的仪表盘能在 5 秒内传递系统健康状态。从设计原则出发,覆盖变量系统、面板选型、告警集成,最后用一个完整的 SLO 仪表盘串联所有知识点。

参考来源:Grafana 官方文档

一、仪表盘设计原则

1.1 五秒规则

一个仪表盘应该在 5 秒内回答最核心的问题:系统现在是否正常? 超过 5 秒才理解,说明信息层次不对。

实践方法:

  • 顶部放置全局状态行:用 Stat 或 Gauge 面板展示 SLO 达成率、核心错误率、P99 延迟,绿/黄/红阈值一目了然。
  • 中部放置趋势图:Time series 面板展示过去 1-6 小时的指标趋势。
  • 底部放置明细表:Table 面板列出实例级明细,供深入排障。

1.2 从左到右、从上到下

人类阅读习惯是从左上到右下,仪表盘的信息流应顺应这一规律:

┌─────────────────────────────────────────────┐
│  [SLO]  [错误率]  [P99延迟]  [流量]        │  ← 第一行:一眼看状态
├─────────────────────────────────────────────┤
│  CPU 趋势图         │   内存趋势图           │  ← 第二行:趋势
│  请求量趋势图       │   错误率趋势图         │
├─────────────────────────────────────────────┤
│  实例明细表                                  │  ← 第三行:明细
└─────────────────────────────────────────────┘

1.3 其他设计要点

  • 一个仪表盘只服务一个主题:不要把"数据库监控"和"业务指标"混在一个仪表盘里。
  • 合理利用阈值颜色:绿色=正常,黄色=警告,红色=严重,不要滥用颜色。
  • 默认时间范围设为"最近 1 小时":值班场景最常用。
  • 命名清晰:面板标题写"CPU 使用率 (%)“而非"cpu”。

二、变量模板系统

变量(Variables)是仪表盘可复用性的核心。通过变量可以实现"一套模板,多环境切换"。

2.1 创建变量

在 Dashboard Settings → Variables 中添加变量。以下是常用变量配置:

数据源变量 $datasource

Type: Datasource
Name: datasource
Query: Prometheus

服务器变量 $server

Type: Query
Name: server
Query: label_values(node_uname_info, instance)

实例变量 $instance(级联依赖 $server

Type: Query
Name: instance
Query: label_values(node_uncpu_info{instance=~"$server"}, cpu)

自定义变量 $environment

Type: Custom
Name: environment
Query: prod, staging, dev

2.2 变量引用方式

# 在面板查询中引用变量
up{instance=~"$server"}

# 多选变量(Multi-value 开启时)
up{instance=~"$server"}   # $server 展开为 node-1|node-2|node-3

# 在面板标题中使用
CPU 使用率 - $server

# 在仪表盘链接中使用
/dashboard/sre-overview?var-server=$server

2.3 变量链式联动

多级变量可以实现"选环境 → 选集群 → 选节点"的级联过滤:

# 第一级:$environment(Custom: prod, staging, dev)

# 第二级:$cluster(依赖 $environment)
label_values(kube_node_info{cluster=~"$environment"}, node)

# 第三级:$pod(依赖 $cluster)
label_values(kube_pod_info{node="$cluster"}, pod)

三、面板类型选择指南

面板类型适用场景典型指标
Time series时序趋势分析CPU、内存、QPS、延迟趋势
Stat单个关键数值SLO 达成率、当前在线用户数
Gauge仪表盘式数值展示磁盘使用率、CPU 使用率
Bar gauge多实例横向对比各节点内存使用率对比
Table结构化明细实例列表、告警列表
Heatmap分布式延迟分析请求延迟分布
Pie chart占比分析各 status code 流量占比
State timeline状态变化时间线节点存活状态

选型决策树

需要展示单个关键数值?
├── 是 → 需要仪表盘效果? → Gauge
│       不需要 → Stat
└── 否 → 需要趋势?
        ├── 是 → Time series
        └── 否 → 需要对比多个实例?
                ├── 是 → Bar gauge
                └── 否 → 需要明细? → Table

关键面板配置示例

Stat 面板:SLO 达成率

# Query
1 - (
  sum(rate(http_requests_total{status=~"5..", service="$service"}[5m]))
  / sum(rate(http_requests_total{service="$service"}[5m]))
)

# Thresholds
Thresholds:
  - Base: 0    (Green)
  - T1: 0.01   (Yellow)   # 错误率 > 1% 变黄
  - T2: 0.05   (Red)      # 错误率 > 5% 变红

# Color mode: Background
# Unit: Percent (0.0-1.0)

Bar Gauge 面板:各节点 CPU 对比

# Query
100 * (1 - avg by (instance) (
  rate(node_cpu_seconds_total{mode="idle", instance=~"$server"}[5m])
))

# Calculation: Last *
# Orientation: Horizontal
# Display mode: Gradient

四、告警集成:Grafana Alerting

Grafana 统一告警(Unified Alerting)支持跨数据源告警,相比在 Prometheus 侧单独配置 Alertmanager,它更适合"可视化告警管理"场景。

4.1 告警架构

Alert Rule → Notification Policy → Contact Point → 通知渠道
Notification Policy(路由匹配)→ Silences(静默)

4.2 创建告警规则

通过 UI 或 Terraform 配置告警规则。以下是一个 YAML 形式的规则示例(Grafana provisioning):

# alerting/alert_rules.yaml
apiVersion: 1

groups:
  - orgId: 1
    name: SLO Alerts
    interval: 60s
    rules:
      - uid: slo-error-rate-high
        title: "SLO 错误率告警 - {{ $labels.service }}"
        condition: B
        data:
          - refId: A
            relativeTimeRange:
              from: 600   # 过去 10 分钟
              to: 0
            datasourceUid: prometheus
            model:
              expr: |
                sum(rate(http_requests_total{status=~"5..", service="$service"}[5m]))
                / sum(rate(http_requests_total{service="$service"}[5m]))                
              instant: true
          - refId: B
            relativeTimeRange:
              from: 600
              to: 0
            datasourceUid: __expr__
            model:
              type: threshold
              expression: A
              conditions:
                - evaluator:
                    params: [0.05]
                    type: gt
        noDataState: NoData
        execErrState: Error
        for: 5m
        annotations:
          summary: "服务 {{ $labels.service }} 错误率超过 5%"
          description: "当前错误率: {{ $values.A }},SLO 阈值: 5%"
        labels:
          severity: critical
          team: sre
        notification_settings:
          group_by: ['service', 'alertname']
          group_wait: 30s
          group_interval: 5m
          repeat_interval: 4h

4.3 通知策略与联系点

# alerting/notification_policies.yaml
apiVersion: 1

policies:
  - receiver: default
    group_by: ['alertname', 'service']
    routes:
      - receiver: critical-team
        matchers:
          - severity="critical"
        group_wait: 0s
        repeat_interval: 1h

      - receiver: warning-team
        matchers:
          - severity="warning"
        group_wait: 30s
        repeat_interval: 4h

contactPoints:
  - orgId: 1
    name: critical-team
    receivers:
      - uid: webhook-critical
        type: webhook
        settings:
          url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY
          httpMethod: POST
      - uid: email-critical
        type: email
        settings:
          addresses: ['oncall@example.com']

  - orgId: 1
    name: default
    receivers:
      - uid: default-slack
        type: slack
        settings:
          url: https://hooks.slack.com/services/xxx

五、实战:构建完整的 SLO 仪表盘

以下通过 JSON 模型片段展示一个 SLO 仪表盘的核心结构,包含多面板联动。

5.1 仪表盘变量定义

{
  "templating": {
    "list": [
      {
        "name": "datasource",
        "type": "datasource",
        "query": "prometheus",
        "current": { "text": "Prometheus", "value": "Prometheus" }
      },
      {
        "name": "service",
        "type": "query",
        "datasource": "$datasource",
        "query": "label_values(http_requests_total, service)",
        "refresh": 2,
        "includeAll": false
      },
      {
        "name": "status_filter",
        "type": "custom",
        "query": "2xx,3xx,4xx,5xx",
        "default": "2xx"
      }
    ]
  }
}

5.2 面板一:SLO 达成率(Stat)

{
  "title": "SLO 达成率 - $service",
  "type": "stat",
  "datasource": "$datasource",
  "targets": [
    {
      "expr": "1 - (sum(rate(http_requests_total{status=~\"5..\", service=\"$service\"}[5m])) / sum(rate(http_requests_total{service=\"$service\"}[5m])))",
      "legendFormat": "SLO"
    }
  ],
  "fieldConfig": {
    "defaults": {
      "unit": "percentunit",
      "thresholds": {
        "mode": "absolute",
        "steps": [
          { "value": null, "color": "red" },
          { "value": 0.95, "color": "yellow" },
          { "value": 0.99, "color": "green" }
        ]
      }
    }
  },
  "options": {
    "colorMode": "background",
    "reduceOptions": { "calcs": ["lastNotNull"] }
  }
}

5.3 面板二:错误率趋势(Time Series)

{
  "title": "错误率趋势 - $service",
  "type": "timeseries",
  "datasource": "$datasource",
  "targets": [
    {
      "expr": "sum(rate(http_requests_total{status=~\"5..\", service=\"$service\"}[5m])) / sum(rate(http_requests_total{service=\"$service\"}[5m])) * 100",
      "legendFormat": "5xx Error Rate (%)"
    }
  ],
  "fieldConfig": {
    "defaults": {
      "unit": "percent",
      "custom": {
        "drawStyle": "line",
        "lineInterpolation": "smooth",
        "fillOpacity": 20
      },
      "thresholds": {
        "steps": [
          { "value": null, "color": "green" },
          { "value": 1, "color": "yellow" },
          { "value": 5, "color": "red" }
        ]
      }
    }
  }
}

5.4 面板三:P99/P50 延迟对比(Time Series)

{
  "title": "请求延迟 P50 / P99 - $service",
  "type": "timeseries",
  "targets": [
    {
      "expr": "histogram_quantile(0.50, sum by (le) (rate(http_request_duration_seconds_bucket{service=\"$service\"}[5m])))",
      "legendFormat": "P50"
    },
    {
      "expr": "histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket{service=\"$service\"}[5m])))",
      "legendFormat": "P99"
    }
  ],
  "fieldConfig": {
    "defaults": {
      "unit": "s",
      "custom": {
        "drawStyle": "line",
        "fillOpacity": 10
      }
    }
  }
}

5.5 面板四:各实例 QPS 明细(Table)

{
  "title": "实例 QPS 明细",
  "type": "table",
  "targets": [
    {
      "expr": "sum by (instance) (rate(http_requests_total{service=\"$service\"}[5m]))",
      "format": "table",
      "instant": true
    }
  ],
  "transformations": [
    {
      "id": "organize",
      "options": {
        "excludeByName": { "Time": true },
        "renameByName": { "Value": "QPS" }
      }
    }
  ]
}

六、JSON 模型导出与版本管理

6.1 导出 JSON

在 Dashboard 页面点击顶部菜单 → Share → Export → Save to file,即可获得完整的 JSON 模型。

6.2 通过 Git 版本管理

将导出的 JSON 文件存入 Git 仓库,实现仪表盘的版本控制:

mkdir -p dashboards/slo
cp exported-dashboard.json dashboards/slo/slo-overview.json
git add dashboards/
git commit -m "feat: add SLO overview dashboard"

6.3 Provisioning 自动加载

Grafana 支持通过文件系统自动加载仪表盘 JSON,无需手动导入:

# /etc/grafana/provisioning/dashboards/dashboards.yaml
apiVersion: 1

providers:
  - name: SRE Dashboards
    orgId: 1
    folder: SRE
    folderUid: sre-folder
    type: file
    disableDeletion: false
    updateIntervalSeconds: 30
    allowUiUpdates: true
    options:
      path: /var/lib/grafana/dashboards

将 JSON 文件放到 /var/lib/grafana/dashboards/ 目录后,Grafana 每 30 秒自动扫描并加载。

6.4 使用 Terraform 管理(推荐生产环境)

# terraform/main.tf
resource "grafana_dashboard" "slo" {
  folder       = grafana_folder.sre.uid
  config_json  = file("${path.module}/dashboards/slo-overview.json")
}

resource "grafana_folder" "sre" {
  title = "SRE"
}
terraform plan
terraform apply

优势:基础设施即代码,仪表盘变更可审计、可回滚,适合多人协作团队。

七、性能优化建议

  1. 减少面板数量:单个仪表盘面板数不超过 12 个,过多会导致浏览器卡顿和 Prometheus 查询风暴。
  2. 使用记录规则:将复杂 PromQL 预计算为指标,仪表盘只查询预计算结果。
  3. 合理设置查询间隔:面板的 Interval 不要低于 30s,减少对后端的压力。
  4. 使用 $__rate_interval:Grafana 内置变量,自动根据面板时间范围和抓取间隔计算合适的 rate 窗口:
# 推荐
rate(http_requests_total[$__rate_interval])

# 而非固定窗口
rate(http_requests_total[5m])
  1. 限制返回序列数:在数据源设置中配置 Max data points,避免返回过多序列导致浏览器崩溃。

总结

Grafana 仪表盘设计的核心不是"把所有指标都堆上去",而是围绕"值班人员能否在 5 秒内判断系统状态"来组织信息。记住三个层次:顶部看状态、中部看趋势、底部看明细。配合变量模板系统实现多环境复用,通过 Provisioning 或 Terraform 实现版本管理,就能构建出专业级运维仪表盘。

更多详情请参阅 Grafana 官方文档

参考资料与致谢

本文在撰写过程中参考了以下资料,感谢原作者的贡献:

  1. Grafana 官方文档 — Grafana,参考了Grafana 官方文档相关内容