ECS 서비스 자동 배포 구현하기 (feat. Git Action)

앞선 포스팅에서 ECS Service 까지 생성을 완료하고 확인까지 마쳤다.
이번에는 Git을 통해 ECS Service 자동 배포를 구현하고자 한다.

Task.json 파일 생성

먼저 서비스 생성에 성공한 Task의 Json을 긁어온다.

태스크 정의 - 태스크를 선택하고 JSON - 클립보드 복사 진행

 

그 다음 프로젝트 루트에 [.ecr] -> [prod] -> [task.json] 파일을 생성한다.

{
  "taskDefinitionArn": "arn:aws:ecs:ap-northeast-2:448648424519:task-definition/sample-next-app:latest",
  "containerDefinitions": [
    {
      "name": "sample",
      "cpu": 0,
      "portMappings": [
        {
          "name": "sample-7000-tcp",
          "containerPort": 7000,
          "hostPort": 7000,
          "protocol": "tcp",
          "appProtocol": "http"
        }
      ],
      "essential": true,
      "environment": [],
      "mountPoints": [],
      "volumesFrom": []
    }
  ],
  "family": "sample-next-app",
  "executionRoleArn": "arn:aws:iam::448648424519:role/ecsTaskExecutionRole",
  "networkMode": "awsvpc",
  "revision": 5,
  "volumes": [],
  "status": "ACTIVE",
  "requiresAttributes": [
    {
      "name": "com.amazonaws.ecs.capability.ecr-auth"
    },
    {
      "name": "ecs.capability.execution-role-ecr-pull"
    },
    {
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
    },
    {
      "name": "ecs.capability.task-eni"
    }
  ],
  "placementConstraints": [],
  "compatibilities": ["EC2", "FARGATE"],
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "runtimePlatform": {
    "cpuArchitecture": "X86_64",
    "operatingSystemFamily": "LINUX"
  },
  "registeredAt": "2023-10-27T01:49:45.973Z",
  "registeredBy": "arn:aws:iam::448648424519:root",
  "tags": []
}

AWS에 있던 Task json에서 containerDefinitions -> image 부분은 삭제한다.
ECR에 Docker Image가 계속 갱신되어서 이미지 버전이 계속 변경되기 때문에 task.json에 고정으로 사용하는건 의미가 없다.

workflow.yml 파일 수정

ECS 자동 배포를 위한 workflow를 추가해준다.

name: Staging # 워크플로 Action의 이름
on:
  push:
    branches: [main] #github main 브런치에 푸시 발생 시 실행 ( main 외 다른브런치 이용시 이름변경)
jobs:
  staging: #staging이라는 작업
    name: deploy to staging # 작업의 이름
    runs-on: ubuntu-latest # 실행될 작업환경을 말함.
    steps:
      - name: Checkout
        uses: actions/checkout@v3 #체크아웃 받기
      - name: HelloWorld
        uses: actions/hello-world-javascript-action@v1 # 헬로월드 찍어보기
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} # Github Secret 이름
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # Github Secret 이름
          aws-region: ap-northeast-2 # AWS 리전 선택
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1
      - name: Build, tag, and push image to Amazon ECR
        id: build-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: sample # ecr 이름
          IMAGE_TAG: ${{ github.run_number }} # git 커밋넘버를 이미지 이름 구분을 위해 넣어줌
        run: |
          # Build a docker container and
          # push it to ECR so that it can
          # be deployed to ECS.
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
      - name: Fill in the new image ID in the Amazon ECS task definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: .ecs/prod/task.json # task파일
          container-name: sample #이미지 컨테이너 이름
          image: ${{ steps.build-image.outputs.image }}
      - name: Deploy Amazon ECS task definition
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: ${{ steps.task-def.outputs.task-definition }}
          service: sample-ecs-service #클러스터 서비스 명
          cluster: sample-cluster # 클러스터 명
          wait-for-service-stability: true

ECS task definition 부분부터 추가해주면 된다.

page.tsx 내용 수정

이제 next app의 내용을 수정해서 자동 배포 후 정상적으로 적용되는지 확인해본다.

export default function Home() {
  return (
    <div>
      <h1>Hello Next.js!</h1>
      <h2>feat. CI/CD with AWS-ECS</h2>
    </div>
  );
}

결과

이로써 ECS 자동배포까지 구현이 완료되었다.

다음 포스팅에서는 Route53으로 그럴싸한 도메인을 붙이는 과정을 진행해보고자 한다.

–끝–

ECS Cluster 생성

Cluster란?
ECS 클러스터는 Docker Container를 실행할 수 있는 논리적인 공간이다.
ECR에 등록한 Docker Image를 가지고 컨테이너를 구성하려면 이 클러스터를 생성 및 사용해야 한다.

클러스터 이름을 sample-cluster로 짓고, 나머지 설정은 그대로 둔 후 생성한다.

인프라 탭에서 주로 Fargate와 EC2 인스턴스 둘을 사용한다.
간략하게 두 인프라의 차이점을 살펴보면 아래와 같다.


EC2인스턴스
EC2는 AWS에 대표적인 서비스 중 하나이다. 주로 서버로 사용하며, 비용을 지불하고 가상의 서버를 대여 받을 수 있다.

ECS 서비스를 구축할 때 EC2 인스턴스를 인프라로 선택하면 서비스에 속한 EC2 인스턴스 서버와 클러스터를 직접 관리해주어야 한다.

Fargate
EC2 인스턴스와 다르게 서버 인스턴스를 관리할 필요가 없는 컨테이너 서비스이며 사용량에 따라 요금이 부과되는 종량제 형식으로 제공된다.
(서버리스란 서버가 없다는 것이 아니다. 서버가 필요한 순간이 왔을 때 잠시 서버를 깨워 기능을 수행하고 다시 잠든다. 이렇게 종량제 서비스가 되는 것이다.)

EC2 인스턴스와 Fargate는 상황에 맞게 잘 고려해서 선택하면 될 것 같다.


Task 정의 생성

다시 본론으로 돌아와서 클러스터를 생성했다면, 이제 Task 정의를 생성한다.

클러스터를 생성할 때 인프라를 Fargate로 생성했으니 시작 유형을 AWS Fargate로 설정한다.
태스크 크기는 CPU : 0.25vCPU, 메모리 : 0.5GB 으로 설정한다. (캡처와 다르니 참고)

컨테이너 이름은 sample로 지었다.
이미지 URI는 지난번 ECR에 등록된 이미지 URI를 복사해서 사용한다.
여기서 주의할 점이 있는데, 레포지토리 URI가 아닌 이미지 URI를 복사해야한다.
(그래야 sample:${version}까지 복사된다.)


다음, 포트 매핑에서는 컨테이너 포트를 7000으로 설정한다.
Docker File에서 Port를 7000으로 설정했기 때문에 여기서도 맞춰준다.

이후에는 기본 값으로 두고 작업을 생성한다.


서비스 생성

앞서 클러스터와 Task를 정의 한 것은 바로 서비스를 생성하기 위함이다.
이제 대망의 서비스를 올려보자!

생성한 ECS 클러스터를 클릭하면 서비스 생성이 가능하다.


컴퓨팅 옵션 : 시작 유형
시작 유형 : Fargate
플랫폼 버전 : LATEST



태스크 : 2 (Fargate 인스턴스가 2개 생성된다.)



네트워킹은 별도로 건들지 않았다.
로드밸런서 및 대상 그룹은 서비스를 생성하며 함께 생성해준다.


서비스가 정상적으로 생성되고 활성화가 되었다.

이제서야 비로소 개인 프로젝트가 모든 서버 인스턴스에 Docker를 통한 동일한 서비스 환경을 구축한 것이다.

아래 로드밸런서 DNS 이름의 주소를 가지고 웹에서 나의 프로젝트를 확인할 수 있다.

결과 화면


마무리하며

포스팅을 하면서 서버와 네트워크에 대해 점점 친숙해지는 기분이다.
사실 여기까지의 과정에서 우여곡절이 많았지만 시간이 걸려도 해내면 그만이다.

다음 포스팅에서는 Git을 통한 ECS 서비스 자동 생성을 구현해볼 예정이다.

글을 읽으시는 모든 분들께 알량한 나의 지식이 도움이 되었으면 한다.

Next.js 샘플 프로젝트를 Git Workflow / Action을 통해 AWS ECS로 자동 배포하는 시스템을 구축해보려 한다.


자동 배포 sequence

아래와 같은 순서를 거쳐 최종적으로 AWS ECS에 프로젝트가 자동 배포되는 과정을 경험해보고자 한다.

  1. Next.js 프로젝트 생성 및 배포
  2. Git workflow 작성
  3. AWS ECS 서비스에 연동

uml diagram

선행되어야 할 학습 내용

  • Next.js 설치 및 프로젝트 생성
  • Git / Github - workflow 설정과 Git Action
  • AWS
    • Route53 - 도메인 호스팅
    • EC2 - 클라우드 인스턴스
    • Load Balancer - 인스턴스에 접근할 트래픽 분산 처리
    • ECR - Docker Image의 저장소
    • ECS - ECR에 저장된 Docker Image를 기반으로 인스턴스를 생성하는 서비스
  • Docker - 배포한 시스템이 모든 환경에서 동일할 수 있도록 컨테이너라는 청사진 역할을 하는 시스템 구축 환경 제공

+ Recent posts