ANDPAD バックエンド担当の原田(@tomtwinkle)です。
普段はGolangでモリモリコードを書いていますがDevOps関連の記事ばかり書いてます。
お察しの通りなんですが、今回もDevOps関連の記事だったりします。
やり方だけ知りたい人は Motivation の部分は読み飛ばして貰っても問題ないです。
Motivation
私が担当しているANDPADボードではRDS(MySQL)を利用しています。 そして、DBのschemaはRidgepoleのschemaファイルで定義され、変更がある場合はRidgepoleでMigrationを行っています。
Ridgepoleはバージョンにより挙動が若干変わるので例えばRidgepoleのバージョンを上げただけで予期せぬschema変更が走るかもしれません。
schemaファイルのReviewだけで、Migrationを走らせるのは怖いですね? なので最終的にDBに流し込むDDLをGithubのPRの段階で確認したくなります。
と、ここまで読んで「あれ?その話どっかで見覚えがあるぞ?」と思った方は流石ですね。普段からよく技術記事を読まれている方だと思います。
実は私も去年のkamillleさんの↓この記事↓を見て良さそう!と思い、実際にやってみた口です。
ただし、今回はAWS CodeBuildで動かしている点が違うため対応の内容も結構変わってきています。
何故Github ActionsではなくAWS CodeBuild なのか……
DBは機密情報の塊、アクセスできる方法は極力制限すべきです。
そのためDBにdry-runを実行するためにアクセスできる環境が限られてきます。
特にGithubのSecretsに本番DBへのアクセスが可能になる情報は載せたくない、RDSを操作できる口をVPCの外に出したくないとなると必然的にAWSの内部でどうにかMigrationを行う必要が出てきます。
そのため今回、AWS CodeBuildでdry-runを走らせてCodeBuildからGithubに対してPRコメントを出す方法を取りました。
AWS CodeBuild側の準備
GithubのPrivate Access Tokenを発行する
CodebuildからGithubに対してPRコメントを書き込むためにはGithubのPrivate Access Tokenを使用する必要があります。 これは個人のアカウントではなく会社で管理するbot用の専用のユーザーを作ったほうが良いと思います。
Github Private Access Tokenを発行する手順は ここらへんを参考にして下さい。
AWS Systems ManagerのParameter StoreにDBとGithubへのアクセス情報を記述
AWS Systems ManagerのParameter StoreにCodeBuildの環境変数で渡すパラメータを作成します。
作成が必要なパラメータは
- DBのユーザー
- DBのパスワード
- Github Private Access Token
TokenやUser/Passwordは勿論Secret情報ですので Type: SecureString
で作成していきます。
ちなみにCodeBuild用のParameterの名前は「/CodeBuild/」で始まることが推奨されています。
CodeBuildを作成
まずはCodeBuildのソースをGithubに指定
Repository in my GitHub account
から 対象のRepositoryを選択します。
まだAWSとGithub を連携していない場合は ここらへん を参考にして連携を行って下さい。
SourceVersion
は手動でCodeBuildを走らせた時に見る内容なので手動で走らせることがない限り中身無くても良いと思います。
次にWebHookの設定
PullRequestの作成/更新時に動いて欲しいので Event type
には PULL_REQUEST_CREATED
と PULL_REQUEST_UPDATED
を指定します。
BASE_REF
に PRをmergeする先のリリースbranch をrefsから指定します 今回の例では仮にmain branchをリリースbranchとするので ^/refs/heads/main$
にしておきます。
ここの値は正規表現を解釈するので特に末尾 $
を指定しないと予期せぬbranchで動いてしまうかもしれません。
FILE_PATH
を指定すると指定されたファイルが変更された場合にCodeBuildが動いてくれるようになります………が、このパラメータは検証の結果ファイルの変更が多すぎる場合どうも無視されるようです。
指定したら必ず動かないという訳でも無さそうなのでそこだけ注意が必要そうです。
ここらへんの設定は後でCodeBuild内で取得できるSystem環境変数の値にも響いてくるので別のWebHookを設定する場合は別途検証が必要です。
お次は Environment
の設定。
OS Image
は特に理由がない場合は Amazon Linux 2
で良いでしょう。
Role
は特に理由がない場合新規に作成するのが一番楽です。既存のRoleを使用したい場合は追加でAction許可する必要があるかもしれません。少なくとも ssm:GetParameters
の追加は必要になります。
Timeout
の値はdefaultでも良いですが、今回の場合はどんなに長くても10分超えたら何かおかしいのでその程度に設定しておくのが良さそうです。
VPCの設定は環境によって異なるので省きます。
Environment variables
には
- DBのエンドポイント
- DBのデータベース名
- DBのユーザー
- DBのパスワード
- Github Private Access Token
を設定します。 AWS Systems ManagerのParameter Storeを使用する環境変数はTypeに Secrets Manager
を指定します。
Buildspec
には実際にCodeBuildの動作を定義するyamlファイルを指定します。
ここには対象Repositoryにあるbuildspecファイルのパスを指定します。
Logs
はaudit的な意味で記録しておいた方が良いでしょう、必要かどうかは各自の判断で。
Github Repository側の用意
Buildspecの記載
ここからが本題。
Ridgepoleを dry-run して 変更がある場合にのみ GithubのPullRequestコメントにDDLを追記させていきます。
CodeBuildの Buildspec
で指定したパスに以下のbuildspec yamlファイルを配置します。
今回の例では .codebuild/buildspec.dryrun-db-migrate.yml
ですね。
schema変更はDBREにレビューして欲しい!みたいな事があるかもしれないので GITHUB_PR_REVIEWER
が指定されていればその人を Reviewerに追加するみたいな処理も入っています。
version: 0.2 env: variables: GITHUB_ORGANIZATION: <GithubのOrganization> GITHUB_REPOSITORY: <GithubのRepository> GITHUB_PR_APPEND_WORDS: "このPRで以下のDDLがDBに反映されるよ!しっかり確認してね!" GITHUB_PR_REVIEWER: "" RIDGEPOLE_VERSION: 1.2.0 RIDGEPOLE_SCHEMA_FILE: <RidgepoleのSchemafileパス> phases: install: commands: - gem -v - gem install ridgepole:${RIDGEPOLE_VERSION} mysql2 - yum update -y -q --skip-broken - yum install -y -q mariadb-server - ridgepole --version build: commands: - mysql -u${RDS_USER} -p${RDS_PASS} -h${RDS_ENDPOINT} ${RDS_DATABASE} -vvv -e "show databases;" - | result=$(ridgepole \ -c '{database: "'${RDS_DATABASE}'", adapter: mysql2, username: "'${RDS_USER}'", password: "'${RDS_PASS}'", host: "'${RDS_ENDPOINT}'"}' \ -f ${RIDGEPOLE_SCHEMA_FILE} \ --apply --dry-run) # GithubのPull request numberを取得 - GITHUB_PR_NUMBER=${CODEBUILD_WEBHOOK_TRIGGER#pr/} - echo ${GITHUB_PR_NUMBER} # Ridgepoleのdry-runの結果からDDLのみをgrep - result=$(echo "${result}" | grep "# " || exit 0) - | # Ridgepoleのdry-runの結果で変更がある場合のみ動作するようにする if [ "${result}" != "" ]; then result=$(echo -e "${GITHUB_PR_APPEND_WORDS}\n\`\`\`\n${result//# /}\n\`\`\`") jq -n \ --arg result "$result" \ '{ "body":$result, }' > body.json cat body.json # GithubのPull requestにdry-runの結果のDDLをコメントする curl \ -X POST \ -H "Accept: application/vnd.github+json" \ -H "Authorization: token ${GITHUB_API_TOKEN}" \ -H "Content-Type: application/json" \ -d @body.json \ "https://api.github.com/repos/${GITHUB_ORGANIZATION}/${GITHUB_REPOSITORY}/issues/${GITHUB_PR_NUMBER}/comments" # schema変更がある場合にレビュアーを追加して欲しい場合 if [ "${GITHUB_PR_REVIEWER}" != "" ]; then jq -n \ --arg team_reviewers "${GITHUB_PR_REVIEWER}" \ '{ "team_reviewers": [$team_reviewers] }' > body2.json cat body2.json curl \ -X POST \ -H "Accept: application/vnd.github+json" \ -H "Authorization: token ${GITHUB_API_TOKEN}" \ -H "Content-Type: application/json" \ -d @body2.json \ "https://api.github.com/repos/${GITHUB_ORGANIZATION}/${GITHUB_REPOSITORY}/pulls/${GITHUB_PR_NUMBER}/requested_reviewers" fi fi
動作結果
ここまでの設定で特に問題がなければPull requestを作成したタイミングでCodeBuildのWebHookが動作し、DBの変更がある場合は以下のようにPull Requestにコメントが付くようになりました。
これでPull RequestのReviewも捗りますね! めでたしめでたし
謝辞
今回の環境構築に辺りSREチームのDanさんに設定の検証のためお世話になりました。ありがとうございました! 変更箇所がない場合にPRコメントしない修正を加えていただいた bushiyama さんにも感謝!
さいごに
当社アンドパッドでは日々協力しながらDevOps活動に取り組んでいます。 ご興味を持って頂けましたら、下記よりお気軽にカジュアル面談や採用職種についてお問い合わせください!