開發與維運

Grabana:使用 Golang 或 Yaml 生成 Grafana Dashboard

前言

在之前的一篇文章《如何使 Grafana as code》中介紹了使用 Jsonnet 實現 Grafana as code,通過代碼來批量、動態、可複用的生成 Grafana Dashboard。但畢竟 Jsonnet 是一門小眾的編程語言,可用文檔不多且示例較少,那麼有沒有使用我們熟悉的編程語言來生成 Grafana Dashboard 的辦法呢?答案是肯定的,本篇文章就介紹一款用於生成 Grafana Dashboard 的 Golang 庫:Grabana

Grabana

Grabana 提供了一種面向開發人員友好的創建 Grafana Dashboard 的方式,也就是俗稱的 Grafana as code。

不止於此,Grabana 還支持使用 yaml 文件來生成 Dashboard。並且完全不需要像 Jsonnet 那樣先生成 json 配置,再將配置導入 Grafana,而是直接基於寫好的代碼或者 yaml 文件,通過封裝好的 Grafana API 直接將 Dashboard 發佈到指定 Grafana 中,省去了繁瑣的操作,實現了完全的自動化。

Dashboard as code

使用 Golang 可以通過如下方式構建 Dashboard 配置:

builder := dashboard.New(
    "Awesome dashboard",
    dashboard.AutoRefresh("5s"),
    dashboard.Tags([]string{"generated"}),
    dashboard.VariableAsInterval(
        "interval",
        interval.Values([]string{"30s", "1m", "5m", "10m", "30m", "1h", "6h", "12h"}),
    ),
    dashboard.Row(
        "Prometheus",
        row.WithGraph(
            "HTTP Rate",
            graph.DataSource("prometheus-default"),
            graph.WithPrometheusTarget(
                "rate(prometheus_http_requests_total[30s])",
                prometheus.Legend("{{handler}} - {{ code }}"),
            ),
        ),
    ),
)

配置構建好之後,只需調用 Grabana 的 client,傳入 Grafana 的地址,以及事先創建的 Grafana API Key 即可一鍵發佈 Dashboard。

API Key 的創建方法 Configuration - API Keys - Add API Keys 如下圖:

新建 API Key

獲得 API Key

創建好 API Key 之後,將其填入 grabana.WithAPIToken() 中即可,創建/更新 Dashboard 代碼如下:

ctx := context.Background()
client := grabana.NewClient(&http.Client{}, grafanaHost, grabana.WithAPIToken("such secret, much wow"))

// create the folder holding the dashboard for the service
folder, err := client.FindOrCreateFolder(ctx, "Test Folder")
if err != nil {
    fmt.Printf("Could not find or create folder: %s\n", err)
    os.Exit(1)
}

if _, err := client.UpsertDashboard(ctx, folder, builder); err != nil {
    fmt.Printf("Could not create dashboard: %s\n", err)
    os.Exit(1)
}

當然官方還提供了一個比較完整的 example,直接使用 go run main.go 即可體驗一鍵創建 Dashboard。

Dashboard as YAML

Grabana 的特別之處還在於他還提供了使用 yaml 創建 Dashboard 的方式,作為一名資深 yaml 工程師,每當看到 yaml 都會感到格外的親切。

同樣的 Dashboard ,yaml 配置如下:

# dashboard.yaml
title: Awesome dashboard

editable: true
tags: [generated]
auto_refresh: 5s

variables:
  - interval:
      name: interval
      label: Interval
      values: ["30s", "1m", "5m", "10m", "30m", "1h", "6h", "12h"]

rows:
  - name: Prometheus
    panels:
      - graph:
          title: HTTP Rate
          height: 400px
          datasource: prometheus-default
          targets:
            - prometheus:
                query: "rate(promhttp_metric_handler_requests_total[$interval])"
                legend: "{{handler}} - {{ code }}"

目前官方還沒有提供類似 grabana apply -f dashboard.yaml 這樣的 CLI 命令來發布 Dashboard,還是要使用 Golang 代碼才能將其發佈,代碼如下:

content, err := ioutil.ReadFile("dashboard.yaml")
if err != nil {
    fmt.Fprintf(os.Stderr, "Could not read file: %s\n", err)
    os.Exit(1)
}

dashboard, err := decoder.UnmarshalYAML(bytes.NewBuffer(content))
if err != nil {
    fmt.Fprintf(os.Stderr, "Could not parse file: %s\n", err)
    os.Exit(1)
}

ctx := context.Background()
client := grabana.NewClient(&http.Client{}, grafanaHost, grabana.WithAPIToken("such secret, much wow"))

// create the folder holding the dashboard for the service
folder, err := client.FindOrCreateFolder(ctx, "Test Folder")
if err != nil {
    fmt.Printf("Could not find or create folder: %s\n", err)
    os.Exit(1)
}

if _, err := client.UpsertDashboard(ctx, folder, dashboard); err != nil {
    fmt.Printf("Could not create dashboard: %s\n", err)
    os.Exit(1)
}

同樣也可以找到比較完整的 example,這些示例都可以在官方 GitHub 倉庫中找到,有興趣的同學可以看一下。

結語

總的來說,這是一個挺有意思的項目,使用 Golang 代碼或 yaml 文件來生成 Grafana Dashboard,方便易用不繁瑣。美中不足的是,使用 yaml 生成 Dashboard 並沒有完全脫離 Golang 代碼。就像筆者上文中提到的,其實可以將項目包裝成一個 CLI 工具,使用類似 grabana apply -f dashboard.yaml 的方式來發布 yaml 配置可能會更好,並且實現起來也並不困難:)。

生成 Grafana Dashboard 其實還有很多其他語言的實現方式,比如使用 Python 實現的 grafanalib,與 Grabana 相比 grafanalib 的來頭更大,貢獻者和 star 數也更多,有興趣的朋友可以關注一下,這裡就不展開詳細介紹了。

Leave a Reply

Your email address will not be published. Required fields are marked *