
はじめに
こんにちは、SREチームの佐々木です。
現在、私はSREの中でもEmbedded SREの役割を担っています。各プロダクトのチームに参画して、プロダクトの運用が円滑にできるような下支えとして日々課題に向き合っています。
さて、今回は過去に遭遇した運用課題の中の1つとして対応した「Private Subnet下のRDSに対してのMigrateをCD環境から実施する対応」についてお話させていただきます。
背景と課題
本件はAWSで展開しているプロダクトにて、CD環境をElastic BeanstalkからCodeDeployへ変更したタイミングで発生した2つの課題が起因でした。
CodeDeployからRDSに対してのMigrationが行われることになっていたのですが、以下の2つの課題が発生してしまいました。
- Migrateが長すぎてCodeDeployがタイムアウトしてしまう問題
- AutoScalingGroupを利用している環境でMigrateタスクが衝突してしまってデプロイが止まる問題
しばらくはMigrateを隔離してアプリケーションサーバーから任意のタイミングで手動実行していましたが、手動故の障害が数回発生してしまいMigrateの自動化が急務となりました。
試した事・検討した事
幾つかの方法を検討しました。
まずは無難にバイセルで広く利用しているCI/CD環境のGitHub Actionsを利用してMigrateを実行する例を検証しました。
この方法は残念ながらRDSが外部に公開されていないという点で難しいと判断しました。
次に踏み台サーバーから何かしらのHook起因でMigrateを動作させるという方法も検討しましたが、踏み台サーバー自体も直接RDSへ接続できる訳でもなく、アプリケーションサーバーをトンネルする必要があったので見送りました。
LambdaFunction化するという方法も検討しましたが、担当チームのMigrateは時間が掛かりやすいものになるので長時間化するリスクを恐れて見送りました。
GitHub Actionsを利用しつつもセルフホステッドランナーというエージェントをインストールしたインスタンスを用意してRunタスクがあるかどうかをPullしてくる仕組みがある事を知り、それを利用することも検討しました。
非常に有用な手段だったのですが、Embedを離れた後のメンテナンスコストの懸念やもう1つの手段が今回のケースでは有用だったこともあり、今回は見送る事にしました。
今回最終的に採用した方法はAWS CodeBuildを利用する方法でした。
CodeBuildにはVPCサポート機能があり、閉じたVPC環境でもタスクを実行することが可能です。
AWS内部の機能で完結出来た方が良さそうだという点から今回はこちらを採用することにしました。

CodeBuildを導入してみる
実際にCodeBuildを使ってタスクランナーを設定する方法を簡略しつつ紹介します。
ECRの作成
CodeBuildで動かすイメージの作成を行います。
本件ではRailsアプリケーションだったのでRubyの動作環境を構築します。実際の細かいセットアップはBuildSpecで実施されるので、ベース環境のセットアップのみが出来ていれば問題ありませんでした。
BuildSpecファイルの作成
BuildSpecは別名が許容されているので任意のファイル名のyamlファイルを作成します。
公式のリファレンスには非常に助けられました。
BuildSpecには様々なPhasesがありますが、今回は「install」「pre_build」「build」の3つのPhaseを使ってタスクを実現しました。
CodeBuildプロジェクトを作成して起動確認を行う
動作させる環境の構築とタスクの作成が終われば、いよいよCodeBuildで実際にプロジェクトを作成して動作確認を実施します。
プロジェクト作成画面上でプロジェクト名、ビルドを実行するソースコードの保存場所、実行する環境の設定やBuildspecファイルパスを設定してプロジェクトを作成します。

実際の動作は作成したプロジェクトページから手動で実行することも可能です。AWS CLIで実行する際には以下のコマンドで実行が可能です。
aws codebuild start-build --project-name [project]
実際一連の作業をしていると以前触っていたAnsibleやChefといったプロビジョニングツールに近い感覚を得ました。動作確認の面倒さは若干ありますが機能としては非常に取っつきやすく扱いやすかったです。

CDに組み込んでみる
実行環境を作成して、単独での実行で動作確認が取れたタイミングでCDに組み込んでいきます。
本件のデプロイはGitHub Actionsを利用して行われており、各環境のデプロイワークフローにCodebuildを呼び出す命令を追加するだけでも良いのですが少し工夫してみることにしました。
1. ワークフローを共通化してみる
ここまででCodeBuildのビルドはプロジェクト名とMigrateに必要な情報があれば実行できる事が分かりますのでDevelop、Staging、Productionそれぞれの環境へのリリースを1つのワークフローで共通化してみました。
workflow_callという再利用ワークフローを使用し、Inputsに環境変数を持たせて呼び出し側のワークフロー上で以下のように呼び出すことで並列実行が可能になります。
jobs: deploy: runs-on: ...... migrate: uses: migrate/workflow/path with: val: env secrets: sec: secret
並列実行することで実行時間が今まで20、30分程掛かっていたものが10分以下で完了するようになりました。

2. Codebuildの呼び出しは外部パッケージを利用して実行
上記確認タイミングのようにコマンドで実行することも可能ですが、実行時のログの処理など少し課題が残っている状況でした。AWS公式側がパッケージを用意しているのではと考え探したところ
aws-actions/aws-codebuild-run-build というパッケージを公開されておりました。
パッケージの中に disable-source-override という設定があり、これをTrueにすることで余分なログが流れないようになり、実行時のワークフローログの可読性が飛躍的に向上します。
元々「提供されている機能は万遍無く利用して有効活用しよう」という方針が個人としてもあり、うまくまとまったなと満足しています。
まとめ
今回はMigrationのタスクをCodeBuildにまとめて移動させた話を書かせていただきました。
良い結果を得る事も出来て大満足で、GitHubのワークフローやAWSのCodePipeline系統の機能を触れたのは新しい知見を得る事が出来て楽しかったです。
また機会があれば次はセルフホステッドランナーを本格的に学べると良いなと考えています。
もし、同じような構成でMigrateなどCD時に発生するビルド系タスクの面でお悩みの方の一助になれば幸いです。