ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Github action + spring boot CI/CD 구축하기
    응애개발일기/DevOps 2024. 11. 3. 19:08

    계기

    8명의 팀원과 함께 팀 프로젝트 진행 중 번거로움을 해결하기 위해 CI/CD를 구축하기로 함.

    Why CI? develop 브랜치로 PR이 굉장히 많이 생성됨. 기존 코드와 합치기 위한 build, test 과정을 local 환경에서 일일이 수행하기 번거로움.

    Why CD? 패키징, 추가 테스트, 파일 업로드, 실행 등 반복되고 번거로운 배포 작업을 자동화하기 위해.

    Why Github Action? CI/CD 작업을 위한 다양한 툴 존재. on Premise 환경의 툴을 설정하기보다 클라우드 환경의 툴로 결정하여 빠른 구축하기 위함.


    Github action?

    https://docs.github.com/en/actions/about-github-actions/understanding-github-actions

    1. 유저가 설정한 이벤트 발생(특정 일시마다, github push 및 PR 등 다양한 trigger 존재)
    2. 이벤트에 따라 Runner 생성 후 Job 실행. Job에 설정된 step들 실행.
      • Runner? Job이 실행될 환경 ex) linux, windows 등. 기본적으로 Cloud환경에서 실행됨. 유저의 on Premise 환경에서도 실행 가능.
      • Job? 해당 Runner에서 실행될 작업. Step들의 묶음.  ex) 빌드, 테스트, 배포, 이슈 생성 등
        기본적으로 병렬적으로 실행. 설정을 통해 순차적 실행 가능.
      • Step? Job으로 묶여 해당 작업 필요한 명령어들. ex) mvn clean, mvn test 등

    적용하기

    Maven 환경의 Spring boot 프로젝트에 CI/CD를 적용하였습니다. action에서 다양한 action syntax를 제공합니다. 현재 글에선 spring boot의 빌드, 테스트, 배포에 사용될 구문만 사용하였습니다.

     

    Github Action을 사용하기 위해선 Project의 root 경로에 .github/workflows 폴더를 생성해야합니다. 해당 폴더의 yml 파일을 분석하여 자동으로 Action을 실행해줍니다.


    CI/CD 파일 만들기

    ci-actions.yml

    name: dev-ci-phase # Action 이름
    on: 
      # Action이 실행될 event
      pull_request:
        types: [ opened, reopened ]
      push:
        branches-ignore:
          - main
    
    jobs:
      # 빌드 자동화. 빌드 과정 중 오류가 있는지 확인
      build:
        # ubuntu 환경에서 실행. runner 설정 과정.
        runs-on: ubuntu-latest
        # 현재 job에서 실행될 명령어들
        # '-'로 나뉘어진 부분들이 하나의 step!
        steps:
          - uses: actions/checkout@v4
    
          - name: Set up JDK 21
            uses: actions/setup-java@v4
            with:
              java-version: '21'
              distribution: 'temurin'
              cache: maven
    
          - name: Build with Maven
            run: mvn -B compile --file pom.xml
    
      # test 자동화. test 후 커버리지 결과를 action bot을 이용하여 확인 가능
      test:
      	# build가 성공적으로 완료되었을 때 실행.
        needs: build
        # ubuntu 환경에서 실행
        runs-on: ubuntu-latest
        # 현재 job에서 실행될 명령어
        steps:
          - uses: actions/checkout@v4
    
          - name: Set up JDK 21
            uses: actions/setup-java@v4
            with:
              java-version: '21'
              distribution: 'temurin'
              cache: maven
    
          - name: test with Maven
            run: mvn -B test jacoco:report --file pom.xml
    
    	  # test 결과 파일 업로드
          - name: Upload coverage report
            uses: actions/upload-artifact@v3
            with:
              name: jacoco-report
              path: |
                ${{ github.workspace }}/target/site/jacoco/jacoco.xml
                ${{ github.workspace }}/target/site/jacoco/index.html
    
    	  # test coverage 확인할 수 있도록 bot 사용. GITHUB_TOKEN은 action에서 제공하는 default env variable.
          - name: Add coverage to PR
            id: jacoco
            uses: madrapps/jacoco-report@v1.6.1
            with:
              paths: ${{ github.workspace }}/target/site/jacoco/jacoco.xml
              token: ${{ secrets.GITHUB_TOKEN }}
              min-coverage-overall: 60
              min-coverage-changed-files: 80
              title: '# :recycle: Coverage Report'
              update-comment: true
              pass-emoji: ':green_circle:'
              fail-emoji: ':red_circle:'

     

    use? 반복되는 action 설정이 많음. ex) java 설정, 파일 upload 설정. 반복되는 명령들을 많은 사람들이 사용할 수 있도록 라이브러리처럼 만들어서 제공합니다. use는 이런 라이브러리들을 import하여 사용할 수 있도록 하는 syntax입니다. 라이브러리 확인하기


    cd-actions.yml

    name: prod-deploy-actions # action 이름
    on:
      # action trigger
      push:
        branches:
          - main
    
    jobs:
      # 배포 job
      deploy:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v3
          
          - name: Set up JDK 21
            uses: actions/setup-java@v3
            with:
              java-version: '21'
              distribution: 'temurin'
              cache: maven
    
          - name: Build with Maven
            run: mvn -B package --file pom.xml
    
    	  # scp를 이용하여 jar 파일 업로드.
          - name: upload file
            uses: appleboy/scp-action@master
            with:
              host: ${{ secrets.SSH_IP }}
              username: ${{ secrets.SSH_ID }}
              key: ${{ secrets.SSH_KEY }}
              port: ${{ secrets.SSH_PORT }}
              source: "target/*.jar"
              target: "~/"
              rm: false
    
    	  # jar가 실행될 서버에 이미 작성된 startup.sh 스크립트 파일 실행
          - name: execute shell script
            uses: appleboy/ssh-action@master
            with:
              host: ${{ secrets.SSH_IP }}
              username: ${{ secrets.SSH_ID }}
              key: ${{ secrets.SSH_KEY }}
              port: ${{ secrets.SSH_PORT }}
              script_stop: true
              script: "./startup.sh 8080"

    Secret 환경 변수 설정하기

     Github -> 내 Repository -> Settings -> security -> Secrets and variables -> Actions

    실행 환경에 따라 secret을 설정할 수 있습니다. Settings -> Code and automation -> Environments에서 설정한 실행 환경에 맞게 secret을 적용할 수 있습니다.

    현재 글에서는 Repository 전체에 적용될 Secret을 생성하고자 합니다.

     

    New repository secret -> key값과 value 설정하기

    SSH_ID={server_user_name} ex) ubuntu

    SSH_IP={your.IP.address.plz} ex) 127.0.0.1

    SSH_PORT={ssh_port} ex) 22

    SSH_KEY={.pem이나 .key 파일 내용 넣기} ex) -----start뭐시기-----  ~~~~~ -----end뭐시기-----


    ./startup.sh 파일 만들기

     

    서버에 파일을 만들기 위해 ssh로 서버에 접속한다.

    ssh -i {your_pem_file_path} {your_user_name}@{your_ip_address}

    startup.sh 파일 만들기!

    cd ~
    vi startup.sh

    vim에서 입력을 위해서는 'i' 키를 누르면 쓰기 모드로 변경할 수 있고 'esc'를 누르면 읽기 모드로 변경 가능하다.

     

    startup.sh

    port=$1
    pid=$(netstat -tulpn | grep $port | awk '{split($7,arr,"/"); print arr[1]}')
    
    # 현재 열려있는 서버 닫기
    if [ -n "$pid" ]
    then
    	kill -9 $pid
    	echo server is stopped
    fi
    
    echo server is excuting...
    
    # 서버 실행
    /usr/local/java/java21/bin/java -Dserver.port=$port -Dfile.encoding=UTF-8 -Dspring.profiles.active=prod -jar ~/target/*.jar > ~/log 2>&1 &
    
    echo server is started...

    작성 완료 후 'esc' -> :wq 입력.

    실행 시 권한 오류가 발생했을 때 'chmod' 명령어를 이용해 권한을 변경해주세요.

    !!Java 설치가 필요합니다! Java 버전에 맞게 변경해주세요!!

     

    끝!

    이제 github에 push하거나 pr을 생성하면 ci 작업이, main에 push 될 경우(merge 포함) cd 작업이 실행됩니다!

    어려움

    scp로 jar 파일을 업로드하는 과정에 rm:true 옵션을 줬다.target 옵션의 값의 파일들을 모두 지우고 새로 업로드해주는 기능이어서 사용했다.

    하지만 target directory가 '~' 였기에 ~ 경로 안에 들어있던 서버의 user 정보(ubuntu)가 삭제되어 서버 접속 권한을 모두 잃고 서버에 접속하지 못하는 오류가 생겼다. 결국 서버를 새로 생성해야했다. 조심해야겠다...

    rm:true 옵션을 사용할 땐 target을 잘 확인하고 사용하자!

    마무리

    Gitlab Runner를 이용한 cicd만 이용해봤는데 이번 경험을 통해 다시 한 번 리눅스와 클라우드에 대해 학습할 수 있었던 것 같다.


    참고: https://docs.github.com/ko/actions

     

Designed by Tistory.