異なるAWSアカウントでBlue/Green自動デプロイする(クロスアカウントデプロイ@ECS Fargate)


マルチアカウント環境でAWS FargateでBlue/Greenの自動デプロイを設定します(開発環境->本番環境へのデプロイを想定)。
ソースレポジトリにはGitHub、ビルドにはCodeBuild、デプロイパイプラインにはCodePipelineを用います。
自動ビルドの設定は、CloudFormationとマネジメントコンソールを利用します。

※以下の手順を実行すると、AWSのサービス利用料が発生します。
※開発環境(デプロイ元環境)、本番環境(デプロイ先環境)の前提で記載します。
※開発環境(デプロイ元環境)、本番環境(デプロイ先環境)のBlue/Greenデプロイ設定は完了しているものとします。
FargateのBlue/Greenデプロイ設定は、以下を参照してください。
ECS FargateでBlue/Greenデプロイ設定

※開発環境(デプロイ元環境)のBlue/Green自動デプロイ設定は完了しているものとします。
FargateのBlue/Green自動デプロイは、以下を参照してください。
ECS FargateでGitHubのWebHookでBlue/Green自動デプロイする

※今回のサンプルは以下を利用しています。
https://github.com/Thirosue/cross-accout-deploy-sample

構成イメージ

今回の構成のイメージは下図のとおりです。

1. 本番環境(デプロイ先環境)の前準備

ECR Pushを許可するロールおよびデプロイ設定ファイルを配置するS3へのアクセスを許可するロールを作成(開発環境->本番環境)します。
->以下yamlAWS CLIかマネジメントコンソールで実行します(CloudFormation)。

yaml
  # ------------------------------------------------------------#
  #  Cross Account Role For Develop Account
  # ------------------------------------------------------------#
  ECRRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            AWS: !Sub arn:aws:iam::${TargetAccountId}:root #開発環境のアカウントIDをParameterに設定
          Action: sts:AssumeRole 
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess #必要に応じ権限を絞る
      RoleName: auto-deploy-for-fargate

  # ------------------------------------------------------------#
  #  Bucket For DeploySetting 
  # ------------------------------------------------------------#
  ECSDeploySettingBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${Env}-${ServiceName}-auto-deploy
      VersioningConfiguration:
        Status: Enabled
  ECSDeploySettingBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref ECSDeploySettingBucket
      PolicyDocument:
        Id: "Allow-Cross-Account-Access"
        Version: '2012-10-17'
        Statement:
          - Sid: CrossAccountBucketPolicy
            Effect: Allow
            Principal:
              AWS: !Sub arn:aws:iam::${TargetAccountId}:root #開発環境のアカウントIDをParameterに設定
            Action:
              - s3:*
            Resource: !Sub arn:aws:s3:::${Env}-${ServiceName}-auto-deploy/*

※設定ファイルの全量は以下を参照してください。
https://github.com/Thirosue/cross-accout-deploy-sample/blob/master/provisioning/prod/CrossAccountDeployForFargate.yaml

2. 開発環境(デプロイ元環境)の追加設定(ビルド/デプロイ)

CodeBuildに以下を追加し、本番環境にコンテナイメージおよびデプロイ設定ファイルを連携します。
->ユーザスイッチせずにPushするとCodePipelineがトリガーされないため、アカウントをスイッチします。
->タスク定義ファイル(taskdef.json)などAWSアカウントIDを指定する必要のあるファイルは、CodeBuildの環境変数で置換します。

buildspec.yaml
 - echo ########## 【Production】 Prepare Deploy Settings ... on `date`
 - PROD_REPOSITORY_URI=${PROD_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${IMAGE_NAME}  #本番のアカウントIDでURIを生成
 - docker tag $REPOSITORY_URI:latest $PROD_REPOSITORY_URI:latest
 - docker tag $REPOSITORY_URI:$IMAGE_TAG $PROD_REPOSITORY_URI:$IMAGE_TAG
 - sed -i -e "s/__ACOUNTID__/$PROD_ACCOUNT_ID/" .prod-config  #クレデンシャルファイルを本番のアカウントIDで置換
 - echo ########## 【Production】 Pushing the Docker image ... on `date`
 - export AWS_CONFIG_FILE=.prod-config #クレデンシャルファイルパスを環境変数から設定
 - $(aws ecr get-login --no-include-email --region $AWS_DEFAULT_REGION --profile cross-account-role) #本番環境で付与したアカウントでECRにログインする
 - docker push $PROD_REPOSITORY_URI:latest #本番アカウントにイメージPush
 - docker push $PROD_REPOSITORY_URI:$IMAGE_TAG
 - echo ########## 【Production】 Writing Task Definitions file...
 - sed -i -e "s/__ACOUNTID__/$PROD_ACCOUNT_ID/" taskdef.json  #タスク定義ファイルを本番のアカウントIDで置換
 - echo ########## 【Production】 Writing image definitions file...
 - printf '{"Version":"1.0","ImageURI":"%s"}' $PROD_REPOSITORY_URI:$IMAGE_TAG > imageDetail.json #イメージ定義ファイルの書き出し
 - echo ########## 【Production】 Sync Deploy Setting file...
 - aws s3 cp appspec.yaml s3://prod-sample-app-auto-deploy/ --acl bucket-owner-full-control #本番アカウントでアクセスできるようにbucket-owner-full-controlオプション付与
 - aws s3 cp taskdef.json s3://prod-sample-app-auto-deploy/ --acl bucket-owner-full-control
 - aws s3 cp imageDetail.json s3://prod-sample-app-auto-deploy/ --acl bucket-owner-full-control
.prod-config
[profile cross-account-role]
credential_source = EcsContainer
role_arn = arn:aws:iam::__ACOUNTID__:role/auto-deploy-for-fargate ## 1.で作成したロール ※アカウントIDを置換
taskdef.json
{
  "executionRoleArn": "arn:aws:iam::__ACOUNTID__:role/sample-app-ECSTaskExecutionRole", ## アカウントIDを置換
  "containerDefinitions": [
    {
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/var/log/sample-app",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "front"
        }
      },
      "portMappings": [
        {
          "hostPort": 80,
          "protocol": "tcp",
          "containerPort": 80
        }
      ],
      "image": "<IMAGE_NAME>",
      "name": "front"
    }
  ],
  "memory": "512",
  "compatibilities": ["EC2", "FARGATE"],
  "family": "sample-app-front",
  "requiresCompatibilities": ["FARGATE"],
  "networkMode": "awsvpc",
  "cpu": "256"
}
imageDetail.json
{"Version":"1.0","ImageURI":"__ACOUNTID__.dkr.ecr.ap-northeast-1.amazonaws.com/sample-app:ec5b627"}

※設定ファイルの全量は以下を参照してください。
https://github.com/Thirosue/cross-accout-deploy-sample/blob/master/buildspecForCrossAccount.yml

※本番のAWSアカウントへAssumeRoleせずにイメージをPushするとCodePipelineがトリガーされません(以下、引用元)。
CodeBuildのビルド内でAssumeRole(クロスアカウントアクセス)する方法とハマった話

3. 本番環境(デプロイ先環境)のビルドパイプライン生成

最後に本番環境のビルドパイプラインをCloudFormationとマネジメントコンソールで作成します。
-> ターゲットソースはECR
->S3に連携したデプロイ設定ファイル(appspec.yamltaskdef.jsonimageDetail.json)を元にCodeDeployFargateにデプロイします。

3-1. パイプラインのビルドステージまで作成

以下yamlAWS CLIかマネジメントコンソールで実行します(CloudFormation)。

yaml
  BuildProject:
    Type: AWS::CodeBuild::Project
    Properties:
      Artifacts:
        Type: CODEPIPELINE
      Source:
        Type: CODEPIPELINE
        BuildSpec: |
          version: 0.2
          phases:
            install:
              runtime-versions:
                docker: 18
            post_build:
              commands:
                - aws s3 cp s3://prod-sample-app-auto-deploy/appspec.yaml . ## 連携されたデプロイ設定ファイルを取得
                - aws s3 cp s3://prod-sample-app-auto-deploy/taskdef.json .
                - aws s3 cp s3://prod-sample-app-auto-deploy/imageDetail.json .
                - cat imageDetail.json
          artifacts:
            files:
                - appspec.yaml
                - taskdef.json
                - imageDetail.json
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/standard:3.0
        Type: LINUX_CONTAINER
        PrivilegedMode: true
      LogsConfig:
        CloudWatchLogs:
          Status: ENABLED
      Name: !Sub ${ServiceName}-codebuild
      ServiceRole: !Ref CodeBuildServiceRole

  BuildPipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: !Sub ${ServiceName}-codepipeline
      RoleArn: !GetAtt CodePipelineServiceRole.Arn
      ArtifactStore:
        Type: S3
        Location: !Ref ECSDeploySettingBucket
      Stages:
      - Name: Source
        Actions:
        - ActionTypeId:
            Category: Source
            Owner: AWS
            Provider: ECR ## ECR Push Trigger
            Version: 1
          Name: Source
          Configuration: 
            RepositoryName: !Ref ServiceName
          OutputArtifacts:
            - Name: BuildSource
          RunOrder: 1
      - Name: Build
        Actions:
        - ActionTypeId:
            Category: Build
            Owner: AWS
            Provider: CodeBuild
            Version: 1
          Name: Build
          Configuration:
            ProjectName: !Ref BuildProject
          InputArtifacts:
            - Name: BuildSource
          OutputArtifacts:
            - Name: BuildOutPut
          RunOrder: 1

※設定ファイルの全量は以下を参照してください。
https://github.com/Thirosue/cross-accout-deploy-sample/blob/master/provisioning/prod/CrossAccountDeployForFargate.yaml

3-2. パイプラインのデプロイステージを追加

作成したビルドパイプラインの末尾にBlue/Greenデプロイ設定を追加します。
設定方法は、以下を参照してください。
ECS FargateでBlue/Greenデプロイ設定

クロスアカウントの自動デプロイ確認

変更をGitHubにPushします。
->サンプルプロジェクトであれば、index.htmlのタイトルを修正してみてください。

以下のとおり開発環境および本番環境に正常にデプロイされるはずです。