

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

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

오랜시간동안 구현하고 테스트하고 배포하는 과정을 N개의 서버에 반복해서 진행하는형식이었다.
출시 직후 테스트과정에서 버그가 발생할 경우 되돌리거나 수정하고 테스트하는 방식으로 많은 리소스가 발생하였다.
현재의 배포

스크럼으로 대표되는 애자일 개발방식을 주로 사용된다.
특정주기 마다 개발, 테스트 및 통합, 배포를 진행하는 과정을 거치며 위의 폭포수 방식을 사용한다면 이점중 하나인 빠른 배포를 놓치게 된다.
Docker를 통해 서버를 표준화하고 동일한 환경에서 테스트 및 배포 테스트를 진행및 자동화하여, 검증된 자동화 시스템을 통해 실패확률을 낮출수 있다.

출처 : https://k21academy.com/docker-kubernetes/docker-and-kubernetes/
테스트뿐만 아니라 배포 단계까지 담당이 가능해졌다.
.github/workflows 디렉토리에 필요한 Actions 파일들을 yaml 형식으로 작성 한다.test를 통과한 코드만 develop 브랜치와 main 브랜치에 merge되도록 하여 오류를 방지하고 안정적인 코드가 배포되고 버그를 빠르게 발견 할 수 있다.
동작시점
활용 예시
develop 브랜치에 merge 된 경우, gradle test 를 진행한다.feature/**(feature하위 ) 브랜치가 push 된 경우, gradle test 를 진행한다.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로 종료
배포를 자동화 하는 작업을 기술해서 빠르고 간편하게 배포한다.
main 브랜치에 코드가 통합된 경우 운영 환경에 빠르게 배포할 수 있게 함.
동작시점
활용예시
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 배포 인증에 필요하다.



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

jobs로 지정한 명령을 GitHub 내에서 수행되는것을 확인이 가능하다.


개념도

전체흐름
feature/ 로 시작하는 브랜치를 만들어서 test코드를 포함한 수정 작업을 완료한 뒤 Pull Request 생성한다.gradle test를 수행한다.test-pr.ymlname: 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.ymlname: 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가 오류가 나는것을 확인할 수 있다.




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