CodePipelineからECSにBlue/Greenデプロイする

こんにちは、かたいなかです。

以前、ECSがCodeDeployによるBlue/Greenデプロイに対応したことをお伝えしました。

今回は、CodePipelineからECS+CodeDeployへのデプロイを行うことで、CodeBuildでDockerイメージをビルドし、ビルドしたイメージをもとにタスク定義の新しいリビジョンを登録、ECSのサービスを更新するといった一連の流れを自動で行えるようにする方法をご紹介します。

構築するパイプラインの概要

今回は、以下のようなパイプラインを構成します。

構成図

GitHubの特定のブランチを変更を契機に処理を開始します。CodeBuildでイメージをビルドし、ECRにプッシュします。そして、最後にCodeDeploy + ECSを使用してデプロイを行うという流れです。

手順

では、実際にやっていきましょう。

設定としては以下の流れで進めます。

  1. CodeDeployにデプロイを行うための設定ファイル(appspec.yamltaskdef.json)をリポジトリに追加
  2. CodeBuild用の設定ファイル(buildspec.yml)をリポジトリに追加
  3. CodePipeline作成

前提条件

今回は、CodeDeploy + ECSのデプロイを行えるような、ECSのServiceやCodeDeployのデプロイグループが作成されている状態を前提として進めます。こちらの設定方法は以下のリンク先の記事を参考にしてください。

CodeDeploy用の設定ファイルをソースコードに追加

appspec.yaml

appspec.yamlというファイルを以下のような内容で作成し、リポジトリのルートに置いておきます。

version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "<TASK_DEFINITION>" ## <TASK_DEFINITION>という文字列を置き換えることなくそのまま使用してください。
        LoadBalancerInfo:
            ContainerName: "<ALBがトラフィックを流す対象とするコンテナの名前>" ##適切な名前に置き換えてください
            ContainerPort: "<ALBがトラフィックを流す対象とするポート>" ##適切なポート番号に置き換えてください

ハイライトした行のようにTaskDefinitionの部分を<TASK_DEFINITION>というプレースホルダにしておきます。ここは後ほどCodeDeployが置き換えてくれますのでそのままにしておきましょう。コンテナ名とポートは適切な値に置き換えてください。

taskdef.json

次にタスク定義の内容を設定するtaskdef.jsonを以下のような内容で作成し、リポジトリのルートに置いておきます。

{
  "executionRoleArn": "<ECSのタスク実行ロールのARN>",
  "containerDefinitions": [
    {
      "name": "fizzbuzz",
      "image": "<IMAGE1_NAME>",
      "portMappings": [
        {
          "containerPort": 80,
          "hostPort": 80,
          "protocol": "tcp"
        }
      ],
      "essential": true
    }
  ],
  "requiresCompatibilities": ["FARGATE"],
  "networkMode": "awsvpc",
  "cpu": "256",
  "memory": "512",
  "family": "<タスク定義のファミリー名>"
}

ハイライトした行のようにimageの部分を<IMAGE1_NAME>というプレースホルダにしておきます。ここは後ほどCodeDeployが置き換えてくれますのでそのままにしておきましょう。ECSのタスク実行ロールのARNとタスク定義のファミリー名の部分は置き換えてください。

CodeBuild用の設定ファイルを準備

CodeBuildで行う処理を指定するのbuildspec.ymlというファイルをリポジトリのルートに作成します。

version: 0.2

phases:
  pre_build:
    commands:
      ## ECRにログイン
      - $(aws ecr get-login --no-include-email --region $AWS_DEFAULT_REGION)

      ## Dockerイメージのタグとして使用するため、Gitのコミットハッシュを取得
      - IMAGE_TAG=$CODEBUILD_RESOLVED_SOURCE_VERSION
  build:
    commands:
      ## Dockerイメージのビルド
      - docker build -t $IMAGE_REPOSITORY_NAME:$IMAGE_TAG .

      ## DockerイメージのECRへのプッシュ
      - docker push $IMAGE_REPOSITORY_NAME:$IMAGE_TAG

      ## ECS+CodeDeployにどのイメージを使用するか指示するためのファイルを作成
      - printf '{"Version":"1.0","ImageURI":"%s"}' $IMAGE_REPOSITORY_NAME:$IMAGE_TAG > imageDetail.json

artifacts:
  ## buildの最後で作成したファイルをアーティファクトとして流す
  files: imageDetail.json

ハイライトした行で、以下のようなファイルをimageDetail.jsonという名前で作成し、アーティファクトとして指定しています。

{
    "Version":"1.0",
    "ImageURI": "<DockerイメージのURI>"
}

このようにしておくことで、後ほど、CodeDeploy+ECSでのデプロイ時にtaskdef.json内でプレースホルダにしておいた部分が、ここでビルドしたイメージの情報で置き換えることができます。

CodePipeline構築

最後に、CodePipelineを構築していきます。

デプロイの部分でCodePipelineの作成時のウィザードでは設定が行えない項目があるため、GitHubとCodeBuildのみのパイプラインを先に作成し、後からECS (Blue/Green) のステージを追加する形ですすめます。

GitHub + CodeBuildのパイプラインを先に作成

この部分に関してはCodeDeploy + ECSにデプロイする場合に限ったものではないためこの記事内では割愛します。例えば、以下の記事を参考にデプロイ部分の設定をスキップする形で設定してください。

パイプラインにECS (Blue/Green) でデプロイを行うステージ、アクションを追加

パイプラインの一番うしろに新しいステージを作成し、そのステージ内に以下のような設定でアクションを作成します。

設定項目 設定値  備考
アクション名 <任意> ここで行う処理がわかりやすい名前
アクションプロバイダ Amazon ECS(Blue/Green)
AWS CodeDeploy アプリケーション名 <先に作成しておいたアプリケーション名> 
AWS CodeDeploy デプロイグループ <先に作成しておいたアプリケーション名>
Amazon ECS タスク定義 SourceArtifact taskdef.json Sourceの出力アーティファクトと先に作っておいたファイルを指定
AWS CodeDeploy AppSpec ファイル SourceArtifact appspec.yaml Sourceの出力アーティファクトと先に作っておいたファイルを指定
入力アーティファクトとイメージの詳細 BuildArtifact Buildの出力アーティファクト imageDetail.jsonを含む
タスク定義のプレースホルダーテキスト  IMAGE1_NAME taskdef.json内のプレースホルダ
入力アーティファクト SourceArtifact BuildArtifact Source,Buildの出力アーティファクト

動作確認

パイプラインの画面で変更のリリースボタンをクリックすると、新たなデプロイが開始されます。正しく設定が行われていれば、一連の処理が実行されECSへのデプロイが実行されます。

なお、CodeDeployで、置き換え先環境にトラフィックを向けるのを指示されるまで待つ設定や置き換え前の環境を一定時間残すように設定している場合は、トラフィックの向き先を変える処理や置き換え前の環境の削除が完了した時点でCodePipeline側で処理が完了した扱いになるようです。

まとめ

CodePipelineの一連の処理の中にECS+CodeDeployでのBlue/Greenデプロイを組み込む方法をご紹介しました。

CodePipelineから実行することで、ソースコードの変更からデプロイまでの一連の流れの中にECSへのBlueGreenデプロイを組み込むことができます。

参考