LoginSignup
0
0

More than 3 years have passed since last update.

PHP Slim 環境構築(16) ECS(EC2)+ローリングデプロイ

Posted at

PHP Slim 環境構築(16) ECS(EC2)+ローリングデプロイ

Introduction

前回は、ECS(Fargate)の構築を行いました。
今回は、一つ戻って、ECS(EC2)のDeployを試してみます。

この一連のシリーズは、自分への備忘録が第一目的のため、だいぶ不親切です。
申し訳ございません。

ローリングデプロイ

手動

手動で行う方法は、前々回に記載していますが、適当なec2のshell上で、docker build → docker push → task 更新 → サービス更新です (詳細は割愛)。

# docker build -t hoge-repo:latest --build-arg environment=devaws -f compose/web_hoge/Dockerfile.ecs .
# docker tag hoge-repo:latest $ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/hoge-repo:latest
# docker push $ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/hoge-repo:latest

コンソールでタスク更新。そのタスクを使ってサービス更新。

バッチ

次に、Code Deployなどを使わずにバッチでやってみます。なお、デプロイの種類はローリングデプロイです。Blue/Greenはまた後程。。
バッチは、--service-updateオプションを付けるとサービス更新まで行うようにしています。付けない場合は、タスク更新までです。
なお、バッチ内でawsコマンドを実行しているのでaws configureによる設定済みであると想定しています。また、json解析にjqコマンドを用いています。
インストールされていない場合は、yum install jqコマンドでインストールしておいてください。

#!/bin/sh

# サービス更新するかどうか
SERVICE_UPDATE=0

# 引数解析
for OPT in "$@"
do
    case $OPT in
        --service-update)
            SERVICE_UPDATE=1
        shift 1
        ;;
    esac
done

# Git update (必要ならgit checkoutなども)
git pull

# ECRのログイン手続き
$(aws ecr get-login --no-include-email)

# ECRのリポジトリ名
REPOSITORY="slimtemplate"

# ECSに定義済みのtask名とcluster名
TASK_NAME="hoge-task"
CLUSTER_NAME="hoge-cluster"

# Dockerタグ
DOCKER_TAG="hoge-repo"

# docker buildする際にenvironment環境変数に引き渡す値
ENVIRONMENT="devaws"

# ECRのDockerイメージに付けられるタグ
NOW=$(date -u "+%Y%m%d%H%M%S")

# バッチ実行者のAWSアカウント
ACCOUNT_ID=$(aws sts get-caller-identity | grep Account | grep -oE '[0-9]+')

# docker buildを行う。更新の有無を確認するために、ビルド前後のイメージのidを取得する
OLD_IMAGE_ID=$(docker images ${DOCKER_TAG}:latest -q)
docker build -t ${DOCKER_TAG}:latest --build-arg environment=${ENVIRONMENT} -f compose/web_hoge/Dockerfile.ecs .
NEW_IMAGE_ID=$(docker images ${DOCKER_TAG}:latest -q)

if [ "${OLD_IMAGE_ID}" == "${NEW_IMAGE_ID}" ]; then
  # 更新なし
  echo "Docker image is not updated."
  exit 0
fi

# タグ付けおよびECRへのPUSH
docker tag ${DOCKER_TAG}:latest ${DOCKER_TAG}:${NOW}
docker tag ${DOCKER_TAG}:${NOW} ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${REPOSITORY}:${NOW}
docker push ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${REPOSITORY}:${NOW}
docker tag ${DOCKER_TAG}:latest ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${REPOSITORY}:latest
docker push  ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${REPOSITORY}:latest

# タスクを更新
aws ecs describe-task-definition --task-definition ${TASK_NAME} > /tmp/task.json

cat <<__COMMAND__ | tr '\n' ' ' > /tmp/task.cmd
aws ecs register-task-definition --family ${TASK_NAME} --container-definitions
'$(cat /tmp/task.json | jq -Mc '.taskDefinition.containerDefinitions' \
   | sed -re "s|(\"image\":[^:]*):[0-9a-z]*|\1:${NOW}|")'
--task-role-arn $(cat /tmp/task.json | jq -Mc '.taskDefinition.taskRoleArn')
--volumes '$(cat /tmp/task.json | jq -Mc '.taskDefinition.volumes')'
> /dev/null
__COMMAND__

sh /tmp/task.cmd

if [ "$SERVICE_UPDATE" == "1" ]; then
  # 最新のタスク定義family:revisionを取得
  REVISION=$(aws ecs describe-task-definition --task-definition ${TASK_NAME} | jq -M '.taskDefinition.revision')

  # サービス名を取得
  SERVICE=$(aws ecs list-services --cluster ${CLUSTER_NAME} | grep -oP '(?<=[0-9]:service/)([a-z0-9-]+)')

  # サービスのタスクを更新
  aws ecs update-service --cluster ${CLUSTER_NAME} --service ${SERVICE} --task-definition ${TASK_NAME}:${REVISION} > /dev/null
fi

少しずつ見ていきます。まずはコマンドラインの処理。

#!/bin/sh

# サービス更新するかどうか
SERVICE_UPDATE=0

# 引数解析
for OPT in "$@"
do
    case $OPT in
        --service-update)
            SERVICE_UPDATE=1
        shift 1
        ;;
    esac
done

すでにgitリポジトリがあるものとしています。もし無い場合は、あらかじめgit checkoutなどをしておく必要があります。

# Git update (必要ならgit checkoutなども)
git pull

スクリプトなどでecrを使うための処理です。ECRに対して適当なdocker loginコマンドを実行してくれます。

# ECRのログイン手続き
$(aws ecr get-login --no-include-email)

リポジトリ名などの設定です。また、作成したdockerイメージには、ビルドの日時をタグとして張り付けるようにしています。そのための日付を取得しています。
また、このコマンドを実行しているユーザーのAWSアカウントも取得しています。

# ECRのリポジトリ名
REPOSITORY="slimtemplate"

# ECSに定義済みのtask名とcluster名
TASK_NAME="hoge-task"
CLUSTER_NAME="hoge-cluster"

# Dockerタグ
DOCKER_TAG="hoge-repo"

# docker buildする際にenvironment環境変数に引き渡す値
ENVIRONMENT="devaws"

# ECRのDockerイメージに付けられるタグ
NOW=$(date -u "+%Y%m%d%H%M%S")

# バッチ実行者のAWSアカウント
ACCOUNT_ID=$(aws sts get-caller-identity | grep Account | grep -oE '[0-9]+')

docker buildします。更新が無い場合は、ここでおしまい。

# docker buildを行う。更新の有無を確認するために、ビルド前後のイメージのidを取得する
OLD_IMAGE_ID=$(docker images ${DOCKER_TAG}:latest -q)
docker build -t ${DOCKER_TAG}:latest --build-arg environment=${ENVIRONMENT} -f compose/web_hoge/Dockerfile.ecs .
NEW_IMAGE_ID=$(docker images ${DOCKER_TAG}:latest -q)

if [ "${OLD_IMAGE_ID}" == "${NEW_IMAGE_ID}" ]; then
  # 更新なし
  echo "Docker image is not updated."
  exit 0
fi

buildしたイメージに対して、"latest"以外に日付のタグも付けます。
これにより、最新のイメージは"latest"タグで更新され、それ以外のイメージは日付のタグが残ることになります(残り続けるのでなんらかの掃除が必要になります)。
そして、ECRにdockerイメージをpushします。"latest"イメージは上書きされますが、日付イメージはECR内に残ります。これは、なんらかの理由でバックグレードするためのものです。

# タグ付けおよびECRへのPUSH
docker tag ${DOCKER_TAG}:latest ${DOCKER_TAG}:${NOW}
docker tag ${DOCKER_TAG}:${NOW} ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${REPOSITORY}:${NOW}
docker push ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${REPOSITORY}:${NOW}
docker tag ${DOCKER_TAG}:latest ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${REPOSITORY}:latest
docker push  ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${REPOSITORY}:latest

タスクを更新します。ただし、新規でタスク定義用のjsonを作るのではなく、既存のタスクのjsonのイメージだけを書き換えるようにしています。
したがって、初回は手動でタスクを定義する必要があります。
また、イメージのタグには、日付タグのイメージを指定しています。これにより、サービス内のタスクを前のバージョンにすれば、サービスが巻き戻るようになります(latestにするとイメージが上書きされてしまうため)。

# タスクを更新
aws ecs describe-task-definition --task-definition ${TASK_NAME} > /tmp/task.json

cat <<__COMMAND__ | tr '\n' ' ' > /tmp/task.cmd
aws ecs register-task-definition --family ${TASK_NAME} --container-definitions
'$(cat /tmp/task.json | jq -Mc '.taskDefinition.containerDefinitions' \
   | sed -re "s|(\"image\":[^:]*):[0-9a-z]*|\1:${NOW}|")'
--task-role-arn $(cat /tmp/task.json | jq -Mc '.taskDefinition.taskRoleArn')
--volumes '$(cat /tmp/task.json | jq -Mc '.taskDefinition.volumes')'
> /dev/null
__COMMAND__

sh /tmp/task.cmd

必要であれば、サービスを更新します。使用するタスクのrevisionは最新のものを自動的に取得しています。

if [ "$SERVICE_UPDATE" == "1" ]; then
  # 最新のタスク定義family:revisionを取得
  REVISION=$(aws ecs describe-task-definition --task-definition ${TASK_NAME} | jq -M '.taskDefinition.revision')

  # サービス名を取得
  SERVICE=$(aws ecs list-services --cluster ${CLUSTER_NAME} | grep -oP '(?<=[0-9]:service/)([a-z0-9-]+)')

  # サービスのタスクを更新
  aws ecs update-service --cluster ${CLUSTER_NAME} --service ${SERVICE} --task-definition ${TASK_NAME}:${REVISION} > /dev/null
fi

CodeDeploy

えー、結論から言うと、現在のバージョンではCodeDeployはローリングデプロイをサポートしてません。
どちらかと言うとBlue/Greenデプロイを使うべきということですね。(たぶん)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0