概要
こちらのサイトにあるシャイニングウィザードをECS環境で行おうとしたメモ 本家のシャイニングウィザードは、github の webhookを受け取って、シャイニングウィザードが実施されるけど、そこはまだ作ってなくて、一旦Localから叩くようにしてる。
シャイニングウィザードって?
開発時に、現在開発中のブランチの内容を、ディレクターさんとかにチェックしてほしいってことが良くある。
このとき、stagingとかdevelop ブランチにあげて、確認してもらうと、その確認が終わるまで、他の人がリリースできない。
自分のlocalを直接みてもらうこともできるけど、それはそれで自分が他ブランチで作業できず、いろいろ面倒。
そのため、github pushで、確認環境を自動生成しようというのがシャイニングウィザード。
EC2の時は、先輩が作ったシャイニングウィザードを使ってたけど、ECS環境に変わったので自分で作ってみた。
やったこと
ECRとECSを利用しているのを前提とする。
1. Dockerファイルを作成
// 既にECRにあるイメージを利用することで、yarn install を早くする FROM {Dev環境で使ってるImageのECR上のURL} RUN apk update && apk add git // .git フォルダをgithubからcloneする。 RUN git clone --no-checkout https://{User名}:{GithubToken}@github.com/{レポジトリ名} gittmp RUN cp -r gittmp/.git . RUN git reset --hard HEAD // Branch Nameは外から渡す ARG BRANCH_NAME # この時点以降で、キャッシュを効かせないためにdummyfileをADD(必要か微妙) ADD dummyfile /data/ // fetchしてbranch情報を最新に RUN git fetch // 対象ブランチをチェックアウト RUN git checkout -b ${BRANCH_NAME} origin/${BRANCH_NAME} // アプリケーションを起動 RUN yarn install RUN yarn run build CMD yarn run start
2. Shell ファイルを作成
#!/bin/bash BRANCH_RAW_NAME="$1" if [ "$1" == "" ]; then BRANCH_RAW_NAME=$(git symbolic-ref --short HEAD) fi AWS_ACCOUNT_ID={ECRとかECSの頭についてるID} APP_NAME={アプリケーションの名前} BRANCH_NAME=`echo ${BRANCH_RAW_NAME} | sed -e 's/#//g' -e 's/\//_/g'` CLUSTER_NAME=`echo ${APP_NAME}_${BRANCH_NAME} | sed 's/_/-/g'` echo "${BRANCH_RAW_NAME} shining wizzard start" echo 'Create Docker Image' # Dockerログイン $(aws ecr get-login --no-include-email --region ap-northeast-1) # docker Build時に、キャッシュ利用をやめさせるために追加 touch dummyfile # Docker Buildして、push! Imageはいらないので削除。 AWS_IMAGE_NAME=${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/{ECRのフォルダ名}:${BRANCH_NAME} docker build . -t ${AWS_IMAGE_NAME} -f Dockerfile_nuxt --build-arg BRANCH_NAME=${BRANCH_RAW_NAME} docker push ${AWS_IMAGE_NAME} docker rmi ${AWS_IMAGE_NAME} echo 'ECS Task Create' # ECSタスク追加 NET_CONFIG="awsvpcConfiguration={subnets=[{subnet}],securityGroups=[{securitygroup}],assignPublicIp=ENABLED}" VPC=[{VPC}] ROLE={ROLE} echo 'Check Task Definition' TASK_DEFINITION_ARN=$(aws ecs describe-task-definition --task-definition ${CLUSTER_NAME} --query='taskDefinition.taskDefinitionArn') if [ "$TASK_DEFINITION_ARN" == "" ]; then echo 'Create Task Definition' # Task Definition 作成 TASK_DEFINITION= {ECSのタスク定義をJSON形式で挿入} fi echo 'Check Cluster' CLUSTER_STATUS=$(aws ecs describe-clusters --clusters ${CLUSTER_NAME} --query='clusters[].status') CLUSTER_STATUS=`echo $CLUSTER_STATUS | tr '\n' ' ' | sed -e "s/ //g" -e "s/\"//g" -e "s/\[//g" -e "s/\]//g"` // Clusterが存在するかチェック if [ "$CLUSTER_STATUS" == "ACTIVE" ]; then // Clusterが既にあるなら、タスクもあるよねってことで古いタスクはストップ echo 'Old Task STOP' OLD_TASK_ID=$(aws ecs list-tasks --cluster ${CLUSTER_NAME} --query='taskArns[0]') OLD_TASK_ID=`echo $OLD_TASK_ID | sed "s/\"//g"` STOP_TASK_INFO=$(aws ecs stop-task --cluster ${CLUSTER_NAME} --task $OLD_TASK_ID) else # cloudwatch logs groupを作成しておく aws logs create-log-group --log-group-name /ecs/${CLUSTER_NAME} aws logs put-retention-policy --log-group-name /ecs/${CLUSTER_NAME} --retention-in-days 30 # ECSのクラスターとか作成 echo 'ECS Cluster create' aws ecs create-cluster --cluster-name ${CLUSTER_NAME} echo 'Task Definition create' aws ecs register-task-definition --family ${CLUSTER_NAME} --task-role-arn ${ROLE} --execution-role-arn=${ROLE} --network-mode awsvpc --container-definitions ${TASK_DEFINITION} --requires-compatibilities FARGATE --cpu 512 --memory 1024 fi echo 'New Task Run' RUN_TASK_INFO=$(aws ecs run-task --cluster ${CLUSTER_NAME} --task-definition arn:aws:ecs:ap-northeast-1:${AWS_ACCOUNT_ID}:task-definition/${CLUSTER_NAME} --network-configuration ${NET_CONFIG} --launch-type FARGATE) TASK_ID=$(aws ecs list-tasks --cluster ${CLUSTER_NAME} --query='taskArns[0]') TASK_ID=`echo $TASK_ID | sed "s/\"//g"` echo 'タスクが生成されるまで待機' sleep 60 NETWORK_INTERFACE_ID=$(aws ecs describe-tasks --cluster ${CLUSTER_NAME} --tasks ${TASK_ID} --query='tasks[].attachments[].details[?name==`networkInterfaceId`].value[]') NETWORK_INTERFACE_ID=`echo $NETWORK_INTERFACE_ID | sed -e "s/\[ \"//g" -e "s/\" \]//g"` IP_INFO=$(aws ec2 describe-network-interfaces --network-interface-ids ${NETWORK_INTERFACE_ID} --query='NetworkInterfaces[].Association[]') printf "\e[33m IP情報 \e[m" printf "\e[1;31m ${IP_INFO} \e[m"
// ちなみに削除コマンドもここに // task definitionは一個しかないはずなので、一個だけDeregister aws ecs deregister-task-definition --task-definition arn:aws:ecs:ap-northeast-1:${AWS_ACCOUNT_ID}:task-definition/${CLUSTER_NAME}:1 # Taskは常に一個しかないはずなので、一個TaskをSTOP OLD_TASK_ID=$(aws ecs list-tasks --cluster ${CLUSTER_NAME} --query='taskArns[0]') OLD_TASK_ID=`echo $OLD_TASK_ID | sed "s/\"//g"` aws ecs stop-task --cluster ${CLUSTER_NAME} --task $OLD_TASK_ID # クラスターを削除 aws ecs delete-cluster --cluster ${CLUSTER_NAME} # ロググループも削除 aws logs delete-log-group --log-group-name /ecs/${CLUSTER_NAME} printf "\e[33m 削除完了しました \e[m"
3.実行
上のshellファイルを自分の環境に合わせて修正して、実行すればうまく行くはず。
ざっくり流れ
DockerFile上で対象ブランチの変更を取得して、それを利用してDocker buildして、ECR上にDocker push。
ECSのClusterやTask定義、cloudwatchのlog groupを作成して、ECSのTaskを生成。
上記設定だったら、IPアドレスが振られるので、それを表示!
もっとシャイニングウィザードに近づけるために
注意
shellファイル書くの初めてだったので、変なところとかあったらすいません。
terraform とか https://github.com/aws/amazon-ecs-agent で作った方が楽なのかも。
Dockerの部分は、実際はLocalのものをDocker buildして、pushしちゃった方が楽です!
(今回は、一つのコマンドで、Backend API の Railsと、Frontend の Nuxt を行いたかったから、こういう特殊な方法を取りました!)
感想
aws ecs cli をがっつり触るのは初めてだったけど、めっちゃ便利だった。
あと、先輩に流れのアドバイスをもらったぐらいで、作業自体は、自分の力で簡単にできた。