과정을 즐기자

팀 프로젝트를 위한 github actions CI/CD 세팅 본문

DevOps

팀 프로젝트를 위한 github actions CI/CD 세팅

320Hwany 2024. 3. 15. 16:58

프로젝트를 진행하면서 서비스를 배포하는 방법에는 여러 가지가 있습니다.

먼저 단순한 방법으로는 내 로컬 PC에서 프로젝트를 빌드하여 scp 명령어로 운영 서버로 파일을 전송하고 서버에 들어가서 해당 파일을

실행 시키는 방법이 있습니다. 하지만 매번 이러한 과정을 수행하는 것은 굉장히 번거로운 작업입니다.

 

이를 개선하기 위해서 운영 서버에서 github에 있는 코드를 가져와서 프로젝트를 빌드하고 프로젝트를 실행하도록 하는 

쉘 스크립트를 작성할 수도 있습니다.

하지만 이러한 경우도 여전히 단점이 존재합니다. 우선 개발자가 운영 서버에 직접 접속하여 쉘 스크립트를 실행해야 합니다.

또한 운영 서버에서 프로젝트를 빌드하기 때문에 운영 서버의 리소스를 낭비할 수 있다는 단점도 있습니다.

이러한 단점들은 개발자가 많아질 수록 서버가 많아질 수록 더욱 커질 수 있습니다.

📚 CI/CD가 뭔데?

위와 같은 단점들을 개선하기 위해서 CI/CD를 구축해볼 수 있습니다. 그렇다면 CI/CD란 무엇일까요?

CI(Continuous Integration)는 지속적 통합이라는 말입니다. 여기서의 통합이라는 의미는 단순히 github에 코드를 통합한다는 의미는

아닙니다. 여기서의 통합은 테스트를 모두 통과하여 빌드된 프로젝트가 유지된다는 의미입니다.

CD(Continuous Deployment)는 지속적 배포라는 말입니다. CI 프로세스를 통과한 프로젝트를 배포하는 것을 말합니다.

📘 그래서 왜 쓰는데?

이전의 단점들을 바탕으로 CI/CD 사용 이유에 대해 생각해보겠습니다.

 

1. 빌드하는 환경을 통일할 수 있다.

만약 각자의 로컬 PC에서 프로젝트를 빌드하고 운영 서버로 전송한다면 배포마다 빌드하는 환경이 달라 문제가 발생할 수도 있습니다.

 

2. 직접 서버에 들어가지 않고도 배포할 수 있다.

배포 과정을 단순화하기 위해 쉘 스크립트를 만든다고 하더라도 쉘 스크립트를 실행하기 위해 직접 서버로 들어가야 하는데

이 과정을 반복하면 굉장히 번거로울 수 있습니다.

 

3. 운영 서버의 리소스를 사용하지 않고 빌드할 수 있다.

로컬 PC에서 빌드하고 운영 서버로 전송하지 않고 환경을 통일하기 위해 직접 운영 서버에서 빌드를 한다면

그 빌드 과정에서 이는 서버의 비즈니스 관련 트래픽을 처리할 리소스를 사용하게 됩니다.

 

따라서 CI/CD를 구축하여 배포 과정을 자동화하면 함께 개발하는 개발자들의 실수를 줄여주고 편의성을 줍니다.

이는 개발자가 많아질 수록 서버가 많아질 수록 장점이 더 커집니다.

📙 Github Actions

저는 이번 프로젝트를 하면서 CI/CD를 위한 툴로 github actions를 선택했습니다. 

보통 JVM 계열의 프로젝트를 진행한다면 Jenkins를 가장 많이 사용하지만 github과 연동하여 가장 단순하게 적용할 수 있는 툴이

github actions이기 때문에 선택하였습니다. 또한 github actions의 가장 큰 단점이 비용 문제일 수 있는데 public으로 생성한

프로젝트라면 비용이 발생하지 않기 때문에 이번 프로젝트에서는 문제가 되지 않았습니다.

📗 적용 해보자

우선 아래와 같이 github repo에 들어가서 Actions를 클릭해줍니다.



Actions에 들어가서 New workflow를 클릭해줍니다.

 

 

저는 Gradle을 이용하여 빌드를 하였기 때문에 Java With Gradle을 선택하였습니다.

 

선택을 하면 repo에서 /.github/workflows 아래에 yml 파일을 생성할 수 있습니다.

저는 yml 파일로 백엔드 애플리케이션 빌드를 위한 backend-build.yml과 배포를 위한 backend-deploy.yml 2가지를 생성했습니다.

backend-build.yml

먼저 전체 yml 설정은 다음과 같습니다.

전체 과정을 요약해보면 우분투 가상 환경에서 JDK 17을 설정하고, Gradle를 활용해 백엔드 프로젝트를 빌드한 후 생성된

jar 파일을 SSH를 통해 서버로 전송합니다.

name: Backend Build with Gradle

on:
  pull_request:
    branches: [ "main", "backend-main" ]
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Github Repository 파일 불러오기
        uses: actions/checkout@v4

      - name: JDK 17버전 설치
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: 17

      - name: application yml 파일 설정
        run: |
          mkdir -p src/main/resources
          mkdir -p src/test/resources
          echo "${{ secrets.MAIN_APPLICATION_YML }}" > src/main/resources/application.yml
          echo "${{ secrets.MAIN_APPLICATION_DEV_YML }}" > src/main/resources/application-dev.yml
          echo "${{ secrets.MAIN_APPLICATION_PROD_YML }}" > src/main/resources/application-prod.yml
          echo "${{ secrets.TEST_APPLICATION_YML }}" > src/test/resources/application.yml
          echo "${{ secrets.TEST_APPLICATION_TEST_YML }}" > src/test/resources/application-test.yml
        working-directory: backend

      - name: 테스트 및 빌드
        run: ./gradlew clean build
        working-directory: backend
        
      - name: 빌드된 파일 이름 변경
        run: mv ./build/libs/*.jar ./moim-today.jar  
        working-directory: backend
        
      - name: SCP로 EC2에 빌드된 파일 전송하기
        uses: appleboy/scp-action@v0.1.7
        with:
          host: ${{ secrets.BE_SERVER_IP }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          source: backend/moim-today.jar
          target: /home/ubuntu/deploy/tobe

 

실행 조건

  1. main 또는 backend-main 브랜치에 풀 리퀘스트가 생성될 때
  2. 수동으로 워크플로를 실행할 때 (workflow_dispatch)

작업(Job)

  • 운영 환경: ubuntu-latest 이미지
  • 단계(Steps):
    1. GitHub Repository 파일 불러오기:
      • actions/checkout@v4 액션을 사용하여 현재 리포지토리의 파일을 체크아웃합니다.
    2. JDK 17버전 설치:
      • actions/setup-java@v4 액션을 사용하여 Temurin JDK 17을 설치합니다.
    3. application.yml 파일 설정:
      • backend 디렉터리 내에 필요한 application.yml 파일들을 생성합니다.
      • MAIN_APPLICATION_YML, MAIN_APPLICATION_DEV_YML, MAIN_APPLICATION_PROD_YML, TEST_APPLICATION_YML, TEST_APPLICATION_TEST_YML과 같은 시크릿 변수를 사용하여 해당 내용을 파일에 기록합니다. (민감한 정보가 유출되지 않도록)
    4. 테스트 및 빌드:
      • backend 디렉터리에서 ./gradlew clean build 명령을 실행하여 Gradle 빌드를 수행합니다.
    5. 빌드된 파일 이름 변경:
      • backend/build/libs 디렉터리에 있는 빌드된 JAR 파일을 moim-today.jar로 이름을 변경합니다.
    6. SCP로 EC2에 빌드된 파일 전송하기:
      • appleboy/scp-action@v0.1.7 액션을 사용하여 빌드된 JAR 파일을 EC2 서버로 전송합니다.
      • 이 때 사용되는 정보는 시크릿 변수로 제공됩니다: BE_SERVER_IP, SSH_USER, SSH_PRIVATE_KEY
      • 파일은 /home/ubuntu/deploy/tobe 디렉터리에 전송됩니다.

backend-deploy.yml

name: Deploy Backend Application

on:
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: SSH로 EC2에 접속하여 배포하기
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.BE_SERVER_IP }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script_stop: true
          script: |
             rm -rf /home/ubuntu/deploy/current
             mkdir /home/ubuntu/deploy/current
             cp /home/ubuntu/deploy/tobe/backend/moim-today.jar /home/ubuntu/deploy/current/moim-today.jar
             cd /home/ubuntu/deploy/current
             sudo fuser -k -n tcp 8080 || true
             nohup java -jar moim-today.jar > /dev/null 2>&1 &

 

실행 조건

  1. 수동으로 워크플로를 실행할 때 (workflow_dispatch)

작업(Job)

  • 운영 환경: ubuntu-latest 이미지
  • 단계(Steps):
    1. SSH로 EC2에 접속하여 배포하기:
      • appleboy/ssh-action@v1.0.3 액션을 사용하여 EC2 서버에 SSH로 접속합니다.
      • 접속에 필요한 정보는 시크릿 변수로 제공됩니다: BE_SERVER_IP, SSH_USER, SSH_PRIVATE_KEY
      • script_stop: true 옵션을 사용하여 스크립트 실행 중 오류가 발생하면 작업을 중지합니다.
      • 다음 명령들을 실행하는 스크립트를 수행합니다:
        1. /home/ubuntu/deploy/current 디렉터리를 삭제합니다.
        2. 새로운 /home/ubuntu/deploy/current 디렉터리를 생성합니다.
        3. /home/ubuntu/deploy/tobe/backend 디렉터리에 있는 moim-today.jar 파일을 current 디렉터리로 복사합니다.
        4. current 디렉터리로 이동합니다.
        5. 현재 8080 포트를 사용 중인 프로세스를 종료합니다.
        6. moim-today.jar 파일을 백그라운드에서 실행합니다.
          (로그는 /dev/null로 리다이렉트하여 출력하지 않고 따로 로그 파일로 관리)

🌟 정리

먼저 프로젝트의 배포 방식과 CI/CD가 필요한 이유에 대해 알아보았습니다. CI/CD는 개발자들에게 실수할 확률을 줄여주고

편의성을 제공합니다. 이번 프로젝트에서는 github actions를 이용하여 CI/CD를 적용 하였습니다.

 

이제 github에 main, backend-main에 push 하거나 pr을 날리게 되면 테스트가 통과할 때만 통일된 환경에서

프로젝트를 빌드하여 유지할 수 있습니다. 또한 서버에 접속하지 않더라도 github에서 해당 워크 플로우들을 실행하여

배포를 진행할 수 있습니다.

참고한 자료

 

GitHub Actions 설명서 - GitHub Docs

GitHub Actions를 사용하여 리포지토리에서 바로 소프트웨어 개발 워크플로를 자동화, 사용자 지정 및 실행합니다. CI/CD를 포함하여 원하는 작업을 수행하기 위한 작업을 검색, 생성 및 공유하고 완

docs.github.com

 

Github Action을 활용한 SpringBoot 프로젝트 CI/CD

Intro 매번 새로운 커밋이 발생할 때마다 서버에 새로운 버전으로 업로드하고, 기존에 실행 중이던 프로세스를 종료한 후, 새로 업로드한 프로젝트를 실행하는 일련의 과정은 상당히 번거로운 작

shanepark.tistory.com

 

비전공자도 이해할 수 있는 CI/CD 입문·실전 | JSCODE 박재성 - 인프런

JSCODE 박재성 | 비전공자 입장에서도 쉽게 이해할 수 있고, 실전에서 바로 적용 가능한 CI/CD 입문 강의를 만들어봤습니다!, 🤬 에라이, 못 해먹겠네!비전공자로 개발을 시작해 여러 회사에서 CTO로

www.inflearn.com