개발공부/CI&CD

[CI/CD] GitHub Actions ECS 구성하기

키크니개발자 2022. 10. 11. 17:45

 

안녕하세요!

키크니 개발자 입니다. 🦒

 

회사에서 AWS ECS로 배포를 진행하고 있는데 하나하나 커맨드를 입력하면서 배포하기에는 너무 번거로워서 

간편하게 배포하기 위해 Github Actions를 적용하였습니다.

 

GitHub Action 이란?

  • github에서 제공하는 CI(Continuous Integration, 지속적 통합)/CD(Continuous Deployment, 지속적 배포) 서비스 입니다.

 

Github Action Core 개념

  • 깃허브 액션을 이해하기 위해서 알아야 하는 개념은 workflow, event, job, step, action, runner 등이 있습니다.
  • Workflow
    • 여러 Job으로 구성되고, Event에 의해 트리거 될 수 있는 자동화 된 프로세스를 의미합니다.
    • 깃허브 액션에서 최상위 개념으로 자동화 해놓은 작업 과정이라고 볼 수 있습니다.
    • Workflow 파일은 YAML으로 작성되고, Github Repository의 .github/workflows 폴더 아래에 저장됩니다.
    • name : workflow의 이름. 나중에 해당 값을 사용할 수 있기 때문에 저는 유니크하게 사용합니다.
  • Event
    • workflow를 trigger(실행)하는 특정 활동이나 규칙을 의미합니다.
    • 예를 들어 다음과 같은 상황을 사용할 수 있음
      • 특정 브랜치로 Push하거나
      • 특정 브랜치로 Pull Request하거나
      • 특정 시간대에 반복(Cron)
      • Webhook을 사용해 외부 이벤트를 통해 실행
      • 자세한 내용은 Events that trigger workflows 참고
    • on : workflow를 동작하게 하는 trigger입니다.
      • push(Branch or Tag), pull_request, schedule을 사용할 수 있음
      • 단일 Event를 사용할 수도 있고, array로 작성할 수도 있음
on: push

# 또는
on: [pull_request, issues]

# 또는
on:
    push:
      branches: [ master ]
    pull_request:
      branches: [ master ]
  • Job
    • 깃허브액션에서 Job(작업)은 독립된 가상머신 또는 컨테이너에서 돌아가는 하나의 처리 단위를 의미합니다.
    • 하나의 워크플로우는 여러 개의 작업(job)으로 구성되며 적어도 하나의 작업은 있어야 합니다.
    • Job은 여러 Step으로 구성되고, 가상 환경의 인스턴스에서 실행됩니다.
    • 다른 Job에 의존 관계를 가질 수 있고, 독립적으로 병렬 실행도 가능합니다.
    • 모든 작업은 기본적으로 동시에 실행되며 필요시 작업간에 의존관계를 설정하여 작업이 실행되는 순서를 제어할 수 있습니다.
    • 작업의 세부 내용으로는 여러 가지 내용을 명시할 수 있으며 필수로 들어가야 하는 runs-on속성을 통해 해당 리눅스나 윈도우즈와 같은 실행환경을 지정해줘야 합니다.
      • 예를 들어, 가장 널리 사용되는 우분투의 최신 실행 환경에서 해당 작업을 실행하고 싶다면 다음과 같이 설정합니다.
      • 아래 예시는 build라는 job을 생성하고, 그 아래에 2개의 step이 존재하는 구조
jobs:
  job1:
    # job1에 대한 세부 내용
  job2:
    # job2에 대한 세부 내용
  job3:
    # job3에 대한 세부 내용


jobs:
    build:
      runs-on: ubuntu-latest

      steps:
      - uses: actions/checkout@v2
      - name: Run a one-line script
        run: echo Hello, world!

      - name: Run a multi-line script
        run: |
          echo Add other actions to build,
          echo test, and deploy your project.
  • Step
    • Task들의 집합으로, 커맨드를 날리거나 action을 실행할 수 있습니다.
    • 깃허브 액션에서는 각 작업(Job)이 하나 이상의 단계(Step)로 모델링이 됩니다.
    • 커맨드나 스크립트를 실행할 때는 run 속성을 사용하며, 액션을 사용할 때는 uses 속성을 사용합니다.
      • 예를 들어 자바스크립트 프로젝트에서 테스트를 돌리려면 코드 저장소에 코드를 작업 실행 환경으로 내려 받고, 패키지를 설치한 후, 테스트 스크립트를 실행해야할텐데요. 이 3단계의 작업은 아래와 같이 steps 속성을 통해서 명시될 수 있을 것입니다.
    • 워크플로우 파일 내에서 작업 단계를 명시해줄 때는 주의할 부분이 있는데요. YAML 문법에서 시퀀스(sequence) 타입을 사용하기 때문에 각 단계 앞에 반드시 - 를 붙여줘야 합니다.
    • steps의 uses는 어떤 액션을 사용할지 지정함. 이미 만들어진 액션을 사용할 때 지정한다.
    • runs-on은 어떤 OS에서 실행될지 지정한다.
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: npm install
      - run: npm test
  • Action
    • Workflow의 가장 작은 블럭(smallest portable building block)
    • Job을 만들기 위해 Step들을 연결할 수 있음
    • 재사용이 가능한 컴포넌트
    • 개인적으로 만든 Action을 사용할 수도 있고, Marketplace에 있는 공용 Action을 사용할 수도 있습니다.
  • Runner
    • Gitbub Action Runner 어플리케이션이 설치된 머신으로, Workflow가 실행될 인스턴스
    • Github에서 호스팅해주는 Github-hosted runner와 직접 호스팅하는 Self-hosted runner로 나뉨
    • Github-hosted runner는 Azure의 Standard_DS2_v2로 vCPU 2, 메모리 7GB, 임시 스토리지 14GB

Secret Key 저장하기

  • 클라우드의 저장소에 데이터를 저장하거나, Dockerhub에 접근하는 등을 할 때 고유한 Key가 필요할 수 있습니다.
    • 이런 경우 Secrets에 등록해서 사용할 수 있습니다.
  1. Github Access Token 생성하기
    • Github Issue를 남기기 위해 Github Access Token이 필요함
    • Settings - Developer settings - Personal access tokens - Generate new token 클릭
    • 체크박스에 필요한 권한 체크. repo, workflow 설정 후 저장
    • Value 복사하기
    • Repository의 Settings > 왼쪽에 Secrets 클릭
    • New secret 클릭하고 Name과 Value 입력하기
    • Repository access 쪽으로 가면, access policy를 정할 수 있음
    • workflow에서 사용할 땐 $ 으로 사용하고, python에선 os.getenv(변수 이름)으로 사용
    • 더 자세한 내용은 Creating and storing encrypted secrets 참고Secret Key 등록하기

Workflow 정의하기

  • Github Repo에서 Actions 클릭
  • 깃허브액션을 적용할 레파지토리를 들어간 후 github action에서 ecs를 선택합니다.
  • aws.yml 파일에서 ecr repository name, region, ecs container name을 수정해주고 repository setting tab에 secret에 secret 변수들을 설정해줍니다.
  • 어디 브랜치에 커밋이 되었거나 Pr이 되었을 때 CI/CD가 작동할 수 있도록 브랜치 설정도 해줍니다.

GItHub Actions Yaml 예시

# This is a basic workflow to help you get started with Actions

name: Build Docker Image with bootBuildImage(Gradle) & Deploy to Amazon ECS

# Controls when the workflow will run
# release 브랜치로 pull_request가 올라오고, closed 되었을 때 실행
on:
  pull_request:
    branches:
      - release
    types: [closed]

env:
  AWS_REGION: ECS_REGION  # set this to your preferred AWS region
  ECR_REPOSITORY: ECR_NAME # set this to your Amazon ECR repository name
  ECS_SERVICE: SERVICE_NAME # set this to your Amazon ECS service name
  ECS_CLUSTER: CLUSTER_NAME # set this to your Amazon ECS cluster name
  ECS_TASK_DEFINITION: .aws/ecs/task-definition.json (TASK_DEFINITION_PATH) # set this to the path to your Amazon ECS task definition
    # file, e.g. .aws/task-definition.json
  CONTAINER_NAME: CONTAINER_NAME # set this to the name of the container in the
    # containerDefinitions section of your task definition
    # 컨텍스트 : 워크플로 및 작업에서 컨텍스트 정보에 액세스할 수 있습니다.
    # 컨텍스트 정보 : 워크플로 실행, 실행자 환경, 작업 및 단계에 대한 정보에 엑세스 하는 방법입니다.
    # 각 컨텍스트는 속성을 포함하는 개체이며 문자열 또는 기타 개체일 수 있습니다.  
    # 표현식 : ${{ <context> }}
    # ${{ github.sha }} : 워크플로우를 트리거한 commit SHA입니다. 워크플로우를 트리거한 이벤트에 따라 다릅니다. (일종의 중복이 없는 임의의 문자열 이라고 생각하시면 됩니다.)
    # 버전을 따로 관리하기위해 latest가 아닌 임의의 string을 설정합니다.
  IMAGE_TAG: ${{ github.sha }}
  IMAGE_NAME: DOCKER_IMAGE_NAME

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  deploy:
    name: Deploy
    # 만약 pull_request 가 merged 되었을 경우 아래의 steps들을 실행한다.
    if: ${{ github.event.pull_request.merged }}
    runs-on: ubuntu-latest
    environment: prod

    steps:
    # Checkout : GitHub Actions는 해당 프로젝트를 리눅스 환경에 Checkout 하고 나서 실행합니다. (브랜치 만들때 checkout 하는 것이라고 생각하면 됩니다, 저장소(리포지토리)에서 파일을 받아오는 것)
      - name: Checkout
      	# uses : 누군가 만들어놓은 action을 사용할 때 표기합니다.
        uses: actions/checkout@v2

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

      - name: Grant execute permission for gradlew
    # chmod +x : 접근 권한 변경 / +x : 모든 사용자(소유자, 그룹, 기타사용자)에게 실행 권한 추가 (chmod +x gradlew : gradlew에 실행 권한 부여) 	
        run: chmod +x gradlew

      - name: build docker
        id: build-image
        run: ./gradlew clean build bootBuildImage --imageName=${{env.IMAGE_NAME}}:${{ env.IMAGE_TAG }} -P profile=prod

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
        # secrets.XXXX : Github repository에서 setting -> Secrets에서 설정한 key를 사용합니다.
          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: tag, and push image to Amazon ECR
        id: tag-and-push-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        run: |
          docker tag $ECR_REPOSITORY:${{ env.IMAGE_TAG }} $ECR_REGISTRY/$ECR_REPOSITORY:${{ env.IMAGE_TAG }}
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:${{ env.IMAGE_TAG }}
          # echo 명령어 : 유닉스 혹은 리눅스 운영체제에서 지정한 문자열 또는 텍스트를 터미널에 출력하는 명령어입니다.
          # 출력 변수의 값을 쓰려면 ::set-output name={name}::{value} 문법을 사용해야 합니다.
          # 출력 변수의 값을 읽으려면 GitHub Actions의 문맥인 steps.<step_id>.outputs.<output_name> 문법을 사용해야 합니다.
          echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:${{ env.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: ${{ env.ECS_TASK_DEFINITION }}
          container-name: ${{ env.CONTAINER_NAME }}
          image: ${{ steps.tag-and-push-image.outputs.image }}

	# ECS task definition을 새로 배포합니다. 배포 후 ECS task definition을 보면 수정되어있는 것을 확인할 수 있습니다.
      - 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

 

위의 yml파일을 프로젝트의 루트 경로 기준으로 /.github/workflows 안에 위 스크립트 yml을 위치시켜줍니다.

 

 

 

References

https://fe-developers.kakaoent.com/2022/220106-github-actions/

https://roseline.oopy.io/dev/github-action-cahce

https://zzsza.github.io/development/2020/06/06/github-action/

https://www.daleseo.com/github-actions-basics/

https://insight-bgh.tistory.com/473

https://velog.io/@rudwnd33/zero-downtime-deployment

https://www.daleseo.com/github-actions-steps/

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

https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request

https://medium.com/prnd/앱-배포후-jira에서-버전-release처리-자동으로-하는-방법-feat-github-action-ab4c4ecf437d

https://jojoldu.tistory.com/668

https://www.daleseo.com/github-actions-steps/

 

 

 

반응형