[CI/CD] CI/CD 와 Github Actions

SJ.CHO·2024년 11월 14일
post-thumbnail

CI/CD

  • Continuous Integration / Continuous Deployment(Delivery) 의 약자로 지속적인 통합 과 지속적인 제공 을 의미한다.

기본개념

  • 지속적인 통합(Continuous Integration)
    • 자동화된 빌드와 자동화된 테스트를 제공 및 안정적인 코드를 빠르게 제공한다.
  • 지속적인 서비스 제공 (Continuous Delivery)
    • 변경된 부분을 빠르게 테스트하여 안정적인 코드를 빠르게 제공한다.
  • 지속적인 배포(Continuous Deployment)
    • 배포를 자동화하여 배포 시간을 단축하고 코드 결과물을 빠르게 지속적으로 제공한다.

배포단계

  • 코드작성
    • 개발자들은 코드를 작성하고 저장소(repository)에 업로드하는 과정이다.
  • 빌드
    • 저장소에서 최신 소스 코드를 가져와 빌드를 수행. 빌드는 소스 코드를 컴파일하고, 라이브러리를 추가하고, 필요한 파일을 생성하는 과정이다.
  • 테스트
    • 빌드된 결과물을 대상으로 테스트를 수행. 테스트는 기능이 정상적으로 작동하는지 확인하고, 버그를 발견하고 수정하는 과정이다.
  • 배포
    • 테스트를 통과한 결과물을 배포. 배포는 서버에 업로드하거나, 사용자에게 제공하는 과정이다.

과거와 현재의 배포

  • 과거의 배포

  • 과거의 SW 개발주기는 주로 폭포수 개발방식을 주로 사용하였다.

  • 오랜시간동안 구현하고 테스트하고 배포하는 과정을 N개의 서버에 반복해서 진행하는형식이었다.

  • 출시 직후 테스트과정에서 버그가 발생할 경우 되돌리거나 수정하고 테스트하는 방식으로 많은 리소스가 발생하였다.

  • 현재의 배포

  • 스크럼으로 대표되는 애자일 개발방식을 주로 사용된다.

  • 특정주기 마다 개발, 테스트 및 통합, 배포를 진행하는 과정을 거치며 위의 폭포수 방식을 사용한다면 이점중 하나인 빠른 배포를 놓치게 된다.

  • Docker를 통해 서버를 표준화하고 동일한 환경에서 테스트 및 배포 테스트를 진행및 자동화하여, 검증된 자동화 시스템을 통해 실패확률을 낮출수 있다.


출처 : https://k21academy.com/docker-kubernetes/docker-and-kubernetes/

  • 기술의 발전에 따라서 테스트뿐만 아니라 배포 단계까지 담당이 가능해졌다.

Github Actions

  • Github에 내장된 CI/CD 도구 이다.
  • github와 통합이 쉽고, CI/CD 서버가 내장 되어 CI/CD서버를 따로 구축할 필요 없으며, 일정 수준까지 가격이 무료 이다.

동작 방법

  • repository의 .github/workflows 디렉토리에 필요한 Actions 파일들을 yaml 형식으로 작성 한다.
  • 작성된 actions 파일들을 github에서 자동으로 실행하도록 설정된다.

Github Actions 의 CI

  • test를 통과한 코드만 develop 브랜치와 main 브랜치에 merge되도록 하여 오류를 방지하고 안정적인 코드가 배포되고 버그를 빠르게 발견 할 수 있다.

  • 동작시점

    • PR이 main 브랜치로 병합되기 전에 CI가 실행된다.
    • pull_request 이벤트가 발생할 때만 CI가 실행되며, 이때 코드 테스트, 빌드, 코드 품질 체크 등을 수행된다.
    • PR이 성공하면, main 브랜치에 병합된다.
  • 활용 예시

    • develop 브랜치에 merge 된 경우, gradle test 를 진행한다.
    • feature/**(feature하위 ) 브랜치가 push 된 경우, gradle test 를 진행한다.
    • 만약 gradle test가 실패한 경우, slack 등 알림을 보내 코드를 수정하도록 개발자에게 안내하도록 설정이 가능.
      • 이메일은 자동으로 발송되도록 기본 설정 되어있다.
      • 존재하는 테스트들을 실행해보고 테스트 커버리지를 로그로 남긴다.
  • yml 파일 예시

name: Gradle Build and Test  # 워크플로의 이름을 지정

# Event Trigger 특정 액션 (Push, Pull_Request)등이 명시한 Branch에서 일어나면 동작을 수행한다.
on:
  push:
    branches:
      - main  # main 브랜치에 푸시할 때 이 워크플로를 실행
  pull_request:
    branches:
      - main  # main 브랜치로의 풀 리퀘스트 발생 시 이 워크플로 실행

 # 실제 어떤 작업을 실행할지에 대한 명시
jobs:
  build:
    runs-on: ubuntu-latest  # 워크플로는 Ubuntu 최신 버전의 환경에서 실행됨

    steps:
    - name: Check out code
      uses: actions/checkout@v4  # GitHub 리포지토리의 코드를 워크플로 환경에 체크아웃

    - name: Set up JDK 17
      uses: actions/setup-java@v2  # JDK 17을 설치하는 GitHub Actions의 공식 액션
       	  distribution: 'adopt'
          java-version: '17'

    - name: Cache Gradle dependencies
      uses: actions/cache@v2  # Gradle 의존성을 캐시하여 매번 의존성을 다시 다운로드하지 않도록 함
      with:
        path: ~/.gradle/caches  # Gradle 캐시의 경로
        key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}  # 캐시 키는 Gradle 설정 파일들에 따라 생성됨
        restore-keys: |
          ${{ runner.os }}-gradle-  # 복원할 수 있는 기본 키 설정

    - name: Build with Gradle
      run: ./gradlew build --stacktrace  # Gradle을 사용해 프로젝트 빌드 실행, --stacktrace는 에러 발생 시 상세한 스택 트레이스를 출력

    - name: Run tests with Gradle
      run: ./gradlew test  # Gradle을 사용해 정의된 테스트를 실행

    - name: Upload test results
      if: always()  # 테스트가 실패하더라도 항상 실행하여 테스트 결과를 업로드
      uses: actions/upload-artifact@v2  # GitHub Actions의 아티팩트 업로드 기능 사용
      with:
        name: test-results  # 아티팩트의 이름 지정
        path: build/test-results/test  # Gradle에서 생성된 테스트 결과 파일 경로 지정

    - name: Check test coverage
      run: |
        # 테스트 리포트를 읽어 커버리지를 계산하는 스크립트
        TEST_REPORT=build/test-results/test  # 테스트 결과가 저장된 디렉토리
        if [ -d "$TEST_REPORT" ]; then  # 디렉토리가 존재하는지 확인
          echo "Test results found"  # 테스트 결과가 있음을 출력
          # 테스트 통과한 수와 전체 테스트 수를 추출
          TEST_PASSED=$(grep -oP '(?<=tests passed: )\d+' $TEST_REPORT/*.xml | head -n 1)  
          TEST_TOTAL=$(grep -oP '(?<=tests total: )\d+' $TEST_REPORT/*.xml | head -n 1)
          if [ -z "$TEST_PASSED" ]; then  # 만약 테스트 결과가 없다면
            echo "No test results found"
            exit 1  # 에러 코드 1로 종료
          fi
          # 테스트 통과율 계산 (테스트 통과 수 / 전체 테스트 수 * 100)
          TEST_PERCENTAGE=$((TEST_PASSED * 100 / TEST_TOTAL))
          echo "Test coverage: $TEST_PERCENTAGE%"  # 커버리지 % 출력
        else
          echo "No test results found"  # 테스트 리포트가 없으면 경고 출력
          exit 1  # 에러 코드 1로 종료

Github Actions 의 CD

  • 배포를 자동화 하는 작업을 기술해서 빠르고 간편하게 배포한다.

  • main 브랜치에 코드가 통합된 경우 운영 환경에 빠르게 배포할 수 있게 함.

  • 동작시점

    • PR이 main 브랜치에 병합된 후, main 브랜치에 푸시될 때 CD가 실행된다.
    • CD에서는 애플리케이션을 배포하고, 배포 후 배포 확인 작업이 실행된다.
  • 활용예시

    • main 브랜치에 merge된 경우, gradle test 를 실행한다.
    • main 브랜치의 코드 기준으로 jar 파일을 생성한다.
    • 생성된 jar파일을 특정 환경(AWS, GCP 등)에 배포한다.
  • yml 파일 예시

name: 'CD'  # 워크플로 이름을 'CD'로 설정. 이 이름은 GitHub Actions UI에서 워크플로를 식별하는데 사용된다.

on: 
    push:
        branches: [ main ]  # 'main' 브랜치에 푸시될 때 워크플로가 트리거된다.

jobs:
  cd:
    runs-on: [ ubuntu-latest ]  # 워크플로는 GitHub의 최신 Ubuntu 환경에서 실행된다.

    steps: 
      - name: Checkout code
        uses: actions/checkout@v4  # GitHub 리포지토리의 최신 코드를 체크아웃하여 워크플로 환경에서 사용하도록 한다.
        
      - name: Java setup
        uses: actions/setup-java@v3  # JDK 설치를 위한 GitHub의 공식 액션을 사용한다.
        with:
          distribution: 'adopt'  # JDK 배포판을 'AdoptOpenJDK'로 설정한다.
          java-version: '17'  # 사용할 JDK 버전을 17로 설정한다.
        
      - name: Run unit tests
        run: |
          ./gradlew clean test  # Gradle을 사용하여 프로젝트를 빌드하고 유닛 테스트를 실행, 'clean'은 이전 빌드를 지우고, 'test'는 테스트를 실행한다.
        
      - name: Deploy to Heroku
        uses: akhileshns/heroku-deploy@v3.12.12  # Heroku에 애플리케이션을 배포하는 GitHub Action을 사용한다.
        with:
          heroku_api_key: ${{ secrets.HEROKU_API_KEY }}  # Heroku API 키는 GitHub Secrets에 저장된 값을 사용. 이는 보안을 위해 중요한 정보를 안전하게 전달한다.
          heroku_app_name: "sampleapp-github-actions"  # 배포할 Heroku 애플리케이션의 이름을 지정. 이 이름은 Heroku에서 고유해야 한다.
          heroku_email: "nbcdocker@proton.me"  # Heroku 계정에 등록된 이메일 주소를 입력. 이는 Heroku 배포 인증에 필요하다.

Github Actions 살펴보기

Workflow

  • 여러 Job으로 구성되고, Event에 의해 트리거될 수 있는 자동화된 프로세스 이다.
  • Workflow 파일은 YAML으로 작성되고, Github Repository의 .github/workflows 폴더 아래에 저장된다.

event

  • Github Repository에서 발생하는 push, pull request open, issue open, 특정 시간대 반복(cron) 등의 특정한 규칙(트리거) 이다.
  • workflow 를 실행(trigger) 한다.

runner

  • Github Action Runner app이 설치된 VM 이다.
  • Workflow가 실행될 instance로, 각각의 Job 들은 개별적인 runner에서 실행된다.
  • 동시에, 독립적으로 실행이 가능하다.

job

  • 하나의 runner에서 실행될 여러 step의 모음을 의미 한다.

step

  • 실행 가능한 하나의 shell script 또는 action 이다.

Actions

  • Workflow의 가장 작은 단위로 재사용이 가능 하다.
  • Job을 만들기 위해 Step들을 연결한다.

Workflow

name

  • github actions의 이름을 정하는 부분 이다.

on

  • 해당 action이 언제 실행되는지에 대한 부분 이다.

jobs


CI 적용해보기

  • yml 파일
# Actions 이름 github 페이지에서 볼 수 있다.
name: Run Test

# Event Trigger 특정 액션 (Push, Pull_Request)등이 명시한 Branch에서 일어나면 동작을 수행한다.
on:
  push:
    # 배열로 여러 브랜치를 넣을 수 있다.
    branches: [ develop, feature/* ]
  # github pull request 생성시
  pull_request:
    branches:
      - develop # -로 여러 브랜치를 명시하는 것도 가능

  # 실제 어떤 작업을 실행할지에 대한 명시
jobs:
  build:
    # 스크립트 실행 환경 (OS)
    # 배열로 선언시 개수 만큼 반복해서 실행한다. ( 예제 : 1번 실행)
    runs-on: [ ubuntu-latest ]

    # 실제 실행 스크립트
    steps:
      # uses는 github actions에서 제공하는 플러그인을 실행.(git checkout 실행)
      - name: checkout
        uses: actions/checkout@v4

      # with은 plugin 파라미터 입니다. (java 17버전 셋업)
      - name: java setup
        uses: actions/setup-java@v2
        with:
          distribution: 'adopt' # See 'Supported distributions' for available options
          java-version: '17'

      - name: make executable gradlew
        run: chmod +x ./gradlew

      # run은 사용자 지정 스크립트 실행
      - name: run unittest
        run: |
          ./gradlew clean test

  • 위 yml 파일에서 명시한 트리거가 발동했을 때 jobs로 지정한 명령을 GitHub 내에서 수행되는것을 확인이 가능하다.

테스트 코드 실패

  • push 하려는 브랜치에 Test코드가 정상적으로 수행이 되지 않을때, Git Actions CI 또한 실패라고 판단하여 push 작업요청을 거절한다.

PR CI

  • PR 요청 또한 트리거에 명시되어져있기에 동일한 CI 과정을 거친다.

CI/CD 모두 적용하기

  • 개념도

  • 전체흐름

    • 개발자는 feature/ 로 시작하는 브랜치를 만들어서 test코드를 포함한 수정 작업을 완료한 뒤 Pull Request 생성한다.
    • (자동화) Pull Request를 만들면 해당 브랜치에 대해 gradle test를 수행한다.
    • Pull Request 코드의 test가 실패한 경우, Pull Request 를 생성한 개발자는 test 코드를 수정하여 Pull Request를 변경 한다.
    • Pull Request 코드의 test가 성공한 경우, 다른 개발자들의 승인을 기다림
    • 다른 개발자들은 Pull Request의 코드를 승인하거나 댓글로 소통 한다.
    • (자동화) main 브랜치에 merge 되면 해당 브랜치를 cloudtype 서버에 배포 한다.

yml 파일

  • test-pr.yml
name: test every pr
on:
  workflow_dispatch:
  pull_request:
permissions:
  contents: read
  pull-requests: read
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: setup jdk
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: gradle
      - name: Grant execute permission for gradlew
        run: chmod +x ./gradlew
      - name: gradlew test
        run: ./gradlew test
  • deploy-main.yml
name: Deploy to cloudtype
on:
  push:
    branches:
      - main
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Connect deploy key
        uses: cloudtype-github-actions/connect@v1
        with:
          token: ${{ secrets.CLOUDTYPE_TOKEN }}
          ghtoken: ${{ secrets.GHP_TOKEN }}
      - name: Deploy
        uses: cloudtype-github-actions/deploy@v1
        with:
          token: ${{ secrets.CLOUDTYPE_TOKEN }}
          project: # 워크스페이스 이름/프로젝트 이름 ex) nbc.docker/cicd
          stage: main
          yaml: |
            name: # 프로젝트 이름 ex) `cicd`
            app: java@17
            options:
              ports: 8080
            context:
              git:
                url: git@github.com:${{ github.repository }}.git
                ref: ${{ github.ref }}
              preset: java-springboot

  • ghtoken: ${{ secrets.GHP_TOKEN }}${{ secrets.CLOUDTYPE_TOKEN }} 토큰 값이 없기에 Actions가 오류가 나는것을 확인할 수 있다.

  • github 내에서 설정을 통해 필요한 Key 값들을 레포지토리내부에 지정해 줄 수 있다.

  • Actions가 성공하는걸 확인할 수 있으면서 배포 또 한 성공하는것을 확인이 가능하다.

  • 전체흐름도


profile
70살까지 개발하고싶은 개발자

0개의 댓글