CodeBuild上でGitHub Actionsランナーを動かしてAuroraのDBマイグレーションを自動化してみる
はじめに
はじめまして、システム基盤チームでSREをしている森と申します。
日々の業務で取り組んだことについて紹介いたします。
2024年は弊社にとって複数の新規プロダクトがリリースされた第二創業期とも言える年でもありました。
一方で複数のサービスがリリースされ始めたことで新たな問題が出てきました。
例として本番環境のDBに対してスキーマ変更を実施する場合、踏み台アカウント上にあるAmazon Workspaces[1]から各プロダクトで使われているDBへアクセスし、スキーマ変更を実施していました。
開発者が作成したクエリファイルをSREが本番環境へアクセスし実行する仕組みになっており、開発と運用の権限を分離するようにしていますが、DBマイグレーション作業の頻度が増えていくにつれ作業にかかる工数が肥大化し運用上の課題が生じてきました。
パッチ適用作業イメージ
この課題を解決するためにDBマイグレーション作業をCI/CDパイプライン上で実現し、運用負荷作業を低減する仕組みを構築してみました。
今回DBマイグレーション作業を実現するCI/CDパイプラインのアーキテクチャ紹介や使用した技術スタックについて説明いたします。
対象読者
- AuroraへのDBマイグレーションをGitHub Actionsで実現したい
- セキュアなDBマイグレーションのCI/CDパイプライン設計に興味がある
CI/CDパイプライン設計の課題
弊社ではVCS(Version Control System)としてGitHubを採用しており、GitHub Actionsを活用したCI/CDパイプラインもすでにいくつかあります。
ただ、GitHub ActionsではAmazon Auroraのようなプライベートなネットワークに閉じたリソースへアクセスできない問題があります。
ECS経由でDBマイグレーションを実現する方法もいくつかある[2][3]みたいですが、構成が複雑になるデメリットもあります。
AWS CodePipeline上でCI/CDパイプラインを構築してもいいのですが、CI/CDパイプラインツールが複数あると新規参画者への学習コストも上がるといった課題もありました。
課題を認識していたちょうどそのころにAWSから新しいアップデートとしてCodeBuild上でGitHub Actionsランナーをサポートできるようになったとアナウンスがありました。
以前個人ブログでも書いて検証していましたが、上述した課題を解決できそうな代物でしたので、CodeBuild上でGitHub Actionsランナーを起動するCI/CDパイプラインアーキテクチャを実装してみることにしました。
構成図
そうして構築したCI/CDパイプラインがこちらになります。
CI/CDパイプライン構成図
次の章で構成について詳しく解説いたします。
パイプライン構築
まずはじめにCodeBuildとGitHubをどうやって連携するかですが、以前私が検証していた時はOAuth[4]を使用して接続するか、GitHubのPAT[5]を使用して接続するかの二択でした。[6]
その後CodeBuildがGitHub Apps[7]を使用したリポジトリアクセスをサポートするようになり、よりきめ細やかなアクセス制御ができ人が介在する必要性がなくなったことからGitHub Appsを使ってCodeBuildとGitHubを接続していきます。
CodeBuildプロジェクト作成
CodeBuildプロジェクトを作成する際のソースプロバイダーでGitHubを選択後、認証情報をカスタムソース認証情報にして、認証情報タイプをGitHubアプリにします。
Webhookイベントでフィルタリング項目からWORKFLOW_JOB_QUEUED
を選択することで、GitHub ActionsワークフロージョブでのCodeBuild起動を有効化します。
Buildspecが無視されるようになります
Auroraへ接続するためにCodeBuildの実行環境をVPC内に配置します。この時にAurora側のSecurity GroupにもCodeBuildからの通信を許可するためのルールを追加する必要があります。
環境変数はSecretsManagerに保存しているDB接続情報を参照できるARNをセットします。
GitHub Actionsランナーを動かせる設定は以上です。他の設定は各自の要件に沿って修正し、CodeBuildプロジェクトを作成します。
GitHub Actionsワークフロー作成
前述の設定でGitHub Actionsのワークフローに記された条件の場合でのみCodeBuildが起動するようになりました。
ワークフローファイルのruns-on[8]箇所を以下のように更新することでビルド環境をCodeBuild上に変更できます。
runs-on: codebuild-<CodeBuildプロジェクト名>-${{ github.run_id }}-${{ github.run_attempt }}
この後のCI/CDの流れにて説明しますが、ワークフローは現在のパッチ適用状況を確認するプレビュージョブとパッチを適用するマイグレーションジョブの2つのジョブを記載しています。
ワークフローファイル
name: db-migration
on:
push:
branches:
- main
paths
- dest/path/*.sql
jobs:
flyway-preview:
#ビルド設定
runs-on: codebuild-<CodeBuildプロジェクト名>-${{ github.run_id }}-${{ github.run_attempt }}
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup JDK
uses: actions/setup-java@v4
- name: Setup gradle
uses: gradle/actions/setup-gradle@v3
- name: Flyway Preview
id: preview_output
run: |
./gradlew flywayInfo > $GITHUB_OUTPUT
- name: notify Slack
run: |
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"preview Output: ```\n'"${{ steps.preview_output.outputs.message }}"'\n```"}' \
$SLACK_WEBHOOK_URL
env:
#結果を通知させたいSlackのWebhook URLをシークレット変数にセットする
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
flyway-migration:
#ビルド設定
runs-on: codebuild-<CodeBuildプロジェクト名>-${{ github.run_id }}-${{ github.run_attempt }}
#前述のpreviewジョブの後に順列実行させる
needs: flyway-preview
environment: production
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup JDK
uses: actions/setup-java@v4
- name: Setup gradle
uses: gradle/actions/setup-gradle@v3
- name: Run Migration
run: ./gradlew flywayMigrate
- name: Migration output
id: migration_result
run: |
./gradlew flywayInfo > $GITHUB_OUTPUT
- name: Notify Slack
run: |
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"migration complete Output: ```\n'"${{ steps.migration_result.outputs.message }}"'\n```"}' \
$SLACK_WEBHOOK_URL
env:
#結果を通知させたいSlackのWebhook URLをシークレット変数にセットする
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
パッチ適用先アカウント側での作業
CodeBuildが別アカウントにあるSecretsManagerを参照するには、カスタマー管理型のKMSでSecretsManagerを暗号化して、CodeBuildを動かしているアカウント側でSecretsManagerを復号化する必要があります。
KMSにて運用アカウントがアクセスできるポリシーを持ったカスタマー管理型キーを作成し、そのキーでSecretsManagerを暗号化します。SecretsManagerのリソースポリシーにもCodeBuildが参照できるようにポリシーを更新します。
KMSポリシーで別アカウントへ共有
SecretsManagerポリシーで別アカウントへ共有
作成したKMSとSecretsManagerのポリシーに参照先アカウント情報(ここでは運用アカウント)を付与すれば、CodeBuildがプロダクトアカウントのSecretsManagerを読み取れるようになります。
詳しい手順はAWS公式ドキュメントをご覧ください。
AuroraにアタッチされたSecurity Groupに、CodeBuildにアタッチされたSecurity Groupからの通信を許可するルールを追加することでCI/CDパイプラインの構築が完成します。
CI/CDの流れ
実際にDBマイグレーションの流れについて説明いたします。
構成図再掲
開発者がクエリファイルをリポジトリにpushし、PRを起票したらSREチームでマージします。
CodeBuildが起動すると、前述のワークフローファイルに記述されたジョブが動き、GitHub ActionsランナーでDBマイグレーションツールであるFlyway[9]が、Auroraへ接続しプレビュージョブ→マイグレーションジョブの順番でジョブを実行します。
マージのタイミングでジョブを実行することについて
構成図にも記載しているとおりプレビュージョブも含めてCodeBuildが実行されるタイミングはマージされた時だけにしてます。
目的としてはSREが承認する前にDBへ接続することを防ぐ意味合いがあります。
承認前にDBへ接続されたとしてもPR時のDBエンドポイントをReaderにして承認前のクエリDB更新を防ぐことはできます。
不正なSELECTクエリの実行もFlywayのオプションにFLYWAY_OUTPUT_QUERY_RESULTS
[10]の設定値をfalseにすることでFlywayの結果を出力させられないようにしてます。
ただ、Flywayを介さずにSELECTクエリを実行された際に防ぐ手立てがないので、そのリスクを考慮した結果DBへ接続するタイミングをPRがマージされた時のみに限定しています。
とは言え、プレビュージョブが完了しすぐにマイグレーションジョブが実行されるワークフローにしてしまうとプレビュージョブを実行させる意味がありません。
プレビュージョブ実行後、一時的にワークフローが停止しパッチ適用状況に問題がないことが確認できれば、手動による承認でワークフローを再開する。そうすれば指摘された問題点も解消しつつプレビュージョブを実行する意味も生まれてきます。
ワークフローの流れイメージ
GitHub Environmentによる手動承認フェーズ実装
GitHub Actionsで手動承認フェーズを実装するには、GitHub Environmentsの機能として提供されているデプロイメント保護ルールの1つであるレビューアーの必須を有効化する必要があります。[11]
sqlリポジトリの設定ページからEnvironmentsへアクセスし、新規にenvironmentを作成します。
作成したenvironmentの設定項目であるRequired reviewers
のチェックを有効化し、レビューアーにSREチームを追加することで、environemnt
プロパティが含まれるジョブは実行前にレビューアーによる承認が通るまでワークフローが停止されるようになります。
flyway-migration:
#ビルド設定
runs-on: codebuild-<CodeBuildプロジェクト名>-${{ github.run_id }}-${{ github.run_attempt }}
#前述のpreviewジョブの後に順列実行させる
needs: flyway-preview
environment: production
プレビュージョブの実行結果に応じてマイグレーションジョブへのデプロイ可否を判定する
最後にジョブが完了した結果をSlackへ通知して一連のCI/CDパイプラインは完了します。
所感
CodeBuild上でGitHub Actionsランナーを実行してDBマイグレーション運用を効率化するCI/CDパイプラインについて紹介いたしました。
今回のCI/CDパイプライン構築で良くなった点は以下の通りです。
- 手動でのマイグレーション作業がなくなり、開発者と運用者の負担が減らせる
- GitHub ActionsランナーをCodeBuild上で実行することによってセキュアなCI/CDパイプラインが実現
- GitHub Actionsワークフローファイルを再利用できるためBuildspecファイルの作成が不要
- CodeBuildからインターネットへのアウトバウンド通信をAWS Network Firewallで制御可能
今回の構築とは直接関係がなかっため詳細は割愛しますが、運用アカウントはNetwork Firewallによって外部との通信を制御しており、GitHub Actionsの出力結果を許可されていない外部サイトへ転送するといった事態を防げるようにしています。
今年も引き続き新規プロダクト開発が活発化される見通しですので、なるべく面倒な運用業務を自動化して、運用業務を改善できるように頑張っていきたいと思います。
採用宣伝
📣ウェルスナビでは一緒に働く仲間を募集しています📣
参考資料
プロフィール
森 祐太朗
システム基盤グループ システム基盤チーム所属
新卒の会社で金融機関のシステム運用を経験。その後複数の転職を経てウェルスナビ株式会社にSREとして入社。現在は資産運用サービスや新規プロダクトのインフラ環境構築を中心にサービス運用改善、監視運用、IaC開発などを担当しています。
-
https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens ↩︎
-
https://zenn.dev/yuta28/articles/codebuild-support-github-action-runners#codebuildプロジェクト作成 ↩︎
-
https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/using-labels-with-self-hosted-runners ↩︎
-
https://documentation.red-gate.com/fd/flyway-output-query-results-setting-277579016.html ↩︎
-
レビューアーの必須有効化をプライベートリポジトリで利用する場合、GitHub Enterpriseを契約する必要があります。 ↩︎
Discussion