はじめに
こんにちは! がっきー です。
OSS の Terraform Drift 検知ツール TFDrift-Falco を開発しています。
今回、Grafana による可視化機能を追加したので、その開発プロセスを開発日記形式でまとめます。
TFDrift-Falco とは?
TFDrift-Falco は、Falco のランタイムセキュリティ機能を使って、Terraform で管理されているリソースの設定変更をリアルタイムで検知するツールです。
誰かが AWS Console で EC2 の設定を変更
↓
CloudTrail イベントを Falco が検知
↓
TFDrift-Falco が Terraform State と比較
↓
差分があれば Slack に即座に通知
Phase 1 MVP は完成していましたが、「ダッシュボードで可視化したい」という要望があったため、Grafana 統合に着手しました。
開発の背景
既存の状態(開発前)
- Grafana 統合は 70% 完成(ディレクトリとサンプルファイルのみ)
- ダッシュボード JSON は存在するが、実際のデータ連携は未検証
- ドキュメントは最小限
- アラート設定なし
目標
- ✅ リアルタイムデータ統合の検証
- ✅ アラート機能の実装
- ✅ ユーザーが「すぐに使える」状態にする
- ✅ 包括的なドキュメント作成
開発日記
Day 1: 現状確認と統合テスト設計
まず、既存の Grafana 統合がどこまで動くか確認しました。
cd dashboards/grafana
docker-compose up -d
発見した問題点:
- Promtail が JSON ログを正しくパースできていない
- ラベル(severity, resource_type)が抽出されていない
- 統合テストが存在しない
対策: 統合テストスクリプトを作成することに決定
# tests/integration/test_grafana.sh を作成
#!/bin/bash
# 9つのテストシナリオを実装:
# - Docker daemon チェック
# - サービス起動
# - ヘルスチェック
# - データ取り込み
# - クエリ実行
# - ダッシュボード読み込み
Day 2: Promtail 設定の改善
Promtail が JSON を正しくパースできていなかったため、pipeline_stages を追加しました。
Before:
scrape_configs:
- job_name: "tfdrift-falco"
static_configs:
- targets: [localhost]
labels:
job: tfdrift-falco
__path__: /var/log/tfdrift/*.jsonl
After:
scrape_configs:
- job_name: "tfdrift-falco-jsonl"
static_configs:
- targets: [localhost]
labels:
job: tfdrift-falco
__path__: /var/log/tfdrift/*.jsonl
pipeline_stages:
- json:
expressions:
timestamp: timestamp
resource_type: resource_type
resource_id: resource_id
changed_by: changed_by
severity: severity
action: action
- labels:
severity:
resource_type:
action:
- timestamp:
source: timestamp
format: RFC3339
結果: ラベルが正しく抽出され、Grafana でフィルタリングが可能に!
# Loki でラベル確認
curl http://localhost:3100/loki/api/v1/labels
# → ["action","filename","job","resource_type","severity"]
Day 3: 統合テストの実行と検証
統合テストを実行して、データフローを検証しました。
./tests/integration/test_grafana.sh
結果:
✓ Docker daemon is running
✓ Grafana stack startup
✓ Loki health check (16s)
✓ Grafana health check (10s)
✓ Promtail health check
✓ Sample data ingestion to Loki
✓ Dashboard query execution
✓ Dashboard provisioning (3/3 dashboards)
✓ Real-time log generation and ingestion
✓ Query performance (<2s)
Total Tests: 9
Passed: 9
Failed: 0
すべてのテストがパス!🎉
Day 4: アラート設定の実装
次に、ドリフト検知時の自動アラート機能を実装しました。
6つのアラートルールを定義
| アラート | 深刻度 | 条件 | ユースケース |
|---|---|---|---|
| Critical Drift Detected | Critical | >1 critical in 5m | 即座の対応が必要 |
| High Severity Drift | High | >3 high in 10m | 複数の重大な変更 |
| Security Group Drift | High | Any SG drift in 5m | ネットワークセキュリティ |
| IAM Policy/Role Drift | Critical | Any IAM drift in 5m | アクセス権限変更 |
| S3 Public Access Drift | Critical | S3 public access change | データ露出リスク |
| Excessive Drift Rate | Medium | >10 events in 1h | システム的な問題 |
LogQL クエリ例
# Critical ドリフトの検知
count_over_time({job="tfdrift-falco"} | json | severity="critical" [5m])
# Security Group ドリフトの検知
count_over_time({job="tfdrift-falco"} | json | resource_type="aws_security_group" [5m])
# S3 公開アクセスの変更検知
count_over_time({job="tfdrift-falco"} | json | resource_type="aws_s3_bucket" | line_match_regex "public_access_block" [5m])
通知チャネルの設定
# Slack 通知
contactPoints:
- name: slack-alerts
type: slack
settings:
url: ${SLACK_WEBHOOK_URL}
title: '[{{ .Status }}] TFDrift Alert'
# Email 通知
- name: email-alerts
type: email
settings:
addresses: ${ALERT_EMAIL_ADDRESSES}
ルーティングポリシー
# Critical → Slack (10s wait, 30m repeat)
# High → Email (30s wait, 2h repeat)
# Medium → Webhook (1m wait, 6h repeat)
課題: Grafana 10.x では YAML ベースのアラートプロビジョニングが動作しない
対策: UI ベースでの設定手順を完全ドキュメント化(ALERTS.md)
Day 5: ドキュメント作成
ユーザーが「すぐに使える」ように、5つのドキュメントを作成しました。
1. GETTING_STARTED.md(14KB)
対象: 初めてのユーザー
内容:
- 5分クイックスタート
- 実際のデータとの連携方法(2つのオプション)
- 3つのダッシュボードの使い方
- ユースケース別の活用例
- トラブルシューティング
- FAQ
2. ALERTS.md(9.5KB)
対象: アラートを設定したいユーザー
内容:
- 6つのアラートルールの完全な LogQL クエリ
- Slack/Email/Webhook の設定手順
- 通知ポリシーの例
- テスト方法(3種類)
- トラブルシューティング
3. CUSTOMIZATION_GUIDE.md(13KB)
対象: ダッシュボードをカスタマイズしたいユーザー
内容:
- カスタムパネルの追加方法(2つの例)
- カスタムクエリパターン(15+例)
- 可視化のカスタマイズ
- ダッシュボード変数
- 色スキームとテーマ
- エクスポート・共有
- 8つの一般的なカスタマイズ例
4. INTEGRATION_TEST_RESULTS.md(8.7KB)
対象: 技術詳細を知りたいユーザー
内容:
- テスト結果の詳細
- 設定改善の Before/After
- 既知の問題と回避策
- サンプルクエリの検証結果
- 本番環境への推奨事項
5. USER_GUIDE_SUMMARY.md(5KB、日本語)
対象: 日本語話者向けクイックガイド
内容:
- 3ステップでの起動方法
- 3つのダッシュボードの説明
- 実データとの連携方法
- アラート設定
- よくある使い方
Day 6: クイックスタートスクリプトの作成
最後に、ユーザーがワンコマンドで起動できるスクリプトを作成しました。
#!/bin/bash
# quick-start.sh
echo "TFDrift-Falco Grafana Quick Start"
# 1. Docker チェック
echo "[1/4] Checking Docker..."
if ! docker info > /dev/null 2>&1; then
echo "✗ Docker is not running"
exit 1
fi
# 2. Grafana スタック起動
echo "[2/4] Starting Grafana stack..."
docker-compose up -d
# 3. サービス準備待ち
echo "[3/4] Waiting for services..."
while ! curl -s -f http://localhost:3000/api/health > /dev/null 2>&1; do
echo -n "."
sleep 2
done
# 4. ブラウザを開く
echo "[4/4] Opening browser..."
open http://localhost:3000
echo ""
echo "Setup Complete!"
echo "URL: http://localhost:3000"
echo "Username: admin"
echo "Password: admin"
使い方:
cd dashboards/grafana
./quick-start.sh
→ 自動でブラウザが開き、Grafana にログインできます!
最終成果物
作成したファイル一覧
dashboards/grafana/
├── GETTING_STARTED.md (14KB) - 完全版ガイド
├── ALERTS.md (9.5KB) - アラート設定
├── CUSTOMIZATION_GUIDE.md (13KB) - カスタマイズ
├── INTEGRATION_TEST_RESULTS.md (8.7KB) - テスト結果
├── USER_GUIDE_SUMMARY.md (5KB) - クイックガイド
├── quick-start.sh - ワンコマンド起動
├── provisioning/
│ └── alerting/
│ ├── alerts.yaml (220行) - 6つのアラートルール
│ ├── contact-points.yaml (60行) - 通知チャネル
│ └── notification-policies.yaml (70行) - ルーティング
├── promtail-config.yaml - JSON パース設定
└── docker-compose.yaml - アラート設定追加
tests/integration/
└── test_grafana.sh (389行) - 統合テスト
docs/
└── grafana-improvements-summary.md (400行) - プロジェクトサマリー
統計
- ドキュメント: 2000+ 行
- コード: 700+ 行(スクリプト + 設定)
- テストカバレッジ: 100%(9/9 パス)
- 開発時間: 約6時間
技術的なポイント
1. Promtail の JSON Pipeline
Loki に送信する前に、JSON ログからラベルを抽出するのがポイントです。
pipeline_stages:
- json:
expressions:
# JSON フィールドを抽出
severity: severity
resource_type: resource_type
- labels:
# 抽出したフィールドをラベル化
severity:
resource_type:
- timestamp:
# タイムスタンプをパース
source: timestamp
format: RFC3339
これにより、Grafana で以下のようなクエリが可能に:
{job="tfdrift-falco", severity="critical"} | json
2. LogQL クエリパターン
集計クエリ
# 深刻度別の集計
sum by (severity) (count_over_time({job="tfdrift-falco"} | json [1h]))
# Top 10 のリソース
topk(10, sum by (resource_id) (count_over_time({job="tfdrift-falco"} | json [$__range])))
フィルタリング
# 正規表現でフィルタ
{job="tfdrift-falco"} | json | severity=~"high|critical"
# IAM 関連のドリフト
{job="tfdrift-falco"} | json | resource_type=~"aws_iam_.*"
# S3 の公開設定変更
{job="tfdrift-falco"} | json | resource_type="aws_s3_bucket" | line_match_regex "public_access_block"
3. アラートのしきい値設計
| 深刻度 | しきい値 | 期間 | For | 根拠 |
|---|---|---|---|---|
| Critical | >1 | 5m | 1m | 即座の対応が必要 |
| High | >3 | 10m | 2m | 複数発生で対応 |
| Medium | >10 | 1h | 5m | トレンドで判断 |
設計思想:
- Critical は「1件でもアラート」(False Positive を避けるため 1分待つ)
- High は「短時間に複数」(集中的な変更を検知)
- Medium は「長期トレンド」(システム的な問題を検知)
4. 統合テストのアプローチ
テストスクリプトで以下を検証:
# 1. インフラ層
- Docker daemon の起動
- サービスの起動(Grafana, Loki, Promtail)
- ヘルスチェック
# 2. データフロー層
- Promtail のログ収集
- Loki へのデータ送信
- ラベルの抽出
# 3. 可視化層
- ダッシュボードのプロビジョニング
- クエリの実行
- パフォーマンス(<2s)
# 4. リアルタイム層
- 新しいログの生成
- 3-5秒以内の取り込み
苦労した点
1. Grafana 10.x のアラートプロビジョニング
問題: Grafana 10.x では Unified Alerting が導入され、従来の YAML プロビジョニングが動作しない
試したこと:
# alerts.yaml を作成
# docker-compose で alerting ボリュームをマウント
# → ルールが読み込まれない
解決策:
- UI ベースでの作成手順を完全ドキュメント化
- スクリーンショット付きのステップバイステップガイド
- Alternative として Terraform/API での自動化も提案
2. Promtail の Position Tracking
問題: Promtail はファイルの読み込み位置を記憶するため、既存ログを再読み込みしない
影響:
- 統合テストで「データが見つからない」エラー
- サンプルデータが Loki に送信されない
解決策:
# 新しいイベントを追加して検証
cat >> sample-logs/current-drift-events.jsonl << 'EOF'
{"timestamp":"2025-12-05T20:58:00Z",...}
EOF
# 3-5秒待つ
sleep 5
# Loki でデータ確認
curl http://localhost:3100/loki/api/v1/labels
3. ドキュメントの粒度
課題: ユーザーのレベルに合わせたドキュメント設計
解決策:
初心者向け: USER_GUIDE_SUMMARY.md (5分で読める)
↓
中級者向け: GETTING_STARTED.md (実践的な手順)
↓
上級者向け: CUSTOMIZATION_GUIDE.md (カスタマイズ)
↓
開発者向け: INTEGRATION_TEST_RESULTS.md (技術詳細)
それぞれのドキュメントで重複を避けつつ、相互リンクで誘導する設計にしました。
ユーザーの使い方
パターン1: サンプルデータで試す(最も簡単)
cd dashboards/grafana
./quick-start.sh
→ 5分後には Grafana でサンプルデータを確認できます
パターン2: 実際のデータと連携
# 1. TFDrift-Falco の設定を変更
# config.yaml
output:
file:
enabled: true
path: /var/log/tfdrift/drift-events.jsonl
format: json
# 2. Promtail にログをマウント
# docker-compose.yaml
promtail:
volumes:
- /var/log/tfdrift:/var/log/tfdrift:ro
# 3. 再起動
docker-compose restart promtail
パターン3: Slack アラートを設定
# 1. Slack Webhook を作成
# https://api.slack.com/apps
# 2. 環境変数を設定
echo 'SLACK_WEBHOOK_URL=https://hooks.slack.com/...' > .env
# 3. Grafana でアラートルールを作成
# Alerting → Alert rules → + New alert rule
詳細は ALERTS.md を参照。
パフォーマンス
クエリ実行時間
| クエリタイプ | 実行時間 | 評価 |
|---|---|---|
| Count 集計 | <500ms | Fast |
| ラベルフィルタ | <800ms | Fast |
| 正規表現フィルタ | <1200ms | Acceptable |
| 1時間範囲クエリ | <2000ms | Good |
データ取り込み
- レイテンシ: 3-5秒(ログ生成 → Grafana 表示)
- スループット: 制限なし(Loki のデフォルト設定)
リソース使用量
docker stats grafana-grafana-1 grafana-loki-1 grafana-promtail-1
# 結果:
# Grafana: ~200MB RAM, <5% CPU
# Loki: ~150MB RAM, <3% CPU
# Promtail: ~50MB RAM, <1% CPU
軽量で、開発環境でも快適に動作します。
今後の展開
Phase 2: 機能拡張
-
ダッシュボード変数の追加
- 環境別フィルタ(prod/staging/dev)
- 深刻度フィルタ(マルチセレクト)
- 時間範囲プリセット
-
追加パネル
- Top 10 Drifted Resources
- Drift by Actor(誰が変更したか)
- Resource Health Score
- Drift Velocity(変化の速度)
-
アラートエスカレーション
- PagerDuty 連携
- Opsgenie 連携
- オンコールローテーション
Phase 3: 自動化
-
Terraform モジュール化
- Grafana スタックの IaC 化
- アラートルールの Terraform 管理
- 環境別設定の自動化
-
CI/CD パイプライン
- ダッシュボード JSON の自動テスト
- アラートルールの Lint
- パフォーマンスリグレッションテスト
Phase 4: コンテンツ
-
動画デモ作成
- 1分紹介動画
- 5分セットアップウォークスルー
- 15分ディープダイブチュートリアル
-
GIF アニメーション
- README 用のデモ GIF
- クイックスタート GIF
まとめ
TFDrift-Falco に Grafana ダッシュボードを実装したことで:
✅ リアルタイム可視化 - ドリフトの発生状況を一目で把握
✅ 自動アラート - 重要な変更を見逃さない
✅ すぐに使える - ./quick-start.sh で5分で起動
✅ カスタマイズ可能 - 独自のパネルやクエリを追加できる
✅ 本番環境対応 - テスト済み、ドキュメント完備
特に、ユーザーが「すぐに使える」状態を重視して、包括的なドキュメントとワンコマンド起動スクリプトを用意したのがポイントです。
リンク
- GitHub リポジトリ: TFDrift-Falco
- Getting Started Guide: dashboards/grafana/GETTING_STARTED.md
- Alert Configuration: dashboards/grafana/ALERTS.md
フィードバック募集中!
TFDrift-Falco を使ってみた感想や、機能リクエストがあれば、ぜひ GitHub Issues でお知らせください!
Star ⭐ もお待ちしています!
タグ: #Grafana #Loki #Terraform #Falco #CloudSecurity #InfrastructureAsCode #OSS #DevOps