Автоматизация выделения дашбордов с помощью CI/CD

Ручное управление дашбордами Grafana может быть неэффективным и подверженным ошибкам. С Grafana Foundation SDK вы можете определять дашборды с помощью строго типизированного кода, фиксировать их в системах контроля версий и автоматически развёртывать с помощью GitHub Actions.

Зачем автоматизировать?

Автоматизация развёртывания дашбордов Grafana устраняет необходимость в ручном создании и обновлении дашбордов, обеспечивая их согласованность между средами.

Определяя дашборды как код и управляя ими через CI/CD, например GitHub Actions, вы получаете полный контроль версий, что позволяет легко отслеживать изменения со временем и при необходимости откатываться. Это также предотвращает дублирование, поскольку рабочий процесс разумно проверяет, существует ли дашборд, прежде чем решить, создавать его или обновлять.

Благодаря этому полностью автоматизированному конвейеру CI/CD вы можете сосредоточиться на улучшении своих дашбордов вместо ручной загрузки JSON-файлов в Grafana.

Полный исходный код примера можно найти в репозитории Introduction to the Foundation SDK на GitHub.

Обзор

В этом руководстве показано, как:

  1. Сгенерировать дашборд Grafana как код и отформатировать его для развёртывания в стиле Kubernetes
  2. Использовать GitHub Actions для развёртывания, проверки и обновления дашборда

В итоге вы сможете выделять свой дашборд как код, и каждое изменение вашего дашборда будет автоматически создаваться или обновляться в вашем экземпляре Grafana без ручного вмешательства.

1. Генерация JSON дашборда

Прежде чем развёртывать дашборд, определите его как код с помощью Grafana Foundation SDK.

Поскольку Grafana предоставляет API, совместимый с ресурсами Kubernetes, вам нужно внести некоторые изменения в код, чтобы вывести JSON дашборда в подходящем формате.

Этот скрипт:

  • Генерирует JSON-файл дашборда Grafana
  • Оборачивает его в формат API в стиле Kubernetes (apiVersion, kind, metadata, spec)
  • Сохраняет его как dashboard.json для развёртывания
Go
package main

import (
	"encoding/json"
	"log"
	"os"

	"github.com/grafana/grafana-foundation-sdk/go/cog"
	"github.com/grafana/grafana-foundation-sdk/go/common"
	"github.com/grafana/grafana-foundation-sdk/go/dashboard"
)

type DashboardWrapper struct {
	APIVersion string              `json:"apiVersion"`
	Kind       string              `json:"kind"`
	Metadata   Metadata            `json:"metadata"`
	Spec       dashboard.Dashboard `json:"spec"`
}

type Metadata struct {
	Name string `json:"name"`
}

func main() {
	builder := dashboard.NewDashboardBuilder("My Dashboard").
		Uid("my-dashboard").
		Tags([]string{"generated", "foundation-sdk", "go"}).
		Refresh("5m").
		Time("now-1h", "now").
		Timezone(common.TimeZoneBrowser).
		WithRow(dashboard.NewRowBuilder("Overview"))

	dashboard, err := builder.Build()
	if err != nil {
		log.Fatalf("failed to build dashboard: %v", err)
	}

	dashboardWrapper := DashboardWrapper{
		APIVersion: "dashboard.grafana.app/v1",
		Kind:       "Dashboard",
		Metadata: Metadata{
			Name: *dashboard.Uid,
		},
		Spec: dashboard,
	}

	dashboardJson, err := json.MarshalIndent(dashboardWrapper, "", "  ")
	if err != nil {
		log.Fatalf("failed to marshal dashboard: %v", err)
	}

	err = os.WriteFile("dashboard.json", dashboardJson, 0644)
	if err != nil {
		log.Fatalf("failed to write dashboard to file: %v", err)
	}

	log.Printf("Dashboard JSON:\n%s", dashboardJson)
}
typescript
import { DashboardBuilder, RowBuilder } from '@grafana/grafana-foundation-sdk/dashboard';
import * as fs from 'fs';

// Generate the dashboard JSON
const dashboard = new DashboardBuilder('My Dashboard')
  .uid('my-dashboard')
  .tags(['generated', 'foundation-sdk', 'typescript'])
  .refresh('5m')
  .time({ from: 'now-1h', to: 'now' })
  .timezone('browser')
  .withRow(new RowBuilder('Overview'))
  .build();

// Convert to Kubernetes-style format
const dashboardWrapper = {
  apiVersion: "dashboard.grafana.app/v1",
  kind: "Dashboard",
  metadata: {
    name: dashboard.uid!
  },
  spec: dashboard
};

// Save the formatted JSON to a file
const dashboardJSON = JSON.stringify(dashboardWrapper, null, 2);
fs.writeFileSync('dashboard.json', dashboardJSON, 'utf8');

console.log(`Dashboard JSON:\n${}`);

2. Автоматизация развёртывания с помощью GitHub Actions

Далее настройте GitHub Actions для автоматизации развёртывания дашборда Grafana с помощью Foundation SDK и инструмента командной строки gcx, чтобы:

  • Извлечь имя дашборда из dashboard.json
  • Проверить, существует ли дашборд в вашем экземпляре Grafana
  • Обновить его, если он существует, или создать, если его нет

Note

Следующая конфигурация GitHub Action предполагает, что вы используете генератор дашбордов на Go. Если вы используете один из других языков, поддерживаемых Foundation SDK, измените шаг Generate Dashboard JSON соответствующим образом.

Рабочий процесс развёртывания .github/workflows/deploy-dashboard.yml выглядит так:

YAML
name: Deploy Grafana Dashboard

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: 1.24.6

      - name: Verify Go version
        run: go version

      - name: Download and Extract gcx
        run: |
          curl -L -o gcx-x86_64.tar.gz "https://github.com/grafana/gcx/releases/download/${{ vars.GCX_VERSION }}/gcx_Linux_x86_64.tar.gz"
          tar -xzf gcx-x86_64.tar.gz
          chmod +x gcx
          sudo mv gcx /usr/local/bin/gcx

      - name: Generate Dashboard JSON
        working-directory: ./github-actions-example
        run: go run main.go

      - name: Deploy Dashboard with gcx
        env:
          GRAFANA_SERVER: ${{ vars.GRAFANA_SERVER }}
          GRAFANA_STACK_ID: ${{ vars.GRAFANA_STACK_ID }}
          GRAFANA_TOKEN: ${{ secrets.GRAFANA_TOKEN }}
        run: |
          if [ -f dashboard.json ]; then
            echo "dashboard.json exists, deploying dashboard."
            gcx resources push dashboards --path ./dashboard.json
          else
            echo "dashboard.json does not exist."
            exit 1
          fi
        working-directory: ./github-actions-example

1. Извлечение кода и настройка Go

Чтобы настроить Go:

  • Извлеките репозиторий, чтобы получить доступ к коду проекта.
  • Установите Go 1.24.6 с помощью действия actions/setup-go.
  • Убедитесь, что Go установлен правильно.

2. Загрузка и установка gcx

Далее загрузите CLI gcx с GitHub, используя версию, указанную в vars.GCX_VERSION. Он распаковывает архив, делает его исполняемым и перемещает в место в системном PATH.

3. Генерация JSON дашборда

Далее запустите генератор дашбордов (main.go) из каталога ./github-actions-example, чтобы создать файл dashboard.json, содержащий определение дашборда Grafana.

4. Развёртывание дашборда с помощью gcx

Если dashboard.json уже существует, он развёртывается в вашем экземпляре Grafana с помощью:

Bash
gcx resources push dashboards --path ./dashboard.json

Эта команда проходит аутентификацию в Grafana, используя следующие переменные среды:

  • GRAFANA_SERVER: URL вашего экземпляра Grafana
  • GRAFANA_STACK_ID: ID вашего стека Grafana
  • GRAFANA_TOKEN: токен сервисной учётной записи Grafana с достаточными правами

Используемые переменные и секреты GitHub

Убедитесь, что эти переменные настроены в вашем репозитории в разделе Settings > Security > Secrets and variables > Actions:

  • vars.GCX_VERSION: версия gcx для установки
  • vars.GRAFANA_SERVER: URL вашего экземпляра Grafana
  • vars.GRAFANA_STACK_ID: ID стека в Grafana
  • secrets.GRAFANA_TOKEN: токен API Grafana

Это действие гарантирует, что каждый push в main будет заново генерировать и развёртывать последнее определение вашего дашборда в Grafana.