0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

RailsとNuxt3でtodoリストを作ろう[REST-API/Terraform/Fargate]〜その7、Blue/Greenデプロイ後編

Last updated at Posted at 2023-06-05

はじめに

RailsとNuxt3でtodoリストの作り方を
初めから丁寧に説明したいと思います。

使用pcはmacを想定しています。

完成した構成図は以下の通りです。

aws_structure.png

また、githubレポジトリはこちらです。

各シリーズは以下の通りです。

RailsとNuxt3でtodoリストを作ろう[REST-API/Terraform/Fargate]〜その1、Rails基本設定編
RailsとNuxt3でtodoリストを作ろう[REST-API/Terraform/Fargate]〜その2、Rails API編
RailsとNuxt3でtodoリストを作ろう[REST-API/Terraform/Fargate]〜その3、Nuxt.js編
RailsとNuxt3でtodoリストを作ろう[REST-API/Terraform/Fargate]〜その4、TerraformECS前編
RailsとNuxt3でtodoリストを作ろう[REST-API/Terraform/Fargate]〜その5、TerraformECS後編
RailsとNuxt3でtodoリストを作ろう[REST-API/Terraform/Fargate]〜その6、Blue/Greenデプロイ前編
RailsとNuxt3でtodoリストを作ろう[REST-API/Terraform/Fargate]〜その7、Blue/Greenデプロイ後編

codepipline

codepipline

codepiplineはcodeシリーズをつなげるものです。

terraform/modules/codepipeline/aws_codepipeline.tf
resource "aws_codepipeline" "codepipeline" {
  name     = "${var.app_name}-pipeline"
  role_arn = aws_iam_role.codepipeline_role.arn

  artifact_store {
    # S3 (アーティファクト格納用)
    # CodePileline では次のステージにアーティファクトを引き渡す時、S3 バケットを経由させます。
    location = aws_s3_bucket.codepipeline_bucket.bucket
    type     = "S3"
  }

  stage {
    name = "Source"

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

      configuration = {
        RepositoryName       = aws_codecommit_repository.codecommit_repository.repository_name
        BranchName           = "main"
        OutputArtifactFormat = "CODE_ZIP"
      }
    }
  }

  stage {
    name = "Build"

    # webserver
    action {
      name             = "WEB_Build"
      category         = "Build"
      owner            = "AWS"
      provider         = "CodeBuild"
      input_artifacts  = ["SourceArtifact"]
      output_artifacts = ["BuildArtifact1"]
      version          = "1"

      configuration = {
        ProjectName = aws_codebuild_project.web.name
      }
    }

    # apisever
    action {
      name             = "API_Build"
      category         = "Build"
      owner            = "AWS"
      provider         = "CodeBuild"
      input_artifacts  = ["SourceArtifact"]
      output_artifacts = ["BuildArtifact2"]
      version          = "1"

      configuration = {
        ProjectName = aws_codebuild_project.api.name
      }
    }
  }

  stage {
    name = "Deploy"

    # webserver
    action {
      name            = "WEB_Deploy"
      category        = "Deploy"
      owner           = "AWS"
      provider        = "CodeDeployToECS"
      input_artifacts = ["BuildArtifact1"]
      version         = "1"

      configuration = {
        ApplicationName                = var.app_name
        DeploymentGroupName            = aws_codedeploy_deployment_group.web.deployment_group_name
        TaskDefinitionTemplateArtifact = "BuildArtifact1"
        TaskDefinitionTemplatePath     = "taskdef.json"
        AppSpecTemplateArtifact        = "BuildArtifact1"
        AppSpecTemplatePath            = "appspec.yml"
        Image1ArtifactName             = "BuildArtifact1"
        Image1ContainerName            = "IMAGE1_NAME"
      }
    }

    # apserver
    action {
      name            = "API_Deploy"
      category        = "Deploy"
      owner           = "AWS"
      provider        = "CodeDeployToECS"
      input_artifacts = ["BuildArtifact2"]
      version         = "1"

      configuration = {
        ApplicationName                = var.app_name
        DeploymentGroupName            = aws_codedeploy_deployment_group.api.deployment_group_name
        TaskDefinitionTemplateArtifact = "BuildArtifact2"
        TaskDefinitionTemplatePath     = "taskdef.json"
        AppSpecTemplateArtifact        = "BuildArtifact2"
        AppSpecTemplatePath            = "appspec.yml"
        Image1ArtifactName             = "BuildArtifact2"
        Image1ContainerName            = "IMAGE1_NAME"
      }
    }
  }
}

codecommit

codecommitは、githubのミラーリングするために使います。

terraform/modules/codepipeline/aws_codecommit_repository.tf
resource "aws_codecommit_repository" "codecommit_repository" {
  repository_name = "${var.app_name}-repository"
  description     = "This is the ${var.app_name} App Repository"
}

codebuild

BuildAriifnに格納できるのは、3MBまでという制約はあるため、appspec.yml、taskdef.json、imageDetail.jsonの三つのみに絞ります。

terraform/modules/codepipeline/aws_codebuild_project.tf
resource "aws_codebuild_project" "web" {
  name         = "${var.app_name}-web-codebuild"
  description  = "codebuild_project for web ${var.app_name}"
  service_role = aws_iam_role.codebuild-role.arn

  artifacts {
    type = "CODEPIPELINE"
  }

  source {
    type      = "CODEPIPELINE"
    buildspec = "terraform/template/web_buildspec.yml"
  }

  environment {
    compute_type    = "BUILD_GENERAL1_SMALL"
    image           = "aws/codebuild/amazonlinux2-x86_64-standard:3.0"
    type            = "LINUX_CONTAINER"
    privileged_mode = true


    environment_variable {
      name  = "AWS_ACCOUNT_ID"
      value = data.aws_caller_identity.self.account_id
    }
    environment_variable {
      name  = "ECR_WEB_REPOSITORY"
      value = var.web_app_name
    }
    environment_variable {
      name  = "ECS_WEB_TASK_DEFINITION_ARN"
      value = "${var.web_app_name}-definition"
    }
  }
}

resource "aws_codebuild_project" "api" {
  name         = "${var.app_name}-api-codebuild"
  description  = "codebuild_project for api ${var.app_name}"
  service_role = aws_iam_role.codebuild-role.arn

  artifacts {
    type = "CODEPIPELINE"
  }

  source {
    type      = "CODEPIPELINE"
    buildspec = "terraform/template/api_buildspec.yml"
  }

  environment {
    compute_type    = "BUILD_GENERAL1_SMALL"
    image           = "aws/codebuild/amazonlinux2-x86_64-standard:3.0"
    type            = "LINUX_CONTAINER"
    privileged_mode = true

    environment_variable {
      name  = "AWS_ACCOUNT_ID"
      value = data.aws_caller_identity.self.account_id
    }
    environment_variable {
      name  = "ECR_API_REPOSITORY"
      value = var.api_app_name
    }
    environment_variable {
      name  = "ECS_API_TASK_DEFINITION_ARN"
      value = "${var.api_app_name}-definition"
    }
  }
}
terraform/template/api_buildspec.yml
version: 0.2

env:
  variables:
    AWS_REGION: ap-northeast-1
    IMAGE_TAG: latest

phases:
  pre_build:
    commands:
      - API_REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_API_REPOSITORY
  build:
    commands:
      - printf '{"Version":"1.0","ImageURI":"%s"}' $API_REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
      - echo $ECS_API_TASK_DEFINITION_ARN
      - $(aws ecs describe-task-definition --task-definition $ECS_API_TASK_DEFINITION_ARN --query taskDefinition | jq '.containerDefinitions[0].image="<IMAGE1_NAME>"' > taskdef.json)
      - cat taskdef.json
      - $(aws ecs describe-task-definition --task-definition apiserver-definition --query taskDefinition | jq '.containerDefinitions[0].image="<IMAGE1_NAME>"' > taskdef.json)
      - cat taskdef.json
      - cp terraform/template/api_appspec.yml appspec.yml

artifacts:
  files:
    - appspec.yml
    - taskdef.json
    - imageDetail.json
ymlterraform/template/web_buildspec.yml
version: 0.2

env:
  variables:
    AWS_REGION: ap-northeast-1
    IMAGE_TAG: latest

phases:
  pre_build:
    commands:
      - WEB_REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_WEB_REPOSITORY
  build:
    commands:
      - cp terraform/template/web_appspec.yml appspec.yml
      - $(aws ecs describe-task-definition --task-definition $ECS_WEB_TASK_DEFINITION_ARN --query taskDefinition | jq '.containerDefinitions[0].image="<IMAGE1_NAME>"' > taskdef.json)
      - printf '{"Version":"1.0","ImageURI":"%s"}' $WEB_REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
artifacts:
  files:
    - appspec.yml
    - taskdef.json
    - imageDetail.json

codedeploy

terraform/modules/codepipeline/aws_codedeploy_app.tf
resource "aws_codedeploy_app" "main" {
  compute_platform = "ECS"
  name             = var.app_name
}

resource "aws_codedeploy_deployment_group" "web" {
  app_name               = aws_codedeploy_app.main.name
  deployment_config_name = "CodeDeployDefault.ECSAllAtOnce"
  deployment_group_name  = var.web_app_name
  service_role_arn       = aws_iam_role.codedeploy-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 = 5
    }
  }

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

  ecs_service {
    cluster_name = "todolist-cluster"
    service_name = "webserver-service"
  }

  load_balancer_info {
    target_group_pair_info {
      prod_traffic_route {
        listener_arns = [var.http_arn]
      }

      target_group {
        name = var.web_blue_name
      }

      target_group {
        name = var.web_green_name
      }
    }
  }
}


resource "aws_codedeploy_deployment_group" "api" {
  app_name               = aws_codedeploy_app.main.name
  deployment_config_name = "CodeDeployDefault.ECSAllAtOnce"
  deployment_group_name  = var.api_app_name
  service_role_arn       = aws_iam_role.codedeploy-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 = 5
    }
  }

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

  ecs_service {
    cluster_name = "todolist-cluster"
    service_name = "apiserver-service"
  }

  load_balancer_info {
    target_group_pair_info {
      prod_traffic_route {
        listener_arns = [var.http_arn]
      }

      target_group {
        name = var.api_blue_name
      }

      target_group {
        name = var.api_green_name
      }
    }
  }
}
terraform/template/api_appspec.yml
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "<TASK_DEFINITION>"
        LoadBalancerInfo:
          ContainerName: "apiserver"
          ContainerPort: 8080
        PlatformVersion: "1.4.0"
terraform/template/web_appspec.yml
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "<TASK_DEFINITION>"
        LoadBalancerInfo:
          ContainerName: "webserver"
          ContainerPort: 3000
        PlatformVersion: "1.4.0"

結果

最後に実行します。

Screenshot 2023-06-04 at 14.08.49.png

Screenshot 2023-06-04 at 14.09.55.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?