docs(DOC): ops-manual 2-Branch 전략 반영 — stage 브랜치 제거, Jenkins 승인 기반 배포
- 04-service-cicd: credential 타입 수정, Nginx 설정 추가, 동기화 브랜치 테이블 업데이트 - 05-deployment: 파이프라인 설정/흐름도/Jenkinsfile 전면 개편 (develop+main 2-Branch) - React/API: main push → Stage 자동배포 → Jenkins 승인 → Production 배포 - MNG/Sales: main push → Production 직접 배포 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -48,7 +48,7 @@ sudo journalctl -u jenkins --since "2 hours ago" --no-pager
|
|||||||
| Credential ID | 유형 | 용도 |
|
| Credential ID | 유형 | 용도 |
|
||||||
|--------------|------|------|
|
|--------------|------|------|
|
||||||
| deploy-ssh-key | SSH Username with private key | 운영/개발서버 SSH 배포 |
|
| deploy-ssh-key | SSH Username with private key | 운영/개발서버 SSH 배포 |
|
||||||
| gitea-api-token | Secret text | Gitea API 연동 |
|
| gitea-api-token | Username with password | Gitea API 연동 (token을 username, 비밀번호 빈값) |
|
||||||
|
|
||||||
**Credential 위치:** Jenkins 관리 > Credentials > System > Global credentials
|
**Credential 위치:** Jenkins 관리 > Credentials > System > Global credentials
|
||||||
|
|
||||||
@@ -181,18 +181,24 @@ sudo -u git /usr/local/bin/gitea admin user create \
|
|||||||
|
|
||||||
**토큰 파일 (개발서버):** `/data/GIT/.cicd-env` (chmod 600, owner: git)
|
**토큰 파일 (개발서버):** `/data/GIT/.cicd-env` (chmod 600, owner: git)
|
||||||
|
|
||||||
| 저장소 | 동기화 브랜치 |
|
| 저장소 | 동기화 브랜치 | 비고 |
|
||||||
|--------|-------------|
|
|--------|-------------|------|
|
||||||
| sam-react-prod | stage, develop |
|
| sam-react-prod | main, develop | post-update hook 비활성화 (CI/CD가 개발서버 배포 담당) |
|
||||||
| sam-api | stage |
|
| sam-api | main | develop은 기존 post-update hook 유지 |
|
||||||
| sam-sales | main |
|
| sam-sales | main | |
|
||||||
| sam-manage | 없음 (배포관리자 수동 push) |
|
| sam-manage | main | 2026-02-24 hook 추가 |
|
||||||
|
|
||||||
|
> **참고:** react의 개발서버 배포는 Jenkins CI/CD 파이프라인이 처리한다.
|
||||||
|
> 기존 post-update hook의 git pull 방식(`pull_react.sh`)은 비활성화됨 (2026-02-24).
|
||||||
|
> 스크립트 위치: `/home/webservice/script/pull_react.sh`
|
||||||
|
|
||||||
**동기화 로그 확인:**
|
**동기화 로그 확인:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh sam-dev "tail -20 /home/webservice/logs/cicd_push_sam-react-prod.log"
|
ssh sam-dev "tail -20 /home/webservice/logs/cicd_push_react-prod.log"
|
||||||
ssh sam-dev "tail -20 /home/webservice/logs/cicd_push_sam-api.log"
|
ssh sam-dev "tail -20 /home/webservice/logs/cicd_push_api.log"
|
||||||
|
ssh sam-dev "tail -20 /home/webservice/logs/cicd_push_sales.log"
|
||||||
|
ssh sam-dev "tail -20 /home/webservice/logs/cicd_push_manage.log"
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -303,6 +309,13 @@ sudo systemctl status nginx
|
|||||||
| /etc/nginx/sites-available/ci.sam.it.kr | Jenkins 리버스 프록시 |
|
| /etc/nginx/sites-available/ci.sam.it.kr | Jenkins 리버스 프록시 |
|
||||||
| /etc/nginx/sites-available/monitor.sam.it.kr | Grafana 리버스 프록시 |
|
| /etc/nginx/sites-available/monitor.sam.it.kr | Grafana 리버스 프록시 |
|
||||||
|
|
||||||
|
**git.sam.it.kr 주요 설정:**
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
client_max_body_size 500M; # 대용량 Git push 허용
|
||||||
|
proxy_request_buffering off; # 스트리밍 전송 (413 방지)
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## node_exporter / Certbot / fail2ban / UFW
|
## node_exporter / Certbot / fail2ban / UFW
|
||||||
|
|||||||
@@ -17,28 +17,28 @@
|
|||||||
|
|
||||||
| 저장소 | 파이프라인 | 트리거 브랜치 | 배포 대상 |
|
| 저장소 | 파이프라인 | 트리거 브랜치 | 배포 대상 |
|
||||||
|--------|-----------|-------------|----------|
|
|--------|-----------|-------------|----------|
|
||||||
| sam-react-prod | React 빌드+배포 | develop, stage, main | 개발/Stage/운영 |
|
| sam-react-prod | React 빌드+배포 | develop, main | 개발 / Stage→승인→운영 |
|
||||||
| sam-api | Laravel API 배포 | stage, main | Stage/운영 |
|
| sam-api | Laravel API 배포 | main | Stage→승인→운영 |
|
||||||
| sam-manage | Laravel Admin 배포 | main | 운영 |
|
| sam-manage | Laravel Admin 배포 | main | 운영 (직접) |
|
||||||
| sam-sales | 레거시 PHP 배포 | main | 운영 |
|
| sam-sales | 레거시 PHP 배포 | main | 운영 (직접) |
|
||||||
|
|
||||||
### 브랜치별 동작
|
### 2-Branch 전략 (develop + main)
|
||||||
|
|
||||||
|
> **stage 브랜치 없음.** main 브랜치 push 시 Stage 자동 배포 → Jenkins 승인 → Production 배포.
|
||||||
|
|
||||||
| 브랜치 | react | api | mng | sales |
|
| 브랜치 | react | api | mng | sales |
|
||||||
|--------|-------|-----|-----|-------|
|
|--------|-------|-----|-----|-------|
|
||||||
| develop | Jenkins 빌드 -> 개발서버 | 기존 hook | 기존 hook | 기존 hook |
|
| develop | Jenkins 빌드 → 개발서버 | 기존 post-update hook | 기존 post-update hook | 기존 post-update hook |
|
||||||
| stage | Jenkins 빌드 -> 운영 Stage | Jenkins -> 운영 Stage | - | - |
|
| main | Stage 배포 → **승인** → Production 배포 | Stage 배포 → **승인** → Production 배포 | Production 직접 배포 | Production 직접 배포 |
|
||||||
| main | Jenkins 빌드 -> 운영 Production | Jenkins -> 운영 Production | Jenkins -> 운영 Production | Jenkins -> 운영 pull |
|
|
||||||
|
|
||||||
**main 브랜치 배포:** 배포관리자가 CI/CD Gitea에 수동 push 후 자동 실행
|
**main 브랜치 배포 흐름 (react/api):**
|
||||||
|
1. 개발자가 develop → main 머지 후 push
|
||||||
|
2. post-receive hook → CI/CD Gitea 자동 push
|
||||||
|
3. Jenkins 빌드 → Stage 자동 배포
|
||||||
|
4. Jenkins UI에서 **승인 클릭** → Production 배포 (24시간 타임아웃)
|
||||||
|
|
||||||
```bash
|
**main 브랜치 배포 흐름 (mng/sales):**
|
||||||
# 1회 remote 등록
|
1. 개발자가 main push → hook → CI/CD Gitea → Jenkins → Production 직접 배포
|
||||||
git remote add production https://git.sam.it.kr/SamProject/sam-react-prod.git
|
|
||||||
|
|
||||||
# 운영 배포 시
|
|
||||||
git push production main
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ git push production main
|
|||||||
|
|
||||||
```
|
```
|
||||||
개발자 로컬
|
개발자 로컬
|
||||||
│ git push origin (develop/stage/main)
|
│ git push origin (develop / main)
|
||||||
▼
|
▼
|
||||||
개발서버 Gitea (114.203.209.83:3000) ← 모든 개발자의 origin
|
개발서버 Gitea (114.203.209.83:3000) ← 모든 개발자의 origin
|
||||||
│
|
│
|
||||||
@@ -59,30 +59,21 @@ git push production main
|
|||||||
│ ├─ api/mng/sales: 기존 post-update hook (개발서버 pull) ← 현행 유지
|
│ ├─ api/mng/sales: 기존 post-update hook (개발서버 pull) ← 현행 유지
|
||||||
│ └─ react: hook → CI/CD Gitea push → Jenkins 빌드 → 개발서버 배포
|
│ └─ react: hook → CI/CD Gitea push → Jenkins 빌드 → 개발서버 배포
|
||||||
│
|
│
|
||||||
├─ stage push 시
|
└─ main push 시
|
||||||
│ ├─ react: hook → CI/CD Gitea push → Jenkins 빌드 → 운영서버 Stage 배포
|
├─ react: hook → CI/CD Gitea → Jenkins 빌드 → Stage 배포 → 승인 → Production 배포
|
||||||
│ └─ api: hook → CI/CD Gitea push → Jenkins → 운영서버 Stage pull
|
├─ api: hook → CI/CD Gitea → Jenkins → Stage 배포 → 승인 → Production 배포
|
||||||
│
|
├─ mng: hook → CI/CD Gitea → Jenkins → Production 직접 배포
|
||||||
└─ main push 시 (react/mng/api)
|
└─ sales: hook → CI/CD Gitea → Jenkins → Production 직접 배포
|
||||||
└─ ❌ CI/CD Gitea에 자동 push 안함
|
|
||||||
→ 배포관리자가 수동으로 CI/CD Gitea에 push
|
|
||||||
→ Jenkins 자동 배포
|
|
||||||
|
|
||||||
별도 처리:
|
|
||||||
sales/www(landing): hook → CI/CD Gitea → Jenkins → 운영서버 pull
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 브랜치별 배포 정책 상세
|
### 브랜치별 배포 정책 상세
|
||||||
|
|
||||||
| 브랜치 | 저장소 | CI/CD Gitea 동기화 | Jenkins 배포 | 배포 대상 |
|
| 브랜치 | 저장소 | CI/CD Gitea 동기화 | Jenkins 배포 | 배포 대상 |
|
||||||
|--------|--------|-------------------|-------------|----------|
|
|--------|--------|-------------------|-------------|----------|
|
||||||
| **stage** | react | 자동 (hook) | 빌드 + rsync | 운영서버 Stage |
|
| **main** | react | 자동 (hook) | 빌드 → Stage → **승인** → 재빌드 → Production | Stage + Production |
|
||||||
| **stage** | api | 자동 (hook) | SSH pull | 운영서버 Stage |
|
| **main** | api | 자동 (hook) | rsync → Stage → **승인** → rsync → Production | Stage + Production |
|
||||||
| **main** | react | 수동 (배포관리자) | 빌드 + rsync | 운영서버 Production |
|
| **main** | mng | 자동 (hook) | rsync + npm build → Production | Production |
|
||||||
| **main** | mng | 수동 (배포관리자) | SSH deploy | 운영서버 Production |
|
| **main** | sales | 자동 (hook) | rsync → Production | Production |
|
||||||
| **main** | api | 수동 (배포관리자) | SSH deploy | 운영서버 Production |
|
|
||||||
| **main** | sales | 자동 (hook) | SSH pull | 운영서버 Production |
|
|
||||||
| **main** | www | 자동 (hook) | SSH pull | 운영서버 Production |
|
|
||||||
| **develop** | react | 자동 (hook) | 빌드 → 개발서버 배포 | 개발서버 |
|
| **develop** | react | 자동 (hook) | 빌드 → 개발서버 배포 | 개발서버 |
|
||||||
| **develop** | api/mng/sales | ❌ (현행 유지) | ❌ | 개발서버 (post-update hook) |
|
| **develop** | api/mng/sales | ❌ (현행 유지) | ❌ | 개발서버 (post-update hook) |
|
||||||
|
|
||||||
@@ -90,11 +81,11 @@ git push production main
|
|||||||
|
|
||||||
| 저장소 | hook 대상 브랜치 | 동작 |
|
| 저장소 | hook 대상 브랜치 | 동작 |
|
||||||
|--------|-----------------|------|
|
|--------|-----------------|------|
|
||||||
| sam-react-prod | stage, develop | CI/CD Gitea에 push |
|
| sam-react-prod | main, develop | CI/CD Gitea에 push |
|
||||||
| sam-api | stage | CI/CD Gitea에 push |
|
| sam-api | main | CI/CD Gitea에 push |
|
||||||
|
| sam-manage | main | CI/CD Gitea에 push |
|
||||||
| sam-sales | main | CI/CD Gitea에 push |
|
| sam-sales | main | CI/CD Gitea에 push |
|
||||||
| sam-landing | main | CI/CD Gitea에 push |
|
| sam-landing | main | CI/CD Gitea에 push |
|
||||||
| sam-manage | ❌ 없음 | main만 사용, 배포관리자 수동 push |
|
|
||||||
|
|
||||||
hook 스크립트 경로: `/data/GIT/samproject/<repo>.git/hooks/post-receive.d/push-to-cicd`
|
hook 스크립트 경로: `/data/GIT/samproject/<repo>.git/hooks/post-receive.d/push-to-cicd`
|
||||||
토큰 환경변수: `/data/GIT/.cicd-env` (chmod 600, owner: git)
|
토큰 환경변수: `/data/GIT/.cicd-env` (chmod 600, owner: git)
|
||||||
@@ -117,7 +108,7 @@ Repository Settings → Webhooks → Add Webhook (Gitea)
|
|||||||
|
|
||||||
```
|
```
|
||||||
개발자 로컬
|
개발자 로컬
|
||||||
│ git push origin (develop / stage / main)
|
│ git push origin (develop / main)
|
||||||
▼
|
▼
|
||||||
┌──────────────────────────────────────────────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ 개발서버 Gitea (114.203.209.83:3000) ← 모든 개발자 origin │
|
│ 개발서버 Gitea (114.203.209.83:3000) ← 모든 개발자 origin │
|
||||||
@@ -132,46 +123,42 @@ Repository Settings → Webhooks → Add Webhook (Gitea)
|
|||||||
│ │ sales → 기존 post-update hook (pull) │ │
|
│ │ sales → 기존 post-update hook (pull) │ │
|
||||||
│ └───────────────────────────────────────────────────────┘ │
|
│ └───────────────────────────────────────────────────────┘ │
|
||||||
│ │
|
│ │
|
||||||
│ ┌─ stage push ──────────────────────────────────────────┐ │
|
│ ┌─ main push (모든 저장소 자동) ────────────────────────┐ │
|
||||||
│ │ react → hook: CI/CD Gitea push ──→ Jenkins 빌드 │ │
|
│ │ react → hook: CI/CD Gitea push ──→ Jenkins │ │
|
||||||
│ │ → rsync → 운영서버 Stage + PM2 reload │ │
|
│ │ → Stage 빌드+배포 → 승인 → Production 재빌드 │ │
|
||||||
│ │ api → hook: CI/CD Gitea push ──→ Jenkins │ │
|
│ │ api → hook: CI/CD Gitea push ──→ Jenkins │ │
|
||||||
│ │ → 운영서버 Stage Release + 심링크 │ │
|
│ │ → Stage rsync+배포 → 승인 → Production 배포 │ │
|
||||||
│ └───────────────────────────────────────────────────────┘ │
|
│ │ mng → hook: CI/CD Gitea push ──→ Jenkins │ │
|
||||||
│ │
|
│ │ → Production rsync + build │ │
|
||||||
│ ┌─ main push (sales/www만 자동) ────────────────────────┐ │
|
|
||||||
│ │ sales → hook: CI/CD Gitea push ──→ Jenkins │ │
|
│ │ sales → hook: CI/CD Gitea push ──→ Jenkins │ │
|
||||||
│ │ → 운영서버 rsync │ │
|
│ │ → Production rsync │ │
|
||||||
│ │ www → hook: CI/CD Gitea push ──→ Jenkins │ │
|
|
||||||
│ │ → 운영서버 pull │ │
|
|
||||||
│ │ react/mng/api → ❌ 자동 push 안함 │ │
|
|
||||||
│ └───────────────────────────────────────────────────────┘ │
|
│ └───────────────────────────────────────────────────────┘ │
|
||||||
└───────────────────────────────────────────────────────────────┘
|
└───────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
┌─ 운영 배포 (main - react/mng/api) ──────────────────────────┐
|
┌─ Jenkins 승인 흐름 (react/api main) ─────────────────────────┐
|
||||||
│ │
|
│ │
|
||||||
│ 배포관리자 로컬 │
|
│ Jenkins 빌드 시작 │
|
||||||
│ │ git push production main (CI/CD Gitea remote) │
|
│ │ │
|
||||||
│ ▼ │
|
│ ├─ Stage 자동 배포 (react: .env.stage 빌드) │
|
||||||
│ CI/CD Gitea (git.sam.it.kr) │
|
│ │ │
|
||||||
│ │ Webhook │
|
│ ├─ ⏸️ 승인 대기 (24시간 타임아웃) │
|
||||||
│ ▼ │
|
│ │ https://ci.sam.it.kr 에서 "운영 배포 진행" 클릭 │
|
||||||
│ Jenkins → 운영서버 배포 │
|
│ │ │
|
||||||
│ react: CI/CD 빌드 → rsync → PM2 reload │
|
│ ├─ Production 배포 (react: .env.main 재빌드) │
|
||||||
│ api: Release + 심링크 → PHP-FPM reload │
|
│ │ │
|
||||||
│ mng: Release + 심링크 → PHP-FPM reload │
|
│ └─ 완료 │
|
||||||
│ │
|
│ │
|
||||||
└───────────────────────────────────────────────────────────────┘
|
└───────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
### 환경별 배포 비교
|
### 환경별 배포 비교
|
||||||
|
|
||||||
| 항목 | 운영 (main) | Stage (stage) | 개발 (develop) |
|
| 항목 | Production (main→승인) | Stage (main→자동) | 개발 (develop) |
|
||||||
|------|------------|---------------|----------------|
|
|------|----------------------|------------------|----------------|
|
||||||
| **트리거** | 배포관리자 수동 push | 자동 (hook) | react만 자동 (hook), 나머지 기존 hook |
|
| **트리거** | main push → Jenkins 승인 | main push → 자동 | react만 자동 (hook), 나머지 기존 hook |
|
||||||
| **react 전략** | CI/CD 빌드 → rsync | CI/CD 빌드 → rsync | CI/CD 빌드 → rsync |
|
| **react 전략** | CI/CD 빌드(.env.main) → rsync | CI/CD 빌드(.env.stage) → rsync | CI/CD 빌드(.env.develop) → rsync |
|
||||||
| **api 전략** | Release + 심링크 | Release + 심링크 | 기존 post-update (pull) |
|
| **api 전략** | rsync + Release 심링크 | rsync + Release 심링크 | 기존 post-update (pull) |
|
||||||
| **mng 전략** | Release + 심링크 | - | 기존 post-update (pull + build) |
|
| **mng 전략** | rsync + npm build + Release 심링크 | - | 기존 post-update (pull + build) |
|
||||||
| **롤백** | 이전 릴리즈 심링크 | 이전 릴리즈 심링크 | git revert |
|
| **롤백** | 이전 릴리즈 심링크 | 이전 릴리즈 심링크 | git revert |
|
||||||
| **릴리즈 보관** | 최근 5개 | 최근 3개 | - |
|
| **릴리즈 보관** | 최근 5개 | 최근 3개 | - |
|
||||||
|
|
||||||
@@ -188,11 +175,11 @@ CI/CD Gitea push -> Webhook -> Jenkins
|
|||||||
|
|
||||||
**브랜치별 배포 대상:**
|
**브랜치별 배포 대상:**
|
||||||
|
|
||||||
| 브랜치 | 대상 서버 | 대상 경로 | PM2 이름 | 트리거 |
|
| 브랜치 | 배포 단계 | 대상 서버 | 대상 경로 | PM2 이름 |
|
||||||
|--------|----------|----------|----------|--------|
|
|--------|----------|----------|----------|----------|
|
||||||
| develop | 개발서버 (114.203.209.83) | /home/webservice/react/ | sam-react | 자동 (hook) |
|
| develop | 개발서버 | 114.203.209.83 | /home/webservice/react/ | sam-react |
|
||||||
| stage | 운영서버 (211.117.60.189) | /home/webservice/react-stage/releases/ | sam-front-stage | 자동 (hook) |
|
| main | Stage (자동) | 211.117.60.189 | /home/webservice/react-stage/releases/ | sam-front-stage |
|
||||||
| main | 운영서버 (211.117.60.189) | /home/webservice/react/releases/ | sam-front | 수동 push |
|
| main | Production (승인 후) | 211.117.60.189 | /home/webservice/react/releases/ | sam-front |
|
||||||
|
|
||||||
**환경변수 파일 (CI/CD 서버):** /var/lib/jenkins/env-files/react/
|
**환경변수 파일 (CI/CD 서버):** /var/lib/jenkins/env-files/react/
|
||||||
|
|
||||||
@@ -225,8 +212,13 @@ pipeline {
|
|||||||
stage('Prepare Env') {
|
stage('Prepare Env') {
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
def envFile = "/var/lib/jenkins/env-files/react/.env.${env.BRANCH_NAME}"
|
if (env.BRANCH_NAME == 'main') {
|
||||||
sh "cp ${envFile} .env.local"
|
// main: Stage 빌드 먼저 (승인 후 Production 재빌드)
|
||||||
|
sh "cp /var/lib/jenkins/env-files/react/.env.stage .env.local"
|
||||||
|
} else {
|
||||||
|
def envFile = "/var/lib/jenkins/env-files/react/.env.${env.BRANCH_NAME}"
|
||||||
|
sh "cp ${envFile} .env.local"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,9 +248,9 @@ pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── stage → 운영서버 Stage 배포 ──
|
// ── main → 운영서버 Stage 배포 ──
|
||||||
stage('Deploy Stage') {
|
stage('Deploy Stage') {
|
||||||
when { branch 'stage' }
|
when { branch 'main' }
|
||||||
steps {
|
steps {
|
||||||
sshagent(credentials: ['deploy-ssh-key']) {
|
sshagent(credentials: ['deploy-ssh-key']) {
|
||||||
sh """
|
sh """
|
||||||
@@ -277,6 +269,26 @@ pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── 운영 배포 승인 ──
|
||||||
|
stage('Production Approval') {
|
||||||
|
when { branch 'main' }
|
||||||
|
steps {
|
||||||
|
timeout(time: 24, unit: 'HOURS') {
|
||||||
|
input message: 'Stage 확인 후 운영 배포를 진행하시겠습니까?\nStage: https://stage.sam.it.kr',
|
||||||
|
ok: '운영 배포 진행'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── main → Production 재빌드 (운영 환경변수) ──
|
||||||
|
stage('Rebuild for Production') {
|
||||||
|
when { branch 'main' }
|
||||||
|
steps {
|
||||||
|
sh "cp /var/lib/jenkins/env-files/react/.env.main .env.local"
|
||||||
|
sh 'npm run build'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── main → 운영서버 Production 배포 ──
|
// ── main → 운영서버 Production 배포 ──
|
||||||
stage('Deploy Production') {
|
stage('Deploy Production') {
|
||||||
when { branch 'main' }
|
when { branch 'main' }
|
||||||
@@ -306,6 +318,10 @@ pipeline {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **참고:** Next.js는 `NEXT_PUBLIC_*` 환경변수가 빌드 시 바인딩되므로,
|
||||||
|
> Stage(.env.stage)와 Production(.env.main)에서 별도 빌드가 필요하다.
|
||||||
|
> main 빌드 시 Stage용으로 먼저 빌드 → 승인 후 Production용으로 재빌드.
|
||||||
|
|
||||||
### PM2 수동 재시작
|
### PM2 수동 재시작
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -329,16 +345,16 @@ pm2 save
|
|||||||
|
|
||||||
```
|
```
|
||||||
CI/CD Gitea push -> Webhook -> Jenkins
|
CI/CD Gitea push -> Webhook -> Jenkins
|
||||||
-> SSH: git clone -> composer install -> artisan cache -> migrate -> 심링크 전환 -> PHP-FPM reload
|
-> checkout -> rsync → Stage 배포 → 승인 → rsync → Production 배포
|
||||||
```
|
```
|
||||||
|
|
||||||
**브랜치별 배포 대상:**
|
**브랜치별 배포 대상:**
|
||||||
|
|
||||||
| 브랜치 | 대상 서버 | 대상 경로 | 트리거 |
|
| 브랜치 | 배포 단계 | 대상 서버 | 대상 경로 |
|
||||||
|--------|----------|----------|--------|
|
|--------|----------|----------|----------|
|
||||||
| stage | 운영서버 | /home/webservice/api-stage/releases/ | 자동 (hook) |
|
| main | Stage (자동) | 운영서버 | /home/webservice/api-stage/releases/ |
|
||||||
| main | 운영서버 | /home/webservice/api/releases/ | 수동 push |
|
| main | Production (승인 후) | 운영서버 | /home/webservice/api/releases/ |
|
||||||
| develop | 개발서버 | - (기존 post-update hook) | 기존 hook |
|
| develop | 개발서버 | 개발서버 | 기존 post-update hook |
|
||||||
|
|
||||||
### Jenkinsfile (api/Jenkinsfile)
|
### Jenkinsfile (api/Jenkinsfile)
|
||||||
|
|
||||||
@@ -348,8 +364,7 @@ pipeline {
|
|||||||
|
|
||||||
environment {
|
environment {
|
||||||
DEPLOY_USER = 'hskwon'
|
DEPLOY_USER = 'hskwon'
|
||||||
APP_NAME = 'api'
|
RELEASE_ID = new Date().format('yyyyMMdd_HHmmss')
|
||||||
RELEASE_ID = new Date().format('yyyyMMdd_HHmmss')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stages {
|
stages {
|
||||||
@@ -357,18 +372,63 @@ pipeline {
|
|||||||
steps { checkout scm }
|
steps { checkout scm }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── main → 운영서버 (배포관리자 수동 push 후 트리거) ──
|
// ── main → 운영서버 Stage 배포 ──
|
||||||
|
stage('Deploy Stage') {
|
||||||
|
when { branch 'main' }
|
||||||
|
steps {
|
||||||
|
sshagent(credentials: ['deploy-ssh-key']) {
|
||||||
|
sh """
|
||||||
|
ssh ${DEPLOY_USER}@211.117.60.189 'mkdir -p /home/webservice/api-stage/releases/${RELEASE_ID}'
|
||||||
|
rsync -az --delete \
|
||||||
|
--exclude='.git' --exclude='.env' \
|
||||||
|
--exclude='storage/app' --exclude='storage/logs' \
|
||||||
|
--exclude='storage/framework/sessions' --exclude='storage/framework/cache' \
|
||||||
|
. ${DEPLOY_USER}@211.117.60.189:/home/webservice/api-stage/releases/${RELEASE_ID}/
|
||||||
|
ssh ${DEPLOY_USER}@211.117.60.189 '
|
||||||
|
cd /home/webservice/api-stage/releases/${RELEASE_ID} &&
|
||||||
|
ln -sfn /home/webservice/api-stage/shared/.env .env &&
|
||||||
|
ln -sfn /home/webservice/api-stage/shared/storage/app storage/app &&
|
||||||
|
composer install --no-dev --optimize-autoloader --no-interaction &&
|
||||||
|
php artisan config:cache &&
|
||||||
|
php artisan route:cache &&
|
||||||
|
php artisan view:cache &&
|
||||||
|
php artisan migrate --force &&
|
||||||
|
ln -sfn /home/webservice/api-stage/releases/${RELEASE_ID} /home/webservice/api-stage/current &&
|
||||||
|
sudo systemctl reload php8.4-fpm &&
|
||||||
|
cd /home/webservice/api-stage/releases && ls -1dt */ | tail -n +4 | xargs rm -rf 2>/dev/null || true
|
||||||
|
'
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 운영 배포 승인 ──
|
||||||
|
stage('Production Approval') {
|
||||||
|
when { branch 'main' }
|
||||||
|
steps {
|
||||||
|
timeout(time: 24, unit: 'HOURS') {
|
||||||
|
input message: 'Stage 확인 후 운영 배포를 진행하시겠습니까?\nStage API: https://stage-api.sam.it.kr',
|
||||||
|
ok: '운영 배포 진행'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── main → 운영서버 Production 배포 ──
|
||||||
stage('Deploy Production') {
|
stage('Deploy Production') {
|
||||||
when { branch 'main' }
|
when { branch 'main' }
|
||||||
steps {
|
steps {
|
||||||
sshagent(credentials: ['deploy-ssh-key']) {
|
sshagent(credentials: ['deploy-ssh-key']) {
|
||||||
sh """
|
sh """
|
||||||
|
ssh ${DEPLOY_USER}@211.117.60.189 'mkdir -p /home/webservice/api/releases/${RELEASE_ID}'
|
||||||
|
rsync -az --delete \
|
||||||
|
--exclude='.git' --exclude='.env' \
|
||||||
|
--exclude='storage/app' --exclude='storage/logs' \
|
||||||
|
--exclude='storage/framework/sessions' --exclude='storage/framework/cache' \
|
||||||
|
. ${DEPLOY_USER}@211.117.60.189:/home/webservice/api/releases/${RELEASE_ID}/
|
||||||
ssh ${DEPLOY_USER}@211.117.60.189 '
|
ssh ${DEPLOY_USER}@211.117.60.189 '
|
||||||
cd /home/webservice/api/releases &&
|
|
||||||
git clone --depth 1 --branch main https://git.sam.it.kr/SamProject/sam-api.git ${RELEASE_ID} &&
|
|
||||||
ln -sfn /home/webservice/api/shared/storage /home/webservice/api/releases/${RELEASE_ID}/storage &&
|
|
||||||
ln -sfn /home/webservice/api/shared/.env /home/webservice/api/releases/${RELEASE_ID}/.env &&
|
|
||||||
cd /home/webservice/api/releases/${RELEASE_ID} &&
|
cd /home/webservice/api/releases/${RELEASE_ID} &&
|
||||||
|
ln -sfn /home/webservice/api/shared/.env .env &&
|
||||||
|
ln -sfn /home/webservice/api/shared/storage/app storage/app &&
|
||||||
composer install --no-dev --optimize-autoloader --no-interaction &&
|
composer install --no-dev --optimize-autoloader --no-interaction &&
|
||||||
php artisan config:cache &&
|
php artisan config:cache &&
|
||||||
php artisan route:cache &&
|
php artisan route:cache &&
|
||||||
@@ -377,35 +437,7 @@ pipeline {
|
|||||||
ln -sfn /home/webservice/api/releases/${RELEASE_ID} /home/webservice/api/current &&
|
ln -sfn /home/webservice/api/releases/${RELEASE_ID} /home/webservice/api/current &&
|
||||||
sudo systemctl reload php8.4-fpm &&
|
sudo systemctl reload php8.4-fpm &&
|
||||||
sudo supervisorctl restart sam-queue-worker:* &&
|
sudo supervisorctl restart sam-queue-worker:* &&
|
||||||
cd /home/webservice/api/releases &&
|
cd /home/webservice/api/releases && ls -1dt */ | tail -n +6 | xargs rm -rf 2>/dev/null || true
|
||||||
ls -1dt */ | tail -n +6 | xargs rm -rf 2>/dev/null || true
|
|
||||||
'
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── stage → 운영서버 Stage ──
|
|
||||||
stage('Deploy Stage') {
|
|
||||||
when { branch 'stage' }
|
|
||||||
steps {
|
|
||||||
sshagent(credentials: ['deploy-ssh-key']) {
|
|
||||||
sh """
|
|
||||||
ssh ${DEPLOY_USER}@211.117.60.189 '
|
|
||||||
cd /home/webservice/api-stage/releases &&
|
|
||||||
git clone --depth 1 --branch stage https://git.sam.it.kr/SamProject/sam-api.git ${RELEASE_ID} &&
|
|
||||||
ln -sfn /home/webservice/api-stage/shared/storage /home/webservice/api-stage/releases/${RELEASE_ID}/storage &&
|
|
||||||
ln -sfn /home/webservice/api-stage/shared/.env /home/webservice/api-stage/releases/${RELEASE_ID}/.env &&
|
|
||||||
cd /home/webservice/api-stage/releases/${RELEASE_ID} &&
|
|
||||||
composer install --no-dev --optimize-autoloader --no-interaction &&
|
|
||||||
php artisan config:cache &&
|
|
||||||
php artisan route:cache &&
|
|
||||||
php artisan view:cache &&
|
|
||||||
php artisan migrate --force &&
|
|
||||||
ln -sfn /home/webservice/api-stage/releases/${RELEASE_ID} /home/webservice/api-stage/current &&
|
|
||||||
sudo systemctl reload php8.4-fpm &&
|
|
||||||
cd /home/webservice/api-stage/releases &&
|
|
||||||
ls -1dt */ | tail -n +4 | xargs rm -rf 2>/dev/null || true
|
|
||||||
'
|
'
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
@@ -420,17 +452,14 @@ pipeline {
|
|||||||
failure {
|
failure {
|
||||||
echo "❌ api 배포 실패 (${env.BRANCH_NAME})"
|
echo "❌ api 배포 실패 (${env.BRANCH_NAME})"
|
||||||
script {
|
script {
|
||||||
if (env.BRANCH_NAME in ['main', 'stage']) {
|
if (env.BRANCH_NAME == 'main') {
|
||||||
def baseDir = env.BRANCH_NAME == 'main'
|
|
||||||
? '/home/webservice/api'
|
|
||||||
: '/home/webservice/api-stage'
|
|
||||||
sshagent(credentials: ['deploy-ssh-key']) {
|
sshagent(credentials: ['deploy-ssh-key']) {
|
||||||
sh """
|
sh """
|
||||||
ssh ${DEPLOY_USER}@211.117.60.189 '
|
ssh ${DEPLOY_USER}@211.117.60.189 '
|
||||||
PREV=\$(ls -1dt ${baseDir}/releases/*/ | sed -n "2p" | xargs basename) &&
|
PREV=\$(ls -1dt /home/webservice/api/releases/*/ | sed -n "2p" | xargs basename 2>/dev/null) &&
|
||||||
[ -n "\$PREV" ] && ln -sfn ${baseDir}/releases/\$PREV ${baseDir}/current &&
|
[ -n "\$PREV" ] && ln -sfn /home/webservice/api/releases/\$PREV /home/webservice/api/current &&
|
||||||
sudo systemctl reload php8.4-fpm
|
sudo systemctl reload php8.4-fpm
|
||||||
'
|
' || true
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -440,8 +469,15 @@ pipeline {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **참고:** Laravel은 런타임 .env를 사용하므로 Stage/Production 별도 빌드가 필요 없다.
|
||||||
|
> 각 환경의 shared/.env가 심링크로 연결된다.
|
||||||
|
|
||||||
### 수동 배포 절차 (API Production)
|
### 수동 배포 절차 (API Production)
|
||||||
|
|
||||||
|
> **참고:** CI/CD Gitea는 `REQUIRE_SIGNIN_VIEW = true` 설정이므로,
|
||||||
|
> 수동 git clone 시 `https://사용자:비밀번호@git.sam.it.kr/...` 형식 또는
|
||||||
|
> CI/CD 서버에서 rsync로 전송하는 방식을 사용한다.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh sam-prod
|
ssh sam-prod
|
||||||
|
|
||||||
@@ -511,6 +547,82 @@ ls -1dt */ | tail -n +4 | xargs rm -rf 2>/dev/null || true
|
|||||||
|
|
||||||
API와 동일한 releases/shared 구조. 차이점: npm build 추가, Queue Worker 재시작 불필요.
|
API와 동일한 releases/shared 구조. 차이점: npm build 추가, Queue Worker 재시작 불필요.
|
||||||
|
|
||||||
|
### Jenkinsfile (mng/Jenkinsfile)
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
|
||||||
|
environment {
|
||||||
|
DEPLOY_USER = 'hskwon'
|
||||||
|
RELEASE_ID = new Date().format('yyyyMMdd_HHmmss')
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Checkout') {
|
||||||
|
steps { checkout scm }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── main → 운영서버 Production ──
|
||||||
|
stage('Deploy Production') {
|
||||||
|
when { branch 'main' }
|
||||||
|
steps {
|
||||||
|
sshagent(credentials: ['deploy-ssh-key']) {
|
||||||
|
sh """
|
||||||
|
ssh ${DEPLOY_USER}@211.117.60.189 'mkdir -p /home/webservice/mng/releases/${RELEASE_ID}'
|
||||||
|
rsync -az --delete \
|
||||||
|
--exclude='.git' --exclude='.env' \
|
||||||
|
--exclude='storage/app' --exclude='storage/logs' \
|
||||||
|
--exclude='storage/framework/sessions' --exclude='storage/framework/cache' \
|
||||||
|
--exclude='node_modules' \
|
||||||
|
. ${DEPLOY_USER}@211.117.60.189:/home/webservice/mng/releases/${RELEASE_ID}/
|
||||||
|
ssh ${DEPLOY_USER}@211.117.60.189 '
|
||||||
|
cd /home/webservice/mng/releases/${RELEASE_ID} &&
|
||||||
|
ln -sfn /home/webservice/mng/shared/.env .env &&
|
||||||
|
ln -sfn /home/webservice/mng/shared/storage/app storage/app &&
|
||||||
|
composer install --no-dev --optimize-autoloader --no-interaction &&
|
||||||
|
npm install --production=false &&
|
||||||
|
npm run build &&
|
||||||
|
php artisan config:cache &&
|
||||||
|
php artisan route:cache &&
|
||||||
|
php artisan view:cache &&
|
||||||
|
php artisan migrate --force &&
|
||||||
|
ln -sfn /home/webservice/mng/releases/${RELEASE_ID} /home/webservice/mng/current &&
|
||||||
|
sudo systemctl reload php8.4-fpm &&
|
||||||
|
cd /home/webservice/mng/releases && ls -1dt */ | tail -n +6 | xargs rm -rf 2>/dev/null || true
|
||||||
|
'
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// develop → Jenkins 관여 안함 (기존 post-update hook 유지)
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
success { echo "✅ mng 배포 완료 (${env.BRANCH_NAME})" }
|
||||||
|
failure {
|
||||||
|
echo "❌ mng 배포 실패 (${env.BRANCH_NAME})"
|
||||||
|
script {
|
||||||
|
if (env.BRANCH_NAME == 'main') {
|
||||||
|
sshagent(credentials: ['deploy-ssh-key']) {
|
||||||
|
sh """
|
||||||
|
ssh ${DEPLOY_USER}@211.117.60.189 '
|
||||||
|
PREV=\$(ls -1dt /home/webservice/mng/releases/*/ | sed -n "2p" | xargs basename) &&
|
||||||
|
[ -n "\$PREV" ] && ln -sfn /home/webservice/mng/releases/\$PREV /home/webservice/mng/current &&
|
||||||
|
sudo systemctl reload php8.4-fpm
|
||||||
|
'
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 수동 배포
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh sam-prod
|
ssh sam-prod
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user