DevJong12

프로젝트 세팅 - Git Action CI / CD 본문

프로젝트/NoobLoL

프로젝트 세팅 - Git Action CI / CD

Jong12 2022. 7. 29. 16:47
728x90

회사에 다니면서 가장 해보고 싶었으나 못했던 부분에 대해서 작업을 해보고자 한다.

 

CI / Continuos Integration : 지속적 통합

CD / Continuos Delivery : 지속적 배포

 

두가지 의미이며, 초창기에 CD의 구상은 젠킨스를 진행하려했으며 CI에 대한 개념이 부족했어서 설계를 진행하지 않았었다.

 

먼저 CI에 대한 작업을 진행하면서 굳이 CD작업에서 젠킨스밖에 몰랐던 부분에서, 굳이 젠킨스의 사용없이 구상이 가능하다고 생각이 들어 설계를 변경하고자 생각하게 되었다.


사용된 툴?

설계를 진행함에 선택한 도구는 Git Action을 사용해보기로 하였다. Youtube 알고리즘의 간택으로 영상들을 보다 보니 간편한 설계가 가능해 보이면서도 해당 설계를 작성하는 과정이 너무 재밌어 보였다.


Git Action?

Github에서 공식적으로 제공하는 CI / CD툴로 프로젝트 루트경로의 .github/workflows 안의 yaml파일을 통하여 자동화를 진행하는 도구이다. 

 

내 프로젝트의 경우 springboot - gradle을 활용하였기에 레포지토리에서 Action탭을 선택한 이후 아래의 설정을 사용하였다.

설정을 진행하면 아래와 같이 바로 yml을 생성후 커밋을 할 수 있도록 깃허브에서도 준비가 되어있다.


CI전략?

일단 초기 설계단계 이다보니 gradle을 통하여 gradlew test를 통한 테스팅만 진행할 예정이다. 해당 부분에 대해서는 추후 수정을 하게 될 수도 있을 것 같지만 지금은 해당 방식을 통한 테스트만 진행하고자 한다.

 

프로젝트를 진행하면서 선택한 브랜치 전략은 Git-Flow브랜치 전략이다. 따라서 "main", "develop", "feature" 브랜치를 일단 핵심으로 사용하게 될 브랜치로 판단하게 되었다.

 

또한 "feature" 브랜치를 통하여 PR을 발생시킨 이후 "develop" 브랜치로 merge를 진행할 예정이다. 그렇기에 테스트를 진행하는 단계의 설정을 "feature브랜치로의 Push, PR", "develop브랜치로의 PR" 이 발생하게 되는 경우 gradle테스트를 진행하도록 설계하였다.


CD전략?

일단 CD의 작업 역시도 CD를 통하여 진행하였으며, 또한 Git-Flow 브랜치전 전략을 채택하였기 때문에 "main", "develop"브랜치를 통해서 CD전략을 구상하였다.

 

작업의 경우 위 두개의 브랜치로 Push가 발생하게 될 경우 작업은 다음과 같다.

  1.  github Action에서 ubuntu컨테이너 생성 및 JDK설치
    1. application.yml의 생성
      1. 해당 작업은 main브랜치만 작동하도록 하였다.
      2. application파일을 상황별로 분류하였다.
    2. gradle의 권한 부여
    3. gradle을 통한 테스트
      1. 각각의 브랜치가 서로 Profiles값을 다르게 가질수 있도록 하였다.
    4. 브랜치 별로 Dockerfile을 통하여 빌드 이후 push
  2. 각 브랜치 별 서버에 ssh를 사용하여 직접적인 접속 즉 Github Action Ubuntu → Ncloud Ubuntu
    1. docker의 현재 실행된 컨테이너 전체 종료 및 삭제
      1. 개발서버의 경우에는 db까지 모두 삭제이후 재생성
      2. docker-compose를 활용하여 컨테이너 생성 및 실행

 

위의 작업이 푸쉬가 발생할 경우 이뤄지는 작업으로 설정하였다.


작업의 경우 필자는 CI, CD작업을 분할하였으며, 그러다 yml파일을 2개 제작하게 되었다.

두개의 파일은 아래의 사진처럼 프로젝트에서 구성이 되었다.

작업내용 - CI

name: CI

# push, PR이벤트 발생시 아래의 브랜치들에서 발생하게 되는 경우 작업
on:
  push:
    branches:
      - 'feature/*'
  pull_request:
    branches:
      - 'develop'
      - 'feature/*'

# 실행해야 하는 작업들
jobs:
  CI:
    # 일단 현재 네이버 클라우드의 OS가 우분투 18.04이다 보니 최대한 맞추려 하였다.
    runs-on: ubuntu-18.04

    #작업 실행단계
    steps:
      # 체크아웃 및 JDK세팅
      - name: Checkout
        uses: actions/checkout@v3
      - name: Set up JDK 11
        uses: actions/setup-java@v3
        with:
          java-version: '11'
          distribution: 'temurin'

      # Gradle 권한 부여
      - name: Grant Execute permission for gradlew
        run: chmod +x gradlew
        shell: bash

      # test하지 않고 빌드를 진행
      - name: Build with Gradle
        run: ./gradlew build -x test
        shell: bash

      # local환경으로 테스트 진행
      - name: Test with Gradle
        run: SPRING_PROFILES_ACTIVE=[local] ./gradlew clean test
        shell: bash

      # 해당 내용은 PR에서 테스트 진행시 오류가 발생한 경우 표시 
      - name: Publish Unit Test Test Result
        uses: EnricoMi/publish-unit-test-result-action@v1
        if: ${{ always() }}
        with:
          files: build/test-results/**/*.xml

 

작업내용 - CD

# 작업명
name: CD NCP

# 실행해야 할 브랜치, push가 발생한 경우로만 한정하였다.
on:
  push:
    branches:
      - 'develop'
      - 'main'

jobs:
  CD:
    ## NCP OS : Ubuntu 18.04
    runs-on: ubuntu-18.04

    steps:
      ## Project JDK 11 Setting
      - name: Checkout
        uses: actions/checkout@v3
      - name: Set up JDK 11
        uses: actions/setup-java@v3
        with:
          java-version: '11'
          distribution: 'temurin'

      # main브랜치인 경우 application.yml파일을 제작한다
      - name: Main Branch Make application-prod.properties
        if: contains(github.ref, 'main')
        run: |
          cd ./src/main/resources
          touch ./application-prod.properties
          echo "${{ secrets.PROPERTIES_PROD }}" > ./application-prod.properties
        shell: bash
      
      # Gradle의 사용권한 셋팅
      - name: Grant Execute permission for gradlew
        run: chmod +x gradlew
        shell: bash

      # develop브랜치인 경우 profiles를 dev로 세팅한 이후 test를 진행한다.
      - name: Develop Test
        if: contains(github.ref, 'develop')
        run: SPRING_PROFILES_ACTIVE=[dev] ./gradlew clean test
        shell: bash

      # main브랜치인 경우 profiles를 위에서 생성한 prod로 세팅한 이후 테스트를 진행한다.
      - name: main Test
        if: contains(github.ref, 'main')
        run: SPRING_PROFILES_ACTIVE=[prod] ./gradlew clean test
        shell: bash

      # 프로젝트 테스트 진행없이 빌드
      - name: Build with Gradle
        run: ./gradlew build -x test
        shell: bash

      # 개발서버용 Docker 빌드 생성 및 푸시
      - name: dev Docker build & push
        if: contains(github.ref, 'develop')
        run: |
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
          docker build -f Dockerfile-dev -t ${{ secrets.DOCKER_REPO }}/nooblol-dev .
          docker push ${{ secrets.DOCKER_REPO }}/nooblol-dev


      # 운영서버용 Docker 빌드 생성 및 푸시
      - name: prod Docker build & push
        if: contains(github.ref, 'main')
        run: |
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
          docker build -f Dockerfile-prod -t ${{ secrets.DOCKER_REPO }}/nooblol-prod .
          docker push ${{ secrets.DOCKER_REPO }}/nooblol-prod


      # 개발서버 SSH접속 및 배포
      - name: Deploy to Dev
        uses: appleboy/ssh-action@master
        id: deploy-dev
        if: contains(github.ref, 'develop')
        with:
          host: ${{ secrets.NCP_DEV_SERVER_IP }}
          username: ${{ secrets.NCP_DEV_SERVER_USER }}
          password: ${{ secrets.NCP_DEV_SERVER_PASSWORD }}
          port: ${{ secrets.NCP_DEV_SERVER_SSH_PORT }}
          script: |
            sudo docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
            sudo docker rm -f $(docker ps -q -a)
            sudo docker pull ${{ secrets.DOCKER_REPO }}/nooblol-dev
            docker-compose up -d
            docker image prune -f


      #운영서버 SSH접속 및 배포 - 현재 운영서버는 셋팅이 되어있지 않아 모두 주석으로 진행하였다.
      # Main Deploy
      #- name: Deploy to Prod
      #  uses: appleboy/ssh-action@master
      #  id: deploy-prod
      #  if: contains(github.ref, 'main')
      #  with:
      #    host: ${{ secrets.NCP_PROD_SERVER_IP }}
      #    username: ${{ secrets.NCP_PROD_SERVER_USER }}
      #    password: ${{ secrets.NCP_PROD_SERVER_PASSWORD }}
      #    port: ${{ secrets.NCP_PROD_SERVER_SSH_PORT }}
      #    script: |
      #      sudo docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
      #      sudo docker rm -f $(docker ps -q -a)
      #      sudo docker pull ${{ secrets.DOCKER_REPO }}/nooblol-prod
      #      docker-compose up -d
      #      docker image prune -f

CD의 경우 secrets로해서 많은 값들이 들어가 있는 부분을 확인 할 수 있다.

해당 값은 Github에서 셋팅이 가능하다

 

아래에 스크린샷을 같이 포함해놓았다. 

Repository → Settings → Secrets → Actions 항목에서 추가가 가능하며 New repository secret을 통하여 해당 희망하는 Key,Value의 추가가 가능하며 사용을 위와 같이 하면 된다.


 

작업을 진행하면서 생각보다 너무 간편한 셋팅이 가능해서 많이 놀랐으며, 무조건 젠킨스를 써야지! 라는 생각이 박살나게 되었던 순간이었던거 같다...

 

너무 간편하면서 직관적이어서 작업을 진행하면서 시간가는 줄 모르고 한것 같다.


참고한 레퍼런스

 

GitHub-Actions로 CI/CD 구축하기(AWS, Docker, SpringBoot)

GitHub-Actions로 CI/CD 구축하기(AWS, Docker, SpringBoot) 안녕하세요, 이번 시간에는 GitHub-Actions로 CI/CD를 구축하는 방법에 대해 알아보겠습니다. 해당 포스팅이 CI/CD를 전체적으로 포함하고 있기는..

zzang9ha.tistory.com


작업 이 후 ... ( 22. 07. 31)

현재 프로젝트의 프로토 타입 설계 작업을 진행하면서 해당 방법이 자동 배포의 최선일까? 라는 생각을 계속 던지고 있었다.

위의 방법의 경우 ssh를 통한 직접적인 접속을 한 이후에 도커의 이미지를 pull한 이후 컨테이너를 생성하여 서비스를 실행하는 방식으로 제작을 하였는데 ssh를 통하여 접근을 진행한 순간 build된 jar파일을 원하는 위치에 복사하도록 한 이후 파일을 실행하는 방식도 괜찮을 것 같다..

 

현재 서버의 용량이 부족해서 방식을 변경해야 하면 위와 같은 방법으로 변경을 해봐도 좋을 것 같다. (톰캣 메모리 부분들에 대한 설정을 진행한 이후..해야 할 것으로 보인다. 서버로 사용중인 NCP용량이 매우적은점을 감안해야 하기 때문이다.)

728x90
Comments