본문 바로가기

항해 99/Spring

CI/CD 2

개요

GiHub Actions에 대해 학습하고, GitHub Actions를 사용한 CI/CD 과정과 프로젝트에서 사용한 Docker 설정 파일에 대해 분석을 해 볼 것이다.

 

GitHub Actions

 

깃허브 액션은 깃허브에서 제공하는 CI/CD 플랫폼으로 빌드, 테스트, 배포를 자동화 시키는 파이프라인을 만들 수 있다.

 

GitHub Action 구성 요소

Workflow

  • 한 개 이상의 job을 실행할 수 잇는 자동화된 작업
  • YAML 파일로 저장되며 event에 의해 실행된다.

Event

  • workflow 실행을 발동시키는 특정한 활동
  • 깃허브에 소스코드를 푸시하면 발생하는 push event, pull request event, issue event 등 깃허브에서 발생하는 대부분의 작업을 event로 정의할 수 있다.

Jobs

  • 한 가지 러너 안에서 실행되는 여러 가지 step들의 모음
  • 각각의 step들은 일종의 shell script처럼 실행이 된다.
  • Step들은 순서에 따라 실행되며 Step끼리 데이터들을 공유할 수 있다.
  • Job은 다른 Job에 의존관계를 가질 수 있으며, 병렬 실행도 가능하다.

Actions

  • 복잡하고 자주 반복되는 작업을 정의한 커스텀 어플리케이션
  • workflow 파일 안에서 자주 반복되는 코드를 미리 정의해 코드의 양을 줄일 수 있다.
  • 깃허브 마켓플레이스를 통해 공용 Action 또는 다른 사람들이 만든 Action을 사용할 수 있다.

 

예시

name: learn-github-actions
on: [push]
jobs:
  check-bats-version:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: '14'
      - run: npm install -g bats
      - run: bats -v

 

name

  • workflow의 name을 정의
  • 선택사항이며 깃허브 저장소의 깃허브 액션 탭에서 workflow의 이름을 보여준다.

on

  • 해당 workflow를 실행시키는 이벤트를 정의
  • push 이벤트가 발생했을 때 workflow가 실행되도록 정의

jobs

  • check-bats-version-job의 이름을 정의
  • runs-on : 어떤 호스트에서 실행될지 정의 - ubuntu 가상 머신에서 실행되도록 정의

step

  • uses: actions/checkout@v2 - 해당 레포지토리를 pull 받고 이동하는 action 대부분의 workflow에 사용
  • uses: actions/setup-node@v2  - 노드를 설치하는 action으로 가상 머신 안에는 대부분의 프로그래밍 언어가 설치되어 있지 않기 대문에 프로젝트 실행에 필요한 언어들을 action을 통해 설치
  • run: npm install -g bats - run 키워드를 통해 러너가 실행되는 서버에서 명령어를 실행

 

GitHub Action으로 CI/CD 구축

GitHub Action 시작

  • 깃허브 레포지토리의 Actions 탭으로 가면 GitHub Actions Workflow 파일을 작성할 수 있다.
  • 처음 workflow 생성 시 깃허브에서 적절한 Template을 제공한다.
  • 원하는 템플릿을 선택하면 해당 템플릿을 바탕으로 workflow를 자유롭게 만들 수 있으며 저장소의 .github/workflows에 yaml 파일이 생성된다.

Secret 환경 변수 등록하기

  • 깃허브에서는 organization, Repository에 Secret 변수를 등록
  • 해당 변수를 workflow에서 사용하면 workflow의 로그에서는 환경 변수로 나오며 해당 값을 노출시키지 않는다.

 

 

  • Repository Setting에서 Secret 탭으로 들어가면 환경변수 설정 가능
  • username, password 등 보안이 필요한 부분을 환경 변수로 저장

GitHub Action Workflow

프로젝트에서 사용한 workflow을 분석한다.

name: Deploy to AWS EC2

on:
  push:
    branches:
      - develop
  pull_request:
    branches:
      - develop
# 사용할 인프라의 이름을 변수 형태로 저장
jobs:
  build-and-deploy:
    # 실행 환경 지정
    runs-on: ubuntu-latest

    # Task sequence 명시
    steps:
      - uses: actions/checkout@v2
      - name: Set up JDK 17
        uses: actions/setup-java@v1
        with:
          java-version: 17

      # Github Actions 워크플로우가 실행되는 가상 환경내에 application-dev 설정
      - name: Generate application-secret.yml
        run: |
          echo "${{ secrets.APPLICATION_SECRET_CONTENT }}" > ./src/main/resources/yml/application-secret.yml

      # Github Actions 워크플로우가 실행되는 가상 환경내에 application-dev 설정
      - name: Generate application-oauth.yml
        run: |
          echo "${{ secrets.APPLICATION_OAUTH }}" > ./src/main/resources/yml/application-oauth.yml

      # Gradle 빌드 할 때, 이미 저장한 데이터를 캐싱하여 빌드시 이를 사용하도록함.
      - name: Gradle Caching
        uses: actions/cache@v3
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
          restore-keys: |
            ${{ runner.os }}-gradle-

      # Github Actions 워크플로우 가상환경이 grdlew 실행할 수 있도록 ./gradlew 파일에 실행 권한을 부여
      - name: Grant execute permission for gradlew
        run: chmod +x ./gradlew
        shell: bash

      # Gradle build (Test 제외)
      - name: Build with Gradle
        run: ./gradlew clean build -x test

      # Docker 이미지 빌드
      - name: Build Docker image
        run: docker build -f Dockerfile -t myapp:${{ github.sha }} .

      # ECR에 docker image push
      - 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: ap-northeast-2

	  # ECR에 Login
      - name: Log in to Amazon ECR
        run: |
          aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin ${{ secrets.AWS_ECR_REPOSITORY_URL }}

	  # ECR에 이미지 push
      - name: Push image to Amazon ECR
        run: |
          docker tag myapp:${{ github.sha }} ${{ secrets.AWS_ECR_REPOSITORY_URL }}:latest
          docker push ${{ secrets.AWS_ECR_REPOSITORY_URL }}:latest
	  
      # EC2에 ssh로 접속 후 ECR에서 docker로 이미지 pull & docker container 
      - name: Deploy to EC2
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.SSH_KEY }}
          port: 22
          script: |
            sudo docker login -u AWS -p $(aws ecr get-login-password --region ap-northeast-2) ${{ secrets.AWS_ECR_REPOSITORY_URL }}
            sudo docker pull ${{ secrets.AWS_ECR_REPOSITORY_URL }}:latest
            sudo docker-compose down
            sudo docker-compose up -d
            sudo docker image prune -f

 

Job and Steps

1. 환경 설정

  • 환경: ubuntu-latest를 사용하여 작업을 수행한다.
  • 작업: build-and-deploy 작업을 정의한다.

2. 작업 순서(Steps)

  • 코드 체크아웃
    • GitHub 리포지토리의 코드를 체크아웃 한다.
- uses: actions/checkout@v2

 

  • JDK 17 설정
- name: Set up JDK 17
  uses: actions/setup-java@v1
  with:
    java-version: 17

 

  • 환경 파일 생성
    • application-secret.yml, application-monitoring.yml, application-oauth.yml 생성
- name: Generate application-secret.yml
  run: |
    echo "${{ secrets.APPLICATION_SECRET_CONTENT }}" > ./src/main/resources/yml/application-secret.yml

- name: Generate application-monitoring.yml
  run: |
    echo "${{ secrets.APPLICATION_MONITORING }}" > ./src/main/resources/yml/application-monitoring.yml

- name: Generate application-oauth.yml
  run: |
    echo "${{ secrets.APPLICATION_OAUTH }}" > ./src/main/resources/yml/application-oauth.yml

 

  • Gradle 캐싱
    • Gradle 빌드 시 캐시를 사용해 빌드 속도를 향상시킨다.
- name: Gradle Caching
  uses: actions/cache@v3
  with:
    path: |
      ~/.gradle/caches
      ~/.gradle/wrapper
    key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
    restore-keys: |
      ${{ runner.os }}-gradle-

 

  • gradlew 실행 권한 부여
    • gradlew 파일에 실행 권한을 부여한다.
- name: Grant execute permission for gradlew
  run: chmod +x ./gradlew
  shell: bash

 

  • Gradle 빌드 (테스트 제외)
    • 테스트를 제외하고 Gradle 빌드를 수행한다.
- name: Build with Gradle
  run: ./gradlew clean build -x test

 

  • Docker 이미지 빌드
    • Docker 이미지를 빌드한다. 'myapp:${{ github.sha }}' 형식으로 태그를 지정한다.
- name: Build Docker image
  run: docker build -f Dockerfile -t myapp:${{ github.sha }} .

 

  • AWS 자격 증명 구성
    • 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: ap-northeast-2

 

  • Amazon ECR 로그인
    • Amazon ECR에 로그인한다.
- name: Log in to Amazon ECR
  run: |
    aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin ${{ secrets.AWS_ECR_REPOSITORY_URL }}

 

  • 이미지 ECR에 푸시
    • Docker 이미지를 ECR에 푸시한다.
- name: Push image to Amazon ECR
  run: |
    docker tag myapp:${{ github.sha }} ${{ secrets.AWS_ECR_REPOSITORY_URL }}:latest
    docker push ${{ secrets.AWS_ECR_REPOSITORY_URL }}:latest

 

  • EC2에 배포
    • SSH를 통해 EC2 인스턴스에 접속하여, 최신 이미지를 풀(Pull)하고, docker-compose를 사용해 컨테이너를 재시작하며, 사용하지 않는 이미지를 삭제한다.
- name: Deploy to EC2
  uses: appleboy/ssh-action@master
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.SSH_KEY }}
    port: 22
    script: |
      sudo docker login -u AWS -p $(aws ecr get-login-password --region ap-northeast-2) ${{ secrets.AWS_ECR_REPOSITORY_URL }}
      sudo docker pull ${{ secrets.AWS_ECR_REPOSITORY_URL }}:latest
      sudo docker-compose down
      sudo docker-compose up -d
      sudo docker image prune -f

 

 

Secret 사용

  • APPLICATION_SECRET_CONTENT, APPLICATION_MONITORING, APPLICATION_OAUTH 등은 GitHub Secrets로부터 가져온다.
  • AWS 자격 증명 및 ECR URL, EC2 접속 정보 등도 Secrets에 저장된 값을 사용한다.

 

GitHub Action 실행 및 결과 확인

 

  • GitHub develop 레포지토리로 push하면 workflow가 실행
  • 깃허브 저장소의 Actions 탭에서 workflow 실행 결과 확인 가능

 

  • 각 Step들의 로그 확인 가능

 

주의 사항

  • EC2 인스턴스 생성 후 보안 인바운드 규칙 설정 시 Spring은 t2.small로 할 것(CI/CD를 담당한 팀원이 이 문제를 해결하기 위해 고생을 했다)
  • Elastic Container Registry에 리포지토리 생성
  • Nginx 설치 후 Cerbot으로 Let's Encrypt SSL 인증서 발급, 적용
  • Proxy 설정

정리

GitHub Actions와 Docker를 통해 CI/CD를 간편하게 설정할 수 있지만 설정 시 고려해야할 사항이나 주의점을 잘 파악하고 애플리케이션 환경에 맞는 설정을 하는 것이 필요하다는 것을 느꼈다.

'항해 99 > Spring' 카테고리의 다른 글

Spring - Image Resize  (5) 2024.09.03
프로젝트 코드 리팩토링  (0) 2024.06.04
CI / CD  (0) 2024.05.11
ORM  (0) 2024.05.03
Web Game 코드 설계 정리  (0) 2024.04.23