Cloud 과제 1단계: Big Bang 방식 수작업 배포 - 100-hours-a-week/5-yeosa-wiki GitHub Wiki
이 문서는 <온기> 서비스의 클라우드 초기 단계에서 적요되는 Bing Bang 방식 수작업 배포 방식을 정리한 것입니다.
<온기>는 프로젝트 초기 단계로 사용자 수가 적고 배포 빈도도 낮아 자동화 도입보다는 수작업이 빠르다고 판단했습니다.
단일 GCP 인스턴스에서 운영되므로, 단순한 구조에서 빠르게 배포하고 검증할 수 있는 점이 현재 상황에 적합했습니다.
다음은 서비스 동작방식, MVP 아키텍쳐, 배포 프로세스 설명 그리고 Bing Bang 배포의 한계점과 개선점을 정리하였습니다.
<온기> 서비스는 사용자 요청에 따라 이미지 업로드, AI 분류, 태그 저장 및 앨범 생성까지의 흐름을 단일 GCP 인스턴스 기반으로 처리하고 있으며, 전체 서비스는 다음과 같은 방식으로 동작합니다.
- 사용자는 React 기반 프론트엔드를 통해 이미지를 업로드합니다.
- 이미지 파일은 Presigned URL을 통해 Google Cloud Storage(GCS)에 직접 저장됩니다.
- 업로드 완료 후, Spring Boot 백엔드 서버는 해당 이미지들의 경로와 앨범 정보를 AI 서버에 전달하고, AI는 이미지 내용을 분석하여 태그별로 분류합니다.
- AI 서버는 분류된 태그 정보를 백엔드에 반환하고, 백엔드는 이를 MySQL에 저장하여 앨범과 태그 정보로 활용합니다.
- 프론트엔드는 다시 사용자가 요청할 때 저장된 태그 기반으로 이미지를 불러오고, 앨범 내 사진 추가 시에는 전체 이미지를 기반으로 다시 분류가 진행됩니다.
이러한 전체 흐름은 React → Nginx → Spring Boot → AI 서버 → GCS/MySQL로 이어지는 단방향 또는 피드백 기반의 구성입니다.
[GCP Project]
└── VPC: ongi-mvp-vpc (10.0.0.0/20)
└── Subnet: asia-northeast3 (10.178.0.0/20)
└── VM1: Nginx, React, Spring Boot, MySQL
└── VM2: AI CPU 인스턴스
└── VM3: AI GPU 인스턴스
- Google Cloud Platform의 인스턴스를 활용
- 단일 인스턴스 + AI(CPU,GPU서버) 총 3개 인스턴스 사용
- 단일 인스턴스 nginx(proxy server)-front-back-db 구축
- AI 모델이 들어갈 GPU 인스턴스
- 정적 파일 Presigned URL로 S3에 저장 후 서버에서 호출
- 현재 서비스 웹의 모든 구성 요소(frontend, backend, DB)는 동일 인스턴스 내부에서 동작하고, AI 모델은 총 3개로 CPU 인스턴스에서 2개, GPU인스턴스에서 1개의 모델을 처리
구성 요소 | 역할 | 머신 타입 | 사양 | 예상 월 요금 |
---|---|---|---|---|
VM1 | Web 서버 (Nginx + React + Spring Boot) | e2-standard-4 |
4vCPU / 16GB RAM | 약 $48.40 |
VM2 | AI CPU 서버 | e2-standard-4 |
4vCPU / 16GB RAM | 약 $48.40 |
VM3 | AI GPU 서버 | n1-standard-4 + NVIDIA T4 |
4vCPU / 15GB RAM + 1 GPU | 약 $247.92 |
GCS | 사용자 이미지 저장소 (Presigned URL 방식 사용) | - | - | 약 $5.2 (약 200GB 기준) |
간단한 구조도: 단일 GCP VM
→ nginx → frontend → backend → DB
-> AI
구성 요약:
-
Frontend: React (정적 빌드 파일) → Nginx 통해
/
에서 서비스 - Backend: Spring Boot (8080 포트) → MySQL, GCS, AI 서버 연동
-
AI 서버:
- 썸네일 생성 인스턴스
- 이미지 분류 인스턴스
- Cloud Storage (GCS): 이미지 저장
- MySQL: 메타데이터 저장
온기 서비스는 총 3개의 AI 모델을 사용하고 있으며, 이 중 2개는 CPU에서, 1개는 GPU에서 실행됩니다.
특히 CPU 기반 AI 모델들은 동시 다중 이미지 처리를 위해 CPU 전체 코어(400%)를 점유하며 작동하기 때문에,
Web 서버(Spring Boot, React 등)와 동일한 인스턴스에서 실행할 경우 자원 충돌로 인해 웹 서버가 다운될 위험이 매우 높습니다.
이에 따라 아래와 같은 구조적 분리를 적용하였습니다.
-
CPU 모델 분리: CPU 리소스를 많이 사용하는 AI 모델 2개는 전용
e2-standard-4
서버에서 독립 실행 - GPU 모델 분리: 대형 AI 모델은 별도의 GPU 인스턴스에서 운영
- 웹 서비스와 분리: Web 서버는 사용자 요청 처리 및 API 응답을 안정적으로 유지하기 위해 AI와 분리
이렇게 역할 기반으로 인스턴스를 분리함으로써, 각 구성 요소의 성능을 최대한 활용하고
서비스 안정성과 확장 가능성을 동시에 확보할 수 있습니다.
/etc/nginx/sites-available/ongi.conf
server {
listen 80;
server_name localhost;
# React 정적 파일 제공
root /var/www/html;
index index.html index.htm;
location / {
try_files $uri /index.html;
}
# Spring Boot API 프록시
location /api/ {
proxy_pass http://localhost:8080/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 이미지 등 정적 리소스 요청
location /static/ {
alias /var/www/html/static/;
}
}
단계 | 작업 내용 | 도구/명령어 | 예상 시간 | 담당자 |
---|---|---|---|---|
1 | 기존 프로세스 중지 |
sudo kill -9 <PID> 또는 pkill -f jar
|
1분 | |
2 | 프로젝트 디렉토리 이동 및 최신 코드 Pull | cd ~/5-yeosa-ongi-be && git pull origin main |
1분 | |
3 | Spring Boot 빌드 | ./gradlew clean build |
1~2분 | |
4 | jar 실행 | java -jar build/libs/ongi-server-0.0.1-SNAPSHOT.jar & |
1분 | |
5 | React 빌드 | cd ~/5-yeosa-ongi-fe && npm run build |
1~2분 | |
6 | 빌드된 프론트엔드 파일을 Nginx 디렉토리로 복사 | cp -r build/* /var/www/frontend/ |
1분 | |
7 | Nginx 재시작 | sudo systemctl restart nginx |
1분 | |
8 | 배포 확인 | 브라우저 접속 후 테스트 | 1~2분 |
⏱️ 총 소요 시간 약 8~10분
- GCP 인스턴스에 SSH 접속 가능한 상태인지 확인
- GitHub 접근을 위한 SSH Key 또는 Personal Access Token 등록 여부 확인
- MySQL DB, GCS, AI 서버 등 외부 연동 서비스 정상 작동 여부 확인
- GCP 인스턴스에 SSH 접속
- Spring Boot 프로젝트 디렉토리로 이동 후
git pull
로 최신 코드 반영 -
./gradlew clean build
로 jar 파일 빌드 - 기존 프로세스 종료 후 새 jar 실행
- 프론트엔드 디렉토리로 이동해
npm run build
- 빌드된 정적 파일을 Nginx 디렉토리에 복사
- Nginx 재시작 후 서비스 정상 여부 확인
#!/bin/bash
# 변수 정의
APP_DIR="/home/ubuntu/ongi"
FRONT_DIR="$APP_DIR/frontend"
BACK_DIR="$APP_DIR/backend"
JAR_NAME="ongi-server-0.0.1-SNAPSHOT.jar"
NGINX_HTML_DIR="/var/www/html"
BACKUP_DIR="/home/ubuntu/backup/$(date +'%Y%m%d-%H%M')"
# 백업 과정
mkdir -p $BACKUP_DIR/html
mkdir -p $BACKUP_DIR/backend
# 백엔드 백업
if [ -f "$JAR_PATH" ]; then
cp $JAR_PATH $BACKUP_DIR/backend/
fi
if [ -d "$NGINX_HTML_DIR" ]; then
cp -r $NGINX_HTML_DIR/* $BACKUP_DIR/html/
fi
# 1. 기존 백엔드 프로세스 종료
pkill -f $JAR_NAME
# 2. 코드 최신화
cd $APP_DIR || exit
git reset --hard HEAD
git pull origin main
# 3. 백엔드 빌드 및 실행
cd $BACK_DIR || exit
./gradlew clean build
# 백엔드 애플리케이션 실행
nohup java -jar build/libs/$JAR_NAME > logs/backend.log 2>&1 &
# 4. 프론트엔드 빌드
cd $FRONT_DIR || exit
npm install
npm run build
# 5. Nginx 경로로 프론트엔드 파일 복사
sudo rm -rf $NGINX_HTML_DIR/*
sudo cp -r build/* $NGINX_HTML_DIR/
# 6. Nginx 재시작
sudo systemctl restart nginx
# 7. 완료 메시지
echo "배포 완료"
사용방법
-
GCP 인스턴스에 저장 (예:
/home/ubuntu/deploy.sh
) -
실행 권한 부여
chmod +x deploy.sh
-
배포할 때마다 실행
./deploy.sh
서비스 배포 중 문제가 발생했을 경우, 이전 정상 버전으로 빠르게 되돌릴 수 있는 절차입니다. <온기> 서비스는 VM환경이므로 jar파일과 정적 파일(build 디렉토리)의 백업/복구를 통해 롤백합니다.
수동 롤백 전략 (배포 전 백업)
# 백업 디렉토리 생성
mkdir -p ~/backup/$(date +"%Y%m%d-%H%M")
# jar 및 빌드 파일 백업
cp ~/ongi/backend/build/libs/on.jar ~/backup/$(date +"%Y%m%d-%H%M")/
cp -r /var/www/html ~/backup/$(date +"%Y%m%d-%H%M")/html
단계 | 설명 | 명령어 예시 |
---|---|---|
1 | 현재 애플리케이션 종료 | pkill -f ongi-server-0.0.1-SNAPSHOT.jar |
2 | 백업된 jar 복원 | cp ~/backup/20250417-1125/ongi-server-0.0.1-SNAPSHOT.jar ~/ongi/backend/build/libs/ |
3 | 이전 버전 jar 실행 | nohup java -jar ~/ongi/backend/build/libs/ongi-server-0.0.1-SNAPSHOT.jar & |
4 | 프론트엔드 정적 파일 롤백 | sudo rm -rf /var/www/html/* && sudo cp -r ~/backup/20250417-1125/html/* /var/www/html/ |
5 | Nginx 재시작 | sudo systemctl restart nginx |
6 | 롤백 완료 확인 | 브라우저에서 서비스 정상 작동 여부 확인 |
단계 | 체크 항목 | 체크 |
---|---|---|
1. 서버 접속 및 권한 확인 | GCP VM 인스턴스에 SSH 접속 가능한지 확인 (ssh ubuntu@<IP> ) |
[ ] |
배포 스크립트에 실행 권한 부여 완료 (chmod +x deploy.sh ) |
[ ] | |
GitHub 접근을 위한 SSH Key 또는 Personal Access Token 설정 완료 | [ ] | |
sudo 명령어 실행 가능 여부 확인 (Nginx 재시작 등) | [ ] | |
2. 코드 및 환경 점검 | 백엔드 디렉토리에서 git pull origin main 으로 최신 코드 반영 |
[ ] |
프론트엔드 디렉토리에서도 최신 코드 반영 | [ ] | |
백엔드 빌드 (./gradlew clean build ) 성공 여부 확인 |
[ ] | |
프론트엔드 빌드 (npm install , npm run build ) 성공 여부 확인 |
[ ] | |
3. 외부 서비스 연동 확인 | MySQL 서버 정상 작동 및 연결 여부 확인 | [ ] |
GCS Bucket에 Presigned URL로 이미지 업로드 테스트 완료 | [ ] | |
AI 서버(CPU/GPU 인스턴스)와 통신 가능 여부 확인 | [ ] | |
4. 배포 준비 상태 확인 | 기존 JAR 파일 및 프론트 정적 파일 백업 경로 및 복사 완료 | [ ] |
Nginx 설정 포트 및 프록시 경로가 현재 인스턴스에 맞게 작성되어 있는지 확인 | [ ] | |
deploy.sh 파일 내 경로(backend, frontend, jar 등) 정확성 확인 |
-
단일 인스턴스 의존: 한 인스턴스에 컴포넌트가 밀집되어있어서 장애발생시 전체 서비스 중단
→ 계층 분리: 프론트엔드, 백엔드, DB, AI를 별도 인스턴스로 분리하여 장애 전파 최소화 -
수작업 오류 위험: 잘못된 파일 복사, 포트 충돌 등
→ CI/CD자동화: GitHub Actions를 통한 자동화로 수동 실수 방지 및 일관된 배포 보장 -
롤백 어려움: 배포 실패 시 빠르게 되돌릴 수 있는 구조가 없음
→ 무중단 배포 도입: Blue-Green 방식으로 새로운 버전 배포 후 정상 확인 시 트래픽 전환 → 문제 시 즉시 롤백 가능 -
확장성 부족: 멀티 인스턴스 운영 시 복잡도 상승
→ 컨테이너 기반 구조 또는 멀티 인스턴스로 확장 용이하게 설계 -
다운타임: 약 2~3분
→ 무중단 배포 도입: 서비스 중단 없이 신규 버전 전환 가능 (Nginx 라우팅)