[CI/CD] 테스트 시 검사를 해보고 배포를 진행을 해보자.

2025. 3. 30. 20:25Backend/CICD

개요

 

GitHub - countdown10/count10shop: [내배캠] 동시성 제어 프로젝트

[내배캠] 동시성 제어 프로젝트. Contribute to countdown10/count10shop development by creating an account on GitHub.

github.com

 

내배캠에서 동시성 제어 프로젝트를 진행을 하면서

CI/CD 를 담당하게 되었다.

 

이번에 진행을 하면서 생긴 부분에 대해서 정리를 해보았다.

 


요구사항 분석

요구사항

  • 코드 변경 시, 자동으로 빌드 및 테스트를 수행하는 CI 파이프라인을 구성하세요.
  • 테스트가 성공적으로 완료되면 프로덕션에 자동 배포되도록 CD 파이프라인을 설정하세요.

조건

  • CI/CD 관련 이슈를 해결하기 위해 다음을 고려해야 합니다.
  • 테스트 커버리지 강화: 배포 전 코드 안정성을 높이기 위해 테스트 커버리지를 강화해야 합니다.
  • 보안: 배포 과정에서 민감한 정보나 설정이 노출되지 않도록 보안 설정을 철저히 관리합니다.

발표 자료 준비

  • 자동 빌드 및 테스트, 배포 구성 과정
  • 코드 변경 시 어떻게 자동으로 빌드 및 테스트가 수행되는지에 대한 배포 절차
  • 수동 배포와 비교하여 CI/CD 파이프라인을 적용한 후의 효율성 차이와 느낀점

튜터님의 추가사항

  • 도커라이징을 진행할 것
  • 무중단 배포를 진행을 해볼 것

개인적인 테스트 코드 커버리지

  • 40% 이상으로 진행하기

 

 * 도커라이징이란?

도커라이징(Dockerizing)은 Docker 컨테이너를 사용하여 응용프로그램(application)을 패킹(packing),

배포(deploying), 실행(running) 하는 전 과정을 말한다.

 

도커라이징(Dockerizing) 이란?

도커 관련 업무를 시작하면서 "도커라이징"이라는 용어를 알게 되었는데 한국어로 된 설명이 마땅히 없어서 잠시 끄적여본다.. 이 글에 따르면 도커라이징(Dockerizing)은 Docker 컨테이너를 사용하

korsa.tistory.com

 

 * 무중단 배포란(Zero-downtime Deployment)?

무중단, 말 그대로 서비스의 중단 없이 새로운 버전의 소프트웨어를 배포하는 것을 말한다.

 

[DevOps/Infra] 무중단 배포란?(Rolling, BlueGreen, Canary)

현재 재직 중인 회사는 사실 아직 무중단 배포 환경이 아니다. 빌드 후 생성된 jar나 war를 직접 수동으로 배포하기 위해 서비스의 중단이 필연적인 상황이다. 서비스 이용자가 늘어나고 비즈니스

hoehen-flug.tistory.com

 

 

정리를 해본다면

CI 만을 위한 파이프라인을 만들어야하고

CD 만을 위한 파이프라인을 만들어야한다.

 

무중단 배포를 돈을 안쓰고 간단하게 할 수 있는 방법으로 BlueGreen 방식으로 도커로 대략적으로 만들어 볼 수 있을 것이라고

튜터님께서 이야기를 해주셨다.

 

일단 무중단 배포는 나중으로 미루고 기본 배포부터 진행을 해보도록 하겠다.

 

 


어떻게 구성을 할 것인가?

사실 이것이 가장 중요하다고 생각을 한다.

 

간단하게 사용하기 위해서 github actions를 사용을 했지만, 조사를 조금만 해보아도, TeamCity. CircleCI, jenkins 등 많이 있다.

위에 툴은 CI/CD를 위한 도구들이고

 

이미지를 어떻게 저장을 할지도 생각할 수 있다.

jar 파일만 각 서버에 나눠줘서 배포를 할 수도 있고, Docker Image를 Docker Hub, AWS ECR 등에 올려서 배포를 할 수도 있다.

 

그리고 서버가 어떤 형식으로 생성이 될지도 생각할 수 있다.

ECS를 사용해서 각 컨테이너를 만들어서 사용할 수도 있고, 쿠버네티스로 배포를 진행을 할 수 있다.

아니면 AWS EC2에서 받아서 배포를 진행을 할 수도 있다.

 

프로젝트에서 요구를 했던 부분은 CI/CD, 보안, 도커, 부분으로 볼 수 있다.

 

현재 프로젝트에서 사용하는 외부 프로그램은

  • elastic search
  • redis
  • kibana

그리고 spring boot을 올려서 사용을 진행을 한다.

 

도커 이미지를 배포를 해야하기 때문에 Docker hub, AWS ECR 중에서 EC2를 사용하고 있기 때문에

AWS ECR를 사용해서 배포를 진행을 하였다.

 


CI 파이프라인 구성

name: ci

on:
  workflow_dispatch:
  pull_request:
    branches: [ main, develop ]

  push:
    branches:
      - feat/*
      - refactoring/*

permissions:
  pull-requests: write
  contents: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: setup jdk
        uses: actions/setup-java@v3
        with:
          distribution: 'temurin'
          java-version: '17'
          cache: gradle

      - name: Grant execute permission for gradlew
        run: chmod +x ./gradlew

      - name: gradlew build
        run: ./gradlew build

      - name: Jacoco Report to PR
        id: jacoco
        uses: madrapps/jacoco-report@v1.6.1
        with:
          paths: ${{ github.workspace }}/build/reports/jacoco/test/jacocoTestReport.xml
          token: ${{ secrets.GITHUB_TOKEN }}
          min-coverage-overall: '40'
          min-coverage-changed-files: '40'

      - name: Upload jacoco HTML report
        uses: actions/upload-artifact@v4
        with:
          name: jacoco-html-report
          path: ${{ github.workspace }}/build/reports/jacoco/test/html

      - name: Get the Coverage info
        run: |
          echo "Total coverage ${{ steps.jacoco.outputs.coverage-overall }}"
          echo "Changed Files coverage ${{ steps.jacoco.outputs.coverage-changed-files }}"

 

github actions 에 대한 내용은 많기 때문에 참고한 블로그를 밑에 남기도록 하겠다.

 

 

CI 핵심 파이프 라인

      - name: Grant execute permission for gradlew
        run: chmod +x ./gradlew

      - name: gradlew build
        run: ./gradlew build

      - name: Jacoco Report to PR
        id: jacoco
        uses: madrapps/jacoco-report@v1.6.1
        with:
          paths: ${{ github.workspace }}/build/reports/jacoco/test/jacocoTestReport.xml
          token: ${{ secrets.GITHUB_TOKEN }}
          min-coverage-overall: '40'
          min-coverage-changed-files: '40'

      - name: Upload jacoco HTML report
        uses: actions/upload-artifact@v4
        with:
          name: jacoco-html-report
          path: ${{ github.workspace }}/build/reports/jacoco/test/html

      - name: Get the Coverage info
        run: |
          echo "Total coverage ${{ steps.jacoco.outputs.coverage-overall }}"
          echo "Changed Files coverage ${{ steps.jacoco.outputs.coverage-changed-files }}"

 

 

 

 

CI에 필요한 요구사항은 다음과 같다.

  • 자동 테스트
  • 자동 빌드
  • 테스트 커버리지 확인

gradlew build는 안에 테스트까지 같이 하기 때문에 한번에 테스트와 빌드를 진행을 할 수 있다.

 

jacoco는 테스트 커버리지를 commit 메세지 까지 넣어줄 수 있고 레포트를 만들어 각 브랜치에 넣어줄 수 있다.

 

 

작성을 하고 동작을 시키면 github actions bot이 다음과 같이 만들어주게 된다.

 

가장 위에 있는 jacoco가 위에 커밋메세지를 만들어주는 것이다.

 

중간에 있는 jacoco는 report를 /build/reports/test 에 테스트 코드 결과 값을 만들어주게 된다.

 

자세한 설정 값은 위에 블로그에 참고자료에 추가를 해두었다.

 


CD 파이프 라인

 

다음과 같은 구성을 하고 있다.

  1. github actions CD 파이프 라인을 실행을 시킨다.
  2. ECR에 Spring boot 의 이미지가 올라간다.
  3. actions 가 EC2에 접속을 해서 이미지를 가져온다음에 도커로 실행을 시킨다.

다음과 같이 구성을 한 이유는 핵심은 팀에서 프로그래밍을 한 소스코드이기 때문에

spring boot 만 변경이 되면 된다고 생각을 하였다.

 

소스 코드

name: Build and Push Docker Image to AWS ECR

on:
  push:
    branches:
      - main

env:
  AWS_REGION: ap-southeast-2  # AWS 리전 (서울 리전)
  ECR_REPOSITORY: countdown10shop
  IMAGE_TAG: latest

jobs:
  build-and-push:
    runs-on: ubuntu-latest

    steps:
      - name: 리포지토리 체크아웃
        uses: actions/checkout@v3

      - name: AWS 자격 증명 설정
        uses: aws-actions/configure-aws-credentials@v3
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      - name: Set ECR_REGISTRY env variable
        run: echo "ECR_REGISTRY=${{ steps.login-ecr.outputs.registry }}" >> $GITHUB_ENV

      - name: Build, tag, and push image to Amazon ECR
        run: |
          echo "ECR Registry: $ECR_REGISTRY"
          docker build -f ./Dockerfile.spring -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

      - name: Deploy
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USERNAME }}
          key: ${{ secrets.EC2_SSH_PRIVATE_KEY }}
          port: ${{ secrets.EC2_PORT }}
          script: |
            docker stop container || true
            docker rm container || true
            docker rmi ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }} || true
            
            aws ecr get-login-password --region ${{ env.AWS_REGION }} | docker login --username AWS --password-stdin ${{ env.ECR_REGISTRY }}
            docker pull ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}
            
            docker run -d -p 8080:8080 \
            -e DB_URL=${{ secrets.AWS_RDS_URL }} \
            -e DB_USER=${{ secrets.AWS_RDS_USER }} \
            -e DB_USER_PASSWORD=${{ secrets.AWS_RDS_USER_PASSWORD }} \
            --name container --ulimit nproc=60000 --net count10shop_default ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}

 

참고 자료는 밑부분에 올려두었다.

 

핵심 부분만 같이 살펴보도록 하겠다.

 


CD 핵심 파이프 라인

jobs:
  build-and-push:
    runs-on: ubuntu-latest

    steps:
      - name: 리포지토리 체크아웃
        uses: actions/checkout@v3

      - name: AWS 자격 증명 설정
        uses: aws-actions/configure-aws-credentials@v3
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      - name: Set ECR_REGISTRY env variable
        run: echo "ECR_REGISTRY=${{ steps.login-ecr.outputs.registry }}" >> $GITHUB_ENV

      - name: Build, tag, and push image to Amazon ECR
        run: |
          echo "ECR Registry: $ECR_REGISTRY"
          docker build -f ./Dockerfile.spring -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

      - name: Deploy
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USERNAME }}
          key: ${{ secrets.EC2_SSH_PRIVATE_KEY }}
          port: ${{ secrets.EC2_PORT }}
          script: |
            docker stop container || true
            docker rm container || true
            docker rmi ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }} || true
            
            aws ecr get-login-password --region ${{ env.AWS_REGION }} | docker login --username AWS --password-stdin ${{ env.ECR_REGISTRY }}
            docker pull ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}
            
            docker run -d -p 8080:8080 \
            -e DB_URL=${{ secrets.AWS_RDS_URL }} \
            -e DB_USER=${{ secrets.AWS_RDS_USER }} \
            -e DB_USER_PASSWORD=${{ secrets.AWS_RDS_USER_PASSWORD }} \
            --name container --ulimit nproc=60000 --net count10shop_default ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}

 

CD에 필요한 요구사항은 다음과 같다.

  • 자동 빌드
  • 이미지 ECR에 배포
  • 서버 배포

일단 ECR에 접속을 하기 위해 AWS 인증을 진행을 한다.

 

Dockerfile 안에 빌드 내용도 같이 있다. 하지만 앞에서 테스트를 전부 진행을 하고 오기 때문에 ./gradlew build -x test 을 하여서 

테스트를 빼고 진행을 하였다.

 

이후 ECR에서 가져온 정보로 Docker image를 만들고 그 내용을 ECR에 올리게 된다.

 

그 다음에 github actions가 EC2에 접속을 하여서 진행을 하게 된다.

 

하지만 ec2에는 AWS ECR인증이 없기 때문에 새롭게 로그인을 해주고 Docker image를 가져오게 된다.

 

이후 가져온 이미지를 가지고 바로 run 을 하게된다.

 

 

민감한 정보는 Repository secrets 에 정보를 올려두고 사용을 할 수 있다.

 

 

push 를 진행을 하게되면 다음과 같이 github actions 가 동작하게 된다.

 

 


CI/CD 의 적용 후 장점

실제 프로젝트에 적용을 하면서 생겼던 장점에 대해서 이야기를 해볼까 한다.

 

 

PR를 하는 과정에서 CI 부분에서 바로 테스트를 통과를 하지 못하는 문제를 확인하였다.

 

어디서 문제가 생기는 것인지 Commit 을 찾아서 올라가 보았더니 문제가 생기는 부분을 빠르게 알게 되었다.

 

해당 commit 내용에 들어가서 변경된 내용을 빠르게 찾고 변경을 진행할 수 있어서 오류를 찾는 시간이 줄어들었다.

 


느낀점

전에 배포를 진행을 할 때는 항상 EC2에 들어가서 배포를 진행을 하였는데, 번거로운 과정을 생략할 수 있었다.

 

그리고 더 많은 시스템을 진행을 한다면 일일이 들어갈 수 없을 것이다.

 

MSA 환경이 되면 그 많은 환경을 일일이 들어가서 변경을 해야되는데,

자동으로 배포를 진행을 해줘서 일관되게 적용을 할 수 있을 것이다.

 

다음 프로젝트 부터는 처음부터 적용을 끝내서 빠르게 팀원들과 코딩에 집중할 수 있도록 진행을 해야겠다.

 


참고자료

더보기

 

 

[CI/CD] Github Actions로 자동 배포하기(yml 파일 작성) (feat. AWS S3)

Github Action 중 자동 배포하는 action을 만들어보자!배포할 클라이언트 파일 준비 배포 파일 올라갈 새로운 깃허브 레포지토리 생성해놓기 AWS accces key, AWS secret key 준비 (노출 절대 X)깃허브 레포지

velog.io

 

 

EC2 + ECR + Docker를 활용한 CI/CD 구축 with Github Actions

우선 현재 서버의 경우 인프라가 아래처럼 구성되어 있다. 만약 서버에서 코드를 몇줄만 수정해도 아래 과정을 거쳐서 다시 배포해야 한다. 1. 코드 수정 후 도커 이미지 빌드 2. 도커 이미지를 EC

iamiet.tistory.com

 

 

[CICD] GitHub Actions + Docker + ECR + EC2 배포

위 아키텍처와 같이 GitHub에 push하면 GitHub Actions에서 CI/CD가 진행된다.GitHub Actions에서 프로젝트 빌드 및 Docker 이미지 생성ECR에 접속 후 이미지 pushEC2에 접속하여 ECR에서 이미지 pull 후 Docker로 실행

velog.io

 

 

[Github] Github Actions 사용하는법 , SSH 연결

개발하던 프로젝트를 github repo 에 까지 다 push 해있다는 가정하에 설명합니다. workflow 만들기 github 홈페이지에 해당 프로젝트로 진입을하면 "Actions" 부분에서 set up a workflow yourself 가 있는데 클릭

bug41.tistory.com

 

 

[Github Actions/AWS] Github Actions에서 SSH로 EC2 인스턴스에 접근하기

※ 이 방법은 EC2 인스턴스에 SSH로 직접 접근하는 방식으로, 빌드를 EC2 인스턴스 상에서 진행하기 때문에 EC2 메모리 등의 자원을 사용하여 서비스에 영향을 줄 수 있습니다. 빌드를 Github Actions에

tesseractjh.tistory.com

 

 

Github Actions를 이용해 CI/CD를 구축해보자(ssh 연결)

배포 자동화를 해놓지 않았을 때 인스턴스 접속 git pull 백엔드 폴더에서 yarn install -> 배포 프론트 폴더에서 yarn install -> yarn build -> 배포 배포 자동화를 해놓지 않았을 때는 위 과정의 반복이었다.

doooodle932.tistory.com

 

 

CI/CD를 구축해보자2 - JaCoCo와 GitHub Actions으로 CI/CD구축해보기(추가: Report to PR)

테스트 커버리지를 측정하고 검증할 수 있는 Jacoco라이브러리와 자동화 WorkFlow를 제공하는 GithubAction을 사용하여 CI/CD를 구축해봅니다.

velog.io

 

 

[공부정리] Jacoco를 이용한 깃허브 액션 CI 구축

지난번 글에서 Jacoco를 이용해서 코드 커버리지를 검증해보는 시간을 가졌는데, 이번 글에서는 GitHub Actions를 사용해 CI 기능을 구축한 과정을 기록한다. 참고로 아래 CI 코드는 후에 Sonar Cloud를 적

velog.io