Project

[KBOTicket] CI/CD의 개념과 구현

변위니 2025. 1. 7. 00:30

 

이제 배포......

 

이전에 프로젝트에서 배포하던 방식은,

 

코드 수정 후 JAR 파일을 직접 빌드하고, FileZilla를 사용하여 FTP를 통해 서버에 파일을 업로드하고, 

PuTTY를 이용해 서버에 접속한 후 java -jar 명령어로 애플리케이션을 실행하는 방식이었습니다.

 

이 과정에서는 매번 파일을 수동으로 전송하고, 서버를 재시작하는 등의 반복적인 작업이 필요했으며, 수동으로 처리하다 보니 실수할 가능성도 있었습니다. 실제로 개발 서버를 다운시켰던 적도 있었

 

 

기능별로 빌드, 테스트 그리고 병합까지 매번 해야 하는 것은 정말 번거롭고 시간이 상당히 소요됩니다.

이 문제를 해결하기 위해 CI/CD를 이용해보았습니다.

 



CI/CD

코드 변경 사항을 자동으로 빌드, 테스트, 배포하여 소프트웨어 개발과 배포 과정을 효율적이고 안정적으로 만드는 자동화 프로세스

CI (Continuous Integration)

지속적인 통합
코드 작업 및 변경 시 자동으로 빌드 및 테스트를 진행하여, 검증된 코드를 공유 저장소에 통합하는 과정이다.

개발자들은 각자 작업한 코드를 develop 브랜치나 중앙 저장소에 자주 병합한다.

코드를 병합할 때마다 빌드와 테스트가 자동으로 실행되며, 이를 통해 각 코드 변경이 올바르게 작동하는지 확인할 수 있다.

이러한 과정을 통해 문제를 조기에 발견할 수 있고, 개발자 간의 작업한 코드 사이에 충돌을 줄이고, 안정적으로 프로젝트를 관리할 수 있다.



CD (Continuous Delivery/ Deployment) 

Continuous Deployment

검증을 거쳐 통합된 코드를 사용자에게 자동으로 배포하는 과정이다.

개발자가 코드를 수정 후 CI를 거쳐 문제가 없다면 바로 사용자에게 배포한다.

사용자로부터 빠르게 피드백을 받고, 신속하게 수정 사항을 반영할 수 있으며, 또한 모든 과정이 자동으로 이루어지기 때문에 자동화된 테스트와 모니터링이 중요하다.

 

시나리오

  1. 코드 변경 후 PR을 생성한다.
  2. CI서버가 코드를 빌드하고 자동화된 테스트를 실행한다.
  3. 모든 테스트를 통과하면, 자동으로 운영에 배포된다.



Continuous Delivery

언제든 운영 환경에 배포할 준비가 된 상태를 유지하는 것을 목표로하는 프로세스이다.

개발자가 코드를 수정하면, CI를 거쳐 배포 가능한 상태로 만들고, 실제 배포는 수동으로 결정한다.
즉, 언제 배포할지 팀이나 관리자가 수동으로 결정할 수 있으며, 이로 인해 배포 시점을 유연하게 제어할 수 있다.

 

시나리오

  1. 코드 변경 후 PR을 생성한다.
  2. CI 서버가 코드를 빌드하고 자동화된 테스트를 실행한다.
  3. 모든 테스트를 통과하면, 해당 코드는 배포 가능한 상태가 된다.
  4. 배포 담당자의 최종 검토 후 배포를 승인한다.
  5. 운영에 배포한다.

 

CI는 코드 변경 사항을 자주 통합하고 자동으로 테스트하여 코드의 품질을 유지하는 것을 목표로 하며, CI를 통해 개발자는 조기에 문제를 발견할 수 있고 피드백할 수 있다.

CD는 CI의 연장선으로 새로운 기능이나 수정 사항을 사용자에게 신속하게 제공하도록 한다.

CI/CD를 통해 통합과 배포 시간을 줄임으로써 효율성을 높일 수 있으며, 새로운 기능과 수정사항을 정기적으로 구현할 수 있는 안정적인 프로제스를 갖게된다.
이러한 자동화된 과정은 코드 품질을 유지하고, 빠른 피드백 과정을 통해 사용자 요구에 신속하게 대응할 수 있는 기반을 제공한다.



CI/ CD 종류

  • Jenkins : 오픈 소스 자동화 서버로, 다양한 플러그인을 통해 CI/CD 파이프라인 구축 가능
  • CircleCI : 클라우드 기반 CI/CD 서비스
  • TravisCI : 오픈 소스 프로젝트에 많이 사용되는 클라우드 기반 CI 서비스
  • GitLab CI : GitLab에 내장된 CI/CD 도구로, GitLab 프로젝트와 통합하여 사용
  • AWS CodePipeline : AWS 서비스와 통합된 CI/CD 서비스, AWS 환경에서의 배포를 자동화
  • GitHub Actions : GitHub에 통합된 CI/CD 도구로, 코드 변경 시 자동화된 빌드, 테스트, 배포 기능 제공
 



이 중 나는 GitHub Actions을 이용하여 CI/CD를 구축하였다.

 

 

GitHub Actions은 GitHub 플랫폼에서 제공하는 CI/CD 플랫폼이다.

 

 

 

구축

Repository -> Actions 탭 클릭

 

여기서  GitHub Action Workflow 파일을 작성 할 수 있다.

처음 workflow를 생성할 때 Template를 제공해준다.  

 

작성하면 

.github/workflows/ 아래에 *.yaml 파일이 생성된다.  

 

 

 

Secret 변수 등록

Repository - settings탭 -> [Security - Secrets and variables - actions] 에 Secret 변수를 등록하자.

 

해당 변수를 workflow에서 사용하게 되면 workflow의 로그에서는 환경변수로 나오며 해당 값이 노출되지 않는다.

 

 

 

CI

name: CI

on:
  pull_request:
    branches: [ "develop" ]

jobs:
  build:

    runs-on: ubuntu-latest
    permissions:
      contents: read

    steps:
    - uses: actions/checkout@v4
    - name: Set up JDK 17
      uses: actions/setup-java@v4
      with:
        java-version: '17'
        distribution: 'temurin'

    - name: Setup Gradle
      uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
      with:
        gradle-version: '7.6'

    - name: Build with Gradle
      run: |
        ./gradlew clean build -x test
      shell: bash

    - name: Test Coverage Report
      id: jacoco
      uses: madrapps/jacoco-report@v1.7.1
      with:
        title: Test Coverage Report
        paths: ${{ github.workspace }}/build/reports/jacoco/test/jacocoTestReport.xml
        token: ${{ secrets.GITHUB_TOKEN }}
        min-coverage-overall: 30
        min-coverage-changed-files: 50

 

 

CD

name: CD

on:
  push:
    branches: [ "develop" ]

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
        with:
          gradle-version: '7.6'

      - name: Grant Execute Permission For Gradlew
        run: chmod +x gradlew

      - name: Clean & Build With Gradle
        run: ./gradlew clean build -x test

      - name: Check Docker Username
        run: echo ${{ secrets.DOCKER_USERNAME }}


      - name: Docker build & Push
        run: |
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWD }}
          docker build -f Dockerfile -t ${{ secrets.DOCKER_USERNAME }}/ticketing-repository .
          docker push ${{ secrets.DOCKER_USERNAME }}/ticketing-repository

      - name: Deploy Images with Docker compose
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USERNAME }}
          password: ${{ secrets.SERVER_PASSWORD }}
          script_stop: true
          script: |
            container_ids=$(sudo docker ps -q)
            if [ -n "$container_ids" ]; then
              sudo docker stop $container_ids
              sudo docker rm $container_ids
            fi
            sudo docker pull ${{ secrets.DOCKER_USERNAME }}/ticketing-repository
            sudo docker run -d -p 8080:8080 ${{ secrets.DOCKER_USERNAME }}/ticketing-repository
            sudo docker system prune