Help us understand the problem. What is going on with this article?

AWS ECSでのデプロイをrolling updateからBlue/Greenデプロイに変更する

More than 1 year has passed since last update.

はじめに

こんにちは。Wano株式会社エンジニアのnariと申します。
今週も記事を書いていきます。
今回はECSにおけるデプロイ方法を、デフォルトのrolling updateの仕様がいささか怖いので、Blue/Greenに変更した話を書こうと思います。

今回のプロジェクトでは、ほぼ大体のインフラリソースをTerraformで管理しているため、設定は全てtfファイルで行います、ご了承ください。  

Blue/Greenデプロイ とは

稼働中のBlueに対しては何もせず、別で新しいリビジョンのGreenを作って、任意の戦略に沿って全体をGreenに切り替えていくデプロイのこと。
稼働中の環境に手を加え、変更を重ねる従来のin place方式と比べ、内部の一貫性が保ちやすく、rollbackが容易と言われている。
スクリーンショット 2019-08-18 19.06.32.png
(BlueGreenDeploymentから引用)

ECSでのデプロイの特徴

  • デフォルトのECSのデプロイは、in place方式(rolling update)を採用している。
  • 去年対応したBlue/Green方式はAll at once(全てを一気に切り替える)なRed/Blackデプロイであり、これはBlue/Greenのやり方の一つにすぎないらしい。

ちなみにCanary(部分だけ新しいリビジョンをデプロイして、問題がないかを先に確認すること)なデプロイ設定は対応していないため、行いたい場合は自身でスクリプトを書く必要があります。

何故rolling updateからBlue/Greenデプロイにしたいか

  • 新規リリースによってトラブルが発生しても迅速にロールバックできる
  • 新旧システムの混在の可能性がないため、整合性に気を配る必要がない

というのが、一般的に謳われる理由ですが、加えてcanarycage - AWS ECSへの堅牢なデプロイツール - Qiitaでも書かれているように、

ECSのデフォルトのrolling updateは、壊れたタスクを配置してしまった場合、サービスが落ちる可能性がある

stage環境で試しに運用している際、度々この件で肝を冷やしました、、、
これでは本番運用にあたって、眠れない夜を過ごすことになりそうだったのでBlue/Greenデプロイに変更することとしました。(これによって壊れたタスクであるかがデプロイ前にチェックできるし、デプロイ後に不備が確認されても気軽にrollbackできる)

システム全体像(before/after)

スクリーンショット 2019-08-18 17.27.29.png

  • 上記のようなCD pipelineである
  • この構成は変えずに、deploy stageの仕組みだけ以下のように変える

スクリーンショット 2019-08-18 17.51.21.png

どう変更していったか

1.まず、target group(blue)とそれに対応するlistner、listener ruleを追加する

  • albにport8080許可のsgを追加する必要はない(moduleはこちら)
main.tf
module "ad_blue_listener" {
  source  = "../../modules/common/alb/listener"
  alb_arn = module.laboon_ad_alb.alb.arn
  /* alb listener required */
  listener_port     = 8080
  listener_protocol = "HTTP"
}

/* alb target*/
module "ad_blue_target_group" {
  source            = "../../modules/common/alb/target_group"
  target_group_name = "laboon-stage-blue-target-group"
  alb               = module.laboon_ad_alb.alb
  vpc_id            = local.laboon_stage_vpc_id
  target_port       = 8080
}

/* alb listner_rule*/
module "ad_blue_listener_rule" {
  source = "../../modules/common/alb/listener_rule"
  /* alb listener_rule required */
  listener_arn     = module.ad_blue_listener.lister_arn
  target_group_arn = module.ad_blue_target_group.target_group_arn
}

2.CodeDeploy(Blue/Green対応)を作成する

  • deployment_config_name = "CodeDeployDefault.ECSAllAtOnce"がポイント
main.tf
# CodeDeploy for blue green
resource "aws_codedeploy_app" "app" {
  compute_platform = "ECS"
  name             = var.name
}

resource "aws_codedeploy_deployment_group" "group" {
  app_name               = aws_codedeploy_app.app.name
  deployment_config_name = "CodeDeployDefault.ECSAllAtOnce"
  deployment_group_name  = var.deployment_group_name
  service_role_arn       = var.service_role_arn

  auto_rollback_configuration {
    enabled = true
    events  = ["DEPLOYMENT_FAILURE"]
  }

  blue_green_deployment_config {
    deployment_ready_option {
      action_on_timeout = "CONTINUE_DEPLOYMENT"
    }

    terminate_blue_instances_on_deployment_success {
      action                           = "TERMINATE"
      termination_wait_time_in_minutes = var.termination_wait_time_in_minutes
    }
  }

  deployment_style {
    deployment_option = "WITH_TRAFFIC_CONTROL"
    deployment_type   = "BLUE_GREEN"
  }

  ecs_service {
    cluster_name = var.cluster_name
    service_name = var.service_name
  }

  load_balancer_info {
    target_group_pair_info {
      prod_traffic_route {
        listener_arns = var.listener_arns
      }

      target_group {
        name = var.blue_target_name
      }

      target_group {
        name = var.green_target_name
      }
    }
  }
}

3.Codepipelineのコードのdeploy stageのactionを変更する

  • blue/greenでもrolling updateでも対応できるmoduleに変更する
main.tf
...
  stage {
    name = var.deploy_stage_name

    dynamic "action" {
      for_each = var.has_bg_service ? { dummy : "hoge" } : {}

      content {
        name     = var.ActionName
        category = "Deploy"
        owner    = "AWS"
        provider = "CodeDeployToECS"
        version  = 1
        input_artifacts = [
        "Build"]
        configuration = {
          ApplicationName                = var.ApplicationName
          DeploymentGroupName            = var.DeploymentGroupName
          TaskDefinitionTemplateArtifact = "Build"
          TaskDefinitionTemplatePath     = "taskdef.json"
          AppSpecTemplateArtifact        = "Build"
          AppSpecTemplatePath            = "appspec.yml"
        }
      }
    }

    dynamic "action" {
      for_each = var.has_another_service ? { dummy : "hoge" } : {}

      content {
        name            = var.AnothoerActionName
        category        = "Deploy"
        owner           = "AWS"
        provider        = "ECS"
        version         = 1
        input_artifacts = ["Build"]
        configuration = {
          ClusterName = var.AnothoerClusterName
          ServiceName = var.AnothoerServiceName
          FileName    = var.AnothoerFileName
        }
      }
    }

...

4.CodeDeployの設定ファイル(appspec.yml)を作成する

  • projectの直下に生成する(別ディレクトリで管理し、build時に移動させてもOK)
  • TaskDefinition: "<TASK_DEFINITION>" はいじらずそのままに、ContainerNameとPortだけ変えてください
appspec.yml
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "<TASK_DEFINITION>" 
        LoadBalancerInfo:
          ContainerName: "hogehoge"       
          ContainerPort: "80"

5.CodeBuildの設定ファイル(buildspec.yml)を修正する

  • 一つのサービスの中で、複数のイメージを変更したい場合は、imageDetail.jsonは使わず、taskdef.jsonをsedコマンドで書き換えて渡す。(CodeDeployでECSに複数のImageをBlue/Green Deploymentする方法 - Qiitaより)
  • taskdef-production-template.jsonは自分で作成したタスク定義の中身をそのままコピーし、image部分だけ"image": "<IMAGE_RP_NAME>"のように書き換えておく
buildspec.yml
  post_build:
    commands:
      # 複数のイメージを更新したい場合こうするしかないっぽい
      - cat taskdef-production-template.json | sed -e s@\<IMAGE_APP_NAME\>@$APP_REPOSITORY_URL:latest@ -e s@\<IMAGE_WEB_NAME\>@$WEB_REPOSITORY_URL:latest@ > taskdef.json
artifacts:
  files:
    - taskdef.json
    - appspec.yml

参考文献

fukubaka0825
Site Reliability Engineer at eureka, Inc. devops/AWS/IaC/Terraform/コンテナ/サーバレス/Go/DDD
https://www.fukubaka0825.dev/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした