3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

New Relic 使ってみた情報をシェアしよう! by New RelicAdvent Calendar 2024

Day 14

Terraformで実現するNewRelic Dashboard as Code 〜マルチ環境対応の自動化実践〜

Last updated at Posted at 2024-12-14

はじめに

近年、システム運用においてオブザーバビリティ(可観測性)の重要性がますます高まっています。複雑化するシステムの状態を把握し、迅速に問題を特定・解決するためには、効果的なモニタリングツールが不可欠です。NewRelicは、このオブザーバビリティを実現する強力なプラットフォームとして広く認知されており、システム全体の状態をリアルタイムで可視化し、迅速なトラブルシューティングやパフォーマンス改善を可能にします。
しかし、NewRelicのダッシュボードを手動で作成・管理することは、特に複数環境(開発、ステージング、本番など)を扱う場合、以下のような大きな課題をもたらします:

  • 作業の非効率性: 環境ごとに同じ設定を繰り返す手間が発生
  • 設定ミスのリスク: 人為的エラーによる環境間の設定の不一致
  • 再利用性の欠如: 既存ダッシュボードの他環境への展開が困難

これらの課題を解決するために、「Dashboard as Code」というアプローチが注目されています。Terraformを活用してNewRelicのダッシュボードをコード化することで、次のようなメリットが得られます:

  • 作業の効率化: コードによる一貫した設定の複数環境への適用
  • エラー防止: 手動作業の排除による設定の再現性確保
  • 拡張性の向上: 新規環境への迅速な展開

本記事では、Terraformを使用して開発環境でダッシュボードを調整し、その設定をステージング環境へ効率的に展開する手法を詳しく解説します。この方法により、オブザーバビリティの向上と運用作業の自動化を同時に実現できます。

システム構成

全体アーキテクチャ

以下に 全体アーキテクチャ構成図 を示します。このアーキテクチャは大きく 2つの層 に分かれています:
架构图.png

インフラストラクチャ層

この層では、AWS上にマルチ環境(DEV/STG) を構築します。各環境は以下の主要コンポーネントで構成されています:

  • ECSクラスタ:コンテナ化されたアプリケーションの実行環境。
  • VPCネットワーク:パブリックサブネットおよびプライベートサブネットによるネットワーク構成。
  • AWS CodePipeline:CI/CDパイプラインの自動化を実現。
  • AWS CodeBuild:アプリケーションのビルド環境。
  • Amazon CloudWatch:ログ管理および監視のための基盤。
    これらの環境は Terraform を用いてコードベースで定義され、完全に自動化された方法 で構築・管理されます。これにより、環境ごとの設定の一貫性と再現性が確保されます。

ダッシュボード層

インフラストラクチャ層で構築された基盤環境の情報を、Terraform show を活用して取得します(例:ECS、ALB、RDSのリソース情報)。その取得した情報を基に、Terraform を用いて NewRelicダッシュボード を自動生成します。
Terraformを使用することで、DEV環境で調整したダッシュボード設定を、STG環境 にそのまま展開できるため、作業の効率化と設定ミスの削減が実現されます。

マルチ環境ダッシュボード自動作成フロー

  1. ダッシュボードに必要な情報の整理
    • 取得するリソースのメトリクスやログ、監視対象の項目を整理します(例:ECSのCPU・メモリ使用率、ALBのリクエスト数、RDSの接続数など)。
  2. サンプル環境(Dev環境)でのダッシュボード作成
    • 手作業で Dev環境 のダッシュボードを作成し、必要なグラフやメトリクスを調整・整理します。
  3. Terraform Dashboardモジュールの作成
    • Dev環境で作成したダッシュボードを基に、Terraformを用いて再利用可能な Dashboardモジュール を作成します。
  4. 全環境へのダッシュボード自動展開
    • インフラストラクチャ層で構築された基盤情報(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-masterstg-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"
      
  • それぞれのフォルダに移動し、README.md に記載されている手順に従い環境を構築します。

注意点
Terraformコードで環境構築後、ALBのセキュリティグループはデフォルトで 0.0.0.0/0 が許可されています。このままではセキュリティリスクがあるため、必ずIP(例:自宅のIP)を制限してください。
image.png

開発環境(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環境用サンプルダッシュボード作成

  • 手作業でサンプル用ダッシュボードを作成します。

    1. [Dashboards] -> [Create a dashboard] -> [Create a new dashboard]
      image.png
  • NewRelicが提供するグラフを活用し、0からクエリを作成する工数を削減できます。
    例:DB CPU使用率のグラフ

    1. [Amazon Web Services] もしくは [ALL Entities] から対象のDBインスタンスを選択します。
    2. Metrics から CPU utilization のグラフを探し、右上の [...] をクリックして [Add to Dashboard] を選択します。
      • 既存のダッシュボード(例:sample)を選択し、グラフをコピーします。
        image.png
  • ダッシュボードのグラフを編集して、リソース名を特定します。

    1. ダッシュボード(sample)に移動し、グラフの右上 [...] をクリックして [Edit] を選択します。
      image.png
    2. クエリ内の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
        
    3. 変更理由
      • 今後Terraform化する際に、リソース名(aws.rds.DBInstanceIdentifier は比較的に変換しやすいため、管理が容易になります。
  • サンプルダッシュボードのJSON情報を取得

    • NewRelicで作成したサンプルダッシュボードのJSON情報を取得し、Terraformファイル作成の参考にします。
      image.png
  • 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"
    
  • デプロイされたダッシュボードの確認
    デプロイ後のダッシュボードは以下の通りです:
    image.png

本番ステージング環境(STG環境)への展開

今回の検証では、STG環境のダッシュボードはDEV環境と同じ内容 に設定します。そのため、STG環境への展開は非常に簡単です。
もしカスタマイズが必要な場合は、TerraformコードでSTG専用のカスタマイズを追加すれば対応可能です。
ただし、今回は マルチ環境への適用効率 を検証する目的で、カスタマイズ部分は割愛しています。

  1. 環境変数ファイル(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"
    
  2. Terraformを実行してダッシュボードをデプロイ
    以下のコマンドを実行して、STG環境のダッシュボードをデプロイします:

    terraform apply -var-file="environments/stg.tfvars"
    
  3. デプロイされたダッシュボードの確認
    デプロイ後のダッシュボードは以下の通りです:

    • ダッシュボード名やリソース名は STG環境 のものに置き換わっています。

    image.png

実践のメリットと課題

実践のメリット

  1. 多環境への効率的な展開

    • Terraformを活用することで、DEV環境で作成した設定をそのままSTGや本番環境に適用可能となり、作業の手間を大幅に削減できます。
    • 再利用可能なモジュール化により、新たな環境の追加も容易です。
  2. 設定ミスの削減

    • ダッシュボードをコード化することで、手動設定によるエラーや環境間の設定不一致を防ぐことができます。
  3. 将来の拡張性

    • コードベースで管理することで、将来的にダッシュボード設定をCI/CDパイプラインに組み込み、自動化された運用が可能となります。

課題と注意点

  1. コードの複雑性

    • 多環境への適用時にカスタマイズが必要な場合、Terraformコードが複雑化する可能性があります。
    • 特に、環境ごとに異なるリソース(例:メトリクスやカスタムグラフ)の柔軟な対応には、追加のスクリプトやモジュール設計が必要です。
  2. キー管理のリスク

    • NewRelicのAPIキーやアカウント情報が流出しないよう、十分な管理が必要です。
    • Secret Manager環境変数 を活用し、直接コードに書き込まない運用が推奨されます。
  3. 運用面での注意点

    • 環境変数やリソース情報の管理方法を標準化する必要があります。
    • ダッシュボード設定の変更管理プロセス(例:コードレビューやテスト)の確立が求められます。

まとめ

本記事では、Terraformを活用したNewRelic Dashboardの自動生成について詳しく解説しました。Dashboard as Codeというアプローチを採用することで、複数環境でのダッシュボード管理を効率化し、設定品質を向上できることを実証しました。

このアプローチを導入することで、システム監視の効率化オブザーバビリティの向上 を同時に達成することが可能となります。特に、多環境を管理する運用チームにとって、本手法は非常に有効なツールとなるでしょう。

今後の展望

CI/CDパイプラインとの統合

  • GitHub ActionsJenkins を活用したダッシュボードの自動デプロイ基盤の構築
  • プルリクエストベースのレビュー を導入し、コード変更の透明性を確保
  • 継続的デリバリーによる、迅速かつ安全なデプロイの実現

自動化の更なる可能性

  • アラート設定の自動化 により、異常検知の効率化および精度向上
  • 再利用可能なカスタムダッシュボードテンプレート の整備
  • チーム間で共有可能な、汎用性の高いモジュールの開発と展開

このように、TerraformとNewRelicを組み合わせることで、運用効率を大幅に向上させ、システムの安定性を高めることが期待できます。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?