はじめに
みなさんはどのようにソースコードをデプロイしていますでしょうか?
今更ながらですが、弊社のデプロイフローはAWSの Code兄弟 を利用しておりまして、その流れと各サービスの概要を私風にまとめて書きます。
決して4,5月から増える新入社員用の教育資料に使おうとか考えてませんよ…
※追記した内容でCodeDeployを使わないパターンを書きました。
使用するサービス
使用しているサービスを簡単に紹介します。
AWS CodeCommit
AWSが提供するフルマネージド型のソースコントロールサービス。おそらく本体はGithubだと思ってます。
ほぼ無料でプライベートなGitリポジトリを使用することができます。
ただGithubと比べてしまうと、issueが立てられなかったり、PRがなかなか使いづらかったりと不便な点はあります。
([参考ページ] (https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/welcome.html))
AWS CodeBuild
AWSが提供する完全マネージド型のビルドサービス。テストやCIを実行することができます。
独自のビルドサーバーのプロビジョニング、管理、スケーリングが不要になります。
(参考ページ)
AWS CodeDeploy
AWSが提供するAmazon EC2 インスタンスやオンプレミスインスタンス、サーバーレス Lambda 関数に対するアプリケーションのデプロイを自動化するデプロイメントサービス。
今回はpushしたソースコードからDockerイメージを作成してECRにDockerイメージをpushしています。
(参考ページ)
AWS CodePipeline
AWSが提供するソフトウェアをリリースするために必要な手順のモデル化、視覚化、および自動化に使用できる継続的な配信サービス。
上記の各サービスを組み合わせて一連のデプロイフローなどを設計することができます。
(参考ページ)
Amazon Elastic Container Registry (ECR)
AWSが提供する完全マネージド型の Docker コンテナレジストリ。
CodeDeployで作成したDockerイメージをECRにPushし、管理しています。
(参考ページ)
大まかな流れ
CodePipelineの画面はこんな感じです。
流れとしては
1. CodeCommitにソースコードをPushする
2. CodeBuildでテストやCIが実行される
3. CodeDeployでDockerイメージを作成しECRにイメージをPushする
といった感じになります。
CodeCommitの流れ
CodePipelineのトリガーとなるブランチにソースコードがPushされたときに、次のアクションとなるリソース(今回はCodeBuild)に、最新のソースコードを渡します。
CodeBuildの流れ
CodeBuildはCodeCommitからソースコードを渡され、その中の buildspec.yml
というファイルを元にコマンドを実行します。
おそらく実行されるときに、あらかじめ設定しておいた環境のEC2が立ち上がっていると思います。ユーザーの目には見えませんが…
buildspec.yml
にて各フェーズごとに実行するコマンドを書いたりとか、ソースコードをS3にアップ対象のファイルの設定など書きます。
フォーマットとしてはこんな感じです。( yarn run **
というコマンドは package.json
で定義しているコマンドのことです。)
version: 0.2
phases:
install:
commands:
# パッケージ等のインストール実行
# ex. apt-get update -y, npm install -g yarn, yarn install
pre_build:
commands:
# Lintチェック等を実行
# ex. yarn run eslint, yarn run tslint
build:
commands:
# テスト等実行
# ex. yarn run ava, yarn run test-client
post_build:
commands:
# S3にアップ前のインストールなどを実行
# ex. rm -rf node_modules, yarn install --production
# S3にアップする対象ファイル等と形式の設定
artifacts:
type: zip
files:
- hogehoge
- fugafuga
上記ファイルのどこか一つでも正常に処理が終了しなければ、CodePipelineは処理を終了します。
要するにテストやLintチェックなどでこけるとそこでデプロイが終了するということです。とても健全的ですね。
CodeDeployの流れ
CodeDeployはCodeBuildが正常に処理が完了したらCodePipelineによってキックされます。
CodeBuildが実行されるのは、実際に稼働しているEC2上で実行されます。(他にもやりかたはありますが弊社の例で書いていきます。)
こちらのページを参考にして、AWSCodeDeployエージェントをインストールしておきます。
このEC2の中でDockerイメージを作成するので、EC2にDockerをインストールしておいてください。
$ sudo yum install -y docker
Dockerをインストールしたあと再起動すれば下準備完了です。
弊社のCodeDeployでは「インプレースデプロイ」という方法を選択しています。
実際に実行されて行く流れはこんな感じです。(AWSドキュメントから抜粋)
上の画像のように、各イベントで処理を実行していきます。
簡単な流れとしては
1. リビジョン(今回はS3)からソースコードを取得して解凍する。
(このときのディレクトリは:/opt/codedeploy-agent/deployment-root/hogehoge/fugafuge/deployment-archive)
2. 解凍したソースコードを指定のディレクトリにコピーする
3. Dockerイメージを作成し、ECRにPushするShellを実行する
4. ECRにDockerイメージにタグ付けされて、Pushされる
となります。
各イベントの説明はAWSのドキュメントで確認してください。
少し詳細を書いていきます。
CodeDeployの設定ファイルとしては appspec.yml
というファイルに各設定を書いていきます。
簡単なフォーマットの例としてはこんな感じです。
version: 0.0
os: linux
# Installのイベントでインストールする
files:
- source: /
destination: /var/www/hogehoge
# トリガーとなるイベントでの設定
hooks:
ApplicationStart:
- location: fugafuga.sh
timeout: 300
runas: root
上記の appsepc.yml
では、Installイベント実行時に、CodeDeployAgentがS3から取得したソースコードを、 /var/www/hogehoge
にコピーし、ApplicationStartイベント実行時に fugafufa.sh
をroot権限で実行します。
弊社ではこの fugafuga.sh
の中でDockerイメージを作成してECRにPushするようにしています。
※下記は色々改変しています。
#!/usr/bin/env bash
cd /var/www/hogehoge
# ECRのエンドポイント
repository=xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/hogefuga:latest
# Dockerへのログイン情報を取得
$(aws ecr get-login --no-include-email --region ap-northeast-1)
# Dockerイメージの個数をカウント
image_count=$(docker images | grep hoge/hogefuga.app | wc -l)
zero=0
image_count=$(docker images | grep ${repository} | wc -l)
if [ ${image_count} -gt ${zero} ]; then
docker rmi -f ${repository}
fi
# プロキシの設定を確認
echo http_proxy:${http_proxy}
echo https_proxy:${https_proxy}
# Dockerコンテナをbuildする
echo 'build Docker container'
docker build --no-cache=true \
--build-arg http_proxy=${http_proxy} \
--build-arg https_proxy=${https_proxy} \
-t hoge/hogefuga.app:latest \
.
# ECRにPushする
echo 'push image to ECR'
docker tag hoge/hogefuga.app:latest ${repository}
docker push ${repository}
# Dockerイメージの削除
image_count=$(docker images -f "dangling=true" -q | wc -l)
if [ ${image_count} -gt ${zero} ]; then
echo 'clean'
docker rmi -f $(docker images -f "dangling=true" -q)
fi
docker rmi hoge/hogefuga.app:latest
docker rmi ${repository}
ちなみに小話ですが、Dockerへのログイン情報を取得する際に
$ aws ecr get-login --no-include-email --region ap-northeast-1
と実行していますが、Dockerのバージョンが 17.06
以上だと、廃止された -e
オプションが返ってくるので、--no-include-email
オプションがないとエラーになります。
ちなみに --no-include-email
オプションをつけずに実行すると
$ aws ecr get-login --region ap-northeast-1
docker login -u AWS -p authStrings -e none https://xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com
-e none
がいらないということですね。
料金はどないなん?
サービス名 | 料金 |
---|---|
CodeCommit | ほぼ無料 |
CodeBuild | 0.010 / min (Build実行時間 インスタンスタイプはmedium) |
CodeDeploy | 無料(CodeDeployを実行するEC2の料金のみ) |
CodePipeline | 1USD / month |
ほう…やっすいですな。
さいごに
こんな感じで、ローカルからCodeCommitにソースコードをPushすると、CI/テストを実行して、Dockerイメージを作成して、ECRにPushしてくれるフローが出来上がります。
CodePipelineはCodeCommitだけでなくGithubにも対応していますので、ソースコードの管理はGithubでされている方でも、問題ないと思います。
自動でCI/テストを実行してくれて、自動でDockerイメージを作成してくれるので、CIサーバの面倒を見なくても良いし、毎回手動でDockerイメージを作成する手間も省けます。
こうやって新人たちに自動化の素晴らしさを伝えて行くんですね。わかります。
おまけ(追記)
記事を投稿しましたところ
CodeBuildでDockerイメージをPushした方がもっと楽ですよ?
というコメントをいただいたので、早速試してみました。
改良版のCodePipelineはこんな感じになりました。
ひとつ目のCodeBuildで実行環境(Node.js)のCI/テストを実行して、その後ふたつ目のCodeBuildでDockerイメージをPushするようにしました。
CodeBuildの設定で、実行する buildspec.yml
を個別に設定できるので、ふたつ目のCodeBuildで実行するymlファイルを buildspec_docker.yml
のように変えて対応しました。
これで、いちいちEC2立ち上げて〜Agentなどを入れて〜といった作業がいらなくなりました。
さらに便利になりましたね!
あとは、この構成をymlファイルにまとめて、CloudFormationで構築できるようにしたら完璧なのかな?
ではまた!