프로젝트 개발 효율성을 위한 CI/CD 적용 과정

이상혁·2024년 9월 25일

Project

목록 보기
1/12

CI/CD가 필요하다고 생각한 이유

이전에 개발을 하면서 수동으로 배포를 해왔습니다. 항상 EC2에 접속을 해서 git으로 프로젝트를 다운로드를 받고 build를 실행을 시키는 반복적인 과정을 진행을 해왔습니다. 이런 과정는 시간이 항상 많이 소요가 되었고 배포에 대한 피로도 높았습니다.
이번에는 낭비와 피로를 줄이기 위해서 프로젝트를 진행을 하면서 효율적인 관리를 위해서 지속적인 통합, 지속적인 배포 CI/CD를 적용을 하고 싶었습니다. 그래서 github actions를 통해서 CI/CD를 구축을 하고자 합니다.

github actions를 선택한 이유는 github actions가 github 플랫폼에 내장이 되어 있기 때문에 git repository와 연동이 잘 되기 때문입니다. 또한 따로 설치를 할 필요가 없으며, 이미 다른 사람들이 만들어 놓른 Actions를 사용을 해서 편리하게 구현을 할 수 있어서 입니다.

이번 과정에서는 github actions를 이용해서 dev로 Pull Request를 할 때, CI가 일어나서 테스트 자동화가 되고 main으로 Pull Request가 되면 CD가 일어나도록 gitworkflow를 작성을 해보겠습니다.

CI와 자동 테스트 구현

branch 전략을 dev에서는 feature에서 딴 branch를 통합하는 과정을 거치기로 했습니다.
그래서 dev pull Request를 할 때, 자동을 CI가 작동을 할 수 있도록 구성을 해야 했습니다.
이 때, 고려를 해야하는 것이 docker를 이용하고 있다는 점이었습니다.
저는 공통적인 개발 환경을 만들기 위해서 docker를 사용하고 있었습니다. 그래서 CI를 구현을 할 때 docker를 사용하는 것을 고려 해야 했습니다.

먼저, 빌드와 테스트를 진행을 하기 위해서 소스코드를 받아야 하고 그 소스코드를 실행을 시키기 위한 환경을 제공을 해야 했습니다. 그래서 소스코드를 git repository에서 받고 Java 환경을 설치를 하고 그 안에서 테스트와 빌드가 진행이 되도록 구성을 했습니다. 또한 저는 지금 개발환경을 다른 개발자가 와도 맞출 수 있도록 하기 위해서 현재의 소스코드와 개발 환경을 docker 이미지로 빌드를 했습니다. 그리고 이미지를 다른 로컬 환경에 다운 받을 수 있도록 docker hub에 올리도록 구성을 했습니다.

이제 YMAL 코드를 보면서 하나씩 알아보겠습니다.

전체 YAML 파일

name: CI

on:
  push:
    branches:
      - dev
  pull_request:
    branches:
      - dev

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Check out the repository
        uses: actions/checkout@v3

      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
          
      - name: Build and Test with Gradle
        run: ./gradlew clean build --no-daemon

      - name: Build Docker image
        run: docker build -t tickerflow-my-server .

      - name:  Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_HUB_USERNAME }}
          password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}

      - name: Push Docker image to Docker Hub
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: dktmskf0813/tickerflow-java:latest

소스 코드 다운 받고 Java 환경 설치하기

- name: Check out the repository
  uses: actions/checkout@v3

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

먼저, actions/checkout@v3로 현재의 repository에서 소스코드를 가지고 옵니다.
프로젝트가 Java17버전으로 개발이 되었기 때문에 Java 17버전을 설치를 해서 소스코드가 빌드와 테스트 될 수 있는 환경을 만들어 줍니다.

테스트 및 빌드 진행하기

- name: Build and Test with Gradle
  run: ./gradlew clean build --no-daemon

프로젝트에 테스트를 자동으로 진행을 하고 빌드를 하기 위한 명령어를 실행을 해줍니다.
clean으로 이전 버전의 빌드는 삭제를 하고 새로 가져온 소스 코드를 build라는 명령어를 통해서 테스트하고 빌드를 진행을 합니다.

Docker 이미지로 만들기

- name: Build Docker image
  run: docker build -t tickerflow-my-server .

위에서 언급을 했듯이 Docker를 사용하고 있기 때문에 이미지를 만들어 줘야 합니다.
그래서 docker build 명령어를 통해서 현재의 소스 코드와 라이브러리들을 이미지로 만들어 줍니다.

Docker Hub에 이미지 올리기

- name:  Login to Docker Hub
  uses: docker/login-action@v2
  with:
		username: ${{ secrets.DOCKER_HUB_USERNAME }}
		password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}

- name: Push Docker image to Docker Hub
uses: docker/build-push-action@v4
with:
	context: .
	push: true
	tags: dktmskf0813/tickerflow-java:latest

만든 이미지는 다른 개발자들도 사용을 할 수 있도록 Docker Hub에 올리도록 합니다.
프로젝트 github Repository에 Secrets and variables에서 미리 저장해 둔 DOCKER_HUB_USERNAME, DOCKER_HUB_ACCESS_TOKEN을 이용해서 Docker hub에 로그인을 합니다.
그리고 우리가 만들어 놓은 이미지를 최신 태그를 붙혀서 push를 합니다.

그러면 branch dev로 push를 하거나 pull request를 보내게 되면 우리가 작성한 workflow가 동작을 하게 되면서 자동으로 CI에 대한 검증이 되게 됩니다.

검증이 성공을 하게 된다면 위 사진처럼 초록불이 들어오면서 성공했다고 뜨게 됩니다.

CD로 자동 배포 구현

이제 main으로 push나 pull Request를 했을 때, workflow를 어떻데 구성을 고민을 해보았습니다.
일단 클라우드 서비스로 AWS와 네이버 클라우드를 고민을 했습니다.
제 선택은 AWS였습니다. 그 이유는 AWS는 방대한 기능들을 제공을 합니다. AWS안에서 Load Banlancing, DB, Docker등을 모두 해결을 할 수 있습니다. 또한 거대 기업인만큼 안전성도 보장이 되었기 때문에 AWS를 선택을 했습니다.

다음으로 고려를 해야 할 상황으로 Docker를 사용하는 것과 대용량 트래픽이 들어왔을 때 어떻게 할 것인지에 대한 부분이었습니다. 또 EC2에 소스 파일을 넘겨줘서 실행을 할지를 고려를 해서 workflow를 구성을 하였습니다.

간단하게 설명을 하면 interillJ에서 github로 push 혹은 pull Request를 했을 때, github actions가 트리거가 되면서 workflow가 실행이 됩니다. 그리고 그 workflow 대로 AWS의 Docker hub의 역할을 하는 AWS ERC에 만들어 둔 이미지를 업로드하고 소스 파일 S3에 업로드를 합니다. 그리고 AWS CodeDeploy에 배포를 진행을 하라고 명령을 하면 AWS CodeDeploy가 EC2에게 S3에 배포에 필요한 파일들을 가지고 옵니다. 그리고 AWS ERC에서 이미지를 다운 받아서 컨테이너로 실행을 하면서 프로젝트를 실행을 합니다. 그리고 클라이언트가 접근을 할 때, 한 번에 많은 트래픽이 들어올 것을 고려를 해서 AWS LoadBalance를 사용을 해서 트래픽을 분산을 시킬 수 있도록 했습니다.

이제 전체 YMAL 코드를 보면서 하나씩 알아보겠습니다.

전체 YAML 파일

name: CD

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  Deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Github Repository에 올린 파일을 불러온다.
        uses: actions/checkout@v4

      - name: JDK 17버전 설치
        uses: actions/setup-java@v4
        with:
            distribution: temurin
            java-version: 17

      - name: 테스트 및 빌드하기
        run: ./gradlew clean build

      - name: AWS Resource에 접근할 수 있게 AWS credentials 설정
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ap-northeast-2
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

      - name: ECR에 로그인하기
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Docker 이미지 생성
        run: docker build -t ticket-flow .

      - name: Docker 이미지에 Tag 붙이기
        run: docker tag ticket-flow ${{ steps.login-ecr.outputs.registry }}/ticket-flow:latest

      - name: ECR에 Docker 이미지 Push하기
        run: docker push ${{ steps.login-ecr.outputs.registry }}/ticket-flow:latest

      - name: 압축하기
        run: tar -czvf $GITHUB_SHA.tar.gz appspec.yml scripts

      - name: S3에 프로젝트 폴더 업로드하기
        run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.tar.gz s3://ticket-flow/$GITHUB_SHA.tar.gz

      - name: Code Deploy를 활용해 EC2에 프로젝트 코드 배포
        run: aws deploy create-deployment
          --application-name ticket-flow
          --deployment-config-name CodeDeployDefault.AllAtOnce
          --deployment-group-name Production
          --s3-location bucket=ticket-flow,bundleType=tgz,key=$GITHUB_SHA.tar.gz

github actions에서 소스 코드 다운 및 테스트, 빌드하기

- name: Github Repository에 올린 파일을 불러온다.
  uses: actions/checkout@v4

- name: JDK 17버전 설치
  uses: actions/setup-java@v4
  with:
  	distribution: temurin
    java-version: 17

- name: 테스트 및 빌드하기
  run: ./gradlew clean build

github actions에서 먼저, repository의 소스 코드를 가지고 옵니다. 그리고 Java17을 설치를 해서 Java 환경을 만들어줍니다. 그리고 ./gradlew clean build 명령어를 통해서 테스트 및 빌드를 실행을 합니다.

AWS에 접근을 할 수 있도록 허용하기

- name: AWS Resource에 접근할 수 있게 AWS credentials 설정
  uses: aws-actions/configure-aws-credentials@v4
  with:
  	aws-region: ap-northeast-2
  	aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
  	aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

AWS의 서비스들을 사용을 하고 있기 때문에 AWS에 접근을 할 수 있는 권한이 필요하다. 그래서 AWS에서 발급을 받은 AWS에 키를 넣어서 준다. 여기서 중요한 부분은 이 두 키는 공개가 되면 안 되기 때문에 github repository의 secrets에 담아서 사용하거나 가려서 사용을 해야 한다.

AWS ECR에 로그인 후 이미지를 빌드하고 AWS ECR에 push하기

- name: ECR에 로그인하기
  id: login-ecr
  uses: aws-actions/amazon-ecr-login@v2

- name: Docker 이미지 생성
  run: docker build -t ticket-flow .

- name: Docker 이미지에 Tag 붙이기
  run: docker tag ticket-flow ${{ steps.login-ecr.outputs.registry }}/ticket-flow:latest

- name: ECR에 Docker 이미지 Push하기
  run: docker push ${{ steps.login-ecr.outputs.registry }}/ticket-flow:latest

지금의 프로젝트를 이미지로 빌드를 하고 AWS ECR에 올리기 위해서 로그인을 합니다. 그리고 이미지를 빌드를 하는 것을 AWS ECR에 push를 해줍니다.

소스 파일과 필요한 파일을 압축하고 AWS S3에 올리기

- name: 압축하기
  run: tar -czvf $GITHUB_SHA.tar.gz appspec.yml scripts

- name: S3에 프로젝트 폴더 업로드하기
  run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.tar.gz s3://ticket-flow/$GITHUB_SHA.tar.gz

먼저, 프로젝트 실행에 필요한 파일들을 모아서 압축을 한다. appspec.yml은 AWS CodeDeploy를 사용하기 위해서 필수적인 파일이다. appspec.yml은 S3에서 EC2로 옮길 대상 파일과 CodeDeploy가 실행을 할 때, 실행을 시킬 파일을 지정할 수 있다. 그리고 그 실행할 파일이 scripts이다. script는 실행 중인 컨테이너를 정지하고 새로운 이미지를 받아서 새롭게 컨테이너를 실행을 하는 명령어가 적혀있다. 이 압축한 파일을 S3에 업로드를 해준다.

AWS CodeDeploy에게 배포를 하라고 명령

- name: Code Deploy를 활용해 EC2에 프로젝트 코드 배포
  run: aws deploy create-deployment
	--application-name ticket-flow
	--deployment-config-name CodeDeployDefault.AllAtOnce
	--deployment-group-name Production
	--s3-location bucket=ticket-flow,bundleType=tgz,key=$GITHUB_SHA.tar.gz

AWS CodeDeploy에게 EC2에 프로젝트를 가져와서 배포하라는 명령어를 내리면 배포를 자동화하는 yml이 끝이 난다.

정리

이번 프로젝트를 진행을 하면서 CI/CD를 적용을 해보았습니다. 확실히 psuh나 pull Request를 통해서 자동 배포가 되니 따로 AWS에 들어갈 필요도 없고 수동으로 할 필요가 없어서 매우 편했고 효율성도 좋았습니다. 이번 포스트에서는 저는 어떻게 CI/CD를 적용을 했는지에 대해서 적어 보았습니다. github actions에서 빌드를 해서 이를 EC2에 넘겨서 실행하는 방법으로 시도를 해봤습니다. CI/CD를 구축할 때, 다양한 tool, 다양한 방법들이 있습니다. 각자가 좋다고 생각하는 방법으로 CI/CD를 구현을 해보셨으면 좋겠습니다.

profile
꾸준히!

0개의 댓글