AWS

GitHub Actions으로 Amazon ECS Fargate 배포하기

beekei 2023. 4. 12. 19:59
반응형

다들 아시고 계시겠지만 GitHub Actions가 무엇인지, Amazon ECS Fargate가 무엇인지 간단히 정리해보았습니다.

GitHub Actions란?

GitHub Actions는 GitHub에서 호스팅하는 CI/CD 툴입니다.

개발자가 코드 변경을 자동으로 빌드, 테스트, 배포하는 프로세스를 자동화하기 위한 워크플로우 생성할 수 있습니다.

이는 프로젝트에서 이슈를 해결하고 배포를 관리하는 데 유용합니다.

GitHub Actions는 저장소에 이벤트가 발생할 때 트리거가 됩니다. 

예를 들어, 코드 푸시, 풀 리퀘스트, 이슈 등록, 릴리스 발행 등의 이벤트가 트리거가 될 수 있습니다. 

이러한 이벤트에 대한 트리거를 설정하고, 트리거를 받으면 실행되는 작업을 작성할 수 있습니다.

GitHub Actions를 사용하면 빠른 시간에 안정적인 소프트웨어를 개발하고 배포하는 데 도움이 됩니다. 

또한 GitHub Marketplace에서 다른 개발자들이 작성한 워크플로우를 찾아 사용할 수도 있습니다.

 

Amazon ECS Fargate란?

Amazon Elastic Container Service (ECS)는 AWS에서 제공하는 컨테이너 오케스트레이션 서비스입니다. 

ECS Fargate는 ECS의 실행 모드 중 하나로, 서버리스 컴퓨팅을 지원하는 컨테이너 오케스트레이션 서비스입니다.

기존의 ECS는 EC2 인스턴스에 컨테이너를 배치하고 관리했습니다. 

하지만 Fargate는 이러한 EC2 인스턴스를 관리하지 않고, 컨테이너 자체에 대해서만 집중하여 관리합니다. 

따라서 사용자는 EC2 인스턴스를 구축하고 관리하지 않고도 컨테이너를 실행할 수 있습니다.

Fargate를 사용하면 컨테이너를 보다 쉽고 간편하게 실행할 수 있으며, 인프라 관리를 최소화할 수 있습니다. 

이는 컨테이너 운영을 더욱 쉽고 빠르게 시작할 수 있도록 도와주며, 서버리스 환경에서도 컨테이너를 실행할 수 있는 유연성을 제공합니다.

 

 

그래서 뭘할껀데? GitHub Actions CI/CD로 Serverless 서비스를 배포한다는 소리입니다~

도식화를 간단히 그려보면 아래와 같습니다.


1. Workflow 생성

GitHub Repository에 Actions 탭에 접속해 Workflow를 생성합니다.

GitHub Repository > Actions > New workflow

ECS Workflow 코드를 검색해보면 Deploy to Amazon ECS가 존재합니다. 요것을 선택

(직접 Workflow 코드를 입력해도 무방합니다.)

Deploy to Amazon ECS

저는 Workflow 코드는 아래와 같이 설정하였습니다.

name: Deploy to Amazon ECS

on:
  push:
    branches: [ "develop" ] # develop 브랜치에서만 진행

permissions:
  contents: read

# 환경변수 설정
env:
  # Spring Boot Active Profile
  ACTIVE_PROFILE: ${{ github.ref == 'refs/heads/master' && 'production' || 'develop' }} 
  # AWS Region
  AWS_REGION: ap-northeast-2
  # 도커 컨테이너명
  CONTAINER_NAME: example-application
  # ECR 리포지토리명
  ECR_REPOSITORY: ${{ github.ref == 'refs/heads/master' && 'production-example-application' || 'develop-example-application' }}
  # ECS 클러스터명
  ECS_CLUSTER: ${{ github.ref == 'refs/heads/master' && 'production-example-application' || 'develop-example-application' }}
  # ECS 서비스명
  ECS_SERVICE: ${{ github.ref == 'refs/heads/master' && 'production-example-application' || 'develop-example-application' }}
  # ECS Task definition 파일명
  ECS_TASK_DEFINITION: ${{ github.ref == 'refs/heads/master' && 'task-definition-production.json' || 'task-definition-develop.json' }}

jobs:
  job:
    name: Build & Deploy
    runs-on: ubuntu-latest
    environment: production
    steps:
      # 체크아웃
      - name: Checkout
        uses: actions/checkout@v3
      # JDK 설정
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
      # Gradle clean build
      - name: Build with Gradle
        uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
        with:
          arguments: clean build
        env:
          SPRING_PROFILES_ACTIVE: ${{ env.ACTIVE_PROFILE }}
      # AWS 자격 인증 설정
      - 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 }}
      # ECR 로그인
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1
      # ECR에 도커 이미지 Push
      - name: Push docker image to ECR
        id: build-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: latest
        run: |
          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
      # ECS Task 정의
      - 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 }}
      # ECS Task Push
      - 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

제일 상단에 브랜치별로 환경변수를 설정해주었고

체크아웃 > Application 테스트 및 빌드 > 도커 이미지 빌드 > ECR에 도커 이미지 Push > ECS Task 정의 및 Push 순서로 Job이 진행되도록 설정했습니다.

 

secrets 환경변수(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)는 Repository Settings 탭에서 설정할 수 있습니다.

GitHub Repository > Settings > Secrets and variables > Actions > New repository secret

AWS IAM 권한 정책 은 AmazonEC2ContainerRegistryFullAccess, AmazonECS_FullAccess, AmazonECSTaskExecutionRolePolicy를 추가해줘야 합니다.

 

모두 작성하고 해당 파일을 commit하게 되면 Workflow가 진행됩니다.

허나 지금은 생성한 ECR과 ECS가 없고 Dockerfile, task-definition.json 파일도 없기 떄문에 배포에 실패할겁니다.

 

2. Dockerfile 생성

프로젝트 루트 경로에 Dockerfile을 생성합니다.

# JDK 17
FROM openjdk:17-alpine 

# 8080 포트로 배포
EXPOSE 8080

# 작업 공간 이동
WORKDIR /app
# Jar 파일 경로
ARG JAR_FILE_PATH=build/libs/*.jar
# Jar 파일 복제
COPY ${JAR_FILE_PATH} app.jar

# Jar 실행
ENTRYPOINT ["java", "-jar", "app.jar"]

Workflow에서 Application 테스트 및 빌드 후 해당 Dockerfile을 이용해 도커 이미지로 빌드합니다.

 

3. ECR 생성

AWS 콘솔에 접속해 ECR을 생성해줍니다.

AWS Console > Amazon ECR > 리포지토리 생성

리포지토리 이름과 Workflow 파일에 작성한 ECR_REPOSITORY이 같아야 합니다.

다른 옵션들은 굳이 건드리지 않아도 됩니다.

 

4. ECS 클러스터 생성

AWS 콘솔에 접속해 ECS을 생성해줍니다.

AWS Console > Amazon Elastic Container Service > 클러스터 > 클러스터 생성

마찬가지로 클러스터 이름과 Workflow 파일에 작성한 ECS_CLUSTER가 같아야 합니다.

네트워킹에는 클러스터를 생성할 VCP와 Subnet을 선택하여 클러스터를 생성합니다.

 

5. ECS 테스크 정의

위에서 생성한 클러스트에서 실행될 Task를 정의합니다.

AWS Console > Amazon Elastic Container Service > 태스크 정의 > 새 태크스 정의 생성

컨테이너 이름도 마찬가지로 Workflow 파일에 작성한 CONTAINER_NAME가 같아야 합니다. 

이미지 URI는 위에서 생성한 ECR URI와 태그를 입력하면 됩니다. ({ECR URI}:{Image Tag})

 

이렇게 생성한 테스크 정의를 보면 JSON으로 정의한 내용을 다운받을 수 있습니다.

AWS Console > Amazon Elastic Container Service > 태스크 정의 > 생성한 태스크 정의 > JSON > JSON 다운로드

JSON 파일을 다운받아 파일명을 Workflow 파일에 작성한 ECS_TASK_DEFINITION로 변경한 후 프로젝트 루트 경로에 넣어줍니다.

ECR에 도커 이미지 배포 후 해당 task-definition.json 파일로 ECS 클러스터 내에서 Task를 실행하게 됩니다.

 

6. ECS 서비스 생성

위에서 생성한 ECS 클러스터에 서비스를 생성합니다.

AWS Console > Amazon Elastic Container Service > 클러스터 > 생성한 클러스터 > 서비스 > 생성

배포 구성에서 패밀리는 위에서 생성한 태스크 정의를 선택합니다.

배포 옵션에 배포 유형은 롤링 배포가 고정되어있습니다.

배포 방식에는 블루/그린 배포롤링 배포가 있는데 간단히 설명하면 아래와 같습니다.

더보기

블루/그린 배포(Blue/Green Deployment)
블루(BLUE)와 그린(GREEN) 두 개의 환경을 사용하는 배포 방식입니다.
새로운 버전의 소프트웨어를 그린(GREEN) 환경에 배포하고, 블루(BLUE) 환경에서 현재 운영 중인 버전과 교체합니다.
이후 트래픽을 그린(GREEN) 환경으로 전환하면 새로운 버전이 운영됩니다.
이 방식은 배포 시간 동안 중단 없이 운영이 가능합니다.


롤링 배포(Rolling Deployment)
새로운 버전의 소프트웨어를 점진적으로 배포하는 방식입니다.
일반적으로 서비스의 인스턴스를 순차적으로 교체하는 방식으로 이루어집니다.
이 방식은 블루/그린 배포와 비교해 배포 시간 동안 일부 트래픽이 중단될 수 있습니다.
롤링 배포는 배포 후에 문제가 발생할 가능성이 있으며, 이 경우 이전 버전으로 롤백(rollback)해야 할 수도 있습니다.
이 두 가지 방식은 각각 장단점이 있으며, 상황에 따라 적합한 방식을 선택해야 합니다.

배포중에 실행중인 최소, 최대 태스크도 설정할 수 있습니다.

네트워킹에는 VPC와 Subnet을 위에서 생성한 클러스터와 동일하게 설정하고 보안그룹도 설정합니다.

보안그룹은 포트번호 8080으로 헬스체크를 할 것이기 때문에 8080 포트를 열어줘야 합니다!!

로드 밸런싱도 설정해줍니다. (없으면 바로 만드셔도 됩니다.)

로드 밸런싱할 컨테이너를 선택하고, 헬스체크할 경로와 상태 검사 유예 기간을 설정합니다.

상태 검사 유예 기간을 설정하지 않으면 Application 서버가 구동 전에 헬스체크를 진행해서 unhealthy한 결과를 받을 수 도 있습니다.

(헬스체크 경로는 Spring Boot에 actuator를 사용하였습니다.)

배포 시 Task가 실행이 되고 연결한 로드밸런서에서 헬스체크를 진행한 후 Application이 healthy할때 서비스가 활성화 됩니다.

 

7. 배포 테스트

여기가지 설정이 끝났으면 Code Push를 통해 배포 테스트를 진행해 봅시다.

배포 완료


이렇게 간단하게 설정으로 GitHub Actions를 통해 ECS Fargate를 배포를 진행하였습니다.

진행하며 AWS 생태계를 좀 더 이해할 수 있었고 또 하나의 배포 방식을 경험한 것 같습니다.

 

 

 

블로그 글에 틀린점이나 이해가 안가시는 부분이 있다면 댓글 부탁드립니다!!

반응형