# Cryptoments v2 — 배포 파이프라인 가이드

> 작성일: 2026-03-28 | 버전: 1.0

## 1. 서버 아키텍처

```
┌─────────────────────────────────────────────────────────────────────┐
│                          GitLab CI/CD                               │
│                                                                     │
│  develop push ──→ Staging (자동)                                     │
│  main push ────→ Production (수동 승인)                              │
└──┬───────────┬──────────────┬──────────────┬───────────────┬────────┘
   │           │              │              │               │
┌──▼───┐  ┌───▼────┐   ┌─────▼─────┐  ┌────▼────┐   ┌─────▼─────┐
│db-01 │  │admin-?? │   │  app-01   │  │ node-01 │   │  node-02  │
│ .122 │  │  미정   │   │   .125    │  │  .186   │   │   .28     │
│──────│  │────────│   │───────────│  │─────────│   │───────────│
│MySQL │  │admin   │   │ open-api  │  │ b-chain │   │ relayer   │
│ 8.0  │  │-api    │   │ scheduler │  │  -api   │   │  -api     │
│      │  │partner │   │           │  │ telegram│   │ wallet    │
│      │  │-api    │   │           │  │  -bot   │   │ -activator│
└──────┘  └────────┘   └───────────┘  └─────────┘   └───────────┘
배포없음   JAR(대기)     JAR 배포       dist 배포      dist 배포
                       systemd        PM2            PM2
```

## 2. GitLab CI/CD Variables 설정

Settings → CI/CD → Variables 에 등록:

| Variable | Type | Protected | Masked | 값 |
|----------|------|-----------|--------|-----|
| `SSH_PRIVATE_KEY` | **File** | ✅ | ✅ | 배포 전용 SSH 개인키 (ed25519) |
| `DEPLOY_USER` | Variable | ✅ | ❌ | `ubuntu` |
| `APP_HOST` | Variable | ✅ | ❌ | `103.213.248.125` (open-api + scheduler) |
| `ADMIN_HOST` | Variable | ✅ | ❌ | 미정 (admin-api + partner-api, 설정 시 활성화) |
| `NODE1_HOST` | Variable | ✅ | ❌ | `103.213.248.186` |
| `NODE2_HOST` | Variable | ✅ | ❌ | `103.213.248.28` |

### SSH 키 등록 방법

1. GitLab → Settings → CI/CD → Variables → Add Variable
2. Key: `SSH_PRIVATE_KEY`
3. Type: **File** (중요!)
4. Flags: Protected ✅, Masked ✅
5. Value: `deploy_key` 개인키 내용 붙여넣기

## 3. .env 파일 관리

서버에 직접 관리. GitLab 파이프라인은 코드만 배포.

### app-01 (.125) — open-api + scheduler

```bash
/opt/cryptoments/config/.env.open-api
/opt/cryptoments/config/.env.scheduler
```

### admin 서버 (미정) — admin-api + partner-api

```bash
# 서버 확정 후 동일하게 구성
/opt/cryptoments/config/.env.admin-api
/opt/cryptoments/config/.env.partner-api
```

### node-01 (.186)

```bash
/opt/cryptoments/config/.env.blockchain-api
/opt/cryptoments/config/.env.telegram-bot
```

### node-02 (.28)

```bash
/opt/cryptoments/config/.env.relayer-api
/opt/cryptoments/config/.env.wallet-activator
```

### .env 수정 시

```bash
# 서버에 SSH 접속해서 직접 수정
ssh ubuntu@103.213.248.125
sudo vi /opt/cryptoments/config/.env.open-api

# 서비스 재시작 (재배포 불필요)
sudo systemctl restart cryptoments-open-api  # Spring Boot
# 또는
pm2 restart blockchain-api                    # Node.js
```

## 4. 브랜치 전략

```
feature/xxx ──→ develop (MR) ──→ main (MR)
                   │                 │
                   ▼                 ▼
               Staging 자동      Production 수동
```

| 브랜치 | 환경 | 트리거 | 승인 |
|--------|------|--------|------|
| `develop` | Staging | push 자동 | 불필요 |
| `main` | Production | push 감지 | **수동 승인 필요** |
| `v*` 태그 | Production | 태그 push | **수동 승인 필요** |
| `node-v*` 태그 | Production (Node만) | 태그 push | **수동 승인 필요** |

## 5. 파이프라인 변경 감지

| 변경 경로 | 실행되는 파이프라인 |
|----------|-------------------|
| `common/**`, `core/**`, `admin-api/**`, `partner-api/**`, `open-api/**`, `scheduler/**`, `build.gradle`, `settings.gradle` | **Spring Boot** (빌드 + app-01 배포) |
| `node-service/**` | **Node.js** (빌드 + node-01/node-02 배포) |
| 둘 다 변경 | **둘 다** 실행 |

## 6. 배포 흐름

### Spring Boot

```
spring:build (eclipse-temurin:17-jdk)
  ├── gradlew clean build -x test --parallel
  ├── 4개 JAR 생성 검증
  └── artifacts 저장

spring:deploy-{staging|production} (alpine)
  ├── rsync로 JAR 전송 → app-01:/opt/cryptoments/{service}/
  ├── 이전 JAR 백업 (.jar.prev)
  ├── 새 JAR 교체 + .env 심볼릭 링크
  ├── systemctl restart
  └── /actuator/health 헬스체크
```

### Node.js

```
node:build (node:20-slim)
  ├── pnpm install --frozen-lockfile
  ├── common 먼저 빌드 → 나머지 병렬
  ├── dist/app.js 존재 검증
  └── artifacts 저장

node:deploy-{staging|production} (alpine)
  ├── node-01: blockchain-api + telegram-bot
  │   ├── rsync dist/ + package.json (src/ 제외)
  │   ├── .env 심볼릭 링크
  │   ├── pnpm install --prod
  │   └── pm2 reload --update-env
  ├── node-02: relayer-api + wallet-activator
  │   └── (동일 과정)
  └── /ping 헬스체크
```

## 7. Spring Boot systemd 서비스 등록

각 서비스를 systemd에 등록해야 파이프라인에서 자동 재시작 가능:

```bash
# app-01에서 실행
sudo tee /etc/systemd/system/cryptoments-open-api.service << 'EOF'
[Unit]
Description=Cryptoments Open API
After=network.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/opt/cryptoments/open-api
ExecStart=/usr/bin/java -jar \
  -Xms512m -Xmx1024m \
  -Dspring.profiles.active=production \
  -Dspring.config.additional-location=file:/opt/cryptoments/config/ \
  /opt/cryptoments/open-api/open-api.jar
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
EnvironmentFile=/opt/cryptoments/config/.env.open-api

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable cryptoments-open-api
sudo systemctl start cryptoments-open-api
```

서버별 서비스 등록:

| 서버 | 서비스 | JVM 메모리 | 포트 |
|------|--------|-----------|------|
| app-01 (.125) | cryptoments-open-api | -Xms512m -Xmx1024m | 8081 |
| app-01 (.125) | cryptoments-scheduler | -Xms256m -Xmx512m | 8083 |
| admin (미정) | cryptoments-admin-api | -Xms512m -Xmx1024m | 8080 |
| admin (미정) | cryptoments-partner-api | -Xms512m -Xmx1024m | 8082 |

## 8. 롤백

### Spring Boot 롤백

```bash
# app-01에서 실행
ssh ubuntu@103.213.248.125

# 이전 JAR로 복원
cd /opt/cryptoments/open-api
cp open-api.jar open-api.jar.failed
cp open-api.jar.prev open-api.jar
sudo systemctl restart cryptoments-open-api
```

### Node.js 롤백

GitLab에서 이전 성공 파이프라인의 deploy job을 재실행하거나:

```bash
# node-01에서 수동 롤백
ssh ubuntu@103.213.248.186
cd /opt/cryptoments/node-service

# git으로 이전 버전 복원
git log --oneline -5
git checkout <이전_커밋> -- packages/blockchain-api/dist/

pm2 reload blockchain-api
```

## 9. 운영 명령어

### 서비스 상태 확인

```bash
# Spring Boot (app-01)
sudo systemctl status cryptoments-{admin-api,open-api,partner-api,scheduler}
sudo journalctl -u cryptoments-open-api -f --since "10 minutes ago"

# Node.js (node-01, node-02)
pm2 status
pm2 logs blockchain-api --lines 50
pm2 monit
```

### 수동 배포 (파이프라인 없이)

```bash
# Spring Boot — 로컬에서 빌드 후 전송
./gradlew :open-api:build -x test
scp open-api/build/libs/open-api-*.jar ubuntu@103.213.248.125:/opt/cryptoments/open-api/open-api.jar
ssh ubuntu@103.213.248.125 "sudo systemctl restart cryptoments-open-api"

# Node.js — 로컬에서 빌드 후 전송
cd node-service && pnpm --filter @cryptoments/blockchain-api build
rsync -azv packages/blockchain-api/dist/ ubuntu@103.213.248.186:/opt/cryptoments/node-service/packages/blockchain-api/dist/
ssh ubuntu@103.213.248.186 "pm2 reload blockchain-api"
```

## 10. 체크리스트

### 초기 설정 (1회)

- [ ] GitLab CI/CD Variables 5개 등록
- [ ] app-01에 4개 systemd 서비스 등록
- [ ] 각 서버에 .env 파일 생성
- [ ] develop, main 브랜치 생성
- [ ] main 브랜치 Protected 설정

### 배포 전 확인

- [ ] 로컬 빌드 성공 확인
- [ ] .env 변경사항 서버에 반영 여부
- [ ] DB 마이그레이션 필요 여부
- [ ] develop에서 staging 테스트 완료
