Cloud 과제 3단계: CD(지속적 배포) 파이프라인 구축 - 100-hours-a-week/5-yeosa-wiki GitHub Wiki
1. 업무 개요
업무 제목: CD 파이프라인 구축
업무 설명:
본 업무는 CI 파이프라인의 결과물(build artifact) 을 실제 서버에 자동으로 배포하는 CD(지속적 배포) 환경을 구축하는 것입니다.
개발 완료된 기능을 운영 서버에 신속하고 안전하게 배포함으로써, 릴리즈 속도와 서비스 품질을 동시에 확보하는 것이 목표입니다.
이 단계는 DevOps 환경 구축의 3단계에 해당하며, 실제 사용자에게 전달되는 릴리즈 흐름의 자동화를 구현하는 핵심 작업입니다.
2. CD 도입 필요성
실무 관점
문제 | CD 도입 전 | CD 도입 후 |
---|---|---|
배포 소요 시간 | 수작업으로 30분 이상 | 자동화로 5~10분 |
배포 시 실수 위험 | 파일 누락, 포트 충돌 | 스크립트 자동화로 최소화 |
사용자 경험 | 다운타임 존재 | 무중단 배포 가능 (Blue-Green) |
3. 설계 과제
3-1. CD 적용 범위 및 방식 결정
- 적용 범위:
- Prod 서버 승인 후 수동 배포 → Continuous Delivery 방식
- 배포 전략:
- Blue-Green 배포 (VM 기반) 선택
- 무중단 배포 가능, 실패 시 Nginx 포트 스위칭만으로 신속 복구 가능
- 선택 이유:
- 단일 인스턴스 기반 구조에 적합하며, 다운타임 없이 안정적인 운영 가능
3-2. CD 파이프라인 설계
- 🔁 전체 흐름
- GitHub
main
브랜치에 PR이 머지되면 GitHub Actions가 자동 트리거 - SSH를 통해 원격 VM에 접속
- 기존 jar 및 프론트 빌드 파일 백업 (자동)
- Git Pull 후 새 버전 빌드 & 실행
- 프론트는 Nginx 정적 디렉토리에 복사
- Nginx 재시작 → 트래픽 스위칭
- 문제 발생 시 백업 파일로 수동 롤백
- 배포 스크립트 사용이유
- 서버 내부 환경에 최적화된 로직
- 온기 서비스는 단일 GCP VM 기반의 배포 구조로, 백엔드 실행(JAR), 프론트 파일 복사, Nginx 설정 변경 등 서버 내부에서만 수행 가능한 작업들이 존재함
- 이를 GitHub Actions 워크플로우 내부에서 모두 작성하면 복잡도가 증가하며, 서버 환경 변경에 유연하게 대응하기 어려움
- 배포 스크립트는 이러한 작업들을 서버 환경에 맞게 통합하여 구성함으로써 실행 안정성과 운영 유연성을 확보할 수 있음
- 로컬 상태 기반 동작
- GitHub Actions는 외부 실행 환경으로, 서버 내 현재 상태(예: 실행 중인 포트, 기존 jar 백업, Nginx 설정 상태 등)를 실시간으로 파악하기 어려움
- 반면 배포 스크립트는 서버 내에서 동작하기 때문에, 실행 중인 프로세스 확인, 포트 충돌 방지, 백업 파일 자동 생성, 롤백 처리 등 현실적인 운영 흐름을 반영할 수 있음
- 보안 관리
- 배포 스크립트를 사용하면 GitHub에는 SSH 키만 저장하고, 모든 민감한 로직은 서버 내부에 유지함으로써 보안 리스크를 최소화할 수 있음
3-3. 파이프라인 구성 명세
- 트리거 조건
이벤트 | 대상 브랜치 | 처리 방식 |
---|---|---|
Push | main |
운영용 CD 트리거 |
Pull Request | main |
병합 승인 후 CD 트리거 |
- 주요 구성 요소
항목 | 도구 | 설명 |
---|---|---|
원격 배포 | SSH, Git | VM에 SSH 접속 후 Git Pull & 실행 |
백업 | Shell Script | 배포 전 jar, 프론트 build 자동 백업 |
프론트 배포 | Nginx | /var/www/html 로 복사 후 reload |
백엔드 배포 | Java | 기존 프로세스 종료 후 jar 재실행 |
롤백 | 수동 | 백업 디렉토리에서 jar & build 복원 |
알림 | 선택 | Discord/Webhook 연동 가능 |
- 무중단 배포 도입 (Blue-Green Deployment)
초기 Big Bang 배포 방식은 배포 시 약 2~3분의 다운타임이 발생하여 사용자 경험에 영향을 주었습니다.
따라서 트래픽만 전환하는 Blue-Green 배포 전략으로 안정적인 무중단 배포를 도입하였습니다.
Blue-Green 배포 는 새로운 애플리케이션 버전을 배포할 때, 기존 버전(Blue)과 새 버전(Green)을
동시에 실행한 뒤, 새 버전이 정상적으로 동작하는 것을 확인한 후에 트래픽을 전환하는 무중단 배포 전략입니다.
- 백엔드 Blue-Green 배포 전략
- 포트 분리: 8081, 8082 두 포트를 번갈아 사용하여, 기존의 서비스를 유지한 채 새 버전 실행
- 동시 기동: 새 포트로 Spring Boot 서버를 실행하면서 기존 프로세스는 그대로 유지
- 헬스 체크: 새 버전
/health
엔드포인트에 curl 요청으로 정상 여부 확인 - 트래픽 전환: Nginx 설정 파일의 프록시 포트를 기존 → 새 포트로 변경 후
reload
- 롤백 처리: 헬스체크 실패 시, 새 포트의 프로세스를 종료하고 이전 서비스 유지
- 프론트엔드 Blue-Green 배포 전략
-
현재 사용 중인 디렉토리 확인:
/var/www/current_front
파일을 읽어서 현재 사용 중인 디렉토리(blue
또는green
) 확인 -
반대편 디렉토리 결정: 현재가
blue
면green
을, 현재가green
이면blue
를 새 디렉토리로 지정 -
프론트엔드 프로젝트 빌드:
npm install
,npm run build
명령으로 새 정적 파일 생성 -
새 디렉토리에 정적 파일 복사:
build/*
파일을/var/www/html-green
또는/var/www/html-blue
에 복사, 기존 파일은 삭제 후 새 파일 복사 -
Nginx 설정 파일 수정:
/etc/nginx/sites-available/ongi.conf
의root
경로를 새 디렉토리로 변경예:
root /var/www/html-blue;
→root /var/www/html-green;
-
현재 디렉토리 상태 파일 갱신:
/var/www/current_front
파일을 새 디렉토리 이름으로 덮어씀 -
Nginx 재시작:
sudo systemctl reload nginx
명령으로 설정 반영
- Blue-Green 배포 Back 스크립트
#!/bin/bash
APP_NAME="ongi-server"
JAR_NAME="ongi-server-0.0.1.jar"
JAR_PATH="./build/libs/$JAR_NAME"
PORT1=8081
PORT2=8082
LOG_DIR="./logs"
# 로그 디렉토리 생성
mkdir -p $LOG_DIR
# 최초 배포 감지: 두 포트 모두 비어있을 경우
if ! lsof -i :$PORT1 | grep LISTEN && ! lsof -i :$PORT2 | grep LISTEN; then
echo "최초 배포 감지 → 8081, 8082 모두 실행"
nohup java -jar $JAR_PATH --server.port=$PORT1 > $LOG_DIR/$APP_NAME-$PORT1.log 2>&1 &
sleep 5
nohup java -jar $JAR_PATH --server.port=$PORT2 > $LOG_DIR/$APP_NAME-$PORT2.log 2>&1 &
echo "8081, 8082 실행 완료 (최초 배포)"
exit 0
fi
# 실행 중인 포트 감지
if lsof -i :$PORT1 | grep LISTEN >/dev/null; then
OLD_PORT=$PORT1
NEW_PORT=$PORT2
else
OLD_PORT=$PORT2
NEW_PORT=$PORT1
fi
echo "현재 실행 중인 포트: $OLD_PORT → 새 포트: $NEW_PORT"
# 새 버전 실행
nohup java -jar $JAR_PATH --server.port=$NEW_PORT > $LOG_DIR/$APP_NAME-$NEW_PORT.log 2>&1 &
sleep 10
# 헬스 체크
if curl -s http://localhost:$NEW_PORT/healthz | grep OK; then
echo "새 버전 정상 작동 확인됨"
# Nginx 포트 전환
sudo sed -i "s/$OLD_PORT/$NEW_PORT/g" /etc/nginx/sites-available/ongi.conf
sudo systemctl reload nginx
echo "Nginx 프록시 전환 완료"
# 이전 버전 종료
OLD_PID=$(lsof -ti tcp:$OLD_PORT)
if [ ! -z "$OLD_PID" ]; then
kill -9 $OLD_PID
echo "이전 포트 $OLD_PORT 프로세스 종료 완료"
fi
else
echo "헬스체크 실패 → 롤백 수행"
NEW_PID=$(lsof -ti tcp:$NEW_PORT)
[ ! -z "$NEW_PID" ] && kill -9 $NEW_PID
exit 1
fi
- Blue-Green 배포 Front 스크립트
#!/bin/bash
# 디렉토리 설정
FRONT_DIR="/home/ubuntu/ongi/frontend"
CURRENT_FILE="/var/www/current_front"
BLUE_DIR="/var/www/html-blue"
GREEN_DIR="/var/www/html-green"
# 현재 사용 중인 디렉토리 확인
CURRENT=$(cat $CURRENT_FILE 2>/dev/null || echo "blue")
# 새로 사용할 디렉토리 결정
if [ "$CURRENT" == "blue" ]; then
NEW="green"
TARGET_DIR=$GREEN_DIR
else
NEW="blue"
TARGET_DIR=$BLUE_DIR
fi
echo "현재 서빙 디렉토리: $CURRENT → 새 디렉토리: $NEW"
# 프론트 빌드
echo "프론트 빌드 시작"
cd $FRONT_DIR || exit 1
npm install
npm run build
# 정적 파일 복사
echo "정적 파일을 $TARGET_DIR 로 복사 중"
sudo rm -rf $TARGET_DIR/*
sudo cp -r build/* $TARGET_DIR/
# Nginx 설정 변경
echo "Nginx 설정 변경 중"
NGINX_CONF="/etc/nginx/sites-available/ongi.conf"
sudo sed -i "s/html-$CURRENT/html-$NEW/g" $NGINX_CONF
# 현재 상태 저장
echo "$NEW" | sudo tee $CURRENT_FILE
# Nginx reload
echo "Nginx 재시작"
sudo systemctl reload nginx
echo "프론트 Blue-Green 무중단 배포 완료"
/var/www/html-blue
,/var/www/html-green
두 디렉토리를 미리 생성/var/www/current_front
파일은 초기값으로blue
또는green
을 넣어두면 됨- Nginx 설정 파일에서
root /var/www/html-blue;
혹은html-green;
중 하나로 시작하도록 작성되어 있어야 함
- nginx 설정 파일 /etc/nginx/sites-available/ongi.conf
React로 빌드된 정적 파일을 Nginx가 /
경로에서 서빙
Spring Boot 서버의 API 요청(/api/
)은 Nginx가 백엔드 포트(8081
또는 8082
)로 프록시
배포 시 Blue-Green 전략에 따라 Nginx 프록시 포트를 자동 전환하여 무중단 배포 구현
server {
listen 80;
server_name localhost;
root /var/www/html-blue; # ← 초기값 (스크립트에서 html-blue ↔ html-green 전환)
index index.html;
location / {
try_files $uri /index.html;
}
location /api/ {
proxy_pass http://localhost:8081/api/; # ← backend도 Blue-Green 구조 사용 시 8081 ↔ 8082 전환
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;
}
}
4. CD Architecture
- CI 과정을 통해 생성된 build 파일을 배포에 활용합니다.
- Dev 또는 Prod 브랜치에 Pull Request가 생성되면, 자동으로 빌드가 시작됩니다.
- Build 파일을 배포 서버(Green)에 전송 및 실행
- Nginx가 Green 서버에 대해 Health Check 수행
- Green 서버가 정상 응답 시 Nginx가 트래픽 전환
- 구버전 Process Kill
CD 도입 효과 요약
항목 | 도입 전 | 도입 후 |
---|---|---|
배포 시간 | 30분 이상 | 5~10분 |
다운타임 | 있음 (2~3분) | 없음 (Blue-Green 적용 시) |
롤백 전략 | 없음 | 수동 백업/복원 구조 확보 |
배포 안정성 | 낮음 | Git 기반 자동화로 향상 |
예상 성과
- 릴리즈 속도 증가 → 하루 2~3회 이상 배포 가능
- 오류 발생 시 빠른 롤백 → 서비스 가용성 향상
- 반복 가능한 배포 프로세스 → 팀 협업 및 유지보수 효율화
추가 고려사항
.env
와 같은 시크릿은 GitHub Secrets 또는 서버 내 안전한 위치에서 관리- 이후 고도화 시, Docker 이미지 기반으로 구조 개선 + Blue-Green 인프라 자동화 가능
- Slack/Discord 알림 연동, 헬스체크 자동화도 도입 가능
MVP CD의 한계점 & 개선방안
-
자동화 범위 제한
GitHub Actions는 단순히
deploy.sh
를 트리거할 뿐, 배포 로직 자체는 여전히 수작업 기반→ Docker 이미지 빌드 → 레지스트리 푸시 → 배포 자동화 도입 (CI/CD 완전 통합)
-
상태 의존성
서버 포트 상태(lsof), 파일 경로 등에 강하게 의존
→ 컨테이너로 분리
-
롤백 유연성 부족
→ 헬스 체크 결과에 따라 실패 시 자동 롤백 + Discode 알림 연동
-
확장성 한계
단일 서버에서만 동작하는 구조
→ K8s 환경 기반으로 Blue-Green 외에도 Canary, Rolling 도입 가능성 확보
-
배포 이력 관리 미흡
→ Git 태그 or Docker 이미지 태그 or Github Actions Artifacts 배포 이력 관리 도입