AWS CDKを(途中まで)使用してAmazon ECSの Blue/Greenデプロイ環境を構築する | SEEDS Creators' Blog | 株式会社シーズ

AWS CDKを(途中まで)使用してAmazon ECSの Blue/Greenデプロイ環境を構築する

クラウド事業部エンジニアの川勝です。
AWS CDKを使用してAmazon ECSのBlue/Greenデプロイをcdk deploy一発たたけば構築完了!
としたかったのですが、出来なかった備忘録です。

Amazon ECSでBlue/Greenデプロイを行うにはAWS CodeDeployを使用することで実現ができます。
そこで早速AWS CDKのドキュメントを見て進めようとすると…

AWS CodeDeploy is a deployment service that automates application deployments to Amazon EC2 instances, on-premises instances, serverless Lambda functions, or Amazon ECS services.

The CDK currently supports Amazon EC2, on-premise and AWS Lambda applications.

https://docs.aws.amazon.com/cdk/api/latest/docs/aws-codedeploy-readme.html

はい。
よく見るとAWS CodeDeployはAmazon ECSにデプロイするサービスといいつつサポートされているなかにAmazon ECSがありません…

AWS CodeDeployのアプリケーションの作成はできますが、
肝心のデプロイグループの方をみるとCloudFormationがサポートするまではcdk外部で作成されているものをインポートできる、、、というように記載されていました。
https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-codedeploy.EcsDeploymentGroup.html

無念…

ただせっかくなので初期構築のためにサクッとAWS CDKでAmazon ECSをつくってからコンソールでAWS CodeDeployを作ってみたいと思います。

取り上げないこと

  • Blue/Greenデプロイそのものについての説明
  • AWS CDKの使い方
  • Amazon ECR の作成とpushの方法

AWC CDKに関してはチュートリアルのリンクなどを本文中に貼っていますのでそちらをご覧ください。
Amazon ECRに関してはこちらを。

Blue/Greenデプロイについてはリンクはつけていないですが検索すると情報はいろいろでてきます。

Amazon ECS (Fargate) の作成
aws-ecs-patterns moduleを使う

チュートリアル にあるのですが、AWS CDKに aws-ecs-patterns module というものがあります。
ゼロからコンソールで作成する場合Amazon ECSの作成のほかにロードバランサーの作成をして、、などちょっと手間がかかりますが、aws-ecs-patternsを使用すると簡単に動くところまで作成してくれます。

以下は基本的にチュートリアルのコードですが少し変更しています。

  • imageの指定をECRから取得するように変更
  • クラスターとサービスに名前を設定
import * as cdk from '@aws-cdk/core'
import * as ec2 from '@aws-cdk/aws-ec2'
import * as ecs from '@aws-cdk/aws-ecs'
import * as ecr from '@aws-cdk/aws-ecr'
import * as ecs_patterns from '@aws-cdk/aws-ecs-patterns'

export class FargateSampleStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props)

    // AZは最低2つ選択する必要があったので2
    const vpc = new ec2.Vpc(this, 'fargateSampleVpc', {
      maxAzs: 2,
    })

    const cluster = new ecs.Cluster(this, 'fargateSampleCluster', {
      clusterName: 'fargateSampleCluster',
      vpc: vpc,
    })

    // Amazon ECRのリポジトリを指定。事前に作成してpushしておきます。
    const repository = ecr.Repository.fromRepositoryName(
      this,
      'fargateSampleGo',
      'fargate-sample-go'
    )

    // ALBの作成〜Fargateでコンテナ作成までいい感じにつくってくれます。
    // cpu, memoryは最小値にしています。
    new ecs_patterns.ApplicationLoadBalancedFargateService(
      this,
      'fargateSampleService',
      {
        serviceName: 'fargateSampleService',
        cluster: cluster,
        cpu: 256,
        desiredCount: 1,
        taskImageOptions: {
          image: ecs.ContainerImage.fromEcrRepository(repository),
        },
        memoryLimitMiB: 512,
        publicLoadBalancer: true,
      }
    )
  }
}

クラスターとサービスに名前を設定、は地味に設定しておかないと
スタック名+インスタンスのID+ハッシュ値
みたいに自動生成されるので思ってたんと違う、、、みたいになってしまいました。気にならなければユニークな名称になるので設定しなくても問題はありません。
(ちなみに上記コードだとロードバランサーは自動作成されるのでそちらの名前自動生成されます…)

clusterNameを指定しなかった場合…

それではcdk deployを実行します。
ECRは事前に作成してコンテナをプッシュしておきましょう。

…ちょっと間があってdeploy完了したものとします。
できあがったリソースを確認。

クラスターとサービス
ロードバランサー(自動作成なのでFaga-fagaになっちゃった…)

この状態でロードバランサーのDNSにアクセスすると動いている状態になっています。簡単ですね!

ここからBlue/Greenデプロイをするためにはサービスを作り直す必要があるので新規に作成していきます。

Blue/Greenデプロイ用のサービス作成

事前にAWS CodeDeploy用にIAM roleを作成しておきます。必要な権限は以下を参照してください。
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/codedeploy_IAM_role.html

ステップ1

デプロイメントでBlue/Greenデプロイメントを選択します。
CodeDeployのサービスロールは先程作成したロールを選択します。

ステップ2

VPCとセキュリティグループでAWS CDKで作成されたVPCとサブネットを選択します。
サブネットは今回2つのAZにパブリック/プライベート用のサブネットが作成されいるため4つ候補がでてきます。
ロードバランサーに設定しているサブネット(プライベート用の2つ)と同じにしましょう。
(最初適当選んでしまったのでロードバランサーからつながらなくなってやり直しました….)
セキュリティグループは編集を押してAWS CDKが自動作成したものを設定します。

次にロードバランシングでApplication Load Blancerを選択、ロードバランサーに追加を選択します。

テストなのでロードバランス用のコンテナでテストリスナーはチェックを外しています。
次にAdditional configurationでターゲットグループを2つ設定する必要があります。
1つはAWS CDKで作成されたもの、2つ目は新規作成を選択します。

サービスの検出は今回不要なので無効にしています。

ステップ3はAutoScalingの設定なので今回は飛ばして、ステップ4で確認したのち作成します。

…またまたちょっと間があって完了したらAWS CodeDeployを見てみましょう。

自動でアプリケーションが作成されています!
ではこれをつかって実際にデプロイしてみましょう。

AWS CodeDeployからデプロイ

Amazon ECSのサービスの更新から「新しいデプロイの強制」にチェックを入れるとAWS CodeDeployでデプロイを実行してくれますが、中身を把握するためにAWS CodeDeploy側から実行したいと思います。
実行するにはデプロイグループから新しいデプロイを作成します。

…が、その前に今回はテストなのでちょっとデプロイグループの設定を変更しておきます。
CodeDeploy > アプリケーション からデプロイグループを選択して編集します。

変更箇所は最下部の デプロイ設定 > 元リビジョンの終了 です。
Blue/GreenデプロイはALB or NLB のターゲットグループを切り替えることで実現していますが、デプロイ完了後に一定時間もとのターゲットグループに紐づくECSタスクを残す設定です。
デプロイ後に問題があれば即ロールバックできるようにするためのものですが、デフォルトだと待機時間が1時間になっており、テスト用途だとそんなに待てないので最小値の5分に変更しています。

では本題のデプロイを作成していきましょう。
デプロイグループを選択後に「デプロイの作成」を行います。 

設定が必要なものはAppSpecです。
今回はコンソールから入力するので気にならないのですが、ファイルとして配置する場合は拡張子がymlではなくyamlなので注意が必要です。
以下をみるとEC2/オンプレミスコンピューティングプラットフォームだとymlらしいのですが、、、なぜ…
https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/reference-appspec-file.html#appspec-reference-ecs

以下appspecに入力する最低限の内容です。

version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: <TASK_DEFINITION>
        LoadBalancerInfo:
          ContainerName: web
          ContainerPort: 80

<TASK_DEFINITION>のところはデプロイしたECSのタスク定義のARNに置換してください。(地味に調べにくいのですがjsonのtaskDefinitionArnから調べました。)

AppSpecを入力して「デプロイの作成」を完了するとデプロイが実行されます。

デプロイの進捗状況が確認できます。

新規タスクの実行が完了して、ルーティングも完了した状態。
先程設定した5分の待機状態です。
この状態の時だと「デプロイを停止してロールバック」というボタンが表示されており、実行するともとのルーティングに切り替えてロールバックがさせることができます。

待機時間を追えると元リビジョンは削除されデプロイが完了します。

さて完了したらターゲットグループが変わっていることを確認。
今回だとAWS CDKで作成されたものから、サービスを作り直したときに新規に追加したターゲットグループ(Faga-faga-2)に変わっていればOKです。

デプロイ完了!

検証が終わったらcdk destoryコマンドで作成したリソースを削除しますが、
この時手動で作成したAmazon ECSのサービスがあるとクラスターの削除が出来きないので実行前に手動で削除しておきましょう。
AWS CodeDeployは削除していなくても大丈夫です。

締め

Amazon ECSのBlue/Greenデプロイを試してみました。本当はAWS CDKだけで完結したかったのですが2020/05現在未対応だったのが残念でした。(CDKが、というよりCloudFromationが)
これからも開発の動向は追いかけていきたいと思います。

そして今回デプロイの仕組みが出来上がりましたので次回はAWS CodePipelineを使用してgit pushからデプロイまで一気に実行!というのをやってみたいと思います!

以上、川勝でした。