My Note

自己理解のためのブログ

ECS FargateでBlue/Greenデプロイをする

前回のブログでECS Fargateのローリングアップデートについて書きました。 今回はBlue/Greenデプロイについて異なる部分について書いていこうと思います。

yhidetoshi.hatenablog.com

Blue/Greenデプロイについて

■ Step1

デプロイ前の状態で、ALBのプロダクションポート80番、転送先がargetGroupAにトラフィックが流れている状態。

■ Step2

ALBのテストポート8000番、転送先がTargetGroupBにGreenをデプロイしてトラフィックが流れ動作確認できる状態。

■ Step3

Greenのデプロイが完了したらALBのプロダクションポートの転送先をTargetGroupBに向けてBlueにトラフィックが流れないようになる。

f:id:yhidetoshi:20191210071808p:plain

アーキテクチャとデプロイフロー

アーキテクチャ

f:id:yhidetoshi:20191210074821p:plain

■ デプロイフロー

f:id:yhidetoshi:20191210075328p:plain

ALBとターゲットグループを作成する

まずは TargetGroupA を作成。

f:id:yhidetoshi:20191211214928p:plain

次に TargetGroupB を作成。

f:id:yhidetoshi:20191211215105p:plain

次に ALB を作成します。プロダクションポートの80番とテストポートの8000番を追加して、それぞれの転送先は TargetGroupAとBにします。

f:id:yhidetoshi:20191211214719p:plain

これでECSのサービスを作成するときに指定するALBとTargetGroupの作成が完了です。

ECS Fargateのサービスを作成する

では、サービスを作成していきます。今回はBlue/Greenデプロイで作成します。

f:id:yhidetoshi:20191211215358p:plain

↓↓↓

f:id:yhidetoshi:20191211215526p:plain

CodeDeployのサービスロールを選択する必要があるんですが、AWSCodeDeployRoleForECSの権限が必要になります。 以下の方法でIAMロールを新規作成しました。

f:id:yhidetoshi:20191212231427p:plain


f:id:yhidetoshi:20191212231459p:plain


f:id:yhidetoshi:20191212231752p:plain


作成したIAMロールを選択して次にすすみます。NWの設定をします。

f:id:yhidetoshi:20191211215708p:plain

ALBやターゲットグループの設定をしていきます。

f:id:yhidetoshi:20191211220256p:plain

↓↓↓

f:id:yhidetoshi:20191211220647p:plain

↓↓↓

f:id:yhidetoshi:20191212080921p:plain

CodePipelineについて

今回作成したCodePipeline。Sourceの部分は省略してます。

f:id:yhidetoshi:20191212081731p:plain

前回書いたローリングアップデートと異なるCodeBuildとCodeDeployについて書きます。

CodeBuild

buildspec.yml

ローリングアップデートからBlue/Greenデプロイにするためにbuildspec.ymlを修正します。

  • buildspec.yml
version: 0.2

phases:
  install:
    runtime-versions:
      golang: 1.13
      docker: 18
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws --version
      - $(aws ecr get-login --region ap-northeast-1 --no-include-email)
      - REPOSITORY_URI=`aws sts get-caller-identity --query 'Account' --output text`.dkr.ecr.ap-northeast-1.amazonaws.com/go-scraping-api
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - docker build -t $REPOSITORY_URI:latest .
      - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker images...
      - docker push $REPOSITORY_URI:latest
      - docker push $REPOSITORY_URI:$IMAGE_TAG

      - printf '{"Version":"1.0","ImageURI":"%s"}' $REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
artifacts:
  files: imageDetail.json

ローリングアップデートと異なる部分は↓。 imagedefinitions.json --> imageDetail.json に変更する。このファイル名でないとエラーになります。 また、jsonの中身も変わるので注意。

- printf '{"Version":"1.0","ImageURI":"%s"}' $REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
artifacts:
  files: imageDetail.json

taskdef.jsonについて

次に、Blue/Greenデプロイにするために taskdef.jsonファイルが必要になります。yamlで記述してもOKです。今回はjson形式でやります。 XXXXXXXXXXXX の部分はアカウント番号が入るためにマスクしました。

"image": "<IMAGE1_NAME>",

この部分でCodeBuildで作成されたコンテナイメージを参照します。 また、↓のtaskdef.jsonでは "mackerel-container-agent" をサイドカーとして追加しています。不要であればここの定義は削除してください。 このサイドカーでコンテナを監視する記事は別途作成します。

{
  "executionRoleArn": "arn:aws:iam::XXXXXXXXXXXX:role/go-scraping-api",
  "containerDefinitions": [
    {
      "name": "go-scraping-api",
      "image": "<IMAGE1_NAME>",
      "portMappings": [
        {
          "containerPort": 3000,
          "hostPort": 3000,
          "protocol": "tcp"
        }
      ],
      "essential": true,
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/go-scraping-api",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "ecs"
        }
      }
    },
    {
      "name": "mackerel-container-agent",
      "image": "mackerel/mackerel-container-agent:latest",
      "essential": false,
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/go-scraping-api",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "memory": 128,
      "environment" : [
        {
          "name": "MACKEREL_CONTAINER_PLATFORM",
          "value": "fargate"
        },
        {
          "name": "MACKEREL_ROLES",
          "value": "dev:ecs"
        }
      ],
      "secrets": [
        {
          "name": "MACKEREL_APIKEY",
          "valueFrom": "mackerel_agent_apikey"
        }
      ]
    }
  ],
  "requiresCompatibilities": [
    "FARGATE"
  ],
  "networkMode": "awsvpc",
  "cpu": "256",
  "memory": "512",
  "family": "go-scraping-api"
}

タスク実行のロールを↓で付与しています。

"executionRoleArn": "arn:aws:iam::XXXXXXXXXXXX:role/go-scraping-api",

SSMパラメータを参照する必要があるので、↓のポリシーを付与しています。 f:id:yhidetoshi:20191213080643p:plain


f:id:yhidetoshi:20191213080640p:plain

機密情報の取り扱いについて ( SSM連携 )

mackerel-container-agentをサイドカーとして動かしています。ここについては次のブログにて紹介します。

↓↓↓

yhidetoshi.hatenablog.com

CodeDeploy

次にCodeDeployの設定をしていきます。ECSのサービスを作ったときに生成されたCodeDeployのアプリケーションとデプロイグループを利用します。 デプロイプロバイダは ECS (ブルー/グリーン) を選択。 appspec.yaml のファイル名は固定ですが、taskdef.json のファイル名は任意に設定できるのでEnvで分けるときはenvごとに名前をつけることができます。 IAMロールのarnが入るため、envをまたぐときは複数ファイル用意してCodeDeployのファイル名で合わせればいいと思います。

f:id:yhidetoshi:20191212072740p:plain

↓↓↓

f:id:yhidetoshi:20191212073211p:plain

コンテナのログについて

CloudwatchLogsに今回はログを吐き出すようにしています。

  • taskdef.jsonから一部抜粋
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/go-scraping-api",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "ecs"
        }
      }

f:id:yhidetoshi:20191213084618p:plain

Blue/Greenデプロイの確認

検証でここまで設定してBlue/Greenデプロイを実行したので、80/8000番ポートのターゲットグループがそれぞれTargetGroupAになっています。 この状態からBlue/Greenデプロイを実施して動きを確認しようと思います。

ALBのTargetGroupの動き

ALBの設定されているTargetGroupは 80番 / 8000番 ポートに TargetGroupAがセットされています。

f:id:yhidetoshi:20191212074245p:plain

まず、テストポートである8000番ポートにGreenのTargetGroupBがセットされました。

f:id:yhidetoshi:20191212074333p:plain

次に、8000番ポートのTargetGroupBが正常であれば、プロダクションポートのTargetGroupAもGreenのTargetGroupBに変わります。 プロダクションポートとテストポートがそれぞれ、GreenのTargetGroupBに変わることが確認できました。

f:id:yhidetoshi:20191212074609p:plain

Fargateのタスクの動き

Greenのタスクの状態からBlueが追加されました。

f:id:yhidetoshi:20191212075804p:plain

BlueとGreenの状態からBlueが切り離されました。

f:id:yhidetoshi:20191212080039p:plain

動作確認

外部からプロダクションポートの80番とテストポートの8000番にヘルスチェックのAPIを叩いて確認しました。 BGデプロイ中にテストポートにトラフィックが流れるGreenに対して実行してもOKでした。

curl http://go-scraping-api-XXXXXXXXX.ap-northeast-1.elb.amazonaws.com/ping
pong                                                                                                             
curl http://go-scraping-api-XXXXXXXXX.ap-northeast-1.elb.amazonaws.com:8000/ping
pong

さいごに

今回は、前回のローリングアップデートの続きとして、Goコンテナを使ってBlue/Greenデプロイについて検証してまとめました。 サイドカーを使ってmackerelで監視する方法については省略していますが、ssm連携と合わせて書こうと思います。 今回の環境構築は全体像を掴みたいところもあったのでwebコンソールでポチポチ作っていきましたが、CLIやTerraformで環境を作る ところも取り組んでいければと思います。 また、先日の re:Invent で EKSもかなりアップデートがあったので、そちらについても検証をすすめていきたいと思います。

参考

docs.aws.amazon.com

https://d1.awsstatic.com/webinars/jp/pdf/services/20190731_AWS-BlackBelt_AmazonECS_DeepDive_Rev.pdf