GHAとCodeBuildでRDSマイグレーションを実装
はじめに
ひとつ前の記事でも少し書いたように、2025/1に参加した株式会社ゆめみさんではインフラのタスクを振っていただきました!その一部をアウトプット記事にしても問題ないとのことだったので、今回は振り返りも兼ねてまとめたいと思います。
ちなみに、私はこのインターンをやるまでインフラの構築などはほぼ全く関わったことがなく、terraformがなんなのかもあまりわかっていませんでした😇
タスク概要
- 手動のGHAをトリガーとして、Codebuildを起動しRDSのマイグレーションを走らせる
- GHAでinputを指定できるようにする
- GHAではセルフホステッドランナーを立ててcodebuildを立ち上げるようにする
- codebuildは各環境で建てる(dev,stg,prod)
- サブネットは3つ(public, private, db)で、privateにcodebuildを置きdbにアクセスする
- コンソールではなくterraformで環境構築する
手順
1.タスクを理解する
インフラ初心者なので、まず振られたタスクが何を言っているのかも一発ではわかりません。
そのため、最初は焦らずにわからないことを洗い出し、記事を読みながら知識のインプットを行いました。
①GitHubActionsのセルフホステッドランナーって何
→GitHubが提供するクラウドのランナーではなく、自分の環境でジョブを実行できる
②セルフホステッドランナーを立ててcodebuildを立ち上げるってどういうこと
インターン先の社員さんが書いた記事でインターン業務を行えるいい経験
③コンソールではなくterraformで環境構築する
"②”では、コンソールで作業を行っていますがそれをterraformに書いて行うということを理解しました。
2.GHAからCodebuildを起動するところまで確認
最初からterraformで実装するのではなくこの記事のようにCodebuildをコンソールで指定してGHAから立ち上げられるようにしました。
また、VPCの中のCodebuildを立ち上げる必要があったので、記事に書いてあったことに加えてVPCやセキュリティグループの設定をコンソールで行いました。
ここで疎通が確認できたので、次のステップに移ります。
3.必要なIAM RoleとPolicyを洗い出す
2で作ったCodebuildをコンソールでカスタムしたIAM Roleで起動できるかを確認し、最小限の権限を見つけていきます。この際に、IAM UserとIAM Roleの違いや、セキュリティにおいて大事なポイントなども知ることができました。
また、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.検証
- 各環境で
terraform init
,terraform plan
terraform apply
を行い問題がないか確認 - GHAから起動できるか確認
- GHAのworkflow dispatchでinputを適切に入れれているかを確認
まとめ
ざっくりとした説明でしたが行ったのは以上です!
実際は詰まることも多かったですが(特に権限まわりなど)、どうにか最後まで実装することができました!
インフラへの過度な苦手意識もなくなり、また別の機会で触りたいと思っています⚪︎
インターンでお世話になった皆さん、ありがとうございました🙇♀️
Discussion