AWS ECS + Fargate로 서버 배포 및 GitHub Action을 활용한 무중단 CI/CD 환경 구축하기

KIM TAEHYUN·2023년 6월 19일
post-thumbnail

지난번 포스트를 통해 로컬에서 만든 도커 이미지를 ECR에 업로드까지 진행했습니다. 이제 해당 이미지를 바탕으로 ECS를 사용해 본격적으로 컨테이너를 띄우고 서버를 실행시켜야합니다.
그리고 ECR를 통한 이미지 업로드부터 ECS 배포까지 이 일련의 작업을 GitHub Action을 통해 자동화해 CI/CD를 구축해보겠습니다.

ECS를 사용하기에 앞서 ECS가 어떤 구조로 동작하고 있는지를 간단하게 정리해보고자 합니다.
ECS의 구조를 이해하기 위해서는 먼저 Task Definition, Task, Service, Cluster의 의미를 파악하는 것이 좋습니다.

  • Task
    Task는 ECS에서 컨테이너를 실행하는 최소 단위라고 이해할 수 있습니다. 컨테이너를 ECS 상에서 실행시키기 위해서는 Task 안에서 정의되어야하며, 하나의 Task는 하나 이상의 컨테이너를 포함하고 있습니다.
  • Task Definition
    위에서 설명한 Task를 만들어내기 위한 것으로 Task Definition은 설명서, Task는 Task Definition에 대한 인스턴스로 볼 수 있습니다. Task와 Task Definition의 관계를 Docker의 Docker container와 Docker image와 비슷한 개념으로 볼 수 있습니다. Task Definition을 통해, Task가 실행될 때의 CPU, Memory 성능, 실행할 컨테이너에 대한 image 정의 등을 설정할 수 있습니다.
  • Service
    Service는 Task Defintion과 1:1 관계로 하나의 Service에는 하나의 Task Definition을 연결할 수 있습니다. 이때, 하나의 ECS cluster에 몇 개의 해당 Task Definition을 바탕으로 한 Task를 생성할 것인지를 설정할 수 있습니다. 예를 들어, Service를 생성하여 2개의 Task를 항상 띄워놓는 설정을 했을 때, ECS cluster 안에는 항상 2개의 Task가 띄워져 있도록 스케줄링 됩니다.
  • Cluster
    Cluster는 내부에 여러 Cluster instance가 실행될 수 있는 가상의 공간이며, 같은 Cluster 내부의 태스크와 서비스는 같은 네트워킹 정보와 인프라 설정 (fargate or EC2)를 가지게 됩니다.

ECS Task Definition 만들기

위 개념에 따라 이전 포스트에서 올린 image를 컨테이너로 띄우기 위해, 컨테이너를 실행시키는 최소단위를 만드는 설계서인 Task Definition을 만들어야합니다.

먼저 AWS 콘솔에서 ECS에 들어온 후, 좌측 메뉴의 테스크 정의 탭을 클릭한 다음 새 태스크 정의 생성을 통해 태스크 정의 생성을 시작합니다.

  • 태스크 정의 패밀리
    만들고자 하는 Task Definition의 이름을 지어주면 됩니다.

  • 컨테이너-1
    컨테이너-1 이라고 쓰여진 것을 보면 느낌이 오듯이 하나의 위에 용어 정리에서 설명한 것처럼 하나의 Task Definition에는 여러 개의 컨테이너를 띄울 수 있습니다. 아니나 다를까 맨 아래 + 컨테이너 더 추가 버튼을 통해 추가적인 컨테이너에 대한 설정을 할 수 있습니다.
  • 컨테이너 세부 정보
    이제 지난 포스트에서 ECR에 이미지를 열심히 올린 과정이 빛을 발할 순간입니다.
    이름을 적는 칸에 원하는 컨테이너의 이름을 적어주고 이미지 URL에 ECR에 아까 올린 이미지 repository의 URL과 태그를 복사해 붙여넣습니다. (ex. imageurl:{image url}:{tag}) 이때 tag 자리에 latest를 적으면, 해당 url을 가진 repository에서 가장 최근에 올린 이미지를 자동으로 가져오게 됩니다.
    포트 매핑에는 image에 올린 어플리케이션 서버의 포트 번호를 입력해 포트포워딩을 해줍니다.

만약 추가적으로 띄울 컨테이너가 있으면 컨테이너 추가를 클릭해 같은 방식으로 설정을 해주고 설정을 마친 뒤에 다음을 눌러 추가 설정을 이어나갑니다.

  • 앱 환경
    앱 환경 설정을 통해 인스턴스를 Fargate를 통해 띄울 것인지 EC2 기반으로 띄울 것인지를 선택할 수 있습니다. 둘의 자세한 차이점에 대해서는 다음 링크의 이전 포스트를 통해 확인할 수 있습니다.
    https://velog.io/@tanggu01/%EB%B0%8D%EA%B8%80-ECS-vs-EKS-EC2-vs-Fargate-%EB%B0%B0%ED%8F%AC-%EC%84%9C%EB%B9%84%EC%8A%A4-%EB%B9%84%EA%B5%90%ED%95%B4%EB%B3%B4%EA%B8%B0
  • 운영체제 및 아키텍처
    특별히 선호하는 운영체제와 아키텍처가 있지 않아서 제일 무난한 Linux에 x86 아키텍처를 사용했습니다.
  • CPU & 메모리
    인스턴스의 성능을 결정하는 설정인데, 밍글의 경우 간단한 서버를 테스트 용으로 띄우는 단계라 0.5 vcpu에 1gb 메모리로 demo 서버를 구축했습니다.
  • 태스크 역할
    콘솔에 적혀있는 설명과 같이 태스크의 컨테이너가 AWS 서비스에 API 요청을 할 수 있도록 허용하는데 사용됩니다. 특별히 컨테이너 상에서 API를 호출하는 일이 없으면 없음으로 설정하고, 만약 앱 서버에서 AWS 서비스를 사용하는 일이 있다면, 해당 서비스에 대한 접근 권한을 가진 IAM 역할을 생성하여 태스크에 부여할 수 있습니다.
  • 태스크 실행 역할
    ECS가 AWS API요청을 하는데 필요한 역할을 부여하는 것으로 "새 역할 생성"이라고 설정해두면 자동으로 알맞은 IAM 역할을 생성해줍니다.

위 설정을 모두 마쳤으면 다음 버튼과 완료 버튼을 눌러 태스크 생성을 마칩니다.

ECS 클러스터 생성하기

클러스터 생성에는 클러스터 이름, VPC와 서브넷 설정을 하는 것 이외에는 특별히 설정해줄만한 것은 없습니다. VPC와 서브넷이 무엇이고 어떻게 설정하는지에 대해서는 인프라세팅에 중요한 요소인만큼 추후 포스트에서 따로 자세히 다루어보겠습니다.

ECS 서비스 생성하기

위 용어 정리에서 다루었던 Task, Cluster, Service 중에 마지막인 service를 생성해야합니다. Service는 하나의 Cluster 위에서 돌아가기 때문에 위에서 생성한 클러스터를 클릭해서 생성버튼을 찾을 수 있습니다.

위 사진처럼 생성 버튼을 찾아 클릭하면 Service에 대해 설정할 수 있는 페이지가 나옵니다.

설정 페이지에서 다음과 같은 내용을 고려하여, 서비스를 생성합니다.

  • 컴퓨팅 옵션
    용량 공급자 전략은 용량 공급자를 만들어 클러스터 안에서 Task가 분산되어 실행되는 방식입니다.
    하나의 용량 공급자 전략은 여러 개의 용량 공급자를 만들 수 있고, 용량 공급자는 ECS가 scale in, scale out 하는 클러스터 내부의 용량을 정의합니다. 따라서 시작 유형 옵션에 비해 좀 더 유연한 스케일링 방식을 가지고 있다고 볼 수 있습니다.
    용량 공급자 옵션에서 "기본"과 "가중치"에 대한 옵션이 있는데, 여기서 "기본"은 용량 공급자에서 실행되는 최소한의 Task 수를 의마하며, 용량 공급자 전략에서 하나의 용량 공급자만 "기본" 값을 정의할 수 있습니다. "가중치" 옵션은 두 개의 용량 공급자가 있고 각각의 가중치 옵션이 1:4 라고 할 때, 첫 번째 용량 공급자의 Task 가 1개 일 때 두 번째 용량 공급자에 대해서 Task가 4개 실행됩니다.

    시작유형 옵션은, AWS 클러스터에서 컨테이너를 실행하는데 사용할 수 있는 리소스를 Fargate와 EC2 중에서 결정하게 됩니다. 클러스터에 대한 관리나 스케일링 작업 대신에 서비스를 단순히 정의하고 실행하는 데 초점을 맞춘 컴퓨팅 옵션입니다.

아래 서비스 설정에 보면 Auto Scaling 이라는 옵션이 있는데, 이것은 Service 전체에 대한 Auto Scaling 옵션으로, Task를 대상으로 Scaling 하는 용량 공급자 전략과는 별개입니다.

  • 어플리케이션 유형
    어플리케이션 유형에 서비스와 태스크가 있는데, 포스트 시작에 용어정리에 나와있는 개념의 서비스를 생성하기 위해서는 서비스 유형을 선택해야합니다. 서비스 유형의 경우 서비스의 태스크 중 문제가 생기면 서비스 스케줄러가 태스크 정의의 다른 인스턴스를 시작하여 대체하는 등 스케줄링이 가능합니다. 그러나 태스크 유형 같은 경우 한 번 실행되고 난 후 종료되므로, 배치 작업과 같은 프로세스에 적합합니다.
    아래 태스크 정의 칸에서 위에서 설정한 태스크 정의를 선택하고, 서비스 이름을 지어줍니다.

  • 로드 밸런싱
    서비스에 로드 밸런서를 설정하여 무중단 배포, 서버 대수 변경에 따른 로드 밸런싱을 지원할 수도 있습니다. Application Load Balancer와 Network Load Balancer 중에 선택이 가능한데, 밍글은 어플리케이션 서버이기 때문에 ALB를 사용했습니다.
    대상 그룹은 로드 밸런서에서 서비스의 태스크로 요청을 라우팅 하도록 하는 그룹으로, 기존 로드밸런서가 없다면 새로 이름을 짓는 것만으로 AWS에서 자동으로 생성해줍니다.
    상태 확인 경로는 연결된 컨테이너 포트로 ping을 날려, 연결상태를 확인하게 됩니다. 기본적으로 루트 경로가 사용되지만 따로 health check를 위해 만들어 둔 엔드포인트가 있다면 해당 엔드포인트를 명시해주면 됩니다.
    상태검사 유예 기간은, 태스크가 처음 시작되자마자 health check에 응답하지 못할 수 있기 때문에 일정기간 동안 로드 밸런서에 대해 상태 확인을 무시할 수 있는 기간입니다.

이로써 ECS 서비스에 대한 설정을 마쳤으며, 생성 버튼을 클릭해서 Task Definition 에 정의된 Task를 실행시켜서 서버를 구동할 수 있게 되었습니다.


GitHub Action 도입

이제 ECR를 통한 이미지 업로드부터 ECS 배포까지 일련의 작업을 GitHub Action을 통해 자동화한 과정을 적어보겠습니다.

지금까지 ECS를 사용한 배포 과정을 정리해보자면
1. 프로젝트를 build해서 jar 파일을 생성한다.
2. docker file을 만들어서 jar 파일을 담은 docker image를 생성한다.
3. docker image를 ECR에 업로드한다.
4. ECR에 업로드한 image를 기반으로 Task Definition을 만든다.
5. ECS Cluster와 Service를 생성해 서버를 배포한다.

총 5단계로 구성되어 있습니다.

이 5단계의 과정을 서버를 배포하려고 시도할 때마다 거치는 것은 매번 많은 시간을 잡아먹었으며, 개발의 진행도를 늦추는 큰 요인 중 하나로 자리했었습니다.
이를 자동화 시킬 방안으로 Jenkins, GitHub Action 등의 tool을 염두에 두고 비교해보았고, 결과적으로는 무료로 사용할 수 있으면서 관리가 쉬운 GitHub Action 스크립트를 사용한 자동배포 파이프라인을 구축했습니다.

이제 밍글이 GitHub Action을 도입한 과정과 사용하는 방법에 대해서 적어보겠습니다.

ECS json 파일 가져오기

먼저 ECS에 Service를 GitHub Action을 통해 만들기 위해서 Task Definition에 대한 정보를 프로젝트 파일이 가지고 있어야합니다.

위 사진과 같이 AWS 콘솔에서 ECS > 테스크 정의 > 원하는 테스크 > JSON 카테고리에 들어오면 Taks Definition에 대한 JSON 파일을 다운 받을 수 있는데, 해당 파일을 프로젝트 폴더의 루트 디렉토리에 위치시켜줍니다.

GitHub Action workflow 템플릿 받아오기

GitHub에서는 친절히 처음 사용해보는 유저들도 쉽게 GitHub Action workflow를 만들 수 있도록 많은 유명한 프로세스들에 대해서 템플릿을 제공해주고 있습니다.

위와 같이 리포지토리에서 Actions 탭에 들어가서 New workflow 라는 버튼을 클릭해줍니다.

그러면 많은 자동화 workflow에 대한 템플릿들을 볼 수 있는데 저희는 그 중에서 ecs라고 검색해 Deploy to Amazon ECS 라는 템플릿을 찾은 후 Configure를 눌러서 편집을 시작해줍니다.

GitHub Action Script 만들기

템플릿을 다운 받았으면 해당 템플릿에서 요구하는 사항들을 채워 넣어주어야 합니다.



name: Deploy to Amazon ECS CI/CD

on:
  push:
    branches: [ "dev" ]

env:
  AWS_REGION: ap-northeast-2                   # set this to your preferred AWS region, e.g. us-west-1
  ECR_REPOSITORY: mingle-api-dev                   # set this to your Amazon ECR repository name
  ECS_SERVICE: mingle-api-service-dev                 # set this to your Amazon ECS service name
  ECS_CLUSTER: mingle-api-dev-cluster                 # set this to your Amazon ECS cluster name
  ECS_TASK_DEFINITION: task-definition-dev.json # set this to the path to your Amazon ECS task definition
  #  # file, e.g. .aws/task-definition.json
  CONTAINER_NAME: mingle-api-dev           # set this to the name of the container in the
  # containerDefinitions section of your task definition

jobs:

  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    environment: production

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Set up JDK 11
        uses: actions/setup-java@v3
        with:
          java-version: '11'
          distribution: 'adopt'

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      - name: Copy Secret
        env:
          CREATE_SECRET: ${{secrets.MINGLE_DEV_APPLICATION_YML}}
          CREATE_SECRET_DIR: src/main/resources
          CREATE_SECRET_DIR_FILE_NAME: application.yml
        run: echo $CREATE_SECRET | base64 --decode > $CREATE_SECRET_DIR/$CREATE_SECRET_DIR_FILE_NAME

      - name: Build with Gradle
        run: ./gradlew build

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}

      - 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 }}
          IMAGE_TAG: ${{ github.sha }}
        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 "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT

      - 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: ${{ env.ECS_TASK_DEFINITION }}
          container-name: ${{ env.CONTAINER_NAME }}
          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: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: true

자세한 정리에 앞서, 위 코드는 밍글에서 dev 서버 배포에 사용 중인 github action script 입니다.
아래 내용들을 통해 자세한 설정 방법들에 대해 정리해 보겠습니다.


on:
  push:
    branches: [ "dev" ]

env:
  AWS_REGION: ap-northeast-2                   # set this to your preferred AWS region, e.g. us-west-1
  ECR_REPOSITORY: mingle-api-dev                   # set this to your Amazon ECR repository name
  ECS_SERVICE: mingle-api-service-dev                 # set this to your Amazon ECS service name
  ECS_CLUSTER: mingle-api-dev-cluster                 # set this to your Amazon ECS cluster name
  ECS_TASK_DEFINITION: task-definition-dev.json  # 위에서 다운받은 Task Definition의 파일 이름
  CONTAINER_NAME: mingle-api-dev           # set this to the name of the container in the
  # containerDefinitions section of your task definition

jobs:

  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    environment: production
  • on.push.branches
    어느 branch에 merge 되었을 때 해당 액션이 실행할 것인지를 설정합니다. 위 script의 경우 dev branch에 merge되거나 commit이 올라갈 경우 액션이 실행되게 됩니다.
  • env
    env는 환경변수 설정과 비슷한 역할을 하는 공간으로 아래 script에서 자주 사용할 변수를 미리 등록해놓을 수 있습니다. 주로 repository 이름이나 file 이름 등 노출되어도 큰 문제가 없는 값들을 변수로써 설정해두며, 비밀번호와 같은 민감한 정보는 아래 자세히 설명할 github secret 설정을 통해 가져옵니다.
  • jobs.deploy
    해당 명령어를 통해 github action이 어느 환경에서 실행이 될지를 명시할 수 있습니다. runs-on: ubuntu-latest 설정이므로 ubuntu 최신 버전 위에서 액션 과정이 돌아가도록 할 수 있습니다.
      - name: Checkout
        uses: actions/checkout@v3

      - name: Set up JDK 11
        uses: actions/setup-java@v3
        with:
          java-version: '11'
          distribution: 'adopt'

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew
  • checkout
    GitHub 리포지토리의 코드를 GitHub Action 단계에서 빌드, 테스트, 배포 등의 목적으로 사용할 있도록 checkout을 합니다.
  • set up JDK 11
    밍글은 자바11 버전을 기반으로 제작되었으므로 JDK 11환경을 설정해줍니다.
  • Grant execute permission for gradlew
    빌드를 위해 gradlew의 실행권한을 github action에게 부여해줍니다.
      - name: Copy Secret
        env:
          CREATE_SECRET: ${{secrets.MINGLE_DEV_APPLICATION_YML}}
          CREATE_SECRET_DIR: src/main/resources
          CREATE_SECRET_DIR_FILE_NAME: application.yml
        run: echo $CREATE_SECRET | base64 --decode > $CREATE_SECRET_DIR/$CREATE_SECRET_DIR_FILE_NAME
  • copy secret
    위에서 설명한 것 처럼 비밀번호화 같은 민감한 정보는 repository에 그대로 공개할 수 없기 때문에 따로 격리된 저장소에 암호화하여 보관한 후 필요할 때마다 로드해서 쓰는 구조가 필요합니다.
    저희는 GitHub에서 제공하는 secret 저장소에 민감한 정보들을 저장해서 해당 정보를 빌드 시마다 가져오는 방향을 선택했습니다.

이러한 secret GitHub Repository에 Setting 탭에 들어온 후 Security > Secrets and variables > Action 에 들어와서 원하는 데이터를 저장할 수 있으며, 이후 정보를 가져오고 싶을 때는 secrets.${secret의 이름} 방식으로 가져올 수 있습니다.

위 코드에서는 secrets에서 밍글 프로젝트의 application.yml 파일을 secrets로부터 가져와 base64로 인코딩 된 값을 decoding해서, resources 파일 내부에 저장하고 있습니다.

      - name: Build with Gradle
        run: ./gradlew build
  • build with gradle
    ./gradlew build 명령어를 통해 프로젝트를 build하여 jar 파일을 생성합니다.
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}

      - 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 }}
          IMAGE_TAG: ${{ github.sha }}
        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 "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT

      - 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: ${{ env.ECS_TASK_DEFINITION }}
          container-name: ${{ env.CONTAINER_NAME }}
          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: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: true

위 코드에서부터 본격적으로 AWS와 연결하여 서비스 배포를 시작합니다.

  • Configure AWS credentials
    CLI에서 docker image를 올리기 위해서 로컬에서 credentials를 설정해주었듯이, github action을 실행시키는 인스턴스에게도 계정권한이 필요하기 때문에 credentials를 입력해줍니다. 이 때 해당 계정의 권한은 아래 github action들을 모두 시행시킬 수 있을만한 권한이 반드시 필요합니다.

밍글은 해당 ID와 Password를 github secret에 저장을 해두었기 때문에, secrets.AWS_ACCESS_KEY_ID 와 같은 방식으로 변수를 가져오게됩니다.

  • Login to Amazon ECR
    위에서 작성한 id와 password를 바탕으로 ECR에 접근, 수정할 수 있는 권한을 가집니다.

  • Build, tag, and push image to Amazon ECR
    해당 script를 통해 docker image를 생성하고 ECR로 push하게 됩니다.
    steps.login-ecr.outputs.registry 는 이전 단계인 login to Amazon ECR의 aws-actions/amazon-ecr-login@v1 액션의 출력 중 하나로 ECR 레지스트리 주소를 참조하는 변수입니다.
    github.sha는 푸시된 현재 커밋의 sha 값으로 각 커밋마다 고유한 태그를 사용해서 이미지 식별에 사용할 수 있습니다.

    이렇게 만들어진 환경변수를 바탕으로 docker를 build 하고, ECR에 push 해 줍니다.

  • Fill in the new image ID in the Amazon ECS task definition
    위 env 에서 작성한 task definition의 값에 이번에 배포하게 될 이미지의 이름과 태그 값을 넣습니다.

  • Deploy amazon ecs task definition
    Task Definition을 롤링배포 바탕으로 배포해서 서비스를 업데이트하고 안정화가 될 때까지 기다립니다.


이와 같이 설정을 하고, 해당 설정 yml 파일을 프로젝트 폴더의 루트 디렉토리에 .github 라는 폴더에 위치시켜서 관리합니다.

이제 설정해둔 branch에 push가 될 시 위와 같이 github action이 자동으로 실행되면서 서버가 매번 새롭게 배포되는 것을 알 수 있습니다.


지금까지 ECS와 Fargate를 이용해 서버를 배포해보고, GitHub Action을 통해 ECS에 서버를 자동으로 배포하는 과정을 정리해보았습니다.

감사합니다.

0개의 댓글