[CI/CD] Github Actions으로 CI/CD 구축하기 (2024)

개요

산보실록 서비스를 올릴 물리 서버가 들어오면서, 드디어 운영 단계를 앞두게 되었다.

이후, 운영과 유지보수를 위해 Spring으로 전환하는 과정에서 수월한 유지보수를 위해 CI/CD를 구축하기로 결정했다.

지원 받은 물리 서버의 인프라 구축이 완료되기 전, 프론트엔드 개발자와 원활한 협업을 위해 EC2와 RDS를 이용해 사전 배포를 진행하게 되었다.

그래서 물리 서버에 CI/CD를 구축하기 전 CI/CD 구축과정을 익히고, 개발을 원활히 하기 위해 EC2 서버에 CI/CD를 구축해보고자 한다.

Jenkins와 달리 별도 서버가 필요하지 않고 상대적으로 구축이 간편한 Github Actions을 활용하기로 했다.

사용 기술 스택 및 버전

  • Spring Boot 3.2.5
  • openjdk 17.0.3
  • Gradle 8.7
  • MySQL 8.1.0

구축 과정

우선, .github 디렉토리에 workflows 폴더를 생성한 후 CD.yml 파일을 만들어 주었다.
이제 CD.yml에 build, deploy 스크립트를 작성해주면 된다.

1. 이름 설정

해당 workflow의 이름을 설정해준다.

name : Sanbo-Sillok-Server

설정한 이름은 Github Actions에 다음과 같이 표현된다.

[CI/CD] Github Actions으로 CI/CD 구축하기 (1)

2. branch 설정

CI/CD가 적용될 branch를 설정해준다.

on: push: branches: - develop

위 스크립트에서는 develop branch에 push 또는 merge 될때 해당 workflow가 실행된다.

3. jobs 설정

Github Actions에서 처리할 Task들을 설정해준다.

jobs: build: runs-on : ubuntu-latest deploy: needs: build runs-on: ubuntu-latest

runs-on : 해당 Task가 실행될 환경의 OS와 버전 선택
needs : 해당 Task가 실행되기 전, 완료되어야 할 Task 선택

[CI/CD] Github Actions으로 CI/CD 구축하기 (2)

설정한 내용은 Github Actions에 다음과 같이 표현된다.

4. build

먼저 build Task에 대한 스크립트를 설정해보자.

 build: runs-on : ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Set up JDK uses: actions/setup-java@v3 with: java-version: '17' distribution: 'temurin' - name: permission run: chmod +wrx gradlew - name: Build with Gradle uses: gradle/gradle-build-action@v2.6.0 with: arguments: build - name: Upload build artifact uses: actions/upload-artifact@v2 with: name: Sanbo-Sillok-Server path: build/libs/sanbosillokserver-0.0.1-SNAPSHOT.jar

각 스크립트의 세부 내용은 다음과 같다.

  1. Checkout
    CI 서버에 원격 저장소의 코드를 내려받고, 2번에서 설정한 branch로 checkout하는 과정이다.

  2. Set up JDK
    JDK 설정 작업이다.
    범용적으로 사용되는 오픈소스 JDK인 Eclipse Temurin을 사용하겠다.

  3. permission
    프로젝트 빌드를 위해 gradlew에 권한을 부여한다.

  4. Build with Gradle
    프로젝트를 빌드한다.

  5. Upload build artifact
    CI 서버의 지정 경로의 빌드 결과물을 Github 저장소에 업로드한다.
    보안, 가용성, 관리 용이성 등을 고려하여, CI서버가 아닌 별도의 Github 저장소를 이용한다.

5. deploy

다음으로 deploy Task에 대한 스크립트를 설정해보자.

 deploy: needs: build runs-on: ubuntu-latest steps: - name: Download build artifact uses: actions/download-artifact@v2 with: name: Sanbo-Sillok-Server path: build/libs/ - name: Deploy to EC2 run: | sshpass -p ${{ secrets.EC2_PASSWORD }} scp -o StrictHostKeyChecking=no build/libs/sanbosillokserver-0.0.1-SNAPSHOT.jar ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }}:/home/${{ secrets.EC2_USERNAME }}/ sshpass -p ${{ secrets.EC2_PASSWORD }} ssh -T -o StrictHostKeyChecking=no ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }} "pgrep java | xargs kill -9; nohup java -DATABASE_HOST=${{ secrets.DATABASE_HOST }} -jar sanbosillokserver-0.0.1-SNAPSHOT.jar > nohup.out 2>&1 &"

각 스크립트의 세부 내용은 다음과 같다.

  1. Download build artifact
    Github 저장소에 업로드된 결과물을 다운로드한다.

  2. Deloy to EC2
    다운로드한 결과물을 운영 서버에 옮기고, 실행한다.
    이때, .pem키 없이 서버에 원격으로 명령어를 실행하기 위해 sshpass를 사용했다.
    EC2 password 등 공개돼선 안되는 값들은 github secrets를 활용하여 처리했다.

github secrets
github actions를 활용한 CI/CD 구축 과정에서 드러나서는 안되는 값들을 secrets으로 관리할 수 있는 시스템.
[CI/CD] Github Actions으로 CI/CD 구축하기 (3)Settings \rarr Secrets and variables \rarr Actions 에서 설정할 수 있다.
[CI/CD] Github Actions으로 CI/CD 구축하기 (4)설정한 secret 목록을 아래와 같이 확인할 수 있다.
단, 그 값에 대해서는 수정은 가능하지만 조회할 수는 없다.

마지막으로, 원래는 nohup &를 사용해 프로세스를 백그라운드에서 실행하면 실행 로그를 nohup.out으로 redirect한다.
하지만 sshpass를 사용했을 경우, 실행 로그가 터미널에 출력되어 ssh shell이 정상 종료되지 않고 timeout 에러가 발생한다.
따라서, > nohup.out 2>&1을 명시적으로 추가하여 실행 로그를 nohup.out으로 redirect시키고 ssh shell이 정상 종료 될 수 있도록 해주었다.

실행 로그 redirect 명령어
> nohup.out : 실행 로그를 redirect할 파일 이름
2>&1 : 표준 에러를 표준 출력으로 취급


Trouble shooting

deploy 과정에서 application.yml${환경 변수} 형태로 작성된 부분에 EC2 서버의 .bashrc에 정의한 환경 변수가 제대로 들어가지 않는 문제가 발생한다.

ssh로 직접 접속 했을 때는 해당 문제가 발생하지 않는 것으로 보아, sshpass 명령어를 사용했을 경우에 발생하는 문제로 보인다.

해당 문제의 원인을 파악하기 위해 다음과 같은 과정을 시도해보았다.

1. 환경 변수 출력 시도

source ~/.bashrc.bashrc를 로드한 후, echo $DATABASE_NAME 명령어를 workflow에 추가해 환경 변수 출력을 시도해보았다.

아무것도 출력되지 않았다.

프로젝트 빌드 파일에 문제가 있는 것이 아닌, 애초에 .bashrc의 환경 변수에 접근하지 못하는 것으로 보인다.

2. 권한 확인

환경 변수에 접근하지 못하는 이유가 권한 문제인가 싶어 .bashrc의 권한을 확인해보았다.

[CI/CD] Github Actions으로 CI/CD 구축하기 (5)

권한은 정상적으로 부여되어 있는 것을 확인할 수 있었다.

3. 환경 변수가 정의된 파일 내용 출력

다음으로, workflow에서 환경 변수가 정의된 파일의 내용을 읽을 수 있는지 확인하기 위해 .bashrc의 내용을 출력하는 스크립트를 추가해보았다.

[CI/CD] Github Actions으로 CI/CD 구축하기 (6)

잘 출력되는 것으로 보아, 환경 변수가 정의된 파일에 접근하는 것에는 문제가 없었다.

4. 시스템 전체 환경 변수로 변경

Linux 환경 변수는 동작 범위에 따라 3가지로 나뉜다.

  1. 로컬 환경 변수
    • 현재 세션에서만 동작.
  2. 사용자 환경 변수
    • 특정 사용자에 대해 정의된 환경 변수로, 로그인할 때마다 로드됨.
    • .bashrc, .bash_profile, .bash_login, .profile 등에 정의된다.
  3. 시스템 전체 환경 변수
    • 해당 시스템의 모든 사용자가 사용 가능.
    • /etc/evironment, /etc/profile, /etc/profile.d, /etc/bash.bashrc 등에 정의된다.

기존에는 .bashrc에 사용자 환경변수를 정의했었다.

sshpass를 이용하여 원격 접속할 때와 같은 사용자로 접속했기 때문에 문제가 없을 것이라 생각했지만, 혹시 몰라 시스템 전체 환경 변수로 다시 환경 변수를 작성해보았다.

/etc/environment 에 정의한 환경변수를 다시 sshpass로 출력해보았지만, 제대로 출력되지 않았다.

5. login shell

sshpass는 대화형 터미널을 사용하지 않으므로, Non-login Shell에서 사용하는 .bashrc에 환경 변수를 저장하는 것은 문제가 없다고 생각했었다.

'하지만, 혹시 sshpass가 Non-login Shell 방식이 아니라면 ?' 이라는 생각이 스쳐지나갔다.

그래서 아예 Login Shell 방식을 사용해보기로 했다.

우선, 환경 변수를 .bashrc가 아닌 .bash_profile에 다시 정의했다.

그리고 마지막 deploy 스크립트를 다음과 같이 수정했다.

bash -l -c 'pgrep java | xargs kill -9; nohup java -jar sanbosillokserver-0.0.1-SNAPSHOT.jar &'

bash 명령어 옵션
1. -l : Login Shell을 사용하는 옵션
2. -c : 사용할 명령어를 설정하는 옵션

결과적으로 Login Shell 방식을 사용하여, 배포에 성공했다.

결론

sshpass가 아예 Non-Login Shell을 사용하지 않는 것인지, Non-Login Shell임에도 .bashrc의 환경 변수를 읽어오지 못하는 다른 이유가 있는 것인지 알아볼 필요가 있어보인다.

추후, Non-Login Shell과 Login Shell에 대한 개념을 정리하며, 해당 원인에 대한 내용을 조사해봐야겠다.

References

Github-Actions
Login Shell vs Non-Login Shell

[CI/CD] Github Actions으로 CI/CD 구축하기 (2024)
Top Articles
Latest Posts
Article information

Author: Pres. Carey Rath

Last Updated:

Views: 6558

Rating: 4 / 5 (41 voted)

Reviews: 80% of readers found this page helpful

Author information

Name: Pres. Carey Rath

Birthday: 1997-03-06

Address: 14955 Ledner Trail, East Rodrickfort, NE 85127-8369

Phone: +18682428114917

Job: National Technology Representative

Hobby: Sand art, Drama, Web surfing, Cycling, Brazilian jiu-jitsu, Leather crafting, Creative writing

Introduction: My name is Pres. Carey Rath, I am a faithful, funny, vast, joyous, lively, brave, glamorous person who loves writing and wants to share my knowledge and understanding with you.