Skip to content

Commit

Permalink
feat: 블루/그린 전략으로 배포가 이루어질 수 있는 환경을 설정한다. #24
Browse files Browse the repository at this point in the history
  • Loading branch information
fru1tworld committed Nov 7, 2024
1 parent 10df0ec commit 5adbb20
Show file tree
Hide file tree
Showing 5 changed files with 6,142 additions and 43 deletions.
79 changes: 79 additions & 0 deletions .github/workflows/BACKEND_DEPLOY.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: 백엔드 배포
on:
push:
branches: [main]
paths:
- "packages/backend/**"
- "packages/shared/**"
jobs:
push_to_registry:
runs-on: ubuntu-latest
steps:
- name: 코드 체크아웃
uses: actions/checkout@v3

- name: Docker Buildx 설정
uses: docker/setup-buildx-action@v2

- name: NCP 컨테이너 레지스트리 로그인
uses: docker/login-action@v2
with:
registry: ${{ secrets.CONTAINER_REGISTRY_URL }}
username: ${{ secrets.NCP_ACCESS_KEY }}
password: ${{ secrets.NCP_SECERET_KEY }}

- name: 도커 이미지 빌드 및 푸시
id: docker-build
uses: docker/build-push-action@v3
with:
context: .
file: ./packages/backend/Dockerfile
push: true
tags: ${{secrets.CONTAINER_REGISTRY_URL}}/nodejs-server:latest

- name: 배포 결과 처리
if: always()
uses: actions/github-script@v6
with:
script: |
const buildOutcome = '${{ steps.docker-build.outcome }}';
const repoName = context.repo.repo;
await github.rest.checks.create({
owner: context.repo.owner,
repo: repoName,
name: '백엔드 배포 상태',
head_sha: context.sha,
status: 'completed',
conclusion: buildOutcome === 'success' ? 'success' : 'failure',
output: {
title: buildOutcome === 'success'
? '🚀 백엔드 배포 성공'
: '❌ 백엔드 배포 실패',
summary: buildOutcome === 'success'
? [
'## ✅ 배포 상태: 성공',
'',
'### 배포 정보:',
'- 📅 **배포 시간**: ' + new Date().toISOString(),
'- 🌏 **환경**: Production',
'- 📦 **이미지**: nodejs-server:latest',
'- 🎯 **대상**: NCP Container Registry',
'',
'🎉 프로덕션 환경에 성공적으로 배포되었습니다!'
].join('\n')
: [
'## ❌ 배포 상태: 실패',
'',
'### 오류 정보:',
'- 📅 **실패 시간**: ' + new Date().toISOString(),
'- 🚨 **실패 단계**: Docker 빌드 및 푸시',
'',
'### 문제 해결 방법:',
'1. Dockerfile 설정을 확인해주세요',
'2. 빌드 로그를 확인해주세요',
'3. NCP 인증 정보를 확인해주세요',
'',
].join('\n')
}
});
154 changes: 154 additions & 0 deletions .github/workflows/FRONTEND_DEPLOY.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
name: 프론트엔드 배포
on:
push:
branches: [main]
paths:
- "packages/frontend/**"
- "packages/shared/**"

permissions:
contents: read
checks: write
pull-requests: write

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 코드 체크아웃
uses: actions/checkout@v3

- name: PNPM 설정
uses: pnpm/action-setup@v2
with:
version: 9.4.0

- name: Node.js 설정
uses: actions/setup-node@v3
with:
node-version: "22.9.0"
cache: "pnpm"

- name: 의존성 설치
id: install
run: pnpm install
continue-on-error: true

- name: Shared 패키지 빌드
id: build-shared
if: steps.install.outcome == 'success'
working-directory: packages/shared
run: pnpm build
continue-on-error: true

- name: Frontend 패키지 빌드
id: build-frontend
if: steps.build-shared.outcome == 'success'
working-directory: packages/frontend
run: pnpm build
continue-on-error: true

- name: NCP 컨테이너 레지스트리 로그인
if: steps.build-frontend.outcome == 'success'
uses: docker/login-action@v2
with:
registry: ${{ secrets.CONTAINER_REGISTRY_URL }}
username: ${{ secrets.NCP_ACCESS_KEY }}
password: ${{ secrets.NCP_SECERET_KEY }}

- name: Docker 이미지 빌드 및 푸시
id: docker-build
if: steps.build-frontend.outcome == 'success'
uses: docker/build-push-action@v3
with:
context: .
file: ./packages/frontend/Dockerfile
push: true
tags: ${{ secrets.CONTAINER_REGISTRY_URL }}/frontend:latest

- name: 배포 결과 처리
if: always()
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const getKSTDate = () => {
const date = new Date();
date.setHours(date.getHours() + 9);
return date.toISOString();
};
const installOutcome = '${{ steps.install.outcome }}';
const sharedOutcome = '${{ steps.build-shared.outcome }}';
const frontendOutcome = '${{ steps.build-frontend.outcome }}';
const dockerOutcome = '${{ steps.docker-build.outcome }}';
const getFailedStep = () => {
if (installOutcome === 'failure') return '의존성 설치';
if (sharedOutcome === 'failure') return 'Shared 패키지 빌드';
if (frontendOutcome === 'failure') return 'Frontend 패키지 빌드';
if (dockerOutcome === 'failure') return 'Docker 빌드 및 푸시';
return '알 수 없음';
};
const finalOutcome =
installOutcome === 'success' &&
sharedOutcome === 'success' &&
frontendOutcome === 'success' &&
dockerOutcome === 'success'
? 'success' : 'failure';
try {
await github.rest.checks.create({
...context.repo,
name: '프론트엔드 배포 상태',
head_sha: context.sha,
status: 'completed',
conclusion: finalOutcome,
output: {
title: finalOutcome === 'success'
? '🚀 프론트엔드 배포 성공'
: '❌ 프론트엔드 배포 실패',
summary: finalOutcome === 'success'
? [
'## ✅ 배포 상태: 성공',
'',
'### 배포 정보:',
'- 📅 **배포 시간**: ' + getKSTDate(),
'- 🌏 **환경**: Production',
'- 📦 **빌드된 패키지**: shared, frontend',
'- 🎯 **대상**: NCP Container Registry',
'',
'### 빌드 단계 결과:',
'- ✅ 의존성 설치: 성공',
'- ✅ Shared 패키지 빌드: 성공',
'- ✅ Frontend 패키지 빌드: 성공',
'- ✅ Docker 이미지 빌드 및 푸시: 성공',
'',
'🎉 프로덕션 환경에 성공적으로 배포되었습니다!'
].join('\n')
: [
'## ❌ 배포 상태: 실패',
'',
'### 오류 정보:',
'- 📅 **실패 시간**: ' + getKSTDate(),
'- 🚨 **실패 단계**: ' + getFailedStep(),
'',
'### 빌드 단계 결과:',
`- ${installOutcome === 'success' ? '✅' : '❌'} 의존성 설치: ${installOutcome}`,
`- ${sharedOutcome === 'success' ? '✅' : '❌'} Shared 패키지 빌드: ${sharedOutcome}`,
`- ${frontendOutcome === 'success' ? '✅' : '❌'} Frontend 패키지 빌드: ${frontendOutcome}`,
`- ${dockerOutcome === 'success' ? '✅' : '❌'} Docker 이미지 빌드 및 푸시: ${dockerOutcome}`,
'',
'### 문제 해결 방법:',
'1. 실패한 단계의 로그를 확인해주세요',
'2. 의존성 및 빌드 스크립트를 확인해주세요',
'3. 환경 변수 설정을 확인해주세요',
'',
].join('\n')
}
});
} catch (error) {
console.error('Error creating check:', error);
core.setFailed(`Check creation failed: ${error.message}`);
}
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,9 @@ dist-ssr
*.njsproj
*.sln
*.sw?

# env
.env

# develop
*/backend/docker.compose.yml
74 changes: 73 additions & 1 deletion packages/backend/package.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,75 @@
{
"name": "backend"
"name": "backend",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/mongoose": "^10.1.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/typeorm": "^10.0.2",
"ioredis": "^5.4.1",
"mongoose": "^8.8.0",
"mysql2": "^3.11.4",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1",
"typeorm": "^0.3.20"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/express": "^5.0.0",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/supertest": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"eslint": "^8.0.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"jest": "^29.5.0",
"prettier": "^3.0.0",
"source-map-support": "^0.5.21",
"supertest": "^7.0.0",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.3",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.1.3"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
Loading

0 comments on commit 5adbb20

Please sign in to comment.