[CL] 3단계: CD(지속적 배포) 파이프라인 구축 - 100-hours-a-week/6-nemo-wiki GitHub Wiki
1. 개요
해당 문서는 수작업 기반 Big Bang 배포 경험을 바탕으로 CD(지속적 배포) 자동화 파이프라인을 구축하고 설계하는 것을 목표로 한다.
1.1. 실습 및 평가자 관점
- 본 단계에서는 CI를 넘어, 운영 환경에 자동으로 릴리즈되는 CD 구조의 안정성과 설계 의도를 평가한다.
- 배포 자동화 과정에서 발생할 수 있는 실패, 오류 전파, 무중단 전환 등의 리스크를 어떻게 제어하고 있는지에 중점을 둔다.
- 특히, 배포 실패 시의 대응 전략(자동 롤백, 수동 복구) 및 배포 전후 모니터링/검증 절차의 유무가 핵심 평가요소이다.
1.2 실무자 관점
- CI 이후의 코드를 실제 사용자에게 빠르게 전달할 수 있는 구조는 제품 출시 주기를 획기적으로 단축시킨다.
- 팀은 “왜 지금 CD가 필요한가?”라는 질문에 대한 실무적 관점을 제시해야 하고, 서비스 특성에 맞는 배포 전략과 그 전략을 선택한 배경을 기술해야 한다.
- 또한, 배포 오류 발생 시 빠르게 대응 가능한 롤백 구조나 검증 자동화 전략이 함께 제시되어야 한다.
1.3 과제 목표 및 구성
이번 과제는 다음 세 가지 핵심 작업으로 구성된다:
- CD 적용 범위 및 방식 결정
- 어떤 환경(스테이징, 운영)에 CD를 적용할지와 그 방식(자동/수동, 승인 기반 등)을 명시한다.
- 또한 선택한 배포 전략과 그 배경을 서비스 구조와 함께 설명한다.
- CD 파이프라인 설계
- CI 산출물(build 결과물)을 기준으로 자동 배포 → 검증 → 전환이 이뤄지는 전체 흐름을 설계한다.
- 실패 시 롤백 절차, 상태 체크, 알림, 모니터링 로직을 포함한다.
- 파이프라인 구성 명세
- GitHub Actions 워크플로우 구조, start.sh, validate.sh, 비밀값 관리(Secrets), 서버 내 실행 로직을 명세화한다.
- 실무에서 적용 가능한 수준의 상세한 구성 설계로 문서화한다.
1.4 제풀 항목
- CD 파이프라인 흐름도
- Push → 배포 실행 → 성태 검증 → 전환/롤백 흐름을 시각화한 다이어그램
- CD 설계 설명서
- 도입 이유, 전략 선택 배경, 구성 요소 간 흐름 및 기대 효과를 포함한 서술형 문서
- 설정 및 스크립트 명세
- cd.yml, 배포 스크립트, 검증 스크립트 등 실행 흐름을 담은 주요 설정 파일 내용
- 추가 자료
- 로그, 배포 전후 성능 변화, 시나리오 기반 검증 결과
2. 도입 배경 및 현행 문제 정의
2.1. 현재 배포 구조 개요
1단계 실습에서는 GitHub 없이 .jar
, .py
, .env
등의 파일을 로컬에서 준비한 후,
서버로 직접 전송(scp
)하고 SSH로 접속하여 수동으로 서비스를 실행하는 Big Bang 배포 방식을 사용했다.
모든 서비스(Next.js, Spring Boot, FastAPI, MySQL, Redis)가 하나의 VM(GCP Compute Engine)에
통합되어 있으며, 포트 기반으로 각각 구동되며, 배포자는 다음과 같은 절차를 거쳐 수동 배포를 수행해야 했다:
- 로컬에서 빌드 및 테스트 수행
- scp로 서버에 코드 복사
- SSH 접속 후 실행 명령어 수동 입력 (
java -jar
,npm start
,uvicorn
) - 수동 로그 확인 (
tail
,curl
등)
2.2. 문제 인식: 수작업 배포 구조의 한계
Big Bang 배포 방식의 경우 학습적으로는 유의미했지만, 다음과 같은 실질적 한계 직면했다:
한계점 | 설명 |
---|---|
반복 작업 증가 | 매번 scp → ssh → 명령 실행 반복 → 생산성 저하 |
휴먼 에러 가능성 | 커맨드 누락, 경로 오류, 파일 덮어쓰기 등 실수 위험 |
Git과 단절 | GitHub에 의존하지 않기 때문에 히스토리 관리 및 롤백 불가 |
운영자 의존도 | 특정 인원만 배포 가능, 협업 및 유지보수 어려움 |
확장 불가 | 배포가 수동이기 때문에 서비스가 성장할수록 병목 가능성 발생 |
2.3. CD 자동화 도입의 필요성
위와 같은 문제들을 해결하기 위해 다음과 같은 관점에서 지속적 배포(CD) 구조의 필요성이 도출되었다:
- GitHub 중심 개발 → 자동 배포로 연결되는 통합된 흐름 필요
- Push 기반으로 서버 내부에서 자동으로 실행되는 구조 확보
- 수동 배포에서 발생하는 실수 제거 및 일관성 유지
- 배포 이력 추적, 실패 시 롤백 대응, 상태 검증 자동화 등의 확장 기반 마련
이를 바탕으로,
“이제는 코드가 GitHub에 올라가는 순간, 자동으로 서버에서 안전하게 반영되는 구조가 필요하다.”
라는 명확한 기술적 요구가 발생했다.
3. 배포 자동화 도구 선택 및 비교
3.1. 도구 선택 배경
2단계부터 GitHub을 본격적으로 도입함에 따라,
코드 기반의 자동화된 배포 구조로 전환할 필요성이 명확해졌다.
이전까지는 배포를 위해 다음과 같은 과정을 수동으로 반복해야 했다:
- 로컬에서 빌드
- scp로 서버 전송
- ssh로 접속
- 명령어 수동 실행
이러한 구조는 반복성과 휴먼 에러 발생 가능성이 높기 때문에,
GitHub Push 이벤트 기반으로 서버에 자동 접속 후 배포가 이뤄지는 구조가 필요해졌다.
3.2. 핵심 후보 도구 비교
가장 현실적인 후보는 GitHub Actions 기반의 두 가지 방식이었다:
- GitHub Actions + SSH: GitHub 클라우드 러너가 서버에 SSH로 접속해 명령어 실행
- GitHub Actions + Self-hosted Runner: 서버 내부에 러너를 설치해, Push 이벤트 발생 시 직접 배포
항목 | GitHub Actions + SSH | GitHub Actions + Self-hosted Runner |
---|---|---|
실행 위치 | GitHub 클라우드 → 서버에 SSH 접속 | 서버 내부 러너에서 직접 실행 |
파일 전송 | scp 직접 수행 필요 |
checkout 으로 코드 복제 가능 |
보안 | SSH 키/포트 관리 필요 | 외부 접속 없이 실행 가능 (내부 보안 강화) |
설정 난이도 | 비교적 간단, 익숙한 구조 | 러너 설치, 등록, 서비스 실행 필요 |
확장성 | 명령어 조작 자유로움 | 템플릿화 용이, 러너 라벨링으로 컨트롤 가능 |
디버깅/운영 | GitHub와 서버 로그 분리 | 서버 내 로그로 추적 쉬움 |
운영 환경 통제 | SSH 명령어로 제어 (start.sh 등) | 서버 내부 제어가 직관적임 |
3.3. 기타 CD 도구 간단 비교
도구 | 장점 | 단점 | 서비스 적합도 |
---|---|---|---|
Jenkins | 자유도, 확장성, 플러그인 풍부 | 별도 서버 설치/관리 필요, 복잡 | 단일 서버에는 과도함 |
AWS CodeDeploy | EC2/ASG 통합, 롤백 기능 우수 | AWS 전용, GCP와 비호환 | 현재 GCP 사용 중 |
GitLab CI/CD | GitLab과 통합 우수 | GitHub 사용자에 부적합 | 도구 변경 부담 |
ArgoCD | GitOps + Kubernetes 완벽 연동 | K8s 필요, 러닝커브 있음 | Kubernetes 미사용 중 |
3.4. 결론
결론적으로 현재 단계에서는 다음과 같은 이유로 GitHub Actions + SSH 방식을 채택했다.
- GitHub에 Push만 하면 자동으로 원격 서버에 접속해 배포 수행 가능
- 기존 수작업 배포 방식에서 큰 구조 변경 없이 자동화 도입 가능
- 명령어 기반으로 서비스별 배포 흐름 제어가 유연함
- SSH key 기반 배포로 이후 Rollback, 헬스체크 등 확장 설계도 용이함
- MVP 단계로, self-hosted runner를 운영할 만큼의 복잡한 보안/비용/네트워크 이슈가 없으며, GitHub 클라우드 러너만으로도 충분히 안정적인 운영이 가능함
GitHub Actions + SSH 방식은
현재 우리 구조(GCP 단일 서버, GitHub 중심 개발, 기존 수동 배포 경험)를 기반으로,
가장 현실적이고 적용이 용이한 CD 도구였다.
4. CD 파이프라인 구성 및 실행 흐름 설계
4.1. CD 적용 범위 및 방식 결정
지속적 배포(Continuous Delivery / Deployment)를 설계하기 위해 가장 먼저 고려해야 할 점은
어떤 브랜치에서, 어떤 방식으로 배포가 이루어질 것인가다.
현재 프로젝트는 경량화 된 Git Flow
전략을 도입하여 다음과 같은 브랜치 체계를 운용 중이다:
main
/ develop
/ hotfix/*
/ feature/*
/ bugfix/*
각 브랜치는 목적과 배포 대상이 다르며, 실제 배포가 필요한 main
과 develop
브랜치에
서로 다른 CD 적용 범위 및 자동화 수준을 분리하여 설계하였다.
브랜치 | 목적 | CD 방식 | 배포 서버 |
---|---|---|---|
develop |
기능 통합 및 QA 테스트 | 자동 배포 (Continuous Deployment) | 테스트 서버 |
main |
운영 배포 | 수동 승인 기반 배포 (Continuous Delivery) | 운영 서버 |
develop
브랜치 (Continuous Deployment)develop
브랜치는 기능 통합과 QA 테스트를 위한 공간으로 이곳에서 기능 검증을 수행한다.- 병합된 코드는 자동으로 테스트 서버에 배포되기 때문에 빠른 개발 사이클을 위해 자동화가 필수이다.
- 따라서 별도의 승인 없이 푸시만으로 배포가 이루어지는 Continuous Deployment 전략을 적용했다.
main
브랜치 (Continuous Delivery)- 반면
main
브랜치는 실제 사용자에게 서비스되는 운영 환경이므로 배포 전 인적 검토와 승인 절차가 필요하다. - 이러한 특성상 안정성이 최우선이기에, 수동 검토 및 승인을 거쳐 배포하도록 Continuous Delivery 전략을 적용했다.
- 반면
따라서 본 프로젝트에서
develop
에는 빠른 피드백을 위한 자동 배포(Continuous Deployment)를,main
에는 운영 안정성을 고려한 수동 승인 배포(Continuous Delivery)를 적용함으로써, 속도와 안정성의 균형을 맞춘 실용적인 CD 전략을 구성했다.
4.2. CD 파이프라인 설계
본 프로젝트는 Github Actions를 활용하여 CD 파이프라인을 구성하였다.
브랜치별로 배포 방식이 달라지므로, GitHub Actions 워크플로우 안에서
브랜치를 기준으로 분기하여 서버에 다른 배포 스크립트를 실행하는 구조로 설계하였다.
-
개발 완료 후 PR 생성
개발자는
feature/*
브랜치에서 작업 후,develop
브랜치에 PR을 생성한다.이벤트 워크플로우 설명 pull_request
CI - PR Lint & Test
코드 스타일 및 테스트 자동 검사 수행 -
develop
브랜치에merge
또는push
발생develop
브랜치에 PR이 머지되거나 직접 push되면, 아래 순서로 워크플로우가 실행된다.이벤트 워크플로우 설명 push
todevelop
CI - Backend Build & Upload to GCS
bootJar
실행 후app.jar
생성 → GCS 업로드workflow-run
(CI 워크플로우 종료 후 실행)CD - Deploy to Staging (Develop)
CI 성공 시 자동 트리거되어 Staging 서버에 배포 ( app.jar
,.sh
다운로드 및 실행)Staging 환경에 자동 배포되는 구조 (Continuous Deployment)
workflow_run
을 사용하는 이유:→ CI가 완료되기 전에 CD가 먼저 실행되면, GCS에
app.jar
가 아직 업로드되지 않아 배포가 실패할 수 있기 때문→ 따라서
CI - Build & Upload to GCS
가 완료된 이후에만CD - Deploy to Staging
이 실행되도록workflow_run
트리거를 사용함→ 이로써 빌드 산출물이 누락되지 않고, 안정적으로 Staging 서버에 자동 배포되도록 설계하였다.
-
운영 배포 준비: PR →
main
develop
환경에서 통합 및 기능 테스트를 완료한 후,main
브랜치로 PR을 생성하고 머지한다.이벤트 워크플로우 설명 push
tomain
CI - Backend Build & Upload to GCS
main
기준app.jar
,.sh
파일 GCS 업로드 -
운영 서버 배포
이벤트 워크플로우 설명 push
tomain
CD - Deploy to Production
main
에 push되면 운영 서버에 자동 배포. GCS에서app.jar
및 스크립트 다운로드 후 실행됨Production 환경에 수동 승인 배포되는 구조 (Continuous Delivery)
on: push + environment.approval
을 사용하는 이유:→ 운영 배포는 CI 성공 후 자동 배포되기보단, 사람이 승인한 후 실행되어야 하기 때문
→ GitHub Actions의
environment.production
설정을 통해push to main
발생 시에도 운영자 승인 없이는 실제 배포가 이뤄지지 않도록 설계함→ 이로써 실수로 인한 자동 배포를 방지하고, 운영 안정성을 높였다.
4.3. 배포 신뢰성 확보 전략
지속적 배포(Continuous Delivery/Deployment)가 안정적으로 운영되기 위해서는
배포 이후 서비스 상태를 검증하고, 문제 발생 시 빠르게 복구할 수 있는 구조가 필요하다.
본 프로젝트는 이를 위해 다음과 같은 헬스 체크 절차와 롤백 전략을 함께 설계하였다.
-
헬스 체크 (Health Check)
배포가 완료된 후,
validate.sh
스크립트를 통해 애플리케이션의 상태를 자동 점검한다.해당 스크립트는 /health 엔드포인트를 호출하여 다음 세 가지 구성 요소를 확인한다:
- Spring Boot 애플리케이션 기동 여부 (응답이 왔다는 것 자체가 사실상 배포 성공을 의미)
- MySQL 연결 상태
- Redis 연결 상태
장상적인 응답이 다음과 같은 형식으로 반환되며,
"status": "UP"
일 때만 배포가 성공한 것으로 간주{ "spring": "OK", "mysql": "OK", "redis": "OK", "status": "UP" }
-
롤백 전략 (Rollback Strategy)
본 프로젝트는 개발 초기에 배포 실패 시 빠르게 이전 안정 버전으로 복원할 수 있도록
GCS 기반 수동 롤백 전략을 설계하였다.
특히 직전 배포 버전(
app-previous.jar
)을 자동 보관하여 빠르고 명확한 복구가 가능하도록 구성하였다.GCS 업로드 구조는 다음과 같다:
파일명 용도 app.jar
현재 배포에 사용되는 최신 빌드 파일 app-previous.jar
직전 배포 버전 (롤백 대상) 20250421-1652-abcdef.jar
고유 커밋 기반 히스토리 보관용 배포 실패 시 롤백 수행 절차는 다음과 같다:
- GCS에서
app-previous.jar
다운로드 - 이를 서버에 업로드하여 기존 app.jar를 덮어씀
- 기존 배포 스크립트 (
start.sh
)로 서비스 재기동
해당 전략은 CI/CD 흐름을 크게 변경하지 않으면서도, 배포 실패 시 안정적으로 복구할 수 있는 최소한의 안전장치를 제공하며, 향후 자동화 및 모니터링 시스템과도 자연스럽게 연동될 수 있다.
- GCS에서
5. 파이프라인 구성 명세
5.1. CD 파이프라인 구성 요약
이벤트 | 워크플로우 | 설명 |
---|---|---|
CI 성공 시 | CD - Deploy to Staging (develop) | dev 서버에 자동 배포 |
환경 승인 (main) | CD - Deploy to Production (main) | 운영 서버에 수동 승인 배포 (stop.sh , start.sh , validate.sh 실행) |
5.2 Github Actions 워크플로우 구성
- cd-staging.yml
- 트리거:
workflow_run
(CI 성공 시) - 작업
- GCS에서 파일 다운로드
- dev 서버에 ssh 접속 →
stop.sh
,start.sh
,validate.sh
실행
- 트리거:
- cd-production.yml
- 트리거:
push
tomain
+environment.approval
(GitHub 환경 승인) - 작업
- CS에서 파일 다운로드
- dev 서버에 ssh 접속 →
stop.sh
,start.sh
,validate.sh
실행
- 트리거:
5.3. 배포 스크립트 구성
스크립트 | 설명 |
---|---|
start.sh |
포트 점유 확인 → Spring Boot 실행 (nohup java -jar app.jar |
stop.sh |
기존 8080 포트 프로세스 종료 |
validate.sh |
/healthz API 호출 → status: UP 여부 확인 |
rollback.sh |
app-previous.jar → app.jar 로 복사 후 재시작 |
5.4. GCS 디렉토리 구조
-
브랜치에 따라
TARGET_ENV=dev
또는prod
로 분기 업로드 -
GCS 구조는 배포 환경별로 명확한 격리 및 관리 가능
gs://sample-app-artifact/ ├── dev/ │ └── backend/ │ ├── app.jar │ ├── start.sh │ ├── stop.sh │ └── validate.sh ├── prod/ └── backend/ ├── app.jar ├── app-previous.jar ├── start.sh ├── stop.sh ├── validate.sh └── rollback.sh
5.5. 환경 변수 및 보안 설정 (GitHub Secrets)
환경변수 이름 | 용도 설명 | 적용 대상 브랜치 | 비고 |
---|---|---|---|
GCP_KEY_JSON |
GCP 서비스 계정 인증을 위한 키 JSON | 공통 | google-github-actions/auth@v1 에 사용 |
GCP_PROJECT_ID |
GCP 프로젝트 ID | 공통 | setup-gcloud 에 사용 |
GCS_BUCKET |
GCS 아티팩트 업로드 대상 버킷 이름 | 공통 | jar, sh 업로드 경로 지정에 사용 |
DEV_SSH_USER |
개발 서버 SSH 접속용 유저명 | develop |
스테이징 배포용 |
DEV_SSH_HOST |
개발 서버 IP or 도메인 | develop |
스테이징 배포용 |
SSH_USER |
운영 서버 SSH 접속용 유저명 | main |
운영 배포용 |
SSH_HOST |
운영 서버 IP or 도메인 | main |
운영 배포용 |
SSH_PRIVATE_KEY |
SSH 접속을 위한 개인 키 (PEM 형식) | 공통 | scp , ssh 전송에 사용 |
5.6. 스크립트
-
GitHub Actions 워크플로우 파일 (
.github/workflows/
)-
cd-staging
name: CD - Deploy to Staging (Develop) on: workflow_run: workflows: ["CI - Backend Build & Upload to GCS"] types: - completed jobs: deploy: if: ${{ github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-22.04 environment: name: staging steps: - name: Authenticate to GCP uses: google-github-actions/auth@v1 with: credentials_json: ${{ secrets.GCP_KEY_JSON }} - name: Set up gcloud CLI uses: google-github-actions/setup-gcloud@v1 with: project_id: ${{ secrets.GCP_PROJECT_ID }} - name: Set env run: | echo "TARGET_ENV=dev" >> $GITHUB_ENV echo "SSH_USER=${{ secrets.DEV_SSH_USER }}" >> $GITHUB_ENV echo "SSH_HOST=${{ secrets.DEV_SSH_HOST }}" >> $GITHUB_ENV - name: Download from GCS run: | mkdir -p scripts gsutil cp gs://${{ secrets.GCS_BUCKET }}/$TARGET_ENV/backend/app.jar ./scripts/app.jar gsutil cp gs://${{ secrets.GCS_BUCKET }}/$TARGET_ENV/backend/*.sh ./scripts/ - name: Create SSH private key run: | echo "$SSH_PRIVATE_KEY" > private_key.pem chmod 600 private_key.pem env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} - name: Deploy to staging server run: | ssh -o StrictHostKeyChecking=no -i private_key.pem $SSH_USER@$SSH_HOST "mkdir -p ~/deploy/backend" scp -o StrictHostKeyChecking=no -i private_key.pem ./scripts/* $SSH_USER@$SSH_HOST:~/deploy/backend/ ssh -o StrictHostKeyChecking=no -i private_key.pem $SSH_USER@$SSH_HOST " cd ~/deploy/backend && bash stop.sh && bash start.sh && bash validate.sh "
-
cd-prod
name: CD - Deploy to Production (Manual) on: push: branches: [main] jobs: deploy: runs-on: ubuntu-22.04 environment: name: production steps: - name: Authenticate to GCP uses: google-github-actions/auth@v1 with: credentials_json: ${{ secrets.GCP_KEY_JSON }} - name: Set up gcloud CLI uses: google-github-actions/setup-gcloud@v1 with: project_id: ${{ secrets.GCP_PROJECT_ID }} - name: Set env for main run: | echo "TARGET_ENV=prod" >> $GITHUB_ENV echo "SSH_USER=${{ secrets.PROD_SSH_USER }}" >> $GITHUB_ENV echo "SSH_HOST=${{ secrets.PROD_SSH_HOST }}" >> $GITHUB_ENV - name: Download files from GCS run: | mkdir -p scripts gsutil cp gs://${{ secrets.GCS_BUCKET }}/$TARGET_ENV/backend/app.jar ./scripts/app.jar gsutil cp gs://${{ secrets.GCS_BUCKET }}/$TARGET_ENV/backend/*.sh ./scripts/ - name: Create SSH private key run: | echo "$SSH_PRIVATE_KEY" > private_key.pem chmod 600 private_key.pem env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} - name: Deploy to production server run: | ssh -o StrictHostKeyChecking=no -i private_key.pem $SSH_USER@$SSH_HOST "mkdir -p ~/deploy/backend" scp -o StrictHostKeyChecking=no -i private_key.pem ./scripts/* $SSH_USER@$SSH_HOST:~/deploy/backend/ ssh -o StrictHostKeyChecking=no -i private_key.pem $SSH_USER@$SSH_HOST " set -e cd ~/deploy/backend bash stop.sh bash start.sh bash validate.sh "
-
-
배포 스크립트 파일
-
stop.sh
#!/bin/bash echo "[CD] Stopping Spring Boot..." PID=$(lsof -t -i:8080) if [ -n "$PID" ]; then kill -9 $PID echo "[CD] Process $PID stopped." else echo "[CD] No process running on port 8080." fi
-
start.sh
#!/bin/bash echo "[CD] Start Spring Boot Server..." # 실행 중인 프로세스가 있다면 종료 (예: port 8080) PID=$(lsof -t -i:8080) if [ -n "$PID" ]; then echo "[CD] Stopping existing process on port 8080..." kill -9 $PID fi # JAR 실행 (복사된 ~/deploy 경로 기준) JAR_PATH=$(ls -t ~/deploy/backend/*.jar | head -n 1) if [ -z "$JAR_PATH" ]; then echo "[CD] ❌ No JAR file found to execute." exit 1 fi echo "[CD] Running $JAR_PATH" nohup java -jar "$JAR_PATH" > ~/deploy/spring.log 2>&1 & echo "[CD] Spring Boot started."
-
validate.sh
#!/bin/bash echo "[CD] Validating app..." URL="http://localhost:8080/healthz" for i in {1..10}; do RESPONSE=$(curl -s -w "HTTPSTATUS:%{http_code}" $URL) BODY=$(echo $RESPONSE | sed -e 's/HTTPSTATUS\:.*//g') STATUS=$(echo $RESPONSE | sed -e 's/.*HTTPSTATUS://') APP_STATUS=$(echo "$BODY" | grep -o '"status":"[^"]*"' | cut -d':' -f2 | tr -d '"') if [ "$STATUS" = "200" ] && [ "$APP_STATUS" = "UP" ]; then echo "[CD] ✅ Health check passed" exit 0 fi sleep 3 done echo "[CD] ❌ Health check failed: $BODY" exit 1
-
rollback.sh
#!/bin/bash echo "[CD] ⚠️ Rolling back to previous version..." APP_DIR=~/deploy/backend CURRENT_JAR="$APP_DIR/app.jar" BACKUP_JAR="$APP_DIR/app-previous.jar" # 1. 현재 실행 중인 앱 종료 PID=$(lsof -t -i:8080) if [ -n "$PID" ]; then echo "[CD] Stopping running app on port 8080..." kill -9 $PID fi # 2. 백업된 JAR이 있는지 확인 if [ ! -f "$BACKUP_JAR" ]; then echo "[CD] ❌ No backup file (app-previous.jar) found. Rollback failed." exit 1 fi # 3. 현재 JAR 백업 (선택적 - 원래 실패한 상태라 무의미할 수도 있음) cp "$CURRENT_JAR" "$APP_DIR/app-failed.jar" # 4. 이전 JAR로 복원 cp "$BACKUP_JAR" "$CURRENT_JAR" echo "[CD] ✅ Restored app.jar from backup." # 5. 재실행 nohup java -jar "$CURRENT_JAR" > "$APP_DIR/spring.log" 2>&1 & echo "[CD] 🟢 Rollback complete. Server restarted with previous version."
-
6. 결론 및 고도화 방향 제안
본 문서에서는 기존 수작업 기반의 Big Bang 배포 구조를 개선하고, GitHub Actions와 GCS 기반의 CI/CD 파이프라인을 도입하여, 단일 서버 환경에서도 자동화된 배포 구조를 구축하였다.
이번 3단계 설계를 통해 다음과 같은 주요 성과를 얻었다:
- GitHub와 배포 자동화를 연동하여
push
→ 서버 적용까지의 배포 흐름을 통합함 - **환경별 분기 (develop/main)**에 따라 자동 배포 / 승인 기반 배포를 구분 적용함으로써 속도와 안정성의 균형을 확보함
- GCS에 버전별
.jar
파일을 백업하고, 실패 시 빠르게 복원 가능한 롤백 구조를 마련함 - 배포 후 헬스 체크(Health Check) 를 통해 서비스 상태를 자동 검증하고, 실패 시 즉시 대응할 수 있도록 구성함
운영 배포에서는
environment.approval
기반 수동 승인을 도입하여, 실수로 인한 자동 배포를 방지하고 안정성을 확보하였다. 또한, 이전 버전 자동 보관 및 롤백 전략을 통해 운영 중 장애에도 빠르게 복구할 수 있는 구조를 마련하였다.
이제 다음 단계에서는 다음과 같은 고도화를 고려할 수 있다:
- Docker 기반의 배포 구조로 전환하여, 환경별 실행 환경 불일치 해소 및 서비스 확장 기반 마련
- GitHub Actions와 Discord Webhook 연동, 배포 로그 모니터링, 알림 도입 등 운영 자동화 체계 확대
- Multi-tier 구조로 확장 시, Load Balancer 기반 블루그린 및 카나리 배포 전략 도입 가능성 고려
이번 단계에서는 단일 VM 기반의 배포 자동화를 성공적으로 설계·구현함으로써, MVP 서비스 수준에서 안정성과 신속성을 동시에 확보할 수 있었다.
향후 단계에서는 Docker 기반 구성, 컨테이너 오케스트레이션(Kubernetes), 고급 배포 전략(블루그린, 카나리), 모니터링 및 알림 연동 등을 통해 보다 확장성 있는 클라우드 네이티브 아키텍처로 발전시키고자 한다.