(※この記事は 別媒体に投稿した記事 のバックアップです。 canonical も設定しています)
2020-07-30
こんにちは! フロントエンドエンジニアのもりやです。
コロナの影響でコネヒトも3月からフルリモート体制が始まり、早4ヶ月が過ぎました。
流行に乗り遅れがちな私は、今になって自宅のリモートワーク環境を整えようと動き始めています。
まずはローテーブルを卒業しよう・・・。
さて、今回はそんなフルリモート下で発生した課題の1つを CodeBuild を使って解決したので紹介させていただきます。
コネヒトでは、本番リリース前のチェックや開発時にAWS環境で動作確認に使う検証環境を用意しています。
この検証環境にデプロイするには以下の2つの方法がありました。
master
ブランチにPRをマージする(本番リリース前に動作確認するため)2.
の方法をコネヒトでは「ローカルデプロイ」と呼んでいます。
このローカルデプロイを今回 CodeBuild
を使って改善しました。
主に以下の2つが問題点としてあげていました。
Docker
のバージョンなど、差異が出てしまう場合がある特にコロナでフルリモート環境になって 2.
が深刻な問題となりました。
自宅の通信速度が遅い場合、開発業務に影響が出るレベルの方もいました。
Docker プッシュ中に Zoom のミーティングの時間が来たので泣く泣く中断して、ミーティング後に再実行・・・、なんて最悪ですね。
これらの問題を改善するために、CodeBuild
を使って検証環境へのデプロイを安定して行えるようにしました。
以下のような理由で CodeBuild
を選びました。
CodeBuild
との親和性も高いCodeBuild
に限らずAWS全体に言える話ですが)ちなみに無料枠で月100分(build.general1.small
のみ)はずっと無料で使えるので、試しに使ってみても良いかもしれません。
もともと「ローカルデプロイ」という名前でしたが、CodeBuild
を使った場合は違和感のある名前なので、まずは名前をつけることにしました。
Slack で名前を募集したところ、たくさんの案が出てきました。
ありがたや~。
色々案が出ましたが、最終的に「ブランチデプロイ」に決まりました。
主に開発時に使い、開発時はブランチを作りそこからデプロイするので、イメージに近くて分かりやすい名前ということで決めました。
コネヒトのサービスは主に ECS で動いており、 Docker イメージは ECR に保存しています。
このような流れで動いています。
まず CodeBuild
が実行時に参照する YAML の設定ファイルを作成して、リポジトリ内にコミットします。
こんな感じの YAML ファイルを用意しました。
version: 0.2
env:
variables:
DOCKER_BUILDKIT: "1" # Ref: https://tech.connehito.com/entry/2019/06/17/180404
parameter-store:
GITHUB_ACCESS_TOKEN: "/xxxxxxxxxxxx/GITHUB_ACCESS_TOKEN"
phases:
build:
commands:
- /bin/bash .codebuild/dev_deploy.sh
実際のデプロイ処理は dev_deploy.sh
というスクリプトにまとめて、設定ファイルはシンプルになるようにしています。dev_deploy.sh
の中身は省略しますが、主に Docker ビルド&プッシュと ecs-deploy を使ってECSにデプロイする処理をしています。
ビルド時には GitHub のトークン が必要なのですが、今回は パラメータストア に設定して、parameter-store:
でキー名を設定するだけで自動的に環境変数に設定してくれます。
この辺りは同じAWSサービスならではの便利さですね。
公式ドキュメントなどを参考にAWSコンソールで設定しました。
特筆すべき設定はありません。
強いて言えば、自動実行はしないのでウェブフックイベントの設定はしないことぐらいでしょうか。
(CIだとプッシュなどのタイミングで自動実行する場合が多いと思いますが、今回はユーザーからのリクエストがあって動く仕組みなので)
AWS CLI を使って簡単にブランチデプロイが実行できるように、以下のようなシェルスクリプトを作成しました。
#!/usr/bin/env bash
set -e
readonly GIT_ROOT="$(git rev-parse --show-toplevel)"
readonly REPOSITORY_NAME="(リポジトリ名)"
readonly PROJECT_NAME="(CodeBuildのプロジェクト名)"
readonly DEPLOY_TARGET="${1:-"HEAD"}"
readonly DEPLOY_COMMIT_HASH="$(git rev-parse "${DEPLOY_TARGET}")"
readonly DEPLOY_COMMIT_LOG="$(git log "${DEPLOY_COMMIT_HASH}" --max-count=1)"
# GitHub API を使って GitHub にコミットがプッシュ済みかをチェックする
if [[ "${GITHUB_ACCESS_TOKEN}" != "" ]]; then
HTTP_STATUS_CODE="$(curl -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" -o /dev/null -w '%{http_code}\n' -s \
"https://api.github.com/repos/Connehito/${REPOSITORY_NAME}/git/commits/${DEPLOY_COMMIT_HASH}")"
if [[ "${HTTP_STATUS_CODE}" == "200" ]]; then
printf "\e[34mINFO: GitHubにコミットが存在することが確認できました。\e[0m\n"
else
printf "\e[31mERROR: GitHubにコミットが存在するかをチェックしたところ、ステータスコード(${HTTP_STATUS_CODE})が返却されたためブランチデプロイを中止します。\e[0m\n" >&2
exit 2
fi
else
printf "\e[31mWARNING: 環境変数 'GITHUB_ACCESS_TOKEN' が設定されていないため、GitHubにコミットが存在するかのチェックをスキップします。\e[0m\n"
fi
printf "\nこのコミットでブランチデプロイを実行しますか?\n-----\n%s\n\n-----\n" "${DEPLOY_COMMIT_LOG}"
printf "実行する場合は \e[34m'yes'\e[0m と入力してください: "
read ANSWER
if [[ "${ANSWER}" != "yes" ]]; then
echo "ブランチデプロイを中止します。"
exit 1
fi
echo "ブランチデプロイを開始します。"
aws codebuild start-build \
--project-name "${PROJECT_NAME}" \
--source-version "${DEPLOY_COMMIT_HASH}" \
--output json
echo "ブランチデプロイを開始しました。実行結果はログを確認してください。"
ざっくりいうと、こんな流れで処理を進めています。
git rev-parse
を使ってコミットハッシュを取得しています。"${1:-"HEAD"}"
で第1引数に指定がない場合は HEAD
からコミットハッシュを取得します。GitHub API
を使ってコミットハッシュが GitHub
に存在しているか確認CodeBuild
実行後に「コミットをプッシュし忘れた〜」と気づくトラブルを防いでいます。GITHUB_ACCESS_TOKEN
という環境変数をセットしています。terraform
に倣って、ユーザーが yes
を入力しなければ実行されないようにしています。AWS CLI
を使って CodeBuild
でのビルド&デプロイを実行。AWS CLI
は v1
, v2
がありますが、どちらのバージョンも同じコマンド体系なのでどちらでも実行できます。(下記参照)AWS CLI v1
の場合
$ aws --version
aws-cli/1.18.90 Python/3.8.4 Darwin/19.5.0 botocore/1.17.13
$ aws codebuild start-build help
NAME
start-build -
DESCRIPTION
Starts running a build.
See also: AWS API Documentation
See 'aws help' for descriptions of global parameters.
SYNOPSIS
start-build
--project-name <value>
[--secondary-sources-override <value>]
[--secondary-sources-version-override <value>]
[--source-version <value>]
# (以下略)
AWS CLI v2
の場合
$ aws --version
aws-cli/2.0.24 Python/3.7.3 Linux/4.19.76-linuxkit botocore/2.0.0dev28
$ aws codebuild start-build help
NAME
start-build -
DESCRIPTION
Starts running a build.
See also: AWS API Documentation
See 'aws help' for descriptions of global parameters.
SYNOPSIS
start-build
--project-name <value>
[--secondary-sources-override <value>]
[--secondary-sources-version-override <value>]
[--source-version <value>]
# (以下略)
少なくとも今回使っている範囲では同じコマンドで実行できることがわかります。
先程作ったシェルスクリプトを実行するとこんな感じになります。
AWSへの認証情報がセットされた状態 で以下のコマンドを実行します。
$ ./branch-deploy.sh "(ブランチ名、コミットハッシュなど)"
こんな感じで表示されます。
後は CodeBuild
の実行を待つだけです。
ちなみにコネヒトでは CodeBuild
を実行すると、自動で Slack 通知が来る共通の仕組みがあるので、通知関連は今回実装しませんでした。
※ Slack で通知されます (ちなみに社内では検証環境を「dev」とか「dev 環境」と呼んでいます)
CloudWatch Events
で CodeBuild
のイベントを Lambda
に送って Slack
に通知しています。
このような感じで実行すれば非同期で動くので、ネット回線やPCの負荷を気にせず検証環境へのデプロイが安定して行えるようになりました。
今回は好きなタイミングで任意のコミットを CodeBuild
を使ってデプロイする、ということをやりました。
IAM の権限周りで少しハマりましたが、設定は難しくなくサクッとできるので、こういった活用もありだな〜、と思いました。
最後に、コネヒトでは開発環境の改善にもチャレンジしたいエンジニアを募集中です!