Engineering Note

[SW Engineering] Spring Boot 배포 최적화 본문

SW Engineering

[SW Engineering] Spring Boot 배포 최적화

Software Engineer Kim 2025. 9. 18. 14:02

메모리 문제 해결 및 자동화 구축 보고서

개요

환경 정보

  • 플랫폼: Google Cloud Platform 프리티어
  • 서버 스펙: vCPU 2개, RAM 1GB, 디스크 30GB
  • 애플리케이션: Spring Boot + MySQL

초기 문제 상황

수동 배포 과정에서 서버 메모리 부족으로 인한 빌드 실패 및 성능 저하 발생


문제 분석

1. 메모리 사용량 실측 데이터

서버 메모리 현황 분석

# 실제 측정 결과 (top 명령어 출력)
mysqld: 249MB (26.0% 사용)
기타 시스템 프로세스: 262MB
총 사용량: 511MB

가용 메모리 계산

VM 총 메모리: 958MB (1GB 중 실제 사용 가능)
시스템 기본 사용량: 511MB
실제 가용 메모리: 447MB

2. Maven 빌드 메모리 요구량 측정

스왑 메모리 사용량 분석

# 빌드 중 스왑 사용량 (swapon -s 결과)
총 스왑: 1,024MB
사용량: 278MB (27.1%)

Maven 빌드 실제 메모리 소비

가용 메모리: 447MB
빌드 필요량: 278MB (스왑 사용량으로 측정)
이론상 여유: 169MB

3. 성능 저하 원인 분석

메모리 압박 상황

  • 가용률: 447MB ÷ 958MB = 46.7%
  • 빌드 요구율: 278MB ÷ 447MB = 62.2%
  • 총 사용률: (511MB + 278MB) ÷ 958MB = 82.3%

실제 발생한 문제

  • OutOfMemory 오류: 발생하지 않음
  • 실제 증상: 빌드 프로세스 무응답 및 시스템 전체 응답 지연
  • 원인: 메모리 압박으로 인한 스래싱(Thrashing) 현상

해결 방안 및 구현

1. 스왑 메모리 추가

구현 내용

sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

스왑 크기 결정 근거

고려사항 수치 근거

최소 필요량 278MB 실측된 빌드 메모리 요구량
안전 마진 300MB 시스템 안정성 확보
디스크 사용률 3.3% 30GB 중 1GB 사용 (적정 수준)
성능 고려 1GB 과도한 스왑 사용 방지

효과 검증

빌드 전 가용 메모리: 447MB
스왑 추가 후 총 가용: 1,471MB (447MB + 1,024MB)
메모리 압박 해소: 82.3% → 53.6%

2. GitHub Actions 자동화 구축

기존 수동 배포 프로세스

1. 서버 SSH 접속 (30초)
2. Git pull (1분)
3. Maven 빌드 (3분, 메모리 부족으로 지연)
4. 애플리케이션 재시작 (30초)
총 소요시간: 5분

자동화 배포 프로세스

# GitHub Actions Workflow
1. 코드 체크아웃 (10초)
2. JDK 17 설정 (10초)
3. Maven 빌드 (GitHub 서버, 30초)
4. JAR 파일 서버 전송 (20초)
5. 애플리케이션 재시작 (10초)
총 소요시간: 1분 20초

핵심 개선사항

  • 빌드 환경 분리: GitHub 고성능 서버에서 빌드 수행
  • 서버 부하 경감: 실행만 담당하여 메모리 사용량 최소화
  • 자동화: git push 트리거로 전체 과정 자동 실행

성능 개선 결과

1. 배포 시간 개선

구분 수동 배포 자동 배포 개선율

총 소요시간 5분 1분 20초 73.3% 단축
사람 개입시간 5분 0분 100% 자동화
빌드 실패율 높음 0% 안정성 확보

2. 시스템 리소스 최적화

항목 개선 전 개선 후 효과

서버 메모리 압박 82.3% 53.6% 압박 해소
빌드 성공률 불안정 100% 안정성 확보
서버 다운타임 빈번 없음 서비스 안정성

3. 메모리 사용 패턴 개선

개선 전: 물리 메모리만 사용 → 빌드 실패/지연
개선 후: 물리 메모리 + 스왑 → 안정적 실행

실측 데이터:
- 빌드 시 스왑 사용: 278MB
- 빌드 완료 시간: 24.488초 (안정적)
- 시스템 무응답: 해결


기술적 구현 세부사항

1. 메모리 모니터링 시스템

# 실시간 메모리 사용량 추적
while true; do
  timestamp=$(date '+%H:%M:%S')
  used_mem=$(free -m | grep Mem | awk '{print $3}')
  available_mem=$(free -m | grep Mem | awk '{print $7}')
  swap_used=$(free -m | grep Swap | awk '{print $3}')
  echo "$timestamp - Used: ${used_mem}MB Available: ${available_mem}MB Swap: ${swap_used}MB"
  sleep 2
done

2. GitHub Actions 최적화 설정

# 메모리 제한된 환경에서의 JVM 최적화
script: |
  nohup java -Xmx400m -Xms200m -jar shop-0.0.1-SNAPSHOT.jar > app.log 2>&1 &

3. 성능 모니터링 지표

  • 빌드 성공률: 100%
  • 평균 배포 시간: 1분 20초
  • 서버 안정성: 무응답 현상 해결
  • 메모리 사용 효율성: 스래싱 현상 제거

핵심 성과 및 교훈

1. 문제 해결 접근법

  • 데이터 기반 분석: 실제 메모리 사용량 측정을 통한 정확한 문제 진단
  • 근본 원인 해결: 임시방편이 아닌 구조적 개선
  • 단계별 검증: 각 해결책의 효과를 개별적으로 검증

2. 리소스 최적화 전략

  • 하이브리드 메모리 전략: 물리 메모리 + 스왑 메모리 조합 활용
  • 작업 분산: 리소스 집약적 작업을 외부 환경으로 분리
  • 자동화를 통한 효율성: 인적 리소스 절약 및 일관성 확보

3. 확장 가능성

  • 무중단 배포 기반 마련: Blue-Green 배포 도입 가능
  • 모니터링 시스템 구축: 성능 지표 지속 추적 체계
  • 클라우드 네이티브 전환: Docker, Kubernetes 도입 기반

ROI (투자 대비 효과) 분석

투자 비용

  • 초기 설정 시간: 4시간
  • 학습 비용: GitHub Actions, 스왑 메모리 개념 학습
  • 추가 리소스: 1GB 디스크 공간 (월 $0.04)

절약 효과

  • 배포 시간 단축: 배포당 3분 40초 절약
  • 배포 빈도: 주 2회 × 월 4주 = 월 8회
  • 월간 시간 절약: 29분 20초
  • 연간 효과: 352분 (약 6시간)

정성적 효과

  • 배포 스트레스 제거: 실패 위험 없는 안정적 배포
  • 개발 집중도 향상: 배포 작업에서 해방
  • 서비스 안정성: 서버 다운타임 제거

결론 및 향후 계획

이 프로젝트를 통해 제한된 리소스 환경에서도 체계적인 분석과 최적화를 통해 상당한 성능 개선을 달성할 수 있음을 입증했습니다. 특히 실제 데이터 측정을 바탕으로 한 문제 진단과 해결책 검증이 핵심 성공 요인이었습니다.

향후 개선 방향

  1. 모니터링 자동화: 메모리 사용량 알림 시스템 구축
  2. 성능 최적화: 애플리케이션 레벨 메모리 사용량 최적화
  3. 인프라 업그레이드: 비용 대비 효과 분석 후 메모리 증설 검토

이번 경험은 클라우드 환경에서의 리소스 최적화와 DevOps 자동화 구축에 대한 실질적인 노하우를 제공하며, 향후 유사한 프로젝트의 기반이 될 것입니다.


사용할 리눅스 명령어 정리 

top 명령어

실시간 프로세스 모니터링

top -o %MEM  # 메모리 사용량 순으로 정렬

제공 정보:

  • PID: 프로세스 ID
  • %CPU: CPU 사용률
  • %MEM: 메모리 사용률
  • RES: 실제 물리 메모리 사용량 (KB)
  • VIRT: 가상 메모리 사용량
  • 실시간 업데이트: 2-3초마다 자동 갱신

우리 프로젝트에서의 활용: MySQL이 249MB(26%) 사용 중인 것을 확인하여 시스템 기본 부하 측정

free 명령어

전체 시스템 메모리 현황

free -h  # 사람이 읽기 쉬운 단위로 표시

제공 정보:

  • total: 총 메모리 (958MB)
  • used: 사용 중 메모리 (511MB)
  • free: 완전히 비어있는 메모리
  • available: 실제 사용 가능한 메모리 (447MB)
  • swap: 스왑 메모리 사용량

우리 프로젝트에서의 활용: 빌드 전후 메모리 사용량 변화 추적, 스왑 사용량 278MB 측정

ps aux 명령어

모든 프로세스의 상세 정보

ps aux --sort=-%mem  # 메모리 사용량 내림차순 정렬

제공 정보:

  • USER: 프로세스 소유자
  • %CPU: CPU 사용률
  • %MEM: 메모리 사용률
  • RSS: 실제 메모리 사용량 (KB)
  • COMMAND: 실행 명령어

우리 프로젝트에서의 활용: Maven/Java 프로세스 실행 여부 확인, 개별 프로세스별 메모리 소비량 분석

실제 측정에서의 역할

문제 진단 과정:

  1. free -h: 전체 메모리 상황 파악 (447MB 가용)
  2. top: 주요 메모리 소비자 식별 (MySQL 249MB)
  3. ps aux: Maven 빌드 프로세스 추적 및 실패 확인

 

 

스크립트 를 통한 메모리 분석 :

실시간 메모리 추적 실행 스크립트

# 실시간 메모리 사용량 추적
while true; do
  timestamp=$(date '+%H:%M:%S')
  used_mem=$(free -m | grep Mem | awk '{print $3}')
  available_mem=$(free -m | grep Mem | awk '{print $7}')
  swap_used=$(free -m | grep Swap | awk '{print $3}')
  echo "$timestamp - Used: ${used_mem}MB Available: ${available_mem}MB Swap: ${swap_used}MB"
  sleep 2
done

 

1. 무한 루프 구조

while true; do
  # 반복 실행
done

2. 데이터 수집 과정

timestamp=$(date '+%H:%M:%S')           # 현재 시간 기록
used_mem=$(free -m | grep Mem | awk '{print $3}')      # 사용 중 메모리
available_mem=$(free -m | grep Mem | awk '{print $7}') # 가용 메모리  
swap_used=$(free -m | grep Swap | awk '{print $3}')    # 스왑 사용량

3. 출력 형식

04:31:27 - Used: 583MB Available: 374MB Swap: 0MB
04:31:29 - Used: 583MB Available: 374MB Swap: 0MB

4. 2초 간격 측정

sleep 2  # 2초 대기 후 다시 측정

프로젝트에서의 활용 목적:

Maven 빌드 중 메모리 사용 패턴 추적

  • 빌드 시작 전: 기준선 측정
  • 빌드 진행 중: 메모리 사용량 변화 관찰
  • 스왑 사용량: 실제 메모리 부족량 측정

핵심 측정 데이터:

  • Used: 511MB → 물리 메모리 사용량
  • Swap: 278MB → 추가로 필요했던 메모리량

이 스크립트로 "Maven 빌드가 278MB 추가 메모리를 필요로 한다"는 정량적 근거를 확보할 수 있었습니다.

Comments