🧚

GHAとCodeBuildでRDSマイグレーションを実装

2025/01/31に公開

はじめに

ひとつ前の記事でも少し書いたように、2025/1に参加した株式会社ゆめみさんではインフラのタスクを振っていただきました!その一部をアウトプット記事にしても問題ないとのことだったので、今回は振り返りも兼ねてまとめたいと思います。
ちなみに、私はこのインターンをやるまでインフラの構築などはほぼ全く関わったことがなく、terraformがなんなのかもあまりわかっていませんでした😇

タスク概要

  • 手動のGHAをトリガーとして、Codebuildを起動しRDSのマイグレーションを走らせる
    • GHAでinputを指定できるようにする
    • GHAではセルフホステッドランナーを立ててcodebuildを立ち上げるようにする
    • codebuildは各環境で建てる(dev,stg,prod)
    • サブネットは3つ(public, private, db)で、privateにcodebuildを置きdbにアクセスする
    • コンソールではなくterraformで環境構築する

手順

1.タスクを理解する

インフラ初心者なので、まず振られたタスクが何を言っているのかも一発ではわかりません。
そのため、最初は焦らずにわからないことを洗い出し、記事を読みながら知識のインプットを行いました。

①GitHubActionsのセルフホステッドランナーって何

https://docs.github.com/ja/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners
https://qiita.com/h_tyokinuhata/items/7a9297f75d0513572f4a
→GitHubが提供するクラウドのランナーではなく、自分の環境でジョブを実行できる

②セルフホステッドランナーを立ててcodebuildを立ち上げるってどういうこと

https://qiita.com/k-kojima-yumemi/items/573bda88d0fb607b3224
インターン先の社員さんが書いた記事でインターン業務を行えるいい経験

③コンソールではなくterraformで環境構築する

"②”では、コンソールで作業を行っていますがそれをterraformに書いて行うということを理解しました。

2.GHAからCodebuildを起動するところまで確認

最初からterraformで実装するのではなくこの記事のようにCodebuildをコンソールで指定してGHAから立ち上げられるようにしました。
また、VPCの中のCodebuildを立ち上げる必要があったので、記事に書いてあったことに加えてVPCやセキュリティグループの設定をコンソールで行いました。
ここで疎通が確認できたので、次のステップに移ります。

3.必要なIAM RoleとPolicyを洗い出す

2で作ったCodebuildをコンソールでカスタムしたIAM Roleで起動できるかを確認し、最小限の権限を見つけていきます。この際に、IAM UserとIAM Roleの違いや、セキュリティにおいて大事なポイントなども知ることができました。
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html
また、GitHub ActionsとAWSとの認証に必要なIAM Roleなども必要になることがわかりました。

4.Terraform構築

以下が実装したフォルダの構造になっています。各環境に応じてmoduleの環境変数を埋めるようにしました。

terraform/
└── migration/
    ├── enviroment/
    │   ├── dev/
    │   │   ├── main.tf
    │   │   └── variables.tf
    │   ├── stg/(devと同じ)
    │   └── prod/(devと同じ)
    └── modules/
        ├── main.tf
        ├── outputs.tf
        ├── variable.tf
        └── data_resorces.tf

今回は、modules/main.tfをもとに説明したいと思います。
このTerraformファイルは、AWS CodeBuild に関する各種設定を行っています。具体的には、以下のようなリソースを定義しています。

行っていること

  • IAMロールとポリシー
    CodeBuildが適切な権限を持つためのIAMロールとポリシーを設定しています。
  • VPCアクセス設定
    CodeBuildがVPC内のリソースに接続できるようにするためのポリシーを定義しています。
  • Secrets Managerとの統合
    データベース認証情報などの機密情報を取得するためのIAMポリシーを設定しています。
  • CodeBuildのログ設定
    CloudWatch Logsにビルドログを出力する設定を行っています。
  • CodeBuildプロジェクトの定義
    GitHubのリポジトリをソースとして取得し、特定のマイグレーション処理を実行するビルドプロジェクトを作成しています。

コード(一部抜粋)

# CodeBuild が STS (Security Token Service) を利用して IAM ロールを引き受けるためのポリシー
data "aws_iam_policy_document" "assume_role_policy" {
  statement {
    sid    = "VisualEditor0"
    effect = "Allow"
    actions = [
      "sts:AssumeRole"
    ]
    resources = [
      "arn:aws:iam::*:role/***-${var.environment}-*"
    ]
  }
}

# CodeBuild が自身の IAM ロールを利用できるようにするためのポリシー
data "aws_iam_policy_document" "codebuild_assume_role" {
  statement {
    effect = "Allow"
    actions = [
      "sts:AssumeRole"
    ]
    principals {
      type        = "Service"
      identifiers = ["codebuild.amazonaws.com"]
    }
  }
}

# CodeBuild が VPC 内のリソースにアクセスできるようにするためのポリシー
data "aws_iam_policy_document" "vpc_logs" {
  statement {
    sid    = "CloudWatch"
    effect = "Allow"
    actions = [
      "logs:CreateLogStream",
      "logs:CreateLogGroup",
      "logs:PutLogEvents"
    ]
    resources = [
      aws_cloudwatch_log_group.codebuild.arn,
      "${aws_cloudwatch_log_group.codebuild.arn}:*"
    ]
  }
}

# CodeBuild が Secrets Manager から機密情報を取得するためのポリシー
data "aws_iam_policy_document" "github_actions_secrets_manager" {
  statement {
    sid    = "GetSecrets"
    effect = "Allow"
    actions = [
      "secretsmanager:GetSecretValue"
    ]
    resources = [
      data.aws_secretsmanager_secret.***_credential.arn
    ]
  }
}

# CodeBuild に付与する IAM ロールを定義
resource "aws_iam_role" "codebuild" {
  name               = "***-${var.environment}-codebuild-role"
  assume_role_policy = data.aws_iam_policy_document.codebuild_assume_role.json
}

# CodeBuild のログを CloudWatch Logs に保存するための設定
resource "aws_cloudwatch_log_group" "codebuild" {
  name              = "/aws/codebuild/***-${var.environment}-*"
  retention_in_days = 30 # ログの保持期間を30日に設定
}

# CodeBuild のビルドプロジェクトを定義
resource "aws_codebuild_project" "migration" {
  name          = "***-${var.environment}-*"
  description   = "CodeBuild project for ${var.environment} environment "
  build_timeout = var.build_timeout
  service_role  = aws_iam_role.codebuild.arn

  artifacts {
    type = "NO_ARTIFACTS" # ビルド成果物を保存しない
  }

  environment {
    compute_type                = var.compute_type
    image                       = var.image
    type                        = "LINUX_CONTAINER"
    image_pull_credentials_type = "CODEBUILD"

    environment_variable {
      name  = "DB_CREDENTIALS_SECRET_NAME"
      value = "${var.environment}/database/credentials"
    }
  }

  logs_config {
    cloudwatch_logs {
      status     = "ENABLED"
      group_name = aws_cloudwatch_log_group.codebuild.name
    }
  }

  source {
    type                = "GITHUB"
    location            = var.github_repository_url
    git_clone_depth     = 1
    report_build_status = true
    buildspec           = <<-EOT
      version: 0.2
      phases:
        pre_build:
          commands:
            - apt-get update
            - apt-get install -y ***
            - export DB_HOST=$DB_ACCESS_HOST
            - export DB_USER=$DB_ACCESS_USERNAME
            - export DB_PASSWORD=$DB_ACCESS_PASSWORD
            - export DB_PORT=$DB_ACCESS_PORT
        build:
          commands:
            - ***"
    EOT
  }
}

5.GHAを書き直す

案件特有の処理もあったので割愛
terraformから実行できるように少し手を加えました!

6.検証

  1. 各環境でterraform init, terraform plan terraform applyを行い問題がないか確認
  2. GHAから起動できるか確認
  3. GHAのworkflow dispatchでinputを適切に入れれているかを確認

まとめ

ざっくりとした説明でしたが行ったのは以上です!
実際は詰まることも多かったですが(特に権限まわりなど)、どうにか最後まで実装することができました!
インフラへの過度な苦手意識もなくなり、また別の機会で触りたいと思っています⚪︎
インターンでお世話になった皆さん、ありがとうございました🙇‍♀️

Discussion