Grafana Grafana

Terraform で Grafana を管理する

HCL から、フォルダー、データソース、ダッシュボード、アラート、チーム、サービスアカウントを作成します。

15 min learn.level.中級

ファイルによるプロビジョニングは、データソース、ダッシュボード、アラートをカバーしますが、ユーザー、チーム、サービスアカウント、権限はカバーしません。Terraform はそのすべてをカバーし、API を通じて、実行中の任意の Grafana(セルフホストまたは Grafana Cloud)に対して動作し、ドリフトを検出して修正できるよう、状態ファイルを保持します。このガイドでは、provider をセットアップし、一般的なリソースを管理します。

前提条件と provider

Terraform または OpenTofu、到達可能な Grafana、宣言するものを管理できるロールを持つサービスアカウントのトークン(作成方法は認証のガイドを参照)が必要です。トークンをコードの外に保ちます。

terraform {
  required_providers {
    grafana = {
      source  = "grafana/grafana"
      version = ">= 2.9.0"   # テスト済みのバージョンに固定する
    }
  }
}

variable "grafana_url" {
  type    = string
  default = "http://localhost:3000"
}

variable "grafana_token" {
  type      = string
  sensitive = true
}

provider "grafana" {
  url  = var.grafana_url
  auth = var.grafana_token
}

トークンが決してファイルに残らないよう、環境経由で渡します。

export TF_VAR_grafana_token='glsa_...'
terraform init

Grafana Cloud では、同じ設定内でスタックとそのサービスアカウントを作成できます。Grafana のドキュメントには、2 つの provider を使うパターンが示されています。Cloud のアクセスポリシートークンで認証してスタック(grafana_cloud_stack)とサービスアカウント(grafana_cloud_stack_service_account とそのトークン)を作成する cloud の provider と、そのトークンを使う、スタックを対象とするエイリアス付きの 2 つ目の provider です。

フォルダーとデータソース

resource "grafana_folder" "apps" {
  title = "Applications"
}

resource "grafana_data_source" "prometheus" {
  type       = "prometheus"
  name       = "Prometheus"
  uid        = "prometheus"
  url        = "http://prometheus:9090"
  is_default = true

  json_data_encoded = jsonencode({
    timeInterval = "15s"
  })
}

resource "grafana_data_source" "postgres" {
  type = "postgres"
  name = "Postgres"
  uid  = "postgres-main"
  url  = "postgres.internal:5432"
  username = "grafana"

  json_data_encoded = jsonencode({
    database = "app"
    sslmode  = "require"
  })

  secure_json_data_encoded = jsonencode({
    password = var.postgres_password
  })
}

uid を明示的に設定すると、ダッシュボードとアラートルールが環境間でポータブルに保たれます。secure_json_data_encoded を通じて渡されたシークレットは、Grafana によって暗号化されて保存されますが、Terraform の状態にも書き込まれるため、状態は、暗号化され、アクセスが制御されたバックエンドに保存してください。

ダッシュボード

resource "grafana_dashboard" "webapp" {
  folder      = grafana_folder.apps.uid
  config_json = file("${path.module}/dashboards/webapp.json")
  overwrite   = true
}

ディレクトリ全体を読み込むには、fileset を使います。

resource "grafana_dashboard" "all" {
  for_each    = fileset("${path.module}/dashboards", "*.json")
  folder      = grafana_folder.apps.uid
  config_json = file("${path.module}/dashboards/${each.value}")
}

ダッシュボードの JSON は、ファイルによるプロビジョニングで使うものと同じファイルなので、2 つのアプローチの間を行き来できます。ダッシュボードをフォルダーに関連付けるとき、古い provider のバージョンはフォルダーの id を参照し、新しいものは uid を好みます。固定するバージョンの provider のドキュメントを確認してください。

アラート

Terraform には、アラートの各部分に対応するリソースがあります。

resource "grafana_contact_point" "slack" {
  name = "team-slack"

  slack {
    url       = var.slack_webhook_url
    recipient = "#alerts"
  }
}

resource "grafana_notification_policy" "default" {
  group_by      = ["alertname", "namespace"]
  contact_point = grafana_contact_point.slack.name

  group_wait      = "30s"
  group_interval  = "5m"
  repeat_interval = "4h"
}

ルールグループは、rule ブロックを持つ grafana_rule_group を使い、その data のエントリは、プロビジョニングの YAML(クエリ、reduce、しきい値)を反映します。ミュートタイミングとテンプレートには、grafana_mute_timing と grafana_message_template があります。アラートのリソースは provider のバージョン間で改訂されてきたため、正確な属性名は、provider のドキュメントで確認してください。

grafana_notification_policy は、ファイルによるプロビジョニングと同様に、ポリシーのツリー全体を管理するため、1 つのツールだけがそれを所有するようにしてください。

チーム、サービスアカウント、権限

これらは、ファイルでは表現できないリソースです。

resource "grafana_team" "platform" {
  name  = "platform"
  email = "platform@example.com"
}

resource "grafana_service_account" "ci" {
  name = "ci-dashboards"
  role = "Editor"
}

resource "grafana_service_account_token" "ci" {
  name               = "ci-token"
  service_account_id = grafana_service_account.ci.id
}

resource "grafana_folder_permission" "apps" {
  folder_uid = grafana_folder.apps.uid

  permissions {
    team_id    = grafana_team.platform.id
    permission = "Edit"
  }
}

生成されたトークンは、シークレットな出力として扱い、出力するのではなく、シークレットマネージャーに送ります。

ワークフロー

terraform fmt -check
terraform validate
terraform plan -out tfplan
terraform apply tfplan

CI では、プルリクエストで plan を、マージ時に apply を実行します。plan は、どのダッシュボード、データソース、アラートが変更されるかを正確に示します。

すでに存在するものを取り込む

誰かが UI で作成したものがある場合は、再作成する代わりに、状態にインポートします。

terraform import grafana_folder.apps <folder-uid>
terraform import grafana_dashboard.webapp <dashboard-uid>
terraform import grafana_data_source.prometheus <data-source-uid>

新しいバージョンの Terraform では、import ブロックも使えます。インポートの ID の形式は、provider のバージョン間で異なる場合があるため、リソースのドキュメントで確認してください。

ファイルによるプロビジョニングと並行して Terraform を使う

同じリソースを両方で管理しないでください。境界を選び、それを守ってください。一般的な分け方は、データソースとダッシュボードにはファイルによるプロビジョニング(起動時に読み込まれ、API トークン不要)、チーム、サービスアカウント、権限には Terraform です。もう 1 つは、すべてを Terraform で行い、プロビジョニングのファイルはサーバー設定に限定するものです。

人気のガイド