はじめに
近年、システム運用においてオブザーバビリティ(可観測性)の重要性がますます高まっています。複雑化するシステムの状態を把握し、迅速に問題を特定・解決するためには、効果的なモニタリングツールが不可欠です。NewRelicは、このオブザーバビリティを実現する強力なプラットフォームとして広く認知されており、システム全体の状態をリアルタイムで可視化し、迅速なトラブルシューティングやパフォーマンス改善を可能にします。
しかし、NewRelicのダッシュボードを手動で作成・管理することは、特に複数環境(開発、ステージング、本番など)を扱う場合、以下のような大きな課題をもたらします:
- 作業の非効率性: 環境ごとに同じ設定を繰り返す手間が発生
- 設定ミスのリスク: 人為的エラーによる環境間の設定の不一致
- 再利用性の欠如: 既存ダッシュボードの他環境への展開が困難
これらの課題を解決するために、「Dashboard as Code」というアプローチが注目されています。Terraformを活用してNewRelicのダッシュボードをコード化することで、次のようなメリットが得られます:
- 作業の効率化: コードによる一貫した設定の複数環境への適用
- エラー防止: 手動作業の排除による設定の再現性確保
- 拡張性の向上: 新規環境への迅速な展開
本記事では、Terraformを使用して開発環境でダッシュボードを調整し、その設定をステージング環境へ効率的に展開する手法を詳しく解説します。この方法により、オブザーバビリティの向上と運用作業の自動化を同時に実現できます。
システム構成
全体アーキテクチャ
以下に 全体アーキテクチャ構成図 を示します。このアーキテクチャは大きく 2つの層 に分かれています:
インフラストラクチャ層
この層では、AWS上にマルチ環境(DEV/STG) を構築します。各環境は以下の主要コンポーネントで構成されています:
- ECSクラスタ:コンテナ化されたアプリケーションの実行環境。
- VPCネットワーク:パブリックサブネットおよびプライベートサブネットによるネットワーク構成。
- AWS CodePipeline:CI/CDパイプラインの自動化を実現。
- AWS CodeBuild:アプリケーションのビルド環境。
- Amazon CloudWatch:ログ管理および監視のための基盤。
これらの環境は Terraform を用いてコードベースで定義され、完全に自動化された方法 で構築・管理されます。これにより、環境ごとの設定の一貫性と再現性が確保されます。
ダッシュボード層
インフラストラクチャ層で構築された基盤環境の情報を、Terraform show を活用して取得します(例:ECS、ALB、RDSのリソース情報)。その取得した情報を基に、Terraform を用いて NewRelicダッシュボード を自動生成します。
Terraformを使用することで、DEV環境で調整したダッシュボード設定を、STG環境 にそのまま展開できるため、作業の効率化と設定ミスの削減が実現されます。
マルチ環境ダッシュボード自動作成フロー
- ダッシュボードに必要な情報の整理
- 取得するリソースのメトリクスやログ、監視対象の項目を整理します(例:ECSのCPU・メモリ使用率、ALBのリクエスト数、RDSの接続数など)。
- サンプル環境(Dev環境)でのダッシュボード作成
- 手作業で Dev環境 のダッシュボードを作成し、必要なグラフやメトリクスを調整・整理します。
- Terraform Dashboardモジュールの作成
- Dev環境で作成したダッシュボードを基に、Terraformを用いて再利用可能な Dashboardモジュール を作成します。
- 全環境へのダッシュボード自動展開
- インフラストラクチャ層で構築された基盤情報(ECS、ALB、RDSなど)を Terraform show で取得し、その情報を利用して、DEV環境、STG環境 など複数環境のダッシュボードを自動生成します。
terraformディレクトリ構成
Terraformを使用したNewRelicダッシュボードの自動生成プロジェクトは、以下のような構造で組織化されています。
newrelic-dashboards/
├── environments/
│ ├── dev.tfvars # Dev環境用の変数定義ファイル
│ ├── stg.tfvars # STG環境用の変数定義ファイル
│
└── modules/
├── dashboard/ # ダッシュボードを管理するモジュール
│ ├── main.tf # ダッシュボードリソースの定義
│ ├── outputs.tf # ダッシュボードの出力定義
│ └── variables.tf # モジュールの変数定義
│
├── main.tf # 基盤情報取得のモジュール定義
├── outputs.tf # 環境情報の出力定義
└── variables.tf # 環境全体の変数定義
この構造により、環境ごとの設定を分離しつつ、ダッシュボード生成ロジックを再利用可能なモジュールとして実装することができます。terraform apply -var-file=environments/dev.tfvarsのようなコマンドで、特定の環境用のダッシュボードを生成することが可能となります。
実装手順
事前準備
- AWSアカウント:申請済みであり、IAMユーザーが払い出されていること。
- GitHub環境:アクセス可能な環境を準備しておくこと。
- NewRelicアカウント:登録済みであること。
- NewRelicでのAWSインテグレーション設定:
- Amazon CloudWatch Metric StreamsまたはAPIポーリングの2つの方法があります。筆者は推奨されている Amazon CloudWatch Metric Streams を利用しています。
- 設定方法については、下記の記事をご参考ください。
AWSマルチ基盤環境構築
- GitHubで公開されているサンプルコードを利用します。
-
Terraformコードを修正してマルチ環境対応にします。
- 元のコードはマルチ環境向けに設計されていないため、リポジトリを2つのフォルダ(
dev-aws-ecs-cicd-terraform-master
とstg-aws-ecs-cicd-terraform-master
)にコピーします。 -
/dev-aws-ecs-cicd-terraform-master/terraform/terraform.tfvars
を以下のように修正します:aws_region="ap-northeast-1" stack="dev" fargate-task-service-role="dev-task-role" fargate-task-execution-role="dev-execution-role" aws_ecr="dev-petclinic" aws_profile="default" source_repo_name="dev-petclinic" source_repo_branch="master" image_repo_name="dev-petclinic" family="dev-family"
-
/stg-aws-ecs-cicd-terraform-master/terraform/terraform.tfvars
を以下のように修正します:aws_region="ap-northeast-1" stack="stg" fargate-task-service-role="stg-task-role" fargate-task-execution-role="stg-execution-role" aws_ecr="stg-petclinic" aws_profile="default" source_repo_name="stg-petclinic" source_repo_branch="master" image_repo_name="stg-petclinic" family="stg-family"
- 元のコードはマルチ環境向けに設計されていないため、リポジトリを2つのフォルダ(
-
それぞれのフォルダに移動し、
README.md
に記載されている手順に従い環境を構築します。
開発環境(Dev環境)でのダッシュボード設定
ダッシュボードのグラフ整理
グラフ名(リソース名) | 内容 | 目的 |
---|---|---|
Total Request Count (${var.alb_arn}) | ALBを通じたリクエスト数(AZごと)を表示 | トラフィック状況と負荷分散の状態を把握する |
HTTP Status Codes (${var.alb_arn}) | HTTPステータスコード(2XX、3XX、4XX、5XX)ごとの数を表示 | エラーレスポンスの発生状況を監視する |
ECS-CPU utilization (%) (${var.ecs_cluster_name}) | ECSクラスタのCPU使用率(平均、最小、最大、合計)を表示 | コンテナのCPUリソース使用状況を監視する |
ECS-Memory utilization (%) (${var.ecs_cluster_name}) | ECSクラスタのメモリ使用率(平均、最小、最大、合計)を表示 | メモリ不足やリソース過剰利用を確認する |
Database connections (${var.rds_instance_identifier}) | RDSデータベースへの接続数を表示 | データベースの負荷状況を把握する |
DB Throughput (bytes/s) (${var.rds_instance_identifier}) | RDSデータベースの読み取り・書き込みスループット(bytes/s)を表示 | データベースのパフォーマンスを監視する |
DB CPU utilization (${var.rds_instance_identifier}) | RDSデータベースのCPU使用率(平均、最小、最大)を表示 | データベース処理能力のボトルネックを監視する |
CodeBuild Failed Builds (${var.codebuild_project_name}) | CodeBuildプロジェクトのビルド失敗数を時間帯ごとに表示 | CI/CDパイプラインの問題を特定する |
実際のプロジェクトでは、APM(Application Performance Monitoring) 指標や ECSタスク数、アプリケーションのレスポンス時間 など、さらに重要なメトリクスを追加する必要があります。ここでは指標の網羅性については議論しません。
Dev環境用サンプルダッシュボード作成
-
手作業でサンプル用ダッシュボードを作成します。
-
NewRelicが提供するグラフを活用し、0からクエリを作成する工数を削減できます。
例:DB CPU使用率のグラフ -
ダッシュボードのグラフを編集して、リソース名を特定します。
- ダッシュボード(sample)に移動し、グラフの右上 [...] をクリックして [Edit] を選択します。
- クエリ内のDBインスタンスを特定する部分(
WHERE
句)をAWSリソース名に変更します。-
変更前:
SELECT min(`aws.rds.CPUUtilization`), max(`aws.rds.CPUUtilization`), average(`aws.rds.CPUUtilization`) FROM Metric WHERE (entity.guid = 'NjI2NTg2M3xJTkZSQXxOQXwxMDIzMTI3ODkzMzM2ODE4NzU4') TIMESERIES AUTO
-
変更後:
SELECT min(`aws.rds.CPUUtilization`), max(`aws.rds.CPUUtilization`), average(`aws.rds.CPUUtilization`) FROM Metric WHERE aws.rds.DBInstanceIdentifier = 'dev-petclinic' TIMESERIES AUTO
-
変更前:
-
変更理由:
- 今後Terraform化する際に、リソース名(
aws.rds.DBInstanceIdentifier
) は比較的に変換しやすいため、管理が容易になります。
- 今後Terraform化する際に、リソース名(
- ダッシュボード(sample)に移動し、グラフの右上 [...] をクリックして [Edit] を選択します。
-
サンプルダッシュボードのJSON情報を取得
-
Terraformでダッシュボードをモジュール化
- 以下のパスに
main.tf
を作成します:
/newrelic-dashboards/modules/dashboard/main.tf
- ダッシュボード定義をTerraformコードとして記述します。
terraform { required_providers { newrelic = { source = "newrelic/newrelic" version = "~> 3.0" } } } resource "newrelic_one_dashboard" "infrastructure_dashboard" { name = "${var.environment} Infrastructure Dashboard" permissions = "public_read_only" page { name = "Load Balancer Metrics" widget_line { title = "Total Request Count (${var.alb_arn})" row = 1 column = 1 width = 3 height = 3 nrql_query { query = "SELECT sum(`aws.applicationelb.RequestCount.byAlb`) as 'Requests with AZ' FROM Metric WHERE `aws.applicationelb.LoadBalancer` ='${var.alb_arn}' AND aws.applicationelb.AvailabilityZone IS NOT NULL TIMESERIES auto SINCE 30 minutes ago UNTIL now" } } widget_line { title = "HTTP Status Codes (${var.alb_arn})" row = 1 column = 4 width = 3 height = 3 nrql_query { query = "FROM Metric SELECT sum(aws.applicationelb.HTTPCode_Target_2XX_Count) as '2XX', sum(aws.applicationelb.HTTPCode_Target_3XX_Count) as '3XX', sum(aws.applicationelb.HTTPCode_Target_4XX_Count) as '4XX', sum(aws.applicationelb.HTTPCode_Target_5XX_Count) as '5XX' WHERE aws.applicationelb.LoadBalancer='${var.alb_arn}' SINCE 30 minutes ago TIMESERIES" } } widget_line { title = "ECS-CPU utilization (%) (${var.ecs_cluster_name})" row = 1 column = 7 width = 3 height = 3 nrql_query { query = "SELECT average(`aws.ecs.CPUUtilization.byService`), min(`aws.ecs.CPUUtilization.byService`), max(`aws.ecs.CPUUtilization.byService`), sum(`aws.ecs.CPUUtilization.byService`) FROM Metric WHERE (aws.ecs.ClusterName='${var.ecs_cluster_name}') TIMESERIES AUTO" } } widget_line { title = "ECS-Memory utilization (%) (${var.ecs_cluster_name})" row = 1 column = 10 width = 3 height = 3 nrql_query { query = "SELECT average(`aws.ecs.MemoryUtilization.byService`), min(`aws.ecs.MemoryUtilization.byService`), max(`aws.ecs.MemoryUtilization.byService`), sum(`aws.ecs.MemoryUtilization.byService`) FROM Metric WHERE (aws.ecs.ClusterName='${var.ecs_cluster_name}') TIMESERIES AUTO" } } widget_line { title = "Database connections (${var.rds_instance_identifier})" row = 4 column = 1 width = 3 height = 3 nrql_query { query = "SELECT average(`aws.rds.DatabaseConnections`) as 'Connections' FROM Metric WHERE aws.rds.DBInstanceIdentifier = '${var.rds_instance_identifier}' TIMESERIES auto" } } widget_line { title = "DB Throughput (bytes/s) (${var.rds_instance_identifier})" row = 4 column = 4 width = 3 height = 3 nrql_query { query = "SELECT average(`aws.rds.ReadThroughput`) as 'Read', average(`aws.rds.WriteThroughput`) as 'Write' FROM Metric WHERE aws.rds.DBInstanceIdentifier = '${var.rds_instance_identifier}' TIMESERIES" } } widget_line { title = "DB CPU utilization (${var.rds_instance_identifier})" row = 4 column = 7 width = 3 height = 3 nrql_query { query = "SELECT min(`aws.rds.CPUUtilization`), max(`aws.rds.CPUUtilization`), average(`aws.rds.CPUUtilization`) FROM Metric WHERE aws.rds.DBInstanceIdentifier = '${var.rds_instance_identifier}' TIMESERIES AUTO" } } widget_line { title = "CodeBuild Failed Builds (${var.codebuild_project_name})" row = 4 column = 10 width = 3 height = 3 nrql_query { query = "SELECT average(`aws.codebuild.FailedBuilds`) FROM Metric FACET `aws.codebuild.ProjectName` WHERE `aws.codebuild.ProjectName` = '${var.codebuild_project_name}' SINCE 6 HOURS AGO TIMESERIES" } } } }
- 以下のパスに
-
環境変数ファイル(tfvars)を作成
-
newrelic-dashboards/environments/dev.tfvars
に環境情報を設定します:
environment = "dev" alb_arn = "app/dev-alb/52e8a82b796e3001" ecs_cluster_name = "dev-Cluster" rds_instance_identifier = "dev-petclinic" codebuild_project_name = "codebuild-dev-petclinic-master"
補足:
上記のリソース情報は、インフラ基盤環境構築後にterraform show
を実行することで取得可能です。 -
-
Terraformを実行してダッシュボードをデプロイ
- 以下のコマンドでDEV環境のダッシュボードをデプロイします:
terraform apply -var-file="environments/dev.tfvars"
本番ステージング環境(STG環境)への展開
今回の検証では、STG環境のダッシュボードはDEV環境と同じ内容 に設定します。そのため、STG環境への展開は非常に簡単です。
もしカスタマイズが必要な場合は、TerraformコードでSTG専用のカスタマイズを追加すれば対応可能です。
ただし、今回は マルチ環境への適用効率 を検証する目的で、カスタマイズ部分は割愛しています。
-
環境変数ファイル(tfvars)の作成
以下の環境情報を設定し、newrelic-dashboards/environments/stg.tfvars
を作成します:environment = "stg" alb_arn = "app/stg-alb/45a2fb743b4efc17" ecs_cluster_name = "stg-Cluster" rds_instance_identifier = "stg-petclinic" codebuild_project_name = "codebuild-stg-petclinic-master"
-
Terraformを実行してダッシュボードをデプロイ
以下のコマンドを実行して、STG環境のダッシュボードをデプロイします:terraform apply -var-file="environments/stg.tfvars"
-
デプロイされたダッシュボードの確認
デプロイ後のダッシュボードは以下の通りです:- ダッシュボード名やリソース名は STG環境 のものに置き換わっています。
実践のメリットと課題
実践のメリット
-
多環境への効率的な展開
- Terraformを活用することで、DEV環境で作成した設定をそのままSTGや本番環境に適用可能となり、作業の手間を大幅に削減できます。
- 再利用可能なモジュール化により、新たな環境の追加も容易です。
-
設定ミスの削減
- ダッシュボードをコード化することで、手動設定によるエラーや環境間の設定不一致を防ぐことができます。
-
将来の拡張性
- コードベースで管理することで、将来的にダッシュボード設定をCI/CDパイプラインに組み込み、自動化された運用が可能となります。
課題と注意点
-
コードの複雑性
- 多環境への適用時にカスタマイズが必要な場合、Terraformコードが複雑化する可能性があります。
- 特に、環境ごとに異なるリソース(例:メトリクスやカスタムグラフ)の柔軟な対応には、追加のスクリプトやモジュール設計が必要です。
-
キー管理のリスク
- NewRelicのAPIキーやアカウント情報が流出しないよう、十分な管理が必要です。
- Secret Manager や 環境変数 を活用し、直接コードに書き込まない運用が推奨されます。
-
運用面での注意点
- 環境変数やリソース情報の管理方法を標準化する必要があります。
- ダッシュボード設定の変更管理プロセス(例:コードレビューやテスト)の確立が求められます。
まとめ
本記事では、Terraformを活用したNewRelic Dashboardの自動生成について詳しく解説しました。Dashboard as Codeというアプローチを採用することで、複数環境でのダッシュボード管理を効率化し、設定品質を向上できることを実証しました。
このアプローチを導入することで、システム監視の効率化 と オブザーバビリティの向上 を同時に達成することが可能となります。特に、多環境を管理する運用チームにとって、本手法は非常に有効なツールとなるでしょう。
今後の展望
CI/CDパイプラインとの統合
- GitHub Actions や Jenkins を活用したダッシュボードの自動デプロイ基盤の構築
- プルリクエストベースのレビュー を導入し、コード変更の透明性を確保
- 継続的デリバリーによる、迅速かつ安全なデプロイの実現
自動化の更なる可能性
- アラート設定の自動化 により、異常検知の効率化および精度向上
- 再利用可能なカスタムダッシュボードテンプレート の整備
- チーム間で共有可能な、汎用性の高いモジュールの開発と展開
このように、TerraformとNewRelicを組み合わせることで、運用効率を大幅に向上させ、システムの安定性を高めることが期待できます。