From a11fe745b620af199527367c7f01e89071c46317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Tue, 24 Feb 2026 07:09:09 +0900 Subject: [PATCH] =?UTF-8?q?docs(DOC):=20CI/CD=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EC=85=8B=ED=8C=85=20=EA=B0=80=EC=9D=B4=EB=93=9C=20=E2=80=94=20?= =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=8C=8C=EC=9D=B4=ED=94=84=EB=9D=BC?= =?UTF-8?q?=EC=9D=B8=20=EA=B2=80=EC=A6=9D=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - react develop 파이프라인 E2E 검증 완료 (Jenkins Build #3 SUCCESS) - Jenkinsfile rsync trailing slash 버그 수정 반영 (.next/ → .next) - 환경별 .env 파일 설정 및 도메인 매핑 정보 추가 - Prometheus localhost 바인딩 반영 (보안) - 설치 순서 ⑩⑪⑫ 완료 표시, 상태 업데이트 Co-Authored-By: Claude Opus 4.6 --- deploys/cicd-server-setup.md | 116 +++++++++++++++++++++++------------ 1 file changed, 76 insertions(+), 40 deletions(-) diff --git a/deploys/cicd-server-setup.md b/deploys/cicd-server-setup.md index 39beca5..af1cc8e 100644 --- a/deploys/cicd-server-setup.md +++ b/deploys/cicd-server-setup.md @@ -1,7 +1,7 @@ # CI/CD 서버 셋팅 가이드 > 작성일: 2026-02-23 | 최종 수정: 2026-02-24 -> 상태: 설치 완료 (Jenkinsfile 작성 + 실제 배포 테스트 남음) +> 상태: ✅ 설치 및 배포 테스트 완료 (react develop 파이프라인 검증 완료) --- @@ -661,7 +661,7 @@ ExecStart=/usr/local/bin/prometheus \ --config.file=/etc/prometheus/prometheus.yml \ --storage.tsdb.path=/var/lib/prometheus/ \ --storage.tsdb.retention.time=30d \ - --web.listen-address=:9090 + --web.listen-address=127.0.0.1:9090 Restart=always [Install] @@ -859,7 +859,12 @@ pipeline { ### 파이프라인: Next.js React (react/) -**Jenkinsfile** (`react/Jenkinsfile`): +> **검증 완료**: Jenkins Build #3 (develop) — 빌드 + 배포 성공 (241초) +> - rsync source trailing slash 주의: `.next/` (X) → `.next` (O) +> - 개발서버 PM2: `sam-react` (fork), 운영서버 PM2: `sam-front` (cluster) +> - `npm install --prefer-offline` 사용 (package-lock.json이 .gitignore에 포함) + +**Jenkinsfile** (`react/Jenkinsfile`) — 실제 배포 검증 완료: ```groovy pipeline { @@ -867,7 +872,7 @@ pipeline { environment { DEPLOY_USER = 'hskwon' - RELEASE_ID = new Date().format('yyyyMMdd_HHmmss') + RELEASE_ID = new Date().format('yyyyMMdd_HHmmss') } stages { @@ -875,36 +880,49 @@ pipeline { steps { checkout scm } } - stage('Build') { + stage('Prepare Env') { steps { - sh 'npm ci && npm run build' + script { + def envFile = "/var/lib/jenkins/env-files/react/.env.${env.BRANCH_NAME}" + sh "cp ${envFile} .env.local" + } } } - // ── main → 운영서버 (배포관리자 수동 push 후 트리거) ── - stage('Deploy Production') { - when { branch 'main' } + stage('Install') { + steps { + sh 'npm install --prefer-offline' + } + } + + stage('Build') { + steps { + sh 'npm run build' + } + } + + // ── develop → 개발서버 배포 ── + stage('Deploy Development') { + when { branch 'develop' } steps { sshagent(credentials: ['deploy-ssh-key']) { sh """ - ssh ${DEPLOY_USER}@211.117.60.189 'mkdir -p /home/webservice/react/releases/${RELEASE_ID}' - rsync -az --delete \ - .next/ package.json package-lock.json next.config.* public/ node_modules/ \ - ${DEPLOY_USER}@211.117.60.189:/home/webservice/react/releases/${RELEASE_ID}/ + --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/ - ssh ${DEPLOY_USER}@211.117.60.189 ' - ln -sfn /home/webservice/react/shared/.env.local /home/webservice/react/releases/${RELEASE_ID}/.env.local && - ln -sfn /home/webservice/react/releases/${RELEASE_ID} /home/webservice/react/current && - cd /home/webservice && pm2 reload sam-front && - cd /home/webservice/react/releases && ls -1dt */ | tail -n +4 | xargs rm -rf 2>/dev/null || true - ' + scp .env.local ${DEPLOY_USER}@114.203.209.83:/home/webservice/react/.env.local + + ssh ${DEPLOY_USER}@114.203.209.83 'cd /home/webservice/react && pm2 restart sam-react' """ } } } - // ── stage → 운영서버 Stage ── + // ── stage → 운영서버 Stage 배포 ── stage('Deploy Stage') { when { branch 'stage' } steps { @@ -913,34 +931,39 @@ pipeline { ssh ${DEPLOY_USER}@211.117.60.189 'mkdir -p /home/webservice/react-stage/releases/${RELEASE_ID}' rsync -az --delete \ - .next/ package.json package-lock.json next.config.* 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}/ + scp .env.local ${DEPLOY_USER}@211.117.60.189:/home/webservice/react-stage/releases/${RELEASE_ID}/.env.local + ssh ${DEPLOY_USER}@211.117.60.189 ' - ln -sfn /home/webservice/react-stage/shared/.env.local /home/webservice/react-stage/releases/${RELEASE_ID}/.env.local && ln -sfn /home/webservice/react-stage/releases/${RELEASE_ID} /home/webservice/react-stage/current && - cd /home/webservice && pm2 reload sam-front-stage && - cd /home/webservice/react-stage/releases && ls -1dt */ | tail -n +3 | xargs rm -rf 2>/dev/null || true + 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/react-stage/releases && ls -1dt */ | tail -n +4 | xargs rm -rf 2>/dev/null || true ' """ } } } - // ── develop → 개발서버 (CI/CD에서 빌드 후 배포) ── - stage('Deploy Development') { - when { branch 'develop' } + // ── main → 운영서버 Production 배포 ── + stage('Deploy Production') { + when { branch 'main' } steps { sshagent(credentials: ['deploy-ssh-key']) { sh """ - # 빌드 결과물을 개발서버로 전송 - rsync -az --delete \ - .next/ package.json package-lock.json next.config.* public/ node_modules/ \ - ${DEPLOY_USER}@114.203.209.83:/home/webservice/react/ + ssh ${DEPLOY_USER}@211.117.60.189 'mkdir -p /home/webservice/react/releases/${RELEASE_ID}' - ssh ${DEPLOY_USER}@114.203.209.83 ' - cd /home/webservice/react && - pm2 restart sam-front + 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 + + 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 && + cd /home/webservice/react/releases && ls -1dt */ | tail -n +6 | xargs rm -rf 2>/dev/null || true ' """ } @@ -949,12 +972,25 @@ pipeline { } post { - success { echo "✅ react 배포 완료 (${env.BRANCH_NAME})" } - failure { echo "❌ react 배포 실패 (${env.BRANCH_NAME})" } + success { echo '✅ react 배포 완료 (' + env.BRANCH_NAME + ')' } + failure { echo '❌ react 배포 실패 (' + env.BRANCH_NAME + ')' } } } ``` +**환경별 .env 파일** (`/var/lib/jenkins/env-files/react/`): + +| 파일 | API URL | Frontend URL | +|------|---------|-------------| +| `.env.develop` | `https://api.codebridge-x.com` | `https://dev.codebridge-x.com` | +| `.env.stage` | `https://stage-api.sam.it.kr` | `https://stage.sam.it.kr` | +| `.env.main` | `https://api.sam.it.kr` | `https://sam.it.kr` | + +> **주의사항 (rsync)**: +> - 소스 경로에 trailing slash 사용 금지: `.next/` → 내용만 복사됨, `.next` → 디렉토리째 복사 +> - `--delete`와 함께 사용 시 `--exclude`로 `.git`, `.env*` 보호 필수 (개발서버) +> - Release 기반 배포 (stage/main)는 새 디렉토리이므로 exclude 불필요 + ### 파이프라인: Sales (레거시 PHP) ```groovy @@ -1154,9 +1190,9 @@ sudo certbot certificates | ⑦ | Nginx + SSL | 20분 | ⑤⑥ | | ⑧ | Prometheus + node_exporter | 15분 | ① | | ⑨ | Grafana | 15분 | ⑧ | -| ⑩ | Jenkins 파이프라인 + Webhook 설정 | 1시간 | ⑥⑦ | -| ⑪ | 백업 자동화 | 15분 | ②-s | -| ⑫ | 최종 점검 + 보안 | 30분 | 전체 | +| ⑩ | Jenkins 파이프라인 + Webhook 설정 ✅ | 1시간 | ⑥⑦ | +| ⑪ | 백업 자동화 ✅ | 15분 | ②-s | +| ⑫ | 최종 점검 + 보안 ✅ | 30분 | 전체 | **총 예상 시간: 5~6시간** @@ -1169,4 +1205,4 @@ sudo certbot certificates - [x] ~~도메인 확정~~ → git.sam.it.kr, ci.sam.it.kr, monitor.sam.it.kr (SSL 발급 완료) - [ ] **Jenkins 테스트 실행 여부**: CI에서 phpunit/lint 실행 vs 배포만 - [ ] **알림 채널**: Slack, 이메일, 카카오톡 등 -- [ ] **개발서버 Gitea bare repo 경로 확인** (hook 설정을 위해) \ No newline at end of file +- [x] ~~개발서버 Gitea bare repo 경로 확인~~ → `/data/GIT/samproject/.git/` \ No newline at end of file