From 46c5e23972ce4c8ded84106b524bb99e0689b13a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Wed, 25 Feb 2026 11:37:03 +0900 Subject: [PATCH] =?UTF-8?q?docs:=EB=B0=B0=ED=8F=AC=20=EA=B0=80=EC=9D=B4?= =?UTF-8?q?=EB=93=9C=20=ED=98=84=ED=96=89=ED=99=94=20=E2=80=94=20=EB=8F=99?= =?UTF-8?q?=EC=8B=9C=EB=B9=8C=EB=93=9C=EB=B0=A9=EC=A7=80,=20=EC=8A=B9?= =?UTF-8?q?=EC=9D=B8=EC=95=8C=EB=A6=BC,=20=ED=99=98=EA=B2=BD=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EB=B3=80=EA=B2=BD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 전체 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 --- deploys/ops-manual/05-deployment.md | 62 ++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/deploys/ops-manual/05-deployment.md b/deploys/ops-manual/05-deployment.md index 8a416b2..43f7bd5 100644 --- a/deploys/ops-manual/05-deployment.md +++ b/deploys/ops-manual/05-deployment.md @@ -22,6 +22,13 @@ | sam-manage | Laravel Admin 배포 | main | 운영 (직접) | | sam-sales | 레거시 PHP 배포 | main | 운영 (직접) | +### Slack 알림 채널 + +| 채널 | 용도 | 알림 내용 | +|------|------|----------| +| `#product_infra` | 빌드/배포 상태 | 빌드 시작, 배포 성공/실패 | +| `#product_deploy` | 운영 배포 승인 | Stage 배포 완료 후 승인 대기 알림 (Jenkins 승인 링크 포함) | + ### 2-Branch 전략 (develop + main) > **stage 브랜치 없음.** main 브랜치 push 시 Stage 자동 배포 → Jenkins 승인 → Production 배포. @@ -35,7 +42,12 @@ 1. 개발자가 develop → main 머지 후 push 2. post-receive hook → CI/CD Gitea 자동 push 3. Jenkins 빌드 → Stage 자동 배포 -4. Jenkins UI에서 **승인 클릭** → Production 배포 (24시간 타임아웃) +4. `#product_deploy` Slack 채널에 승인 대기 알림 전송 +5. Jenkins UI에서 **승인 클릭** → Production 배포 (24시간 타임아웃) + +> **동시 빌드 방지:** 모든 파이프라인에 `disableConcurrentBuilds()` 적용. +> 같은 프로젝트에서 빌드가 동시에 2개 이상 돌지 않음. +> 승인 대기 중 새 push 시 → 기존 빌드 Abort 후 새 빌드 자동 시작. **main 브랜치 배포 흐름 (mng/sales):** 1. 개발자가 main push → hook → CI/CD Gitea → Jenkins → Production 직접 배포 @@ -141,6 +153,8 @@ Repository Settings → Webhooks → Add Webhook (Gitea) │ │ │ │ ├─ Stage 자동 배포 (react: .env.stage 빌드) │ │ │ │ +│ ├─ 📢 #product_deploy Slack 알림 (승인 링크 포함) │ +│ │ │ │ ├─ ⏸️ 승인 대기 (24시간 타임아웃) │ │ │ https://ci.sam.it.kr 에서 "운영 배포 진행" 클릭 │ │ │ │ @@ -183,11 +197,11 @@ CI/CD Gitea push -> Webhook -> Jenkins **환경변수 파일 (CI/CD 서버):** /var/lib/jenkins/env-files/react/ -| 파일 | API URL | Frontend URL | APP_ENV | -|------|---------|-------------|---------| -| .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.main | https://api.sam.it.kr | https://sam.it.kr | production | +| 파일 | API URL | Frontend URL | APP_ENV | DEV_TOOLBAR | +|------|---------|-------------|---------|-------------| +| .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.main | https://api.sam.it.kr | https://sam.it.kr | production | false | > `NEXT_PUBLIC_APP_ENV` 값으로 타이틀 접두사 결정: `development` → `[D]`, `local` → `[L]`, 그 외 → 없음 @@ -201,6 +215,10 @@ CI/CD Gitea push -> Webhook -> Jenkins pipeline { agent any + options { + disableConcurrentBuilds() + } + environment { DEPLOY_USER = 'hskwon' RELEASE_ID = new Date().format('yyyyMMdd_HHmmss') @@ -220,10 +238,10 @@ pipeline { script { if (env.BRANCH_NAME == 'main') { // 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 { 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.*' \ .next package.json next.config.ts public node_modules \ ${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' """ } @@ -264,7 +282,7 @@ pipeline { rsync -az --delete \ .next package.json next.config.ts public node_modules \ ${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 ' 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 && @@ -279,6 +297,8 @@ pipeline { stage('Production Approval') { when { branch 'main' } 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') { input message: 'Stage 확인 후 운영 배포를 진행하시겠습니까?\nStage: https://stage.sam.it.kr', ok: '운영 배포 진행' @@ -290,7 +310,7 @@ pipeline { stage('Rebuild for Production') { when { branch 'main' } 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' } } @@ -305,7 +325,7 @@ pipeline { rsync -az --delete \ .next package.json next.config.ts public node_modules \ ${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 ' ln -sfn /home/webservice/react/releases/${RELEASE_ID} /home/webservice/react/current && cd /home/webservice && pm2 reload sam-front && @@ -334,6 +354,10 @@ pipeline { > Stage(.env.stage)와 Production(.env.main)에서 별도 빌드가 필요하다. > main 빌드 시 Stage용으로 먼저 빌드 → 승인 후 Production용으로 재빌드. +> **환경파일:** Jenkins는 CI/CD 서버의 env-files를 `.env.production`으로 복사하여 빌드한다. +> Next.js 우선순위: `.env.local` > `.env.production` > `.env` +> 따라서 서버에 `.env.local`이 있으면 `.env.production`을 덮어쓰므로 `.env.local`은 사용하지 않는다. + ### PM2 수동 재시작 ```bash @@ -374,6 +398,10 @@ CI/CD Gitea push -> Webhook -> Jenkins pipeline { agent any + options { + disableConcurrentBuilds() + } + environment { DEPLOY_USER = 'hskwon' RELEASE_ID = new Date().format('yyyyMMdd_HHmmss') @@ -423,6 +451,8 @@ pipeline { stage('Production Approval') { when { branch 'main' } 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') { input message: 'Stage 확인 후 운영 배포를 진행하시겠습니까?\nStage API: https://stage-api.sam.it.kr', ok: '운영 배포 진행' @@ -583,6 +613,10 @@ API와 동일한 releases/shared 구조. 차이점: npm build 추가, Queue Work pipeline { agent any + options { + disableConcurrentBuilds() + } + environment { DEPLOY_USER = 'hskwon' RELEASE_ID = new Date().format('yyyyMMdd_HHmmss') @@ -813,7 +847,7 @@ ssh sam-prod " cd /tmp git clone --depth 1 --branch main https://git.sam.it.kr/SamProject/sam-react-prod.git 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 run build @@ -824,7 +858,7 @@ ssh sam-prod "mkdir -p /home/webservice/react/releases/${RELEASE_ID}" rsync -az --delete \ .next package.json next.config.ts public node_modules \ 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 재시작 ssh sam-prod "