运维笔记

Grafana Dashboard 声明式配置实战:从 YAML 翻车到 GitOps 自动化

SRE & Observability 技术可视化

先说结论:Grafana provisioning 官方文档写的像屎一样。

不是我嘴臭。上周我们团队刚把 50 多个生产环境的仪表盘从手动导入迁移到 GitOps 流水线,中间踩的坑能写一本《Grafana 血泪史》。Reddit 上 r/sre 板块有人吐槽“配置 provisioning 本身比写仪表盘还痛苦”,我完全同意。

但一旦跑起来,真香。

为什么非要搞 Provisioning?

手动导入仪表盘?那是 2020 年的玩法了。

  • 环境一致性:开发、预发、生产三个环境,手动导入三次?翻车概率 100%。
  • 版本控制:JSON 文件丢 Git 里,出问题直接 git revert,不用跪着求 DBA 恢复数据库。
  • 多租户隔离:不同 Org 需要不同仪表盘?provisioning 天然支持,不用写脚本硬怼 API。

目录结构:别瞎放

Grafana 默认从 /etc/grafana/provisioning/ 读取配置。子目录结构长这样:

provisioning/
├── dashboards/          # 仪表盘配置
│   ├── main.yaml
│   └── sample.json
├── datasources/         # 数据源配置
│   └── prometheus.yaml
├── notifiers/           # 告警通知(5.0+)
│   └── slack.yaml
├── plugins/             # 插件(7.0+)
└── alerting/            # 告警规则(8.0+)

关键点dashboards/ 目录下放的是 YAML 配置文件,不是直接把 JSON 丢进去。YAML 文件告诉 Grafana 去哪里找 JSON 仪表盘定义。

第一个 YAML 文件怎么写?

这是最简单的配置,从本地文件系统加载仪表盘:

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

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

几个参数解释一下:

  • disableDeletion: true — 防止有人在 UI 上误删,但 Git 里还在,下次同步又回来了,很烦。设成 true 更安全。
  • updateIntervalSeconds: 30 — Grafana 每 30 秒扫描一次目录。调太短浪费 CPU,调太长变更延迟高。
  • allowUiUpdates: false — 禁止 UI 修改。既然用了 GitOps,UI 改完 Git 里没有,下次同步直接覆盖,等于白改。

多数据源配置:别踩这个坑

数据源配置在 datasources/ 目录下:

# /etc/grafana/provisioning/datasources/prometheus.yaml
apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    editable: false
    jsonData:
      timeInterval: 15s
      queryTimeout: 60s

坑来了isDefault: true 只能给一个数据源。如果多个数据源都设成 true,Grafana 会随机选一个当默认。我们预发环境翻车就是这原因——Prometheus 和 Thanos 都设了默认,仪表盘有的连 Thanos 有的连 Prometheus,数据对不上,排查了俩小时。

多 Org 配置:官方文档跳着写的部分

Reddit 上有人问“怎么给不同 Org 配不同仪表盘”,官方文档的回答简直就是“你自己看着办”。

实际做法是这样:

# provisioning/dashboards/org1.yaml
apiVersion: 1

providers:
  - name: 'Org1 Dashboards'
    orgId: 1
    folder: 'Team Alpha'
    type: file
    options:
      path: /var/lib/grafana/dashboards/org1

---

# provisioning/dashboards/org2.yaml
apiVersion: 1

providers:
  - name: 'Org2 Dashboards'
    orgId: 2
    folder: 'Team Beta'
    type: file
    options:
      path: /var/lib/grafana/dashboards/org2

注意目录路径要分开,否则 Org1 的仪表盘会出现在 Org2 的列表里,数据源不对应,全是 N/A

JSON 仪表盘模板化:变量注入

直接硬编码 JSON 是低端玩法。用 Go 模板语法注入变量:

{
  "title": "{{ .Name }} - Overview",
  "panels": [
    {
      "title": "CPU Usage",
      "targets": [
        {
          "expr": "avg(rate(node_cpu_seconds_total{mode!=\"idle\", instance=\"{{ .Instance }}\"}[5m]))"
        }
      ]
    }
  ]
}

然后在 YAML 里传变量:

providers:
  - name: 'Template Dashboards'
    orgId: 1
    type: file
    options:
      path: /var/lib/grafana/dashboards
    jsonData:
      Name: "Web Server"
      Instance: "web-01:9100"

这个功能 7.0+ 才有,老版本别想了。

和 Docker Compose 集成

我们生产环境用 Docker Compose 跑,配置挂载方式:

version: '3.8'

services:
  grafana:
    image: grafana/grafana:10.4.2
    volumes:
      - ./provisioning:/etc/grafana/provisioning
      - ./dashboards:/var/lib/grafana/dashboards
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin

启动后 Grafana 自动加载,不用手动操作。Reddit 上有人问“怎么在 Docker 里自动加载仪表盘”,答案就是挂载 provisioning 目录。

常见翻车现场

问题症状解决方案
仪表盘重复出现同一个仪表盘出现多次检查 uid 是否唯一,JSON 里必须有唯一 uid
数据源显示 N/A面板显示无数据检查 orgId 是否匹配,不同 Org 的数据源不通用
修改后不生效改完 JSON 没变化updateIntervalSeconds 设太长,或者 allowUiUpdates: false 没生效
权限错误无法读取文件检查文件权限,Grafana 容器默认用 grafana 用户(uid 472)
模板变量不渲染{{ .Name }} 原样显示检查 Grafana 版本是否 >= 7.0,YAML 里 jsonData 配置是否正确

FAQ

问:Provisioning 和 Grafana API 有什么区别?

Provisioning 是声明式配置,Grafana 启动时自动加载,适合 GitOps 流程。API 是命令式操作,适合动态场景。线上环境建议 provisioning 为主,API 为辅。

问:能同时用 provisioning 和 UI 管理仪表盘吗?

可以,但不建议。allowUiUpdates: true 允许 UI 修改,但 provisioning 下次同步会覆盖。最佳实践是只用 provisioning,UI 只做只读查看。

问:Provisioning 支持哪些数据源?

所有官方数据源都支持:Prometheus、InfluxDB、Elasticsearch、MySQL、PostgreSQL、CloudWatch、Azure Monitor 等。第三方插件需要额外配置。

问:怎么调试 provisioning 不生效?

查看 Grafana 日志:docker logs grafana | grep -i provisioning。常见问题是文件路径错误、权限不足、YAML 格式错误。用 yamllint 检查语法。

问:Provisioning 支持告警规则吗?

8.0+ 支持。在 alerting/ 目录下放 YAML 文件,配置告警规则和通知策略。语法和仪表盘类似,但更复杂,建议从官方示例开始。

社区灵感与参考 (References & Community Insights)

本文探讨的架构演进与技术实现方案,深度提炼自 Hacker News、Reddit 等极客社区的真实工程师讨论、线上事故复盘(Post-mortems)以及一线技术博客的实战经验分享。

Elvin Hui

关于作者:Elvin Hui

Elvin 拥有 10+ 年企业级数据中心、云原生架构和网络安全经验。持有 CCNA、AWS 解决方案架构师认证。我致力于将一线的“踩坑”经验沉淀为真实、硬核的技术指南,拒绝空洞理论。