Skip to main content Link Menu Expand (external link) Document Search Copy Copied

created at 2023-10-07

Table of contents

  1. 1. 기존의 CI/CD 플로우
  2. 2. “수직확장” 과 “리소스 할당 감소” 시 결과
  3. 3. 수평확장 플랜

본 포스팅은 제가 아래의 이슈에서 이야기한 내용을 정리한 글이에요.

CI/CD 자동화 성공 그리고 실패

기존에 CI/CD 플로우는 하나의 머신에 자동으로 업로드하는 플로우를 가지고 있습니다(기존의 CI/CD 방법). 이 방법은 매우 성공적으로 끝났어요. 모든게 잘 수행되었고 완벽히 자동화되어있었어요. 하지만 이 방법에는 큰 단점이 있었습니다. 만약 컴퓨팅 머신의 리소스가 과부화된다면 수직확장을 하거나 다른 방법을 찾아야했거든요.

그리고 우려하던 일이 실제로 발생했습니다 😅😅. 바로 제 머신인 AWS-EC2 t2.micro 의 CPU 사용률이 100% 를 찍어버렸기 때문이죠. 아래의 그래프처럼요!

1

그렇다면 어떻게 해결할 수 있을까요? 크게는 수직확장, 수평확장, 리소스 할당감소, 성능 향상 이 네 가지를 볼 수 있습니다. 저는 현재 수직확장과 리소스 할당 감소를 수행해보았으며, 수평확장을 수행하는 중입니다.

이제부터 아래의 순서에 맞춰 설명하겠습니다.

  1. 기존의 CI/CD 방법
  2. 수직확장과 리소스 할당 감소 시 결과
  3. 수평확장 플랜

1. 기존의 CI/CD 플로우

저는 통합배포과정을 Git-Actions + Gradle + Docker + Shell-Script + 기타 들로 수행합니다. 먼저 이해를 돕기위해 아래의 그림을 볼까요?

1

2

제가 표기한 번호에 맞추어서 각각의 과정을 설명할게요.

  1. 최신 태그버전 가져오기
    • Deploy 워크플로우가 최신 태그 버전을 가져옵니다.
  2. 버전 파싱 및 업데이트
    • 태그에서 “v” 접두사를 제거하고, major, minor, patch 버전을 분리합니다.
    • Pull-Request 제목과 일치하는 버전을 업데이트하고 이를 GitHub 에 푸시합니다.
    • 예를 들어, v5.0.1-abf2154이 최신 태그이고 PR 제목에 **[patch]**가 포함되어 있다면, 이 작업은 v5.0.2-b321ac 버전을 생성하여 GitHub에 푸시합니다. 새로운 태그 끝에는 언제나 커밋 해시를 추가합니다.
  3. 프로젝트 빌드
    • ./gradlew build --build-cache --parallel -Pversion=${new version} 명령을 실행하여 .jar 파일을 빌드합니다.
    • .jar 파일 이름 끝에 ${new version}을 추가합니다.
    • .jar 파일과 함께 Dockerfile 을 자동으로 생성합니다.

      예를 들어, v5.0.2-b321ac가 새 버전 태그라면, Gradle은 새 버전을 읽어들이고 **_v5.0.2-b321ac.jar를 빌드합니다. .jar 파일 빌드 이후에 Gradle 스크립트는 .jar 파일을 복사하여 Dockerfile 을 생성합니다 (예: COPY /build/libs/shop-user-service-v5.0.2-b321ac.jar /null/app.jar).

  4. Docker 이미지 빌드 및 ECR 푸시
    • 모든 Dockerfile 을 빌드하여 생성된 모든 이미지를 ECR에 푸시합니다.
    • AWS-ECR 은 여러 리포지토리를 만들 경우 상당히 비용이 들 수 있기 때문에 하나의 리포지토리만 생성하고, ${service_name}_${new_version}로 태그된 모든 이미지를 식별하기 위해 푸시합니다.
  5. EC2에서 이미지 풀
    • AWS-EC2에 액세스하여 AWS-ECR 에서 모든 이미지를 가져옵니다.
    • 모든 이미지를 가져올 때, 이미지 이름을 가져오고 접미사 이름을 필터링해야 합니다. 예를 들어, ECR에 A-service_v5.0.2, A-service_v5.0.1, B-service_v5.0.2, C-service_v5.0.2와 같은 이미지가 있다면, 각 이름을 _ 구분자로 분리하고 인덱스가 v5.0.2와 정확히 일치하는지 확인하여 for 루프를 사용하여 새 태그를 생성합니다.
  6. Docker Compose 배포
    • run.sh 를 실행하여 docker-compose-prod.yaml`를 백그라운드에서 실행합니다.
    • remove_dangling_image.sh 를 실행하여 dangling 된 이미지들을 제거합니다.

요약하면 아래와 같이 진행되요

  • 새로운 태그를 Git 에 푸쉬
  • 프로젝트 빌드
  • 이미지 빌드
  • AWS-ECR 에 이미지 푸쉬
  • AWS-EC2 에서 AWS-ECR 이미지 풀
  • 컴포즈로 컨테이너 일괄 실행

이렇게 기존에 “하나의 컴퓨팅 머신” 에 배포하는 CI/CD 워크플로우를 완성하고 동작을 확인했습니다.

Originally posted by @ghkdqhrbals in #78

이렇게 완성시키고 실제로 배포해보니… CPU 가 100% 를 찍어버렸습니다. 그래서 “수직확장” 을 하거나 다른 방법을 찾아야했죠.


2. “수직확장” 과 “리소스 할당 감소” 시 결과

그래서 저는 AWS-EC2 t2.micro 에서 AWS-EC2 t2.medium 으로의 “수직확장” 을 먼저 수행해보았습니다. 또한 Kafka 브로커를 한 대만 설정해서 장점 중 하나인 안전성을 버렸어요. 추가적으로 spring 내장 tomcat 의 스레드풀 사이즈들을 전부 낮추었습니다.

결과적으로 CPU 평균 usage 는 약 50% 를 보여주었어요. 이는 잘 된 일이죠! 하지만 불안하지 않나요? 만약 더 큰 리소스가 필요하다면? Kafka 브로커를 안전성을 위해 2n+1(n은 자연수) 으로 설정한다면? 만약 더 많은 서버가 필요하다면? 그럴 때마다 수직확장을 해야하나요? 끝까지 간다면 분명 리소스 확장 비용이 기하급수적으로 증가할텐데요😱.


3. 수평확장 플랜

그래서 저는 수직확장이 아닌 수평확장을 위해 여러 대의 컴퓨팅 머신에서 자동으로 일을 할당해줄 수 있는 Kubernetes 를 추가하여 아래와 같은 플랜을 세웠습니다.

  • 사전준비
    • AWS-EC2 t2.medium 인스턴스를 3대 준비합니다.
    • 쿠버네티스 및 도커를 설치합니다.
    • 하나를 master, 나머지를 worker 노드로 설정합니다.
  • 기존과 같은 플로우
    • 최신 태그버전 가져오기
    • 버전 파싱
    • 버전 업데이트
    • 프로젝트 빌드
    • Docker 이미지 빌드 및 ECR 푸시
  • 이 후 추가되는 새로운 플로우
    • AWS-EC2 Master 노드에서 git 을 pull 받습니다.
    • sed 명령어를 통해 *-deployment.yaml 파일들을 읽고 AWS-ECR 이 가지고 있는 최신 이미지로 PULL 할 이미지들을 교체합니다.

      ECR 의 tag:A-service_v5.0.1${ECR_URL}_A-service:latest 으로 yaml 파일을 변경합니다.

    • 이 후 namespace, PV, PVC, Service, Deploy, Pods 들을 순서대로 apply 합니다.

이러한 새로운 플로우는 test/only-chat, test/only-chat-cicd 브랜치에서 진행하고있어요. 아직 쿠버네티스는 익숙하지 않아서 시간이 걸리고 있습니다. 하지만 이 플로우가 완성되면, 수평확장이 가능한 CI/CD 플로우를 완성할 수 있을 것 같아요!

현재는 마지막 페이즈인 namespace, PV, PVC, Service, Deploy, Pods 들을 구현하고있어요!