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

CodeDeployでECSに複数のImageをBlue/Green Deploymentする方法

More than 1 year has passed since last update.

CodePipelineとCodeDeployを使って、ECSへBlue/Green Deploymentをする。ただし2つ以上のDockerイメージを持ったタスク定義の場合、少しドキュメントとは異なった手法を取らなければならない。今回はTerraformのサンプルも交えつつ紹介する。

結論から言うと、imageDetail.jsonは使わず、taskdef.jsonをsedコマンドで書き換えて渡す。

CodeBuildで行う処理

buildspec.ymlは以下のようにする。

buildspec.yml
version: 0.2

phases:
  install:
    commands:
      - pip3 install awscli --upgrade --user
  pre_build:
    commands:
      - $(aws ecr get-login --no-include-email --region ap-northeast-1)
      - IMAGE_TAG=$CODEBUILD_RESOLVED_SOURCE_VERSION
      - RP_IMAGE_URI=123456.dkr.ecr.ap-northeast-1.amazonaws.com/my-app-rp
      - WEB_IMAGE_URI=123456.dkr.ecr.ap-northeast-1.amazonaws.com/my-app-web
  build:
    commands:
      - docker build -t $WEB_IMAGE_URI:$IMAGE_TAG -f Dockerfile_web .
      - docker build -t $RP_IMAGE_URI:$IMAGE_TAG -f Dockerfile_rp .
  post_build:
    commands:
      - docker push $RP_IMAGE_URI:$IMAGE_TAG
      - docker push $WEB_IMAGE_URI:$IMAGE_TAG
      - cat taskdef-production-template.json | sed -e s@\<IMAGE_RP_NAME\>@$RP_IMAGE_URI:$IMAGE_TAG@ -e s@\<IMAGE_WEB_NAME\>@$WEB_IMAGE_URI:$IMAGE_TAG@ > taskdef.json

artifacts:
  files:
      - appspec.yml
      - taskdef.json

特別なことをやっているのはpost_buildの最後の行ぐらい。ここは用意したtaskdef-production-template.jsonの中にある<IMAGE_RP_NAME><IMAGE_WEB_NAM>をsedコマンドによって新しいイメージURIに書き換えている。そしてそのまま新しくtaskdef.jsonとしてartifactsで出力し、CodeDeployへ渡している。

taskdef-production-template.jsonは自分で作成したタスク定義の中身をそのままコピーし、image部分だけ"image": "<IMAGE_RP_NAME>"のように書き換えておく。

appspec.ymlは以下のようにロードバランサからのリクエストを受け取るイメージ名とポートを指定しておく。

appspec.yml
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "<TASK_DEFINITION>"
        LoadBalancerInfo:
            ContainerName: "rp"
            ContainerPort: "80"

これを毎回CodeBuildのartifactsを渡している理由は、taskdef.json を含むアーティファクトのサイズを 3MB 未満にする必要があるからである。

CodePipelineのTerraformも参考程度に載せておく。

codepipeline.tf
resource "aws_codebuild_project" "production" {
  count         = "${local.is_prod ? 1 : 0}"
  name          = "airconkingdom-com-application-production"
  build_timeout = 10
  service_role  = "${data.terraform_remote_state.samurai_iam_user.codebuild_role_arn}"

  artifacts {
    type = "CODEPIPELINE"
  }

  environment {
    compute_type    = "BUILD_GENERAL1_SMALL"
    image           = "aws/codebuild/golang:1.11"
    type            = "LINUX_CONTAINER"
    privileged_mode = true

    environment_variable {
      "name"  = "DOCKER_BUILDKIT"
      "value" = "1"
    }
  }

  source {
    type      = "CODEPIPELINE"
    buildspec = "buildspec-production.yml"
  }
}

#####################################
# CodePipeline
#####################################
resource "aws_codepipeline" "example" {
  stage {
    name = "Source"

    action {
      name             = "Source"
      category         = "Source"
      owner            = "AWS"
      provider         = "CodeCommit"
      version          = "1"
      output_artifacts = ["app-source"]

      configuration {
        RepositoryName = "my-app"
        BranchName     = "master"
      }
    }
  }

  stage {
    name = "ProductionBuild"

    action {
      name             = "Build"
      category         = "Build"
      owner            = "AWS"
      provider         = "CodeBuild"
      version          = "1"
      input_artifacts  = ["app-source"]
      output_artifacts = ["production-artifacts"]

      configuration {
        ProjectName = "my-app-production-build"
      }
    }
  }

  stage {
    name = "ProductionDeploy"

    action {
      name            = "Deploy"
      category        = "Deploy"
      owner           = "AWS"
      provider        = "CodeDeployToECS"
      input_artifacts = ["production-artifacts"]
      version         = "1"

      configuration {
        ApplicationName                = "my-app-production"
        DeploymentGroupName            = "my-app-production"
        TaskDefinitionTemplateArtifact = "production-artifacts"
        TaskDefinitionTemplatePath     = "taskdef.json"
        AppSpecTemplateArtifact        = "production-artifacts"
        AppSpecTemplatePath            = "appspec.yml"
      }
    }
  }
}
hareku
AWS / Go / Laravel / Vue.js / React
https://mycode.rip/
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
ユーザーは見つかりませんでした