LoginSignup
5
6

More than 3 years have passed since last update.

DevOps with ECS Blue/Green

Last updated at Posted at 2019-06-19

殴り書き。

  1. docker-compose.ymlで楽したい
  2. ECS Blue/Greenを使いたい

ディプロイライフサイクルは二つ

  1. タスク定義の更新に伴ったライフサイクル(インフラの変更に伴うOpsサイクルやDevOpsサイクルにおける環境変数の追加など)
  2. イメージの更新に伴ったライフサイクル(純粋なアプリケーションイメージの更新に伴うDevOpsサイクル)

2のサンプルはいっぱいあるけど、1のサンプルはみつけられなかった。

1の更新では、過去のタスク定義は不要になる。と考える。
2の更新では、タスク定義は基本変わらず、イメージのみ更新していく。(イメージ変更に伴うrevの積立)

1のライフサイクルが大きく周り、その中で2のライフサイクルが小さく回る感じ。
awsのblue/greenのサンプルは、2を対象としている。

1. タスク定義の更新に伴ったライフサイクル

このライフサイクルのゴールは、タスク定義を更新し、それに伴う、 taskdef.jsonと (固定の)appspec.ymlを生成すること。
この二つの成果を用いて、2のライフサイクルを回す。

楽したいのでecs-cliを用いる。

  1. ecs-cliを用いて、docker-compose.yml + ecs-params.ymlから、taskdefを登録する。
  2. この際、既存のtaskdefのrevを全て消す。(変数が変わるため、過去のrevを使うことはほぼないと推測。 汚くしたくない+inactiveのワーニング表示で気持ちいい。)
  3. aws describe-tasks + jqtaskdef.jsonを生成

っということで、上のルールを、 Makefile

準備

docker-compose.yml

2つに分けて書いておくと、良い。

# docker-compose.yml
version: 3.0
services:
  app:
    build: ./app
    image: ...:${VERSION:-latest}

# docker-compose.ecs.yml
version: 3.0
services:
  app:
    env_file:
      - app/.env
    logging:
      ...
    ports:
      - 0:80

ecs-params.yml

version: 1.0
task_definition:
  ...

Makefile

上記のルールをMakefile化しておくと楽。

AWS_PROFILE?=default
TASKDEF_NAME:=my-sample-task
AWS_REGION?=ap-northeast-1
CLUSTER_NAME:=my-cluster

.PHONY: init
init: deinit register-task
  # init 完了後、taskdef.jsonを生成する。
  $(MAKE) -e taskdef.json

# 過去のrev削除
.PHONY: deinit
deinit:
  aws ecs --profile=${AWS_PROFILE} list-task-definitions --family-prefix ${TASKDEF_NAME} --query taskDefinitionArns --output text | xargs -J % -t -n1 aws ecs --profile=${AWS_PROFILE} deregister-task-definition --task-definition %
  rm taskdef.json

# タスク登録
.PHONY: register-task
register-task:
  ecs-cli compose --aws-profile ${AWS_PROFILE} -p ${TASKDEF_NAME} --region ${AWS_REGION} --ecs-params ecs-params.yml --cluster ${CLUSTER_NAME} -f docker-compose.yml -f docker-compose.ecs.yml create --create-log-groups

# taskdef.jsonの生成
taskdef.json:
  # コンテナの数に合わせて、適当に、 `select(.name == "app") | .image) = "<APP_IMAGE>" `を増やす。
  aws ecs --profile=${AWS_PROFILE} describe-task-definition --task-definition ${ECS_PROFILE} |  jq '.taskDefinition | (.containerDefinitions[] | select(.name == "app") | .image) = "<APP_IMAGE>" | del(.taskDefinitionArn, .revision, .status, .requiresAttributes) ' > $@

実行

make init

これで、過去のtaskdefを抹殺+docker-composeにリンクしたtaskdef.jsonに更新される・・・はず。


2. イメージの更新に伴ったライフサイクル

次は、イメージを更新していく。

いろいろな例があるけど、個人的には、 releaseとか prodをpushしたら、あとはpipelineに任せたい。
ってことで・・・。

  1. CodeBuildでポチッとすると、VERSION=prod docker-compose build app
  2. ECRのapp:prodをソースに、CodePipeline (CloudTrailないとイベント飛ばないかも・・・・)
  3. CodePipelineの一発目に、 CodeBuild retag
  4. あとは、定番。 taskdef.json + appspec.yamlからBlue/Green

特記することはないので、
https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/tutorials-ecs-ecr-codedeploy.html
こことかを参考に。

ここでは、 retagだけ。

retag

ECRソースは、taskDetail.jsonを渡してくれる。

https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/file-reference.html#file-reference-ecs-bluegreen
(generated by ECR)

ってこで、taskDetail.jsonを受け取って、 taskDetail.jsonをartifactするCodeBuildを作ればいける。

# ソースがECRなので、consoleのエディタなどでコピペ
# とりあえず適当に
version: 0.2
env:
  variables:
    AWS_DEFAULT_REGION: "ap-northeast-1"
    TARGET_TAG: "prod"
phases:
  install:
    runtime-versions:
      docker: 18
    commands:
      - apt -y update
      - apt -y install jq
  pre_build:
    commands:
      - cat ${CODEBUILD_SRC_DIR}/imageDetail.json
      - REPO=`jq -rM ".ImageURI" ${CODEBUILD_SRC_DIR}/imageDetail.json | awk -F'@|:' '{print $1;}'`
      - DATETIME=`date +%Y%m%d_%H%M`
      - printf 'REPO:%s,TAG=%s,DATETIME=%s' $REPO $TARGET_TAG $DATETIME
      - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
  build:
    commands:
      - docker pull ${REPO}:${TARGET_TAG}
      - docker tag ${REPO}:${TARGET_TAG} ${REPO}:${TARGET_TAG}_${DATETIME}
      - docker push ${REPO}:${TARGET_TAG}_${DATETIME}
  post_build:
    commands:
      - printf '{"ImageURI":"%s"}' ${REPO}:${TARGET_TAG}_${DATETIME} > imageDetail.json
artifacts:
  files:
    - imageDetail.json

まとめ

とりあえずこんな感じ。

5
6
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
5
6