目次
前書き
概要
ECSでブランチごとの動作確認用の環境を準備したので、そのノウハウを共有します。
作った環境はこんな感じです。
動機
ディレクターが、エンジニアが開発中のブランチの挙動確認をするのに「 git pull
して docker-compose up
して、メモリが足りなくてビルド終わらなくて…」というのを見て「大変だなー」と思い、パッと動作確認できる環境を作ろうと思いました。
本文
利用した主なツール
- aws-cli
- ecs-cli
- docker
- docker-compose
コンテナの構成
fargateで起動するコンテナ群はこんな感じです
コンテナ名 | 内容 |
---|---|
app | Nginx+プロダクションコード |
mysql | DB |
redis | バッチジョブ用のキュー |
今回は、これを一つのタスクとして起動させています。
docker-compose.ymlで80番ポートを空けて インターネット → nginx → プロダクションコード
という流れで通信が行われます。
またfargate特有の事情としては、コンテナ間通信の名前解決がコンテナ名でなくlocalhostとなることなので、それは注意が必要です。
(自分はここで結構ハマりました…)
1の解説
現在、ブランチがプッシュされたらCircleCIでphpunitを実行させています。
その工程に、docker imageのビルドとECRへのプッシュを追加します。
実際のコードはこんな感じです。
- run:
name: Install Docker client
command: |
set -x
VER="19.03.5"
curl -L -o /tmp/docker-$VER.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$VER.tgz
tar -xz -C /tmp -f /tmp/docker-$VER.tgz
mv /tmp/docker/* /usr/bin
- run:
name: Install ecs-cli
command: |
set -x
curl -o /usr/bin/ecs-cli https://amazon-ecs-cli.s3.amazonaws.com/ecs-cli-linux-amd64-latest
chmod +x /usr/bin/ecs-cli
- run:
name: push docker image to AWS ECR
command: |
BRANCH_HASH=`echo $CIRCLE_BRANCH | sed 's:/:_:g'`-`git rev-parse HEAD`
docker build -f Dockerfile_Ecs -t {{registory_id}}.dkr.ecr.ap-northeast-1.amazonaws.com/{{repository}}:${BRANCH_HASH} .
ecs-cli push {{registry_id}}.dkr.ecr.ap-northeast-1.amazonaws.com/{{repository}}:${BRANCH_HASH}
ECRの運用としては、一つのレポジトリに対して、「ブランチ名+コミットハッシュ」でタグ付けをしたイメージをプッシュすることにしています。
これでレポジトリにライフサイクルポリシーを設定することで、ECRの容量が肥大化することが防げます。
ただ、イメージのタグに /
が使えないので _
で置換しています。
Dockerfile_Ecsの内容としては、ビルド済みのプロダクションコードを COPY
して、nginxの設定ファイル等を 適切な場所に配置って感じです。
2の解説
ユーザーがブランチ名を引数としてスクリプトを実行したら、URLが出力されるようにします。
このURLからECSで起動したサービスにアクセスすることができれば、ユーザーはビルドやgit操作が不要となります。
3の解説
今回の肝となるのは、4の
ecs-cli compose --project-name ${branch+hash} service up
となります。
しかし、そのためには入力された、ブランチ名から ${branch+hash}
を取得する必要があります。
そのために ECRでイメージ一覧を取得し、ブランチ名の前方一致でフィルターし、そのイメージのタグを取得することで ${branch+hash}
を取得します。
4の解説
今回で一番重要なのが、この処理となります。
図ではスペースの都合でコマンドを省略しましたが、省略しないと下記になります。
BRANCH_HASH=${branch+hash} ecs-cli compose \
-c {{cluster_name}} \
-f docker-compose-ecs-base.yml -f docker-compose-ecs.yml \
--ecs-params docker/ecs/ecs-params.yml \
--project-name ${branch+hash} \
service up \
--launch-type FARGATE
環境変数 BRANCH_HASH
に値を渡して、それを下記の docker-compose.yml で取得しています。
services:
app:
build:
context: .
dockerfile: Dockerfile_Ecs
image: "{{registry_id}}.dkr.ecr.ap-northeast-1.amazonaws.com/{{repository}}:${BRANCH_HASH}"
ports:
- "80:80"
command: docker/ecs/entrypoint-app.sh
これによって ecs-cli compose service up
の実行時に、どのタグのイメージをECSで立てるかを決定しています。
ECSはこのコマンドを受けて、タスク定義の作成、ECRからimageの取得、サービス+タスクの起動を行ってくれます。
そして、最後に entrypoint-app.sh
で、migration、シードデータの作成、nginx起動などのコンテナ起動に必要な処理を行っています。
5の解説
今回は、4でECSのサービス+タスクがパブリックサブネットで作成されています。
そのため、そのタスクにはパブリックIPが付与されています。
そのパブリックIPを aws ecs describe-tasks
等を利用して取得して、そのIPと ${branch+hash}
を紐付けて、 Route53でAレコードを作成します。
ユーザーは、このAコードのURLを使うことで、ECSのサービスにアクセスできるようになります。
(パブリックIPが自動的に変更されて、URLとIPの紐付けが変わる可能性が高いような気はしますが、サービスは毎晩に削除しようと思ってるので、まぁ大丈夫かな?という予想です)
終わりに
続きの作業としては
- slackopsにする
- ECSのサービスを自動的に削除する
- Route53のレコードを自動的に削除する
をやりたいなーと思います。
ただslackops対応が意外と難しい…
参考
下記を参考にさせていただきました。
ありがとうございました