클라우드 2단계: CI(지속적 통합) 파이프라인 구축 설계 - 100-hours-a-week/16-Hot6-wiki GitHub Wiki
상위 문서: 클라우드 위키
관련 문서: 클라우드 WHY? 문서
CI 파이프라인 구축 설계
목차
개요
이 문서는 본 프로젝트의 CI(Continuous Integration) 파이프라인 설계와 구성에 대한 설명을 제공합니다.
CI는 코드가 GitHub 저장소에 push되었을 때, 자동으로 테스트 및 빌드를 수행하고, 결과물(artifact)을 업로드하여 배포 준비 상태로 만드는 자동화 절차입니다.
본 문서에서는 다음을 다룹니다:
- CI를 도입하게 된 필요성과 효과
- GitHub Actions를 CI 도구로 선택한 이유와 다른 도구와의 비교 분석
- 전체 CI 아키텍처 다이어그램과 흐름도
- Frontend(React), Backend(Spring Boot), AI(FastAPI) 각각에 대해 개별적으로 정의된 CI 구성과 동작 방식
- GitHub Actions 워크플로 스크립트 명세
이 프로젝트는 CI와 CD를 분리하여, 빌드 결과물을 GitHub Actions의 artifact로 업로드한 뒤, Cloud Engineer가 수동 배포를 수행하는 구조를 채택하고 있습니다.
CI 필요성 및 도구 선택
CI 필요성
CI는 코드의 변경 사항을 자동으로 통합하는 과정입니다. 단순히 코드의 형상을 일치시키기 위한 목적이 아니라, 린팅, 테스팅 등을 포함하여 "정말 사용할 수 있는 코드" 상태로 통합하는 것을 말합니다.
1. 빅뱅 배포 실험에서 마주한 문제
프로젝트 초기, 저희 팀은 자동화된 빌드·배포 파이프라인 없이, EC2 인스턴스에 접속하여 git pull
→ 빌드
→ 실행
을 직접 수행하는 수작업(Big Bang) 배포 방식을 선택했습니다.
이 방식은 학습 부담이 없고, 별도의 도구 없이 빠르게 결과를 확인할 수 있다는 장점이 있었습니다. 그러나 다음과 같은 실제 실패 경험을 통해 한계가 명확히 드러났습니다.
-
문제 상황:
- 백엔드 서버는 정상 동작 중이었으나, 배포 중 프론트엔드(React)
npm run build
과정에서 **메모리 부족(OOM)**이 발생해 빌드가 실패함 - 서비스 운영 자체는 수백 MB로 가능했지만, 빌드에는 2~4GB 이상의 메모리가 필요했던 것
- EC2 t2.micro 환경에서는 이를 감당할 수 없어, 결국 swap 메모리를 수동 설정해 해결해야 했음
- 백엔드 서버는 정상 동작 중이었으나, 배포 중 프론트엔드(React)
-
그 외 겪었거나 예상되는 어려움:
- 로컬(macOS)에서는 문제 없이 빌드되던 코드가, 서버(Ubuntu)에서는 패키지 호환성 문제로 실패
- 팀원이 늘어나면서 누가, 어떤 환경에서, 어떤 명령어로 배포했는지 이력을 추적하기 어려워짐
- 실수로 테스트하지 않은 상태에서 배포해, 프로덕션 장애가 발생할 가능성도 높아짐
이러한 경험은 단순히 "수동 배포가 귀찮다"는 수준의 문제가 아니라, 서비스 안정성과 팀 협업의 리스크로 이어질 수 있는 구조적 문제임을 보여주었습니다.
2. CI 도입이 왜 해답인가?
CI는 다음과 같은 방식으로 위 문제들을 해소해줍니다.
빅뱅 배포에서 겪은 문제 | CI를 통한 해결 |
---|---|
빌드 시 서버 메모리 부족 | GitHub Actions에서 외부 빌드 → EC2에는 실행만 |
환경 간 패키지 호환 문제 | 통일된 OS/버전에서의 자동 빌드 및 테스트 수행 |
테스트 없이 배포되는 문제 | push 시 자동 유닛 테스트 수행, 실패 시 배포 불가 |
배포 방식 일관성 부족 | 모든 빌드/테스트 작업이 기록되고 공유됨 |
실수 방지 어려움 | Step-by-step으로 검증된 자동화 파이프라인 구성 가능 |
특히, EC2 인스턴스는 서비스를 실행하는 데 필요한 최소 자원만 확보하면 되며, 빌드는 GitHub Actions 같은 외부에서 처리되므로 운영 안정성과 비용 측면 모두에서 효과적인 구조가 됩니다.
결론
"CI가 좋아서 도입했다"가 아니라, "CI 없이 실제로 해봤더니 한계가 명확했고, 직접 겪은 문제를 해결하려고 도입했다" 는 것이 저희 팀의 진짜 이유입니다.
이러한 경험을 바탕으로, 저희는 서비스 품질과 배포 안정성을 확보하기 위해 CI는 반드시 필요하다고 판단했습니다.
CI 도구 비교 표
항목 | GitHub Actions | Jenkins | CircleCI | Travis CI | GitLab CI |
---|---|---|---|---|---|
실행 환경 | GitHub 제공 서버 (Self-hosted 지원) | Self-hosted | Self-hosted 기반 (Cloud 지원) | Self-hosted 기반 (Cloud 지원) | GitLab.com 또는 Self-hosted 지원 |
유연성 | 중간 (Marketplace 확장 활용) | 매우 높음 (플러그인 수백 개 지원) | 높음 (Orbs 및 고급 config 지원) | 낮음 | 중간 ~ 높음 |
설치 및 유지보수 | 없음 (GitHub 기반 자동 제공) | 필요 (운영/보안 포함 유지보수 수반) | 없음 (Cloud 환경), 있으면 Self-hosted | 없음 (Cloud 환경 기준) | 선택적 (Self-hosted 시 유지 필요) |
커스터마이징 | Matrix 빌드, 환경별 조건 분기 등 | 고도 커스터마이징 지원 | Orbs 기반 확장 및 파라미터화 빌드 지원 | 기본 설정만 허용 | Stage/Job/Parallel 등 구성 유연 |
시크릿 관리 | GitHub Secrets | 서버 설정 or OS 환경변수 | 프로젝트 수준에서 키 관리 | 제한적 | GitLab Settings 또는 Vault 활용 가능 |
트리거 방식 | Push, PR, CRON, Manual 등 다양 | Webhook, Polling | Push, PR, CRON | Push, PR | Push, PR, Merge Request, Manual 등 |
UI 및 로그 가시성 | 우수 (GitHub UI 내 확인) | 부족 (플러그인 필수) | 우수 (실시간 로그 제공) | 보통 (간단한 로그 제공) | 우수 (로그 세분화 지원) |
무료 사용 범위 | 무료 (Public은 2,000분/월 제공) | 없음 (직접 관리 필요) | 제한적 (무료 요금제 존재) | Public은 무료, Private는 유료 | Public 무료, Private 일부 제한 존재 |
도구별 장단점 정리
✅ GitHub Actions
장점
- GitHub 생태계에 최적화 (PR/Merge 기반 흐름에 자연스럽게 연결됨)
- 다양한 이벤트 기반 트리거 (Push, PR, Issue, Release 등)
- MarketPlace를 통한 수천 개의 Action 재사용 가능
- macOS, Linux, Windows 등 다양한 실행 환경
단점
- GitHub 외 저장소 사용 시 제약이 있음
- Self-hosted runner 관리가 복잡할 수 있음
- 로그 필터링, 세션 내 상태 공유 등에 제약 있음
정리
- GitHub 생태계 통합 가능(PR, push, Issue, Branch 등과 유연한 통합)
- 러닝 커브 낮음
- 오픈소스 Actions로 다양한 동작 가능(Marketplace)
- 퍼블릭의 경우 무료 제공 시간
- 도입하기로 결정
❌ Jenkins
장점
- 플러그인을 통한 무한한 커스터마이징
- 온프레미스 환경에서 자유롭게 구성 가능
- Groovy 기반 Pipeline Script 작성 가능
- 병렬 처리, 멀티 노드, 조건 분기 등 복잡한 로직 가능
단점
- 설치/운영에 높은 러닝 커브 존재
- 보안, 백업, 버전 충돌 문제 자주 발생
- 현대 DevOps 흐름에 비해 UI가 부족하고 불편함
정리
- 서버 설치 및 유지보수 필요
- Groovy DSL 학습 필요로 러닝 커브 높음
- 플러그인 호환성 관리가 번거롭고 보안 리스크 존재
- 도입하지 않음
❌ CircleCI
장점
- Docker 기반의 빠른 빌드 처리 및 캐싱 기능
- 병렬 처리/매트릭스 빌드 등 성능 최적화 기능 탑재
- Orbs(재사용 가능한 config) 제공
- 다양한 언어 및 OS 지원
단점
- 요금제가 복잡하고 무료 사용에 제약 존재
- 비직관적인 문법과 설정 (초기 진입장벽 있음)
- 비공식 리전의 느린 빌드 속도
정리
- 강력한 기능은 유료 요금제에서 제공됨
- 기본 제공 시간이 적어 비용 부담 발생 가능
- 도입하지 않음
❌ Travis CI
장점
- 단순한
.travis.yml
하나로 시작 가능 - GitHub 연동이 간단하며 빠른 셋업
- 오픈소스 프로젝트에 적합 (무료 지원)
단점
- 상용 서비스에 적합하지 않음 (기능 제약 많음)
- 최근 사용자 수 및 커뮤니티가 감소 추세
- 일부 기능은 유료 전환 (private repo 기준)
정리
- 상업적 변화 이후 무료 플랜 제한적
- 커뮤니티 규모가 줄어들며 유지가 불안정
- 도입하지 않음
❌ GitLab CI
장점
- GitLab과 완전히 통합된 DevOps 툴체인
.gitlab-ci.yml
하나로 배포까지 연계 가능- 환경변수/시크릿 관리, 자동화 스크립트 작성 용이
- Auto DevOps와 GitOps 기능 제공
단점
- GitHub 사용자에겐 도입 장벽이 있음
- CI/CD 외 GitLab 사용을 전제함 (전체 툴셋 필요)
- Auto DevOps 자동 구성은 일부 제약이 따름
정리
- 코드 형상 관리로 GitLab을 사용하지 않음
- 따라서 GitHub 기반 프로젝트와 통합 어려움.
- 도입하지 않음
CI 파이프라인 아키텍처 다이어그램
브랜치 전략
비교 분석
전략 | 특징 | 장점 | 단점 |
---|---|---|---|
GitHub Flow | main 브랜치 기준으로 feature → PR → merge 방식 |
간단하고 빠름 | 운영 코드와 개발 코드 분리가 어려움 |
Git Flow | main , develop , feature , release , hotfix 구분 |
명확한 역할 분담, 안정적인 릴리즈 관리 가능 | 브랜치가 많아지고 관리가 복잡해질 수 있음 |
현재 전략 | main , dev , feat/* , hotfix/* 로 구성 |
단순성과 안전성 절충, CI/CD 흐름과 맞음 | 브랜치 병합 규칙을 초기에 명확히 해야 함 |
GitHub Flow를 단독으로 사용하지 않은 이유
→ 기능 개발과 운영 코드의 분리가 어렵고, 긴급 수정 대응에도 제약이 있기 때문.
Git Flow 전체를 도입하지 않은 이유
→ release
브랜치까지 운용하는 구조는 팀 규모나 프로젝트 단계에 비해 과도하게 복잡하다고 판단.
현재 전략을 채택한 이유
→ GitHub Flow
의 간결함과 Git Flow
의 안정성을 절충하여, 운영과 개발 모두를 유연하게 관리할 수 있기 때문.
기본 구조
이 프로젝트에서는 다음과 같은 브랜치 전략을 사용합니다.
브랜치 | 용도 |
---|---|
main |
운영용 브랜치. 항상 배포 가능한 안정 상태를 유지합니다. |
dev |
개발 통합 브랜치. 여러 기능을 병합하여 테스트하는 공간입니다. |
feat/* |
기능 단위 작업 브랜치. 단일 기능 개발을 위한 브랜치입니다. |
hotfix/* |
운영 중 긴급 수정 브랜치. main 에 바로 반영되며 이후 dev 에 병합됩니다. |
브랜치 흐름도
[feat/기능A] → [dev] → [main]
- 기능 개발은
feat/*
브랜치에서 시작됩니다. - 기능이 완료되면
dev
브랜치에 병합되어 통합 테스트를 진행합니다. dev
브랜치가 충분히 안정적일 경우main
에 병합하여 배포합니다.
[hotfix/버그수정] → [main] → [dev]
- 운영 중 발생한 긴급 이슈는
hotfix/*
브랜치에서 빠르게 수정하여main
에 먼저 반영합니다. - 이후
main
브랜치의 변경사항을dev
에 병합하여 일관성을 유지합니다.
왜 이러한 전략을 사용했는가?
프로젝트 초기에는 단순한 GitHub Flow만으로 충분했지만, 다음과 같은 요구사항이 추가되면서 전략을 확장할 필요가 있었습니다:
-
개발 중인 기능과 운영 코드의 분리 필요
main
과dev
브랜치를 구분함으로써, 배포 가능한 안정 코드와 통합 개발 코드를 나눌 수 있습니다. -
기능별 분리 작업 및 병렬 진행
feat/*
브랜치를 통해 팀원들이 동시에 여러 기능을 개발하고, 독립적으로 코드 리뷰를 받을 수 있습니다. -
긴급한 운영 이슈 대응
hotfix/*
브랜치를 통해 기능 개발 진행과 무관하게 빠르게main
에 반영하고, 이후dev
에도 되돌려 병합함으로써 코드 이력의 일관성을 유지할 수 있습니다. -
풀리퀘스트 기준 병합 구조 정립
feat/*
→dev
: 기능 개발 완료 후 병합dev
→main
: 릴리즈 시 병합hotfix/*
→main
→dev
: 운영 긴급 수정 후 병합
결론
이 전략은 GitHub Flow의 간결함과 Git Flow의 안전성을 절충한 구조로, 기능 개발의 유연성과 운영 안정성, 긴급 대응력을 모두 확보할 수 있는 실용적인 브랜치 전략입니다.
CI 파이프라인 다이어그램
아키텍처 다이어그램
파이프라인 구성 명세
Frontend
항목 | 내용 |
---|---|
빌드 도구 | npm |
산출물 | build/ 디렉터리 (정적 파일) |
CI 단계 작업 | 유닛 테스트, 정적 빌드, artifact 업로드 |
테스트 방법 | npm test -- --watchAll=false (Jest 기반 유닛 테스트) |
CD 방식 | 수동 복사 후 GCE에서 Nginx에 연동 |
Artifact 위치 | GitHub Actions Artifact |
추가 사항 | feat/* push 시 [no-ci] 커밋으로 테스트 생략 가능, PR 시 유닛 테스트 필수 |
[Push] → (조건: no [no-ci]) → [npm install]
→ [npm test]
→ [npm run build]
→ [build/ 디렉터리 Artifact 업로드]
Backend
항목 | 내용 |
---|---|
빌드 도구 | Gradle |
산출물 | .jar 파일 |
CI 단계 작업 | 유닛/통합 테스트, Gradle 빌드, artifact 업로드 |
테스트 방법 | ./gradlew test , ./gradlew integrationTest (Spring 기반) |
CD 방식 | 수동으로 JAR 파일을 서버에 전송하여 실행 |
환경변수 주입 | SPRING_PROFILES_ACTIVE , DB credentials 등 |
Artifact 위치 | GitHub Actions Artifact |
추가 사항 | 통합 테스트는 feat → dev , hotfix → main 에서는 백그라운드로 실행됨 |
[Push] → (조건: no [no-ci]) → [./gradlew test]
→ [./gradlew build]
→ [build/libs/*.jar Artifact 업로드]
AI
항목 | 내용 |
---|---|
빌드 도구 | 없음 |
산출물 | .py 소스, requirements.txt , 모델 파일 등 |
CI 단계 작업 | 유닛 테스트, 의존성 검증, 소스 패키징 및 artifact 업로드 |
테스트 방법 | pytest 기반 유닛 테스트 및 (선택적) 통합 테스트 |
CD 방식 | 수동 복사 후 서버에서 FastAPI 실행 |
Artifact 위치 | GitHub Actions Artifact |
추가 사항 | 통합 테스트는 feat → dev , hotfix → main 에서는 백그라운드로 실행됨 |
[Push] → (조건: no [no-ci]) → [pytest 실행]
→ [requirements.txt + 소스 패키징]
→ [artifact/ 디렉터리 업로드]
설정 및 스크립트 명세
Frontend
Github Actions
name: Frontend CI
on:
pull_request:
branches:
- develop
- main
push:
branches:
- 'feat/**'
- 'hotfix/**'
jobs:
unit-test-on-push:
if: |
github.event_name == 'push' &&
!contains(github.event.head_commit.message, '[no-ci]')
name: Unit Test on feat/* push
runs-on: ubuntu-latest
steps:
- name: Checkout Source
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install Dependencies
run: npm ci
- name: Run Unit Test
run: npm run test
- name: Notify Failure
if: failure()
uses: Ilshidur/action-discord@master
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
message: |
❌ [feat/* push] 유닛 테스트 실패!
브랜치: ${{ github.ref_name }}
[자세히 보기](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
pr-workflow:
if: github.event_name == 'pull_request'
name: Pull Request CI
runs-on: ubuntu-latest
steps:
- name: Checkout Source
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install Dependencies
run: npm ci
- name: Run Unit Test
run: npm run test
- name: Notify Failure - Unit Test
if: failure()
uses: Ilshidur/action-discord@master
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
message: |
❌ [PR] 유닛 테스트 실패!
브랜치: ${{ github.head_ref }} → ${{ github.base_ref }}
[자세히 보기](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
- name: Run Integration Test (only dev -> main)
if: github.base_ref == 'main' && github.head_ref == 'develop'
run: npm run test:integration
- name: Run Integration Test (background for feat → dev or hotfix → main)
if: github.base_ref == 'develop' || (startsWith(github.head_ref, 'hotfix/') && github.base_ref == 'main')
continue-on-error: true
run: npm run test:integration
- name: Notify Integration Test Failure (background)
if: failure() && (github.base_ref == 'develop' || startsWith(github.head_ref, 'hotfix/'))
uses: Ilshidur/action-discord@master
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
message: |
⚠️ [PR] 통합 테스트 실패 (백그라운드)
브랜치: ${{ github.head_ref }} → ${{ github.base_ref }}
[자세히 보기](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
- name: Notify Success
if: success() && github.base_ref == 'main'
uses: Ilshidur/action-discord@master
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
message: |
✅ 모든 테스트 통과! 이제 머지 가능합니다.
브랜치: ${{ github.head_ref }} → ${{ github.base_ref }}
[자세히 보기](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
다운로드 및 전송
scp -i ~/.ssh/your-key.pem frontend-build.zip your-user@your-fe-server:/home/ubuntu/onthetop-frontend
배포
#!/bin/bash
# 1. 아티팩트 압축 해제
unzip -o frontend-build.zip -d frontend-build
# 2. 환경 변수 설정
cp .env.production .env
# 3. 정적 파일을 Nginx 서비스 디렉토리에 복사
sudo rm -rf /var/www/onthetop/*
sudo cp -r frontend-build/* /var/www/onthetop/
# 4. 권한 설정 및 Nginx 재시작 (필요시)
# sudo chown -R www-data:www-data /var/www/onthetop
# sudo systemctl restart nginx
Backend
Github Actions
name: Backend CI/CD
on:
push:
branches:
- 'feat/**'
- 'hotfix/**'
pull_request:
branches:
- dev
- main
jobs:
unit-test-on-push:
if: |
github.event_name == 'push' &&
!contains(github.event.head_commit.message, '[no-ci]')
name: Backend Unit Test on feat/* push
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up JDK
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
- name: Run unit tests
run: ./gradlew test
- name: Notify Failure
if: failure()
uses: Ilshidur/action-discord@master
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
message: |
❌ [feat/* push] 백엔드 유닛 테스트 실패!
브랜치: ${{ github.ref_name }}
[자세히 보기](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
pr-workflow:
if: github.event_name == 'pull_request'
name: Backend Pull Request CI
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up JDK
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
- name: Run unit tests
run: ./gradlew test
- name: Notify Failure - Unit Test
if: failure()
uses: Ilshidur/action-discord@master
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
message: |
❌ [PR] 백엔드 유닛 테스트 실패!
브랜치: ${{ github.head_ref }} → ${{ github.base_ref }}
[자세히 보기](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
- name: Run Integration Test (only dev → main)
if: github.base_ref == 'main' && github.head_ref == 'dev'
run: ./gradlew integrationTest
- name: Run Integration Test (background for feat → dev or hotfix → main)
if: github.base_ref == 'dev' || (startsWith(github.head_ref, 'hotfix/') && github.base_ref == 'main')
continue-on-error: true
run: ./gradlew integrationTest
- name: Notify Integration Test Failure (background)
if: failure() && (github.base_ref == 'dev' || startsWith(github.head_ref, 'hotfix/'))
uses: Ilshidur/action-discord@master
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
message: |
⚠️ [PR] 백엔드 통합 테스트 실패 (백그라운드)
브랜치: ${{ github.head_ref }} → ${{ github.base_ref }}
[자세히 보기](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
- name: Notify Success
if: success() && github.base_ref == 'main'
uses: Ilshidur/action-discord@master
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
message: |
✅ 모든 백엔드 테스트 통과! 이제 머지 가능합니다.
브랜치: ${{ github.head_ref }} → ${{ github.base_ref }}
[자세히 보기](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
build-artifact:
if: github.base_ref == 'main' || github.base_ref == 'dev'
name: Build Backend and Upload Artifact
runs-on: ubuntu-latest
needs: pr-workflow
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up JDK
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
- name: Build with Gradle
run: ./gradlew clean build
- name: Upload jar as artifact
uses: actions/upload-artifact@v3
with:
name: backend-jar
path: build/libs/*.jar
다운로드 및 전송
```bash
scp -i ~/.ssh/your-key.pem backend-jar.zip your-user@your-be-server:/home/ubuntu/onthetop-backend
배포
#!/bin/bash
# 1. 아티팩트 압축 해제
unzip -o backend-jar.zip -d backend-jar
# 2. 실행 중인 백엔드 종료
pkill -f 'onthetop-backend'
# 3. JAR 실행 (Spring profile 설정 포함)
nohup java -jar backend-jar/*.jar --spring.profiles.active=prod > app.log 2>&1 &
AI
Github Actions
name: AI Server CI/CD
on:
push:
branches:
- 'feat/**'
- 'hotfix/**'
pull_request:
branches:
- dev
- main
jobs:
unit-test-on-push:
if: |
github.event_name == 'push' &&
!contains(github.event.head_commit.message, '[no-ci]')
name: AI Unit Test on feat/* push
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run unit tests
run: |
if [ -d "tests" ]; then
pytest tests
else
echo "No test directory found. Skipping tests."
fi
- name: Notify Failure
if: failure()
uses: Ilshidur/action-discord@master
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
message: |
❌ [feat/* push] AI 유닛 테스트 실패!
브랜치: ${{ github.ref_name }}
[자세히 보기](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
pr-workflow:
if: github.event_name == 'pull_request'
name: AI Pull Request CI
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run unit tests
run: |
if [ -d "tests" ]; then
pytest tests
else
echo "No test directory found. Skipping tests."
fi
- name: Notify Failure - Unit Test
if: failure()
uses: Ilshidur/action-discord@master
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
message: |
❌ [PR] AI 유닛 테스트 실패!
브랜치: ${{ github.head_ref }} → ${{ github.base_ref }}
[자세히 보기](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
- name: Run Integration Test (only dev → main)
if: github.base_ref == 'main' && github.head_ref == 'dev'
run: pytest integration_tests || echo "No integration tests."
- name: Run Integration Test (background for feat → dev or hotfix → main)
if: github.base_ref == 'dev' || (startsWith(github.head_ref, 'hotfix/') && github.base_ref == 'main')
continue-on-error: true
run: pytest integration_tests || echo "No integration tests."
- name: Notify Integration Test Failure (background)
if: failure() && (github.base_ref == 'dev' || startsWith(github.head_ref, 'hotfix/'))
uses: Ilshidur/action-discord@master
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
message: |
⚠️ [PR] AI 통합 테스트 실패 (백그라운드)
브랜치: ${{ github.head_ref }} → ${{ github.base_ref }}
[자세히 보기](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
- name: Notify Success
if: success() && github.base_ref == 'main'
uses: Ilshidur/action-discord@master
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
message: |
✅ 모든 AI 테스트 통과! 이제 머지 가능합니다.
브랜치: ${{ github.head_ref }} → ${{ github.base_ref }}
[자세히 보기](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
package-artifact:
if: github.base_ref == 'main' || github.base_ref == 'dev'
name: Package AI Application
runs-on: ubuntu-latest
needs: pr-workflow
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Package AI application
run: |
mkdir -p artifact
cp -r ai_app/* artifact/
cp requirements.txt artifact/
- name: Upload AI artifact
uses: actions/upload-artifact@v3
with:
name: ai-server
path: artifact/
다운로드 및 전송
scp -i ~/.ssh/your-key.pem ai-server.zip your-user@your-ai-server:/home/ubuntu/onthetop-ai
배포
#!/bin/bash
# 1. 기존 서버 종료
pkill -f 'ai_app'
# 2. 아티팩트 압축 해제
unzip -o ai-server.zip -d ai-server
# 3. 가상환경 또는 pip로 의존성 설치
cd ai-server || exit
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
# 4. 서버 실행
nohup python3 main.py > app.log 2>&1 &