docs:배포 가이드 현행화 — 동시빌드방지, 승인알림, 환경파일 변경 반영
- 전체 Jenkinsfile에 disableConcurrentBuilds() 반영 - react/api Production Approval에 #product_deploy Slack 알림 추가 - react 환경파일 .env.local → .env.production 변경 반영 - Slack 알림 채널 테이블 추가 (#product_infra, #product_deploy) - 환경변수 파일 테이블 DEV_TOOLBAR 컬럼 추가 - 수동 배포 섹션 .env.production 반영 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,13 @@
|
|||||||
| sam-manage | Laravel Admin 배포 | main | 운영 (직접) |
|
| sam-manage | Laravel Admin 배포 | main | 운영 (직접) |
|
||||||
| sam-sales | 레거시 PHP 배포 | main | 운영 (직접) |
|
| sam-sales | 레거시 PHP 배포 | main | 운영 (직접) |
|
||||||
|
|
||||||
|
### Slack 알림 채널
|
||||||
|
|
||||||
|
| 채널 | 용도 | 알림 내용 |
|
||||||
|
|------|------|----------|
|
||||||
|
| `#product_infra` | 빌드/배포 상태 | 빌드 시작, 배포 성공/실패 |
|
||||||
|
| `#product_deploy` | 운영 배포 승인 | Stage 배포 완료 후 승인 대기 알림 (Jenkins 승인 링크 포함) |
|
||||||
|
|
||||||
### 2-Branch 전략 (develop + main)
|
### 2-Branch 전략 (develop + main)
|
||||||
|
|
||||||
> **stage 브랜치 없음.** main 브랜치 push 시 Stage 자동 배포 → Jenkins 승인 → Production 배포.
|
> **stage 브랜치 없음.** main 브랜치 push 시 Stage 자동 배포 → Jenkins 승인 → Production 배포.
|
||||||
@@ -35,7 +42,12 @@
|
|||||||
1. 개발자가 develop → main 머지 후 push
|
1. 개발자가 develop → main 머지 후 push
|
||||||
2. post-receive hook → CI/CD Gitea 자동 push
|
2. post-receive hook → CI/CD Gitea 자동 push
|
||||||
3. Jenkins 빌드 → Stage 자동 배포
|
3. Jenkins 빌드 → Stage 자동 배포
|
||||||
4. Jenkins UI에서 **승인 클릭** → Production 배포 (24시간 타임아웃)
|
4. `#product_deploy` Slack 채널에 승인 대기 알림 전송
|
||||||
|
5. Jenkins UI에서 **승인 클릭** → Production 배포 (24시간 타임아웃)
|
||||||
|
|
||||||
|
> **동시 빌드 방지:** 모든 파이프라인에 `disableConcurrentBuilds()` 적용.
|
||||||
|
> 같은 프로젝트에서 빌드가 동시에 2개 이상 돌지 않음.
|
||||||
|
> 승인 대기 중 새 push 시 → 기존 빌드 Abort 후 새 빌드 자동 시작.
|
||||||
|
|
||||||
**main 브랜치 배포 흐름 (mng/sales):**
|
**main 브랜치 배포 흐름 (mng/sales):**
|
||||||
1. 개발자가 main push → hook → CI/CD Gitea → Jenkins → Production 직접 배포
|
1. 개발자가 main push → hook → CI/CD Gitea → Jenkins → Production 직접 배포
|
||||||
@@ -141,6 +153,8 @@ Repository Settings → Webhooks → Add Webhook (Gitea)
|
|||||||
│ │ │
|
│ │ │
|
||||||
│ ├─ Stage 자동 배포 (react: .env.stage 빌드) │
|
│ ├─ Stage 자동 배포 (react: .env.stage 빌드) │
|
||||||
│ │ │
|
│ │ │
|
||||||
|
│ ├─ 📢 #product_deploy Slack 알림 (승인 링크 포함) │
|
||||||
|
│ │ │
|
||||||
│ ├─ ⏸️ 승인 대기 (24시간 타임아웃) │
|
│ ├─ ⏸️ 승인 대기 (24시간 타임아웃) │
|
||||||
│ │ https://ci.sam.it.kr 에서 "운영 배포 진행" 클릭 │
|
│ │ https://ci.sam.it.kr 에서 "운영 배포 진행" 클릭 │
|
||||||
│ │ │
|
│ │ │
|
||||||
@@ -183,11 +197,11 @@ CI/CD Gitea push -> Webhook -> Jenkins
|
|||||||
|
|
||||||
**환경변수 파일 (CI/CD 서버):** /var/lib/jenkins/env-files/react/
|
**환경변수 파일 (CI/CD 서버):** /var/lib/jenkins/env-files/react/
|
||||||
|
|
||||||
| 파일 | API URL | Frontend URL | APP_ENV |
|
| 파일 | API URL | Frontend URL | APP_ENV | DEV_TOOLBAR |
|
||||||
|------|---------|-------------|---------|
|
|------|---------|-------------|---------|-------------|
|
||||||
| .env.develop | https://api.codebridge-x.com | https://dev.codebridge-x.com | development |
|
| .env.develop | https://api.codebridge-x.com | https://dev.codebridge-x.com | development | - |
|
||||||
| .env.stage | https://stage-api.sam.it.kr | https://stage.sam.it.kr | staging |
|
| .env.stage | https://stage-api.sam.it.kr | https://stage.sam.it.kr | staging | - |
|
||||||
| .env.main | https://api.sam.it.kr | https://sam.it.kr | production |
|
| .env.main | https://api.sam.it.kr | https://sam.it.kr | production | false |
|
||||||
|
|
||||||
> `NEXT_PUBLIC_APP_ENV` 값으로 타이틀 접두사 결정: `development` → `[D]`, `local` → `[L]`, 그 외 → 없음
|
> `NEXT_PUBLIC_APP_ENV` 값으로 타이틀 접두사 결정: `development` → `[D]`, `local` → `[L]`, 그 외 → 없음
|
||||||
|
|
||||||
@@ -201,6 +215,10 @@ CI/CD Gitea push -> Webhook -> Jenkins
|
|||||||
pipeline {
|
pipeline {
|
||||||
agent any
|
agent any
|
||||||
|
|
||||||
|
options {
|
||||||
|
disableConcurrentBuilds()
|
||||||
|
}
|
||||||
|
|
||||||
environment {
|
environment {
|
||||||
DEPLOY_USER = 'hskwon'
|
DEPLOY_USER = 'hskwon'
|
||||||
RELEASE_ID = new Date().format('yyyyMMdd_HHmmss')
|
RELEASE_ID = new Date().format('yyyyMMdd_HHmmss')
|
||||||
@@ -220,10 +238,10 @@ pipeline {
|
|||||||
script {
|
script {
|
||||||
if (env.BRANCH_NAME == 'main') {
|
if (env.BRANCH_NAME == 'main') {
|
||||||
// main: Stage 빌드 먼저 (승인 후 Production 재빌드)
|
// main: Stage 빌드 먼저 (승인 후 Production 재빌드)
|
||||||
sh "cp /var/lib/jenkins/env-files/react/.env.stage .env.local"
|
sh "cp /var/lib/jenkins/env-files/react/.env.stage .env.production"
|
||||||
} else {
|
} else {
|
||||||
def envFile = "/var/lib/jenkins/env-files/react/.env.${env.BRANCH_NAME}"
|
def envFile = "/var/lib/jenkins/env-files/react/.env.${env.BRANCH_NAME}"
|
||||||
sh "cp ${envFile} .env.local"
|
sh "cp ${envFile} .env.production"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,7 +265,7 @@ pipeline {
|
|||||||
--exclude='.git' --exclude='.env*' --exclude='ecosystem.config.*' \
|
--exclude='.git' --exclude='.env*' --exclude='ecosystem.config.*' \
|
||||||
.next package.json next.config.ts public node_modules \
|
.next package.json next.config.ts public node_modules \
|
||||||
${DEPLOY_USER}@114.203.209.83:/home/webservice/react/
|
${DEPLOY_USER}@114.203.209.83:/home/webservice/react/
|
||||||
scp .env.local ${DEPLOY_USER}@114.203.209.83:/home/webservice/react/.env.local
|
scp .env.production ${DEPLOY_USER}@114.203.209.83:/home/webservice/react/.env.production
|
||||||
ssh ${DEPLOY_USER}@114.203.209.83 'cd /home/webservice/react && pm2 restart sam-react'
|
ssh ${DEPLOY_USER}@114.203.209.83 'cd /home/webservice/react && pm2 restart sam-react'
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
@@ -264,7 +282,7 @@ pipeline {
|
|||||||
rsync -az --delete \
|
rsync -az --delete \
|
||||||
.next package.json next.config.ts public node_modules \
|
.next package.json next.config.ts public node_modules \
|
||||||
${DEPLOY_USER}@211.117.60.189:/home/webservice/react-stage/releases/${RELEASE_ID}/
|
${DEPLOY_USER}@211.117.60.189:/home/webservice/react-stage/releases/${RELEASE_ID}/
|
||||||
scp .env.local ${DEPLOY_USER}@211.117.60.189:/home/webservice/react-stage/releases/${RELEASE_ID}/.env.local
|
scp .env.production ${DEPLOY_USER}@211.117.60.189:/home/webservice/react-stage/releases/${RELEASE_ID}/.env.production
|
||||||
ssh ${DEPLOY_USER}@211.117.60.189 '
|
ssh ${DEPLOY_USER}@211.117.60.189 '
|
||||||
ln -sfn /home/webservice/react-stage/releases/${RELEASE_ID} /home/webservice/react-stage/current &&
|
ln -sfn /home/webservice/react-stage/releases/${RELEASE_ID} /home/webservice/react-stage/current &&
|
||||||
cd /home/webservice && pm2 reload sam-front-stage 2>/dev/null || pm2 start react-stage/current/node_modules/.bin/next --name sam-front-stage -- start -p 3100 &&
|
cd /home/webservice && pm2 reload sam-front-stage 2>/dev/null || pm2 start react-stage/current/node_modules/.bin/next --name sam-front-stage -- start -p 3100 &&
|
||||||
@@ -279,6 +297,8 @@ pipeline {
|
|||||||
stage('Production Approval') {
|
stage('Production Approval') {
|
||||||
when { branch 'main' }
|
when { branch 'main' }
|
||||||
steps {
|
steps {
|
||||||
|
slackSend channel: '#product_deploy', color: '#FF9800', tokenCredentialId: 'slack-token',
|
||||||
|
message: "🔔 *react* 운영 배포 승인 대기 중\nStage: https://stage.sam.it.kr\n<${env.BUILD_URL}input|승인하러 가기>"
|
||||||
timeout(time: 24, unit: 'HOURS') {
|
timeout(time: 24, unit: 'HOURS') {
|
||||||
input message: 'Stage 확인 후 운영 배포를 진행하시겠습니까?\nStage: https://stage.sam.it.kr',
|
input message: 'Stage 확인 후 운영 배포를 진행하시겠습니까?\nStage: https://stage.sam.it.kr',
|
||||||
ok: '운영 배포 진행'
|
ok: '운영 배포 진행'
|
||||||
@@ -290,7 +310,7 @@ pipeline {
|
|||||||
stage('Rebuild for Production') {
|
stage('Rebuild for Production') {
|
||||||
when { branch 'main' }
|
when { branch 'main' }
|
||||||
steps {
|
steps {
|
||||||
sh "cp /var/lib/jenkins/env-files/react/.env.main .env.local"
|
sh "cp /var/lib/jenkins/env-files/react/.env.main .env.production"
|
||||||
sh 'npm run build'
|
sh 'npm run build'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -305,7 +325,7 @@ pipeline {
|
|||||||
rsync -az --delete \
|
rsync -az --delete \
|
||||||
.next package.json next.config.ts public node_modules \
|
.next package.json next.config.ts public node_modules \
|
||||||
${DEPLOY_USER}@211.117.60.189:/home/webservice/react/releases/${RELEASE_ID}/
|
${DEPLOY_USER}@211.117.60.189:/home/webservice/react/releases/${RELEASE_ID}/
|
||||||
scp .env.local ${DEPLOY_USER}@211.117.60.189:/home/webservice/react/releases/${RELEASE_ID}/.env.local
|
scp .env.production ${DEPLOY_USER}@211.117.60.189:/home/webservice/react/releases/${RELEASE_ID}/.env.production
|
||||||
ssh ${DEPLOY_USER}@211.117.60.189 '
|
ssh ${DEPLOY_USER}@211.117.60.189 '
|
||||||
ln -sfn /home/webservice/react/releases/${RELEASE_ID} /home/webservice/react/current &&
|
ln -sfn /home/webservice/react/releases/${RELEASE_ID} /home/webservice/react/current &&
|
||||||
cd /home/webservice && pm2 reload sam-front &&
|
cd /home/webservice && pm2 reload sam-front &&
|
||||||
@@ -334,6 +354,10 @@ pipeline {
|
|||||||
> Stage(.env.stage)와 Production(.env.main)에서 별도 빌드가 필요하다.
|
> Stage(.env.stage)와 Production(.env.main)에서 별도 빌드가 필요하다.
|
||||||
> main 빌드 시 Stage용으로 먼저 빌드 → 승인 후 Production용으로 재빌드.
|
> main 빌드 시 Stage용으로 먼저 빌드 → 승인 후 Production용으로 재빌드.
|
||||||
|
|
||||||
|
> **환경파일:** Jenkins는 CI/CD 서버의 env-files를 `.env.production`으로 복사하여 빌드한다.
|
||||||
|
> Next.js 우선순위: `.env.local` > `.env.production` > `.env`
|
||||||
|
> 따라서 서버에 `.env.local`이 있으면 `.env.production`을 덮어쓰므로 `.env.local`은 사용하지 않는다.
|
||||||
|
|
||||||
### PM2 수동 재시작
|
### PM2 수동 재시작
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -374,6 +398,10 @@ CI/CD Gitea push -> Webhook -> Jenkins
|
|||||||
pipeline {
|
pipeline {
|
||||||
agent any
|
agent any
|
||||||
|
|
||||||
|
options {
|
||||||
|
disableConcurrentBuilds()
|
||||||
|
}
|
||||||
|
|
||||||
environment {
|
environment {
|
||||||
DEPLOY_USER = 'hskwon'
|
DEPLOY_USER = 'hskwon'
|
||||||
RELEASE_ID = new Date().format('yyyyMMdd_HHmmss')
|
RELEASE_ID = new Date().format('yyyyMMdd_HHmmss')
|
||||||
@@ -423,6 +451,8 @@ pipeline {
|
|||||||
stage('Production Approval') {
|
stage('Production Approval') {
|
||||||
when { branch 'main' }
|
when { branch 'main' }
|
||||||
steps {
|
steps {
|
||||||
|
slackSend channel: '#product_deploy', color: '#FF9800', tokenCredentialId: 'slack-token',
|
||||||
|
message: "🔔 *api* 운영 배포 승인 대기 중\nStage API: https://stage-api.sam.it.kr\n<${env.BUILD_URL}input|승인하러 가기>"
|
||||||
timeout(time: 24, unit: 'HOURS') {
|
timeout(time: 24, unit: 'HOURS') {
|
||||||
input message: 'Stage 확인 후 운영 배포를 진행하시겠습니까?\nStage API: https://stage-api.sam.it.kr',
|
input message: 'Stage 확인 후 운영 배포를 진행하시겠습니까?\nStage API: https://stage-api.sam.it.kr',
|
||||||
ok: '운영 배포 진행'
|
ok: '운영 배포 진행'
|
||||||
@@ -583,6 +613,10 @@ API와 동일한 releases/shared 구조. 차이점: npm build 추가, Queue Work
|
|||||||
pipeline {
|
pipeline {
|
||||||
agent any
|
agent any
|
||||||
|
|
||||||
|
options {
|
||||||
|
disableConcurrentBuilds()
|
||||||
|
}
|
||||||
|
|
||||||
environment {
|
environment {
|
||||||
DEPLOY_USER = 'hskwon'
|
DEPLOY_USER = 'hskwon'
|
||||||
RELEASE_ID = new Date().format('yyyyMMdd_HHmmss')
|
RELEASE_ID = new Date().format('yyyyMMdd_HHmmss')
|
||||||
@@ -813,7 +847,7 @@ ssh sam-prod "
|
|||||||
cd /tmp
|
cd /tmp
|
||||||
git clone --depth 1 --branch main https://git.sam.it.kr/SamProject/sam-react-prod.git react-build
|
git clone --depth 1 --branch main https://git.sam.it.kr/SamProject/sam-react-prod.git react-build
|
||||||
cd react-build
|
cd react-build
|
||||||
cp /var/lib/jenkins/env-files/react/.env.main .env.local
|
cp /var/lib/jenkins/env-files/react/.env.main .env.production
|
||||||
npm install --prefer-offline
|
npm install --prefer-offline
|
||||||
npm run build
|
npm run build
|
||||||
|
|
||||||
@@ -824,7 +858,7 @@ ssh sam-prod "mkdir -p /home/webservice/react/releases/${RELEASE_ID}"
|
|||||||
rsync -az --delete \
|
rsync -az --delete \
|
||||||
.next package.json next.config.ts public node_modules \
|
.next package.json next.config.ts public node_modules \
|
||||||
hskwon@211.117.60.189:/home/webservice/react/releases/${RELEASE_ID}/
|
hskwon@211.117.60.189:/home/webservice/react/releases/${RELEASE_ID}/
|
||||||
scp .env.local hskwon@211.117.60.189:/home/webservice/react/releases/${RELEASE_ID}/.env.local
|
scp .env.production hskwon@211.117.60.189:/home/webservice/react/releases/${RELEASE_ID}/.env.production
|
||||||
|
|
||||||
# 심링크 전환 및 PM2 재시작
|
# 심링크 전환 및 PM2 재시작
|
||||||
ssh sam-prod "
|
ssh sam-prod "
|
||||||
|
|||||||
Reference in New Issue
Block a user