GitHub ActionsでNew Relic監視設定を自動展開する - Terraform実装ガイド

記事タイトルとURLをコピーする

みなさんこんにちは。マネージドサービス課の塩野です。

この記事では、GitHub ActionsとTerraformを使ってNew Relic監視設定を自動展開する方法を解説します。Pull Requestでterraform planの結果を確認し、マージ後に自動的にterraform applyが実行される仕組みを構築します。

実装後は、アラート設定やダッシュボードの変更をコードで管理でき、レビュープロセスを経て安全に本番環境へ展開できるようになります。GitHub ActionsとAWS CodePipelineの比較については前回の記事をご確認ください。

blog.serverworks.co.jp

目次

前提条件

  • GitHubアカウント
  • AWSアカウント(S3バケット用)
  • New Relicアカウント
  • Terraformの基本知識

事前準備

必要なもの

実装を始める前に、以下を準備します。

GitHubリポジトリ

Terraformコードを管理するリポジトリを作成します。既存のリポジトリを使う場合は、専用のディレクトリを作成すると管理しやすいでしょう。

New Relic APIキー

New Relicの管理画面から、User APIキーを取得します。APIキーは「User」タイプを選択し、適切な権限を付与してください。

AWSリソース

tfstate保存用のS3バケットを作成します。バケット名は一意である必要があるため、組織名やプロジェクト名を含めた命名をおすすめします。バージョニングを有効化しておくと、万が一の際にロールバックできます。

ディレクトリ構成

環境ごとにtfstateを分離するため、以下のディレクトリ構成を推奨します。

.
├── .github/
│   └── workflows/
│       ├── terraform-plan.yml
│       └── terraform-apply.yml
├── terraform/
│   ├── environments/
│   │   ├── dev/
│   │   │   ├── backend.tf
│   │   │   └── main.tf
│   │   └── prod/
│   │       ├── backend.tf
│   │       └── main.tf
│   └── modules/
│       ├── main.tf
│       ├── variables.tf
│       └── alerts.tf
└── README.md

environments/配下に環境ごとのディレクトリを作成し、それぞれ独立したbackend設定を持たせます。共通のTerraformコードはmodules/ディレクトリで管理し、各環境から参照する構成です。これにより、dev環境とprod環境でtfstateが分離され、環境間の干渉を防げます。

Terraformコードの準備

Provider設定

共通のTerraformコードをmodules/ディレクトリに配置します。

terraform/modules/main.tf

terraform {
  required_providers {
    newrelic = {
      source  = "newrelic/newrelic"
      version = "~> 3.0"
    }
  }
}

provider "newrelic" {
  account_id = var.newrelic_account_id
  api_key    = var.newrelic_api_key
  region     = "US"
}

Variables設定

terraform/modules/variables.tf

variable "newrelic_account_id" {
  description = "New Relic Account ID"
  type        = string
  sensitive   = true
}

variable "newrelic_api_key" {
  description = "New Relic User API Key"
  type        = string
  sensitive   = true
}

これらの変数は、GitHub ActionsでTF_VAR_プレフィックス付きの環境変数として設定されます。

Backend設定

環境ごとに異なるS3キーを使用してtfstateを分離します。

terraform/environments/dev/backend.tf

terraform {
  backend "s3" {
    bucket  = "your-terraform-state-bucket"
    key     = "newrelic/dev/terraform.tfstate"
    region  = "ap-northeast-1"
    encrypt = true
  }
}

terraform/environments/prod/backend.tf

terraform {
  backend "s3" {
    bucket  = "your-terraform-state-bucket"
    key     = "newrelic/prod/terraform.tfstate"
    region  = "ap-northeast-1"
    encrypt = true
  }
}

環境ごとに異なるkeyを指定することで、tfstateが分離されます。encryptオプションでS3の暗号化を有効にします。本記事ではDynamoDBによるstate lockを省略していますが、本番環境や複数人で実行する環境の場合はDynamoDBによるLockingを実装を検討してください。また、DynamoDBを使わない場合はワークフローファイルに concurrency を追加して同時実行しないようにすることを検討してください。

アラート設定の例

terraform/modules/alerts.tf

resource "newrelic_alert_policy" "example" {
  name = "Example Alert Policy"
}

resource "newrelic_nrql_alert_condition" "high_cpu_usage" {
  policy_id = newrelic_alert_policy.example.id
  name      = "High CPU Usage"
  enabled   = true

  nrql {
    query = "SELECT average(host.cpuPercent) FROM Metric"
  }

  critical {
    operator              = "above"
    threshold             = 80
    threshold_duration    = 300
    threshold_occurrences = "all"
  }
}

この例では、CPU使用率が80%を超えた状態が5分間続いた場合にアラートが発火します。実際の運用では、サーバーの特性に合わせて閾値を調整してください。

環境ごとのエントリーポイント

各環境のディレクトリに、modulesを参照するエントリーポイントを作成します。

terraform/environments/dev/main.tf

module "newrelic" {
  source = "../../modules"

  newrelic_account_id = var.newrelic_account_id
  newrelic_api_key    = var.newrelic_api_key
}

terraform/environments/prod/main.tf

module "newrelic" {
  source = "../../modules"

  newrelic_account_id = var.newrelic_account_id
  newrelic_api_key    = var.newrelic_api_key
}

各環境で同じmoduleを参照しますが、backend設定とSecretが異なるため、tfstateは完全に分離されます。

GitHub Secretsの設定

New Relic APIキーの登録

GitHubリポジトリの「Settings」→「Secrets and variables」→「Actions」から、以下のSecretを登録します。

  • NEWRELIC_API_KEY: New RelicのUser APIキー
  • NEWRELIC_ACCOUNT_ID: New RelicのアカウントID

AWS認証情報の登録

S3バケットへのアクセスには、OIDC(OpenID Connect)を使った認証を推奨します。長期的な認証情報を保存する必要がなく、セキュリティリスクを低減できます。

OIDC設定の手順

1. OIDCプロバイダーの作成

AWSコンソールから「IAM」→「IDプロバイダー」→「プロバイダーを追加」を選択します。

  • プロバイダータイプ: 「OpenID Connect」
  • プロバイダーURL: https://token.actions.githubusercontent.com
  • オーディエンス: sts.amazonaws.com
  • 「プロバイダーを追加」をクリック

2. IAMロールの作成

「IAM」→「ロール」→「ロールを作成」を選択します。

  • 信頼されたエンティティタイプ: 「ウェブアイデンティティ」
  • アイデンティティプロバイダー: 作成したOIDCプロバイダーを選択
  • オーディエンス: sts.amazonaws.com

信頼ポリシーは以下のようになります。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
          "token.actions.githubusercontent.com:sub": "repo:YOUR_GITHUB_USERNAME/YOUR_REPO_NAME:*"
        }
      }
    }
  ]
}

3. IAMロールに権限を付与

以下のカスタムポリシーを作成してアタッチします。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::your-terraform-state-bucket/*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket"
      ],
      "Resource": "arn:aws:s3:::your-terraform-state-bucket"
    }
  ]
}

設定後、以下のSecretを登録します。

  • AWS_ROLE_ARN: AssumeするIAMロールのARN

注意: IAMユーザーのアクセスキーを使う方法もありますが、セキュリティ上の理由からOIDCの利用を強く推奨します。

環境別のSecrets設定

GitHub Environmentsを使えば、環境ごとにSecretを分離できます。「Settings」→「Environments」から、devprodなどの環境を作成し、それぞれにSecretを設定します。本番環境には承認者を設定しておくと、誤った展開を防げます。

GitHub Actions Workflowの作成

terraform planワークフロー

Pull Request作成時にterraform planを実行し、結果をアーティファクトに保存するワークフローです。ベースブランチに応じて環境を自動判定します。

name: Terraform Plan

on:
  pull_request:
    paths:
      - 'terraform/**'

jobs:
  plan:
    runs-on: ubuntu-latest
    environment: ${{ (github.base_ref == 'main' && 'production') || (github.base_ref == 'develop' && 'development') || 'development' }}
    permissions:
      contents: read
      pull-requests: write
      id-token: write

    steps:
      - uses: actions/checkout@v4

      - name: Determine Environment
        id: env
        run: |
          if [ "${{ github.base_ref }}" = "main" ]; then
            echo "env_name=prod" >> $GITHUB_OUTPUT
          elif [ "${{ github.base_ref }}" = "develop" ]; then
            echo "env_name=dev" >> $GITHUB_OUTPUT
          else
            echo "Error: Unsupported base branch ${{ github.base_ref }}"
            exit 1
          fi

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ap-northeast-1

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3

      - name: Terraform Init
        working-directory: ./terraform/environments/${{ steps.env.outputs.env_name }}
        run: terraform init

      - name: Terraform Plan
        working-directory: ./terraform/environments/${{ steps.env.outputs.env_name }}
        env:
          TF_VAR_newrelic_api_key: ${{ secrets.NEWRELIC_API_KEY }}
          TF_VAR_newrelic_account_id: ${{ secrets.NEWRELIC_ACCOUNT_ID }}
        run: |
          terraform plan -no-color | tee plan_output.txt
          
      - name: Mask sensitive information
        working-directory: ./terraform/environments/${{ steps.env.outputs.env_name }}
        run: |
          # New Relic APIキーをマスキング (NRAK-で始まる40文字の英数字)
          sed -i 's/NRAK-[A-Z0-9]\{40\}/***MASKED_API_KEY***/g' plan_output.txt
          # New Relic Account ID をマスキング (account_id = の後の数字のみ)
          sed -i 's/\(account_id[[:space:]]*=[[:space:]]*\)[0-9]\{7,\}/\1***MASKED_ACCOUNT_ID***/g' plan_output.txt
          # AWS ARNをマスキング
          sed -i 's/arn:aws:[a-z0-9:/_-]*\(["[:space:]]\)/***MASKED_ARN***\1/g' plan_output.txt
          
      - name: Upload Plan Output
        uses: actions/upload-artifact@v4
        with:
          name: terraform-plan-${{ steps.env.outputs.env_name }}-${{ github.event.pull_request.number }}
          path: terraform/environments/${{ steps.env.outputs.env_name }}/plan_output.txt

このワークフローは、terraform/ディレクトリ配下のファイルが変更された場合にのみ実行されます。github.base_refを使用してマージ先のブランチを判定し、mainまたはdevelop以外のブランチへのPRの場合はエラーで停止します。mainブランチへのPRの場合はterraform/environments/prod/developブランチへのPRの場合はterraform/environments/dev/ディレクトリで実行されます。これにより、環境ごとに独立したtfstateが使用され、production環境またはdevelopment環境のSecretが適用されます。plan結果は機密情報をマスキングした上でワークフローのアーティファクトとして保存され、「Actions」タブからダウンロードして確認できます。アーティファクト名にはPR番号が含まれるため、同じブランチへの複数PRでも衝突しません。

セキュリティ上の注意: terraform planの出力には、リソース名やタグ値など機密情報が含まれる可能性があります。マスキング処理では、New Relic APIキー、Account ID(account_id =の後の数字のみ)、AWS ARNを対象としています。閾値やポート番号などの数値は保持されます。組織のセキュリティポリシーに応じて追加の対策を検討してください。

terraform applyワークフロー

Pull Requestがマージされた際にterraform applyを実行するワークフローです。ブランチに応じて環境を自動判定します。

name: Terraform Apply

on:
  push:
    branches:
      - main
      - develop
    paths:
      - 'terraform/**'

jobs:
  apply:
    runs-on: ubuntu-latest
    environment: ${{ (github.ref_name == 'main' && 'production') || (github.ref_name == 'develop' && 'development') || 'development' }}
    permissions:
      contents: read
      id-token: write

    steps:
      - uses: actions/checkout@v4

      - name: Determine Environment
        id: env
        run: |
          if [ "${{ github.ref_name }}" = "main" ]; then
            echo "env_name=prod" >> $GITHUB_OUTPUT
          elif [ "${{ github.ref_name }}" = "develop" ]; then
            echo "env_name=dev" >> $GITHUB_OUTPUT
          else
            echo "Error: Unsupported branch ${{ github.ref_name }}"
            exit 1
          fi

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ap-northeast-1

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3

      - name: Terraform Init
        working-directory: ./terraform/environments/${{ steps.env.outputs.env_name }}
        run: terraform init

      - name: Terraform Apply
        working-directory: ./terraform/environments/${{ steps.env.outputs.env_name }}
        env:
          TF_VAR_newrelic_api_key: ${{ secrets.NEWRELIC_API_KEY }}
          TF_VAR_newrelic_account_id: ${{ secrets.NEWRELIC_ACCOUNT_ID }}
        run: |
          terraform apply -auto-approve -no-color | tee apply_output.txt
          
      - name: Mask sensitive information
        working-directory: ./terraform/environments/${{ steps.env.outputs.env_name }}
        run: |
          # New Relic APIキーをマスキング (NRAK-で始まる40文字の英数字)
          sed -i 's/NRAK-[A-Z0-9]\{40\}/***MASKED_API_KEY***/g' apply_output.txt
          # New Relic Account ID をマスキング (account_id = の後の数字のみ)
          sed -i 's/\(account_id[[:space:]]*=[[:space:]]*\)[0-9]\{7,\}/\1***MASKED_ACCOUNT_ID***/g' apply_output.txt
          # AWS ARNをマスキング
          sed -i 's/arn:aws:[a-z0-9:/_-]*\(["[:space:]]\)/***MASKED_ARN***\1/g' apply_output.txt
          
      - name: Upload Apply Output
        uses: actions/upload-artifact@v4
        with:
          name: terraform-apply-${{ steps.env.outputs.env_name }}-${{ github.run_number }}
          path: terraform/environments/${{ steps.env.outputs.env_name }}/apply_output.txt

このワークフローは、mainまたはdevelopブランチへのマージ時に自動的にapplyが実行されます。github.ref_nameを使用してブランチ名を判定し、mainまたはdevelop以外のブランチへのプッシュの場合はエラーで停止します。mainブランチの場合はterraform/environments/prod/developブランチの場合はterraform/environments/dev/ディレクトリで実行されます。これにより、環境ごとに独立したtfstateが使用され、production環境またはdevelopment環境のSecretが適用されます。-auto-approveオプションを付けているため、承認後は確認なしで実行される点に注意してください。アーティファクト名には実行番号が含まれるため、複数回の実行でも衝突しません。

環境別展開の仕組み

上記のワークフローでは、ブランチ戦略に基づいて環境を自動判定する仕組みを採用しています。

ブランチと環境の対応

  • developブランチ → development環境
  • mainブランチ → production環境

動作の流れ

  1. developブランチへのPR作成時:development環境でplan実行
  2. developブランチへのマージ時:development環境でapply実行
  3. mainブランチへのPR作成時:production環境でplan実行
  4. mainブランチへのマージ時:production環境でapply実行(承認者設定がある場合は承認後)

これにより、以下のメリットがあります。

  • 開発環境と本番環境でAPIキーやアカウントIDを分離できる
  • 本番環境には承認者を設定し、誤った展開を防止できる
  • 環境ごとに異なるIAMロールを使用できる
  • ブランチ名で環境が自動判定されるため、ワークフローの管理が簡単

GitHub Environmentsの設定で、production環境に承認者を追加しておくと、mainブランチへのマージ後、指定された承認者が承認するまでterraform applyは実行されません。

動作確認

Pull Request作成

アラート設定を変更するPull Requestを作成します。

  1. ブランチを作成
  2. terraform/modules/alerts.tfを編集
  3. コミット・プッシュ
  4. Pull Request作成(developブランチへ)

数分後、GitHub Actionsが実行されます。plan結果はアーティファクトとして保存されるため、次のステップで確認しましょう。

plan結果の確認

GitHub Actionsの「Actions」タブから該当のワークフロー実行を選択し、「Artifacts」セクションからterraform-planをダウンロードして結果を確認します。追加・変更・削除されるリソースが表示されるため、意図した変更になっているか確認してください。

マージ後のapply実行確認

Pull Requestをマージすると、自動的にterraform applyが実行されます。GitHub Actionsの「Actions」タブから実行状況を確認できます。

New Relicでの設定反映確認

terraform applyが成功したら、New Relicの管理画面で設定が反映されているか確認します。アラートポリシーや条件が正しく作成・更新されていることを確認しましょう。

まとめ

GitHub ActionsとTerraformを使ったNew Relic監視設定の自動展開を実装しました。Pull Requestでplan結果を確認し、マージ後に自動的にapplyが実行される仕組みにより、安全かつ効率的な運用が可能になります。

実装のポイントは、GitHub Secretsでの認証情報管理、S3バックエンドでのtfstate管理、アーティファクトへのplan結果保存です。これによりチーム全体で変更内容を確認しながら、自動化された展開が実現できます。次のステップとして、複数環境への展開、Slack通知の追加、より複雑なアラート設定の管理などに取り組んでみてください。段階的に自動化範囲を広げることで、運用負荷を軽減し、より本質的な監視設計に時間を使えるようになるでしょう。

こちらの記事がどなたかのお役に立てれば幸いです。

宣伝

弊社では、お客様環境のオブザーバビリティを加速するためのNew Relicアカウント開設を含めた伴走型のNew Relic導入支援サービスをご提供しております。 もしご興味をお持ちの方は、こちらのお問合せフォームよりお問合せ頂けましたら幸いでございます。

関連記事

blog.serverworks.co.jp blog.serverworks.co.jp

◆ 塩野 正人
◆ マネージドサービス部 所属
◆ X(Twitter):@shioccii
◆ 過去記事はこちら

前職ではオンプレミスで仮想化基盤の構築や運用に従事。現在は運用部隊でNew Relicを使ってサービス改善に奮闘中。New Relic User Group運営