Terraform で Grafana を管理する
HCL から、フォルダー、データソース、ダッシュボード、アラート、チーム、サービスアカウントを作成します。
ファイルによるプロビジョニングは、データソース、ダッシュボード、アラートをカバーしますが、ユーザー、チーム、サービスアカウント、権限はカバーしません。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 で行い、プロビジョニングのファイルはサーバー設定に限定するものです。
人気のガイド
-
Grafana をコードとして運用する:全体像 ファイルに置けるもの、API が必要なもの、そしてそれらがどう組み合わさるか。 -
grafana.ini と環境変数で Grafana サーバーを設定する 設定ファイルの場所、上書きの順序、設定を Git に保つ方法を理解します。 -
JSON ファイルからダッシュボードをプロビジョニングする ディスクからダッシュボードを読み込み、ディレクトリからフォルダーを対応付け、変更を失わずに作成します。 -
アラートルール、コンタクトポイント、通知ポリシーをプロビジョニングする アラートのパイプライン全体を YAML で定義し、コードのようにレビューしてバージョン管理します。 -
Helm とプロビジョニングの values で Grafana を Kubernetes にデプロイする データソース、ダッシュボード、設定のすべてが values ファイルから来る、Helm チャートで Grafana を実行します。 -
コマンドラインからプロビジョニングを再読み込み、確認、トラブルシューティングする Grafana が何を読み込んだかを調べ、再起動せずに再読み込みし、よくあるプロビジョニングのエラーを修正します。