ecs bluegreen

PHYYOU·2022년 8월 26일
0

[[ECR ECS CodePipeline]]

VPC, Network

---
AWSTemplateFormatVersion: "2010-09-09"
Description: "Amazon VPC - Private and Public subnets"

Parameters:
  VpcBlock:
    Type: String
    Default: 10.1.0.0/16
    Description: The CIDR range for the VPC. This should be a valid prviateate (RFC 1918) CIDR range.

  PublicSubnet01Block:
    Type: String
    Default: 10.1.0.0/24
    Description: CidrBlock for publiclic subnet 01 within the VPC

  PublicSubnet02Block:
    Type: String
    Default: 10.1.1.0/24
    Description: CidrBlock for publiclic subnet 02 within the VPC

  PrivateSubnet01Block:
    Type: String
    Default: 10.1.2.0/24
    Description: CidrBlock for prviateate subnet 01 within the VPC

  PrivateSubnet02Block:
    Type: String
    Default: 10.1.3.0/24
    Description: CidrBlock for prviateate subnet 02 within the VPC

  Subnet01AZ:
    Type: String
    Default: "ap-northeast-2a"

  Subnet02AZ:
    Type: String
    Default: "ap-northeast-2b"

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "Worker Network Configuration"
        Parameters:
          - VpcBlock
          - PublicSubnet01Block
          - PublicSubnet02Block
          - PrivateSubnet01Block
          - PrivateSubnet02Block

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcBlock
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub "${AWS::StackName}-vpc"

  InternetGateway:
    Type: "AWS::EC2::InternetGateway"

  VPCGatewayAttachment:
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: Public Subnets
        - Key: Network
          Value: Public

  PrivateRouteTable01:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: Private Subnet AZ1
        - Key: Network
          Value: Private01

  PrivateRouteTable02:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: Private Subnet AZ2
        - Key: Network
          Value: Private02

  PublicRoute:
    DependsOn: VPCGatewayAttachment
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PrivateRoute01:
    DependsOn:
      - VPCGatewayAttachment
      - NatGateway01
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable01
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGateway01

  PrivateRoute02:
    DependsOn:
      - VPCGatewayAttachment
      - NatGateway02
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable02
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGateway02

  NatGateway01:
    DependsOn:
      - NatGatewayEIP1
      - PublicSubnet01
      - VPCGatewayAttachment
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt "NatGatewayEIP1.AllocationId"
      SubnetId: !Ref PublicSubnet01
      Tags:
        - Key: Name
          Value: !Sub "${AWS::StackName}-NatGatewayAZ1"

  NatGateway02:
    DependsOn:
      - NatGatewayEIP2
      - PublicSubnet02
      - VPCGatewayAttachment
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt "NatGatewayEIP2.AllocationId"
      SubnetId: !Ref PublicSubnet02
      Tags:
        - Key: Name
          Value: !Sub "${AWS::StackName}-NatGatewayAZ2"

  NatGatewayEIP1:
    DependsOn:
      - VPCGatewayAttachment
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: vpc

  NatGatewayEIP2:
    DependsOn:
      - VPCGatewayAttachment
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: vpc

  PublicSubnet01:
    Type: AWS::EC2::Subnet
    Metadata:
      Comment: Subnet 01
    Properties:
      MapPublicIpOnLaunch: true
      AvailabilityZone: !Ref Subnet01AZ
      CidrBlock:
        Ref: PublicSubnet01Block
      VpcId:
        Ref: VPC
      Tags:
        - Key: Name
          Value: !Sub "${AWS::StackName}-public-a"

  PublicSubnet02:
    Type: AWS::EC2::Subnet
    Metadata:
      Comment: Subnet 02
    Properties:
      MapPublicIpOnLaunch: true
      AvailabilityZone: !Ref Subnet02AZ
      CidrBlock:
        Ref: PublicSubnet02Block
      VpcId:
        Ref: VPC
      Tags:
        - Key: Name
          Value: !Sub "${AWS::StackName}-public-b"

  PrivateSubnet01:
    Type: AWS::EC2::Subnet
    Metadata:
      Comment: Subnet 03
    Properties:
      AvailabilityZone: !Ref Subnet01AZ
      CidrBlock:
        Ref: PrivateSubnet01Block
      VpcId:
        Ref: VPC
      Tags:
        - Key: Name
          Value: !Sub "${AWS::StackName}-prviate-a"

  PrivateSubnet02:
    Type: AWS::EC2::Subnet
    Metadata:
      Comment: Private Subnet 02
    Properties:
      AvailabilityZone: !Ref Subnet02AZ
      CidrBlock:
        Ref: PrivateSubnet02Block
      VpcId:
        Ref: VPC
      Tags:
        - Key: Name
          Value: !Sub "${AWS::StackName}-prviate-b"

  PublicSubnet01RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet01
      RouteTableId: !Ref PublicRouteTable

  PublicSubnet02RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet02
      RouteTableId: !Ref PublicRouteTable

  PrivateSubnet01RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet01
      RouteTableId: !Ref PrivateRouteTable01

  PrivateSubnet02RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet02
      RouteTableId: !Ref PrivateRouteTable02

Outputs:
  SubnetIds:
    Description: Subnets IDs in the VPC
    Value:
      !Join [
        ",",
        [
          !Ref PublicSubnet01,
          !Ref PublicSubnet02,
          !Ref PrivateSubnet01,
          !Ref PrivateSubnet02,
        ],
      ]

  VpcId:
    Description: The VPC Id
    Value: !Ref VPC

위 CloudFormation 스택으로 VPC, Network(subnet, route table, igw, nat gateway) 생성

Bastion Host 생성

도커 기본 이미지 빌드 go module init을 위한 Bastion EC2 생성
PowerUserAccess 연결하고 ecs role을 위한 IAM Access도 필요함
jq curl docker golang 까지 설치

aws configure로 region도 미리 설정해 놓자!

ECR 생성

ECS 서비스 및 컨테이너 생성을 위해서 ECR 생성

Application, Dockerfile, Image

현재 상세 과제 내용이 없는 관계로 go언어를 이용한 application 만들고 Dockerfile도 제작함

main.go

package main

import (
		"github.com/gin-gonic/gin"
)

func main() {
		gin.SetMode(gin.ReleaseMode)
		r := gin.Default()
		r.GET("/ping", func(c *gin.Context) {
				c.JSON(200, gin.H{
						"message": "pong",
				})
		})
		r.Run(":80") // port
}

위 파일을 기반으로 의존성 설치를 위해 아래 두개의 명령어 실행
go mod init wsi-api, go mod tidy

Dockerfile

FROM golang:alpine AS builder

ENV GO111MODULE=on \
    CGO_ENABLED=0 \
    GOOS=linux \
    GOARCH=amd64

WORKDIR /build

COPY go.mod go.sum main.go ./

RUN go mod download

RUN go build -o main .

WORKDIR /dist

RUN cp /build/main .

FROM scratch

COPY --from=builder /dist/main .

ENTRYPOINT ["/main"]

해당 도커파일 기반으로 실행시켜보고 ECR에 배포

aws ecr get-login-password | docker login --username AWS --password-stdin $REPOSITORY_URI

docker push $REPOSITORY_URI:latest

ECS 클러스터

Info: 옛날 구버전 ECS 환경에서 작업할 것
새로운 ECS 환경에서는 Route53, CodeDeploy BlueGreen Deployment 등의
기능들을 지원하지 않는 게 많음

기본 설정대로 생성

ECS Task Execution Role 생성

ECS Task Definition을 생성하기 위해 ECS 컨테이너를 실행할 IAM Role 을 생성
콘솔에서 작업하는 것이 간단하여 콘솔에서 작업하는 걸 추천

To create a task execution IAM role (AWS Management Console)

  1. Open the IAM console at https://console.aws.amazon.com/iam/.

  2. In the navigation pane, choose RolesCreate role.

  3. In the Trusted entity type section, choose AWS serviceElastic Container Service.

  4. For Use case, choose Elastic Container Service Task, then choose Next.

  5. In the Attach permissions policy section, do the following:

    1. Search for AmazonECSTaskExecutionRolePolicy, then select the policy.
    2. Under Set permissions boundary - optional, choose Create role without a permissions boundary.
    3. Choose Next.
  6. Under Role details, do the following:

    1. For Role name, type ecsTaskExecutionRole.
    2. For Add tags (optional), specify any custom tags to associate with the policy .
  7. Choose Create role.

ECS Task Definition 생성

Task Definition은 AWS CLI로 생성

아래 파일을 Bastion EC2에 생성 후

명령어를 실행하여 생성한다. family가 task definition 이름, 컨테이너 정의 안에 컨테이너 이름이 있다.
taskdef.json

{
    "containerDefinitions": [
        {
            "name": "wsi-api",
            "image": "492622758225.dkr.ecr.ap-northeast-2.amazonaws.com/wsi-api-ecr:latest",
            "essential": true,
            "portMappings": [
                {
                    "hostPort": 80,
                    "protocol": "tcp",
                    "containerPort": 80
                }
            ]
        }
    ],
    "executionRoleArn": "arn:aws:iam::492622758225:role/ecsTaskExecutionRole",
    "taskRoleArn": "arn:aws:iam::492622758225:role/ecsTaskExecutionRole",
    "requiresCompatibilities": [
        "FARGATE"
    ],
    "networkMode": "awsvpc",
    "cpu": "256",
    "memory": "512",
    "family": "wsi-api"
}

ECS CodeDeploy IAM Role 생성

ECS CodeDeploy Blue Green Deployment를 같이 생성 할 경우 ECS CodeDeploy IAM Role이 필요하므로 생성해준다.

참고로 Blue Green Deployment의 경우 IAM Pass 권한도 필요하다.
아래의 문서를 참고하여 ECSCodeDeployRole을 생성

https://docs.aws.amazon.com/AmazonECS/latest/developerguide/codedeploy_IAM_role.html

ALB, Target group

ECS 배포를 위해서 ALB, Target Group 2개 생성

ECS Service & CodeDeploy & Route53 CloudMap

ECS 서비스를 만들어준다.

ECS 서비스를 생성하면서 CodeDeploy (ECS Blue/Green Deployments) 와 Route53 또한 같이 생성할 수 있으니 같이 생성한다.

CodeCommit

빌드, 배포를 위해 codecommit 레포지토리를 만들고 아래의 모든 파일들을 업로드, 혹은 직접 편집한다

go.mod go.sum 파일은 Bastion EC2에서 생성한 파일을 이용

appspec.yaml

version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: <TASK_DEFINITION>
        LoadBalancerInfo:
          ContainerName: "wsi-api"
          ContainerPort: 80

buildspec.yml

version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws --version
      - aws ecr get-login-password | docker login --username AWS --password-stdin $REPOSITORY_URI
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}
  build:
    commands:
      - echo Docker build and tagging started on `date`
      - docker build -t $REPOSITORY_URI:latest -t $REPOSITORY_URI:$IMAGE_TAG .
      - echo Docker build and tagging completed on `date`
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the docker images...
      - docker push $REPOSITORY_URI:latest
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - echo Update the REPOSITORY_URI:IMAGE_TAG in task definition...
      - echo Container image to be used $REPOSITORY_URI:$IMAGE_TAG
      - sed -i 's@REPOSITORY_URI@'$REPOSITORY_URI'@g' taskdef.json
      - sed -i 's@IMAGE_TAG@'$IMAGE_TAG'@g' taskdef.json
      - echo update the REGION in task definition...
      - sed -i 's@AWS_REGION@'$AWS_REGION'@g' taskdef.json
      - echo update the roles in task definition...
      - sed -i 's@TASK_EXECUTION_ARN@'$TASK_EXECUTION_ARN'@g' taskdef.json
artifacts:
  files:
    - "appspec.yaml"
    - "taskdef.json"

배포를 위해서 REPOSITORY_URI와 IMAGE TAG를 sed로 치환하기 위해 값을 placeholder 로 채워놓은 것을 유의!

taskdef.json

{
    "containerDefinitions": [
        {
            "name": "wsi-api",
            "image": "REPOSITORY_URI:IMAGE_TAG",
            "essential": true,
            "portMappings": [
                {
                    "hostPort": 80,
                    "protocol": "tcp",
                    "containerPort": 80
                }
            ]
        }
    ],
    "executionRoleArn": "TASK_EXECUTION_ARN",
    "taskRoleArn": "TASK_EXECUTION_ARN",
    "requiresCompatibilities": [
        "FARGATE"
    ],
    "networkMode": "awsvpc",
    "cpu": "256",
    "memory": "512",
    "family": "wsi-api"
}

CodeBuild

Codebuild 빌드 프로젝트를 생성

상기한 buildspec.yml을 이용.
이 buildspec.yml을 위해 필요한 환경변수는 다음과 같음

  • REPOSITORY_URI
  • TASK_EXECUTION_ARN

ECR을 사용한 배포이므로 CodeBuild IAM Role에 ECR Builder Policy를 함께 제공할것

CodePipeline

위의 3가지 서비스를 조합하여 CodePipeline 생성

profile
박효영

0개의 댓글