# Cryptoments V2 — 인프라 인벤토리

> 최종 업데이트: 2026-07-17 (Vigil 모니터링 에이전트 6대 설치)

---

## 1. 서버 구성 요약

| 역할 | 호스트명 | IP | OS | 주요 소프트웨어 | SSH 접속 |
|------|---------|----|----|---------------|---------|
| **bastion** | Jump Host + VPN | 13.113.128.166 (AWS EIP) | Ubuntu 24.04 | OpenVPN, fail2ban | `ssh -i aws_ssh_server.pem ubuntu@13.113.128.166` |
| **db-01** | DB + Redis | 103.213.248.122 | Ubuntu 24.04 | MySQL 8.0.45, Redis 7.0.15 | bastion 경유: `ssh db-01` |
| **app-01** | Spring Boot API | 103.213.248.125 | Ubuntu 24.04 | Java 17.0.18 (OpenJDK) | bastion 경유: `ssh app-01` |
| **node-01** | Blockchain API | 103.213.248.186 | Ubuntu 24.04 | Node.js 20.20.2 (fnm), PM2 | bastion 경유: `ssh node-01` |
| **node-02** | Relayer API | 103.213.248.28 | Ubuntu 24.04 | Node.js 20.20.2 (fnm), PM2 | bastion 경유: `ssh node-02` |
| **partner** | Partner API + UI | 103.213.248.81 | Ubuntu 24.04 | Java 17.0.18, Nginx, Certbot | bastion 경유: `ssh partner` |
| **widget** | Widget UI + SDK | 103.213.248.135 | Ubuntu 24.04 | Nginx, Certbot | bastion 경유: `ssh widget` |

> **중요**: 모든 인프라 서버 SSH는 bastion IP(13.113.128.166)에서만 접근 가능. Password 인증 비활성화됨 (widget 서버는 root 사용자, deploy_key 기반).

---

## 2. 서버별 상세 구성

### 2.1 db-01 (103.213.248.122)

**MySQL 8.0.45**
- DB명: `cryptoments_db`
- 사용자: `cryptoments` / `Crypt0m3nts!2026`
- 시스템 사용자: `debian-sys-maint` / `0PyvFlEamjC7wKMi`
- 포트: 3306
- 인코딩: utf8mb4

**Redis 7.0.15**
- 포트: 6379
- 비밀번호: `CryptoRedis2026`
- 바인드: `0.0.0.0` (전체 서버 공용)
- 설정 파일: `/etc/redis/redis.conf`

**UFW 방화벽**
- 22/tcp (SSH)
- 3306/tcp (MySQL)
- 6379/tcp (Redis)

---

### 2.2 app-01 (103.213.248.125)

**서비스 (systemd)**

| 서비스 | 포트 | 유닛 | 역할 |
|--------|------|------|------|
| open-api | 8081 | `cryptoments-open-api.service` | Webhook 수신, 외부 API |
| scheduler | 8083 | `cryptoments-scheduler.service` | 배치 스케줄러 (시세 업데이트 등) |

**환경변수 파일**
- `/opt/cryptoments/config/.env.open-api`
- `/opt/cryptoments/config/.env.scheduler`

**디렉토리 구조**
```
/opt/cryptoments/
├── config/
│   ├── .env.open-api
│   └── .env.scheduler
├── open-api/
│   └── open-api.jar
└── scheduler/
    └── scheduler.jar
```

**주요 환경변수**
```
SPRING_PROFILES_ACTIVE=prod
SPRING_DATASOURCE_URL=jdbc:mysql://103.213.248.122:3306/cryptoments_db?...&characterEncoding=UTF-8
BLOCKCHAIN_API_URL=http://103.213.248.186:3001
RELAYER_API_URL=http://103.213.248.28:3002
SERVER_PORT=8081  (open-api) / 8083 (scheduler)
```

---

### 2.3 node-01 (103.213.248.186)

**PM2 프로세스**

| 프로세스 | 포트 | 메모리 제한 | 역할 |
|---------|------|-----------|------|
| blockchain-api | 3001 | 512M | 지갑 파생, 잔액/TX 조회, 컨트랙트 관리 |
| telegram-bot | — (polling) | 256M | Telegram 알림 봇 |

**Node.js 관리**: `fnm` (Fast Node Manager)
- 경로: `/home/ubuntu/.local/share/fnm/`
- PM2 명령 시 `export PATH="/home/ubuntu/.local/share/fnm/node-versions/v20.20.2/installation/bin:$PATH"`

**환경변수 파일**: `/opt/cryptoments/node-service/.env`
**PM2 설정**: `/opt/cryptoments/node-service/ecosystem.config.cjs`

**디렉토리 구조**
```
/opt/cryptoments/
└── node-service/
    ├── .env
    ├── ecosystem.config.cjs
    ├── logs/
    │   ├── blockchain-api-out.log
    │   ├── blockchain-api-error.log
    │   ├── telegram-bot-out.log
    │   └── telegram-bot-error.log
    └── packages/
        ├── blockchain-api/dist/app.js
        └── telegram-bot/dist/app.js
```

**주요 환경변수**
```
NODE_ENV=production
REDIS_HOST=103.213.248.122
REDIS_PORT=6379
REDIS_PASSWORD=CryptoRedis2026
REDIS_DB=0
TRONZAP_API_TOKEN=pmwKxONfST2JY8UNpSYWMlS9dhkh54f6
TRONZAP_API_SECRET=DRVWJQ1ft1sIGhpJryy2IU2oeXmAk4AI
TRONZAP_BASE_URL=https://api.tronzap.com
```

---

### 2.4 node-02 (103.213.248.28)

**PM2 프로세스**

| 프로세스 | 포트 | 메모리 제한 | 역할 |
|---------|------|-----------|------|
| relayer-api | 3002 | 512M | Relayer 관리, 집금/출금 폴러 |
| wallet-activator | — (worker) | 256M | 가스 전송, ERC-20 approve 실행 |

**Node.js 관리**: `fnm` (동일 구성)
**환경변수 파일**: `/opt/cryptoments/node-service/.env`
**PM2 설정**: `/opt/cryptoments/node-service/ecosystem.config.cjs`

**디렉토리 구조** (node-01과 동일)

**주요 환경변수** (node-01과 동일)

---

### 2.5 partner (103.213.248.81)

**서비스 (systemd)**

| 서비스 | 포트 | 유닛 | 역할 |
|--------|------|------|------|
| partner-api | 8082 | `cryptoments-partner-api.service` | 파트너 콘솔 API |

**Nginx (리버스 프록시 + 정적 파일)**

| 도메인 | 용도 | SSL | 설정 파일 |
|--------|------|-----|----------|
| partner.cryptoments.cc | Partner UI (SPA) + `/api` → 8082 프록시 | Let's Encrypt | `/etc/nginx/sites-available/partner.cryptoments.cc` |
| partner-api.cryptoments.cc | Partner API 직접 프록시 → 8082 | Let's Encrypt | `/etc/nginx/sites-available/partner-api.cryptoments.cc` |

**환경변수 파일**: `/opt/cryptoments/config/.env.partner-api`

**디렉토리 구조**
```
/opt/cryptoments/
├── config/
│   └── .env.partner-api
├── partner-api/
│   └── partner-api.jar
└── partner-front/              ← Vue 3 SPA (빌드 결과물)
    ├── index.html
    └── assets/
```

**주요 환경변수**
```
SPRING_PROFILES_ACTIVE=prod
SPRING_DATASOURCE_URL=jdbc:mysql://103.213.248.122:3306/cryptoments_db?...&characterEncoding=UTF-8
AXIM_REST_SESSION_SECRET_KEY=c3a8501c13f749feda3b11cf88cf1e2a233ea2868181416ad792ca40e088d6be
BLOCKCHAIN_API_URL=http://103.213.248.186:3001
RELAYER_API_URL=http://103.213.248.28:3002
REDIS_HOST=103.213.248.122
REDIS_PORT=6379
REDIS_PASSWORD=CryptoRedis2026
SERVER_PORT=8082
```

### 2.6 widget (103.213.248.135)

**Nginx (정적 파일 서빙)**

| 도메인 | 용도 | SSL | 설정 파일 |
|--------|------|-----|----------|
| widget.cryptoments.cc | Widget UI (SPA) + SDK JS | Let's Encrypt | `/etc/nginx/sites-available/widget.cryptoments.cc` |

**디렉토리 구조**
```
/var/www/widget.cryptoments.cc/
├── index.html              ← Vue 3 SPA 엔트리
├── assets/
│   ├── js/                 ← Vite 빌드 결과 (hashed filenames)
│   └── css/
├── images/                 ← 체인 아이콘 등
├── sdk/
│   └── widget.cryptoments.js  ← 파트너 임베드용 SDK (v2.0.0)
└── favicon.png
```

**SDK JS 서빙**
- URL: `https://widget.cryptoments.cc/sdk/widget.cryptoments.js`
- CORS: `Access-Control-Allow-Origin: *` (파트너 사이트 크로스 오리진 로드)
- 캐시: `max-age=3600` (1시간 — SDK 업데이트 빠른 반영)
- Nginx: `location ^~ /sdk/` (regex 캐시 규칙보다 우선)

**UFW 방화벽**
- 22/tcp (SSH)
- 80/tcp (HTTP → 301 HTTPS)
- 443/tcp (HTTPS)

**SSH 접속**: root 사용자, deploy_key 기반
```bash
# bastion에서
ssh widget    # → root@103.213.248.135
```

**Cloudflare**: DNS 프록시 활성화 (자동 CDN + DDoS 방어)

---

## 3. 서비스 토폴로지

```
                    ┌─────────────────────────────────────────────┐
                    │              db-01 (122)                     │
                    │  MySQL 8.0 :3306   Redis 7.0 :6379          │
                    └──────┬──────────────────┬───────────────────┘
                           │                  │
          ┌────────────────┼──────────────────┼────────────────────┐
          │                │                  │                    │
   ┌──────▼──────┐  ┌──────▼──────┐  ┌───────▼─────┐  ┌──────────▼──────────┐
   │  app-01     │  │  node-01    │  │  node-02    │  │  partner (81)       │
   │  (125)      │  │  (186)      │  │  (28)       │  │                     │
   │             │  │             │  │             │  │  Nginx (SSL)        │
   │ open-api    │  │ blockchain  │  │ relayer-api │  │  ├ partner.cc       │
   │  :8081      │──│  -api :3001 │  │  :3002      │  │  │  → SPA + /api   │
   │ scheduler   │  │ telegram    │  │ wallet-     │  │  └ partner-api.cc   │
   │  :8083      │  │  -bot       │  │  activator  │  │      → :8082       │
   └─────────────┘  └─────────────┘  └─────────────┘  │ partner-api :8082  │
                                                       └────────────────────┘

   ┌───────────────────────┐
   │  widget (135)          │
   │  Nginx (SSL + CF)      │
   │  ├ widget.cc → SPA     │
   │  └ widget.cc/sdk/      │
   │    → SDK JS (CORS)     │
   └────────────────────────┘
```

### 서비스 간 통신

| 출발 | 도착 | 프로토콜 | 용도 |
|------|------|---------|------|
| app-01 (open-api) | node-01 :3001 | HTTP | 블록체인 조회 |
| app-01 (open-api) | node-02 :3002 | HTTP | Relayer 연동 |
| partner (partner-api) | node-01 :3001 | HTTP | 블록체인 조회 |
| partner (partner-api) | node-02 :3002 | HTTP | Relayer 연동 |
| 모든 서비스 | db-01 :3306 | MySQL | DB 접근 |
| 모든 서비스 | db-01 :6379 | Redis | 캐시/분산 락 |
| 외부 Webhook | app-01 :8081 | HTTPS | 블록체인 모니터 이벤트 수신 |
| node-02 (relayer) | TronZap API | HTTPS | TRON 에너지 임대 |

---

## 4. 외부 서비스 연동

| 서비스 | URL | 용도 |
|--------|-----|------|
| QuikNode BSC | `https://alien-hidden-yard.bsc.quiknode.pro/...` | BSC RPC |
| QuikNode Polygon | `https://fabled-cool-field.matic.quiknode.pro/...` | Polygon RPC |
| QuikNode TRON | `https://misty-wiser-diagram.tron-mainnet.quiknode.pro/...` | TRON RPC |
| Alchemy ETH | `https://eth-mainnet.g.alchemy.com/v2/demo` | ETH RPC |
| TronZap | `https://api.tronzap.com` | TRON 에너지 임대 |
| Bithumb API | `https://api.bithumb.com` | 시세 조회 (scheduler) |
| Axim Pay | `https://pay.axim.one` | Axim Pay 연동 |
| Blockchain Monitor | `https://blockchain.axim.one` | 주소 모니터링 등록/해제 (service-id: 4) |

> RPC URL은 `blockchain_networks` DB 테이블에서 런타임 로드. .env에 없음.

---

## 5. 도메인 & SSL

| 도메인 | 서버 | 용도 | SSL |
|--------|------|------|-----|
| api.cryptoments.cc | app-01 (125) | Open API (Webhook 수신) → :8081 | Let's Encrypt (certbot) |
| partner.cryptoments.cc | partner (81) | Partner 콘솔 UI | Let's Encrypt (certbot) |
| partner-api.cryptoments.cc | partner (81) | Partner API (직접) | Let's Encrypt (certbot) |
| widget.cryptoments.cc | widget (135) | Widget UI + SDK JS | Let's Encrypt (certbot) + Cloudflare |

---

## 6. 운영 명령어 Quick Reference

### Bastion 접속 (모든 작업의 시작점)

```bash
# bastion 접속
ssh -i aws_ssh_server.pem ubuntu@13.113.128.166

# bastion에서 인프라 서버 접속 (deploy_key 자동 사용)
ssh db-01      # → 103.213.248.122
ssh app-01     # → 103.213.248.125
ssh node-01    # → 103.213.248.186
ssh node-02    # → 103.213.248.28
ssh partner    # → 103.213.248.81
ssh widget     # → 103.213.248.135 (root)

# OpenVPN 접속 (Mac/Windows)
# dudgh.ovpn 파일을 OpenVPN 클라이언트로 import
```

### 서비스 관리

```bash
# --- app-01 (Spring Boot) --- (bastion에서)
ssh app-01
sudo systemctl restart cryptoments-open-api
sudo systemctl restart cryptoments-scheduler
journalctl -u cryptoments-open-api -f

# --- node-01 (PM2) --- (bastion에서)
ssh node-01
export PATH="/home/ubuntu/.local/share/fnm/node-versions/v20.20.2/installation/bin:$PATH"
pm2 restart all --update-env
pm2 logs blockchain-api --lines 50
pm2 monit

# --- node-02 (PM2) --- (bastion에서)
ssh node-02
export PATH="/home/ubuntu/.local/share/fnm/node-versions/v20.20.2/installation/bin:$PATH"
pm2 restart all --update-env
pm2 logs relayer-api --lines 50

# --- partner (systemd + Nginx) --- (bastion에서)
ssh partner
sudo systemctl restart cryptoments-partner-api
sudo systemctl restart nginx
journalctl -u cryptoments-partner-api -f

# --- db-01 --- (bastion에서)
ssh db-01
mysql -u cryptoments -p'Crypt0m3nts!2026' cryptoments_db
redis-cli -a CryptoRedis2026
```

### 헬스체크

```bash
# Spring Boot
curl -s http://103.213.248.125:8081/actuator/health  # open-api
curl -s http://103.213.248.125:8083/actuator/health  # scheduler
curl -s http://103.213.248.81:8082/actuator/health   # partner-api

# Node.js
curl -s http://103.213.248.186:3001/health           # blockchain-api
curl -s http://103.213.248.28:3002/health             # relayer-api

# Redis
redis-cli -h 103.213.248.122 -a CryptoRedis2026 ping

# 외부 도메인
curl -sf https://partner.cryptoments.cc              # Partner UI
curl -sf https://partner-api.cryptoments.cc/actuator/health  # Partner API
curl -sf https://widget.cryptoments.cc               # Widget UI
curl -sf https://widget.cryptoments.cc/sdk/widget.cryptoments.js -o /dev/null && echo "SDK OK"  # SDK JS
```

### 배포 (수동)

```bash
# Spring Boot JAR 배포 (예: open-api)
scp open-api.jar ubuntu@103.213.248.125:/opt/cryptoments/open-api/
ssh ubuntu@103.213.248.125 "sudo systemctl restart cryptoments-open-api"

# Node.js 배포
rsync -azv node-service/ ubuntu@103.213.248.186:/opt/cryptoments/node-service/
ssh ubuntu@103.213.248.186 "cd /opt/cryptoments/node-service && pm2 restart all --update-env"

# Partner UI 배포
scp -r dist/* root@103.213.248.81:/opt/cryptoments/partner-front/

# Widget UI + SDK 배포 (bastion에서)
ssh widget 'rm -rf /var/www/widget.cryptoments.cc/*'
scp -r widget-ui/dist/* widget:/var/www/widget.cryptoments.cc/
ssh widget 'chmod -R a+rX /var/www/widget.cryptoments.cc/'
```

---

## 7. 환경변수 파일 위치

| 서버 | 경로 | 대상 |
|------|------|------|
| app-01 | `/opt/cryptoments/config/.env.open-api` | open-api |
| app-01 | `/opt/cryptoments/config/.env.scheduler` | scheduler |
| node-01 | `/opt/cryptoments/node-service/.env` | blockchain-api, telegram-bot |
| node-02 | `/opt/cryptoments/node-service/.env` | relayer-api, wallet-activator |
| partner | `/opt/cryptoments/config/.env.partner-api` | partner-api |

---

## 8. Bastion 서버 구성

### 8.1 bastion (13.113.128.166 — AWS EIP)

**OS**: Ubuntu 24.04 (AWS EC2)
**접속**: `ssh -i aws_ssh_server.pem ubuntu@13.113.128.166`

**설치된 소프트웨어**:
- fail2ban (SSH brute-force 방어)
- OpenVPN (UDP 1194, VPN subnet: 10.8.0.0/24)
- sshpass (인프라 password 접속 보조)

**UFW 방화벽**:
- 22/tcp (SSH — Any)
- 1194/udp (OpenVPN)

**SSH Config** (`~/.ssh/config`):
- deploy_key 기반으로 6개 인프라 서버 접속
- 호스트 별칭: db-01, app-01, node-01, node-02, partner, widget

**OpenVPN**:
- 서버: 10.8.0.1
- 클라이언트 config: `dudgh.ovpn` (cryptoments-admin 폴더)
- push route: 103.213.248.0/24

### 8.2 보안 적용 현황 (2026-03-29)

| 서버 | SSH 제한 | Password 인증 | UFW |
|------|---------|-------------|-----|
| bastion | Any (AWS SG로 추가 제한 가능) | PEM 키 전용 | 22, 1194/udp |
| db-01 | bastion IP only | 비활성화 | SSH + MySQL/Redis (인프라 IP only) |
| app-01 | bastion IP only | 비활성화 | SSH + HTTP/HTTPS + 8081/8083 |
| node-01 | bastion IP only | 비활성화 | SSH + 3001/3003 |
| node-02 | bastion IP only | 비활성화 | SSH + 3002 |
| partner | bastion IP only | 비활성화 | SSH + HTTP/HTTPS |
| widget | bastion IP only | 비활성화 | SSH + HTTP/HTTPS |

---

## 9. 알려진 이슈

| 이슈 | 상태 | 비고 |
|------|------|------|
| Actuator health 500 | 미해결 | Axim XExceptionHandler가 SystemHealth 직렬화 실패. 코드 수정 필요 |
| ETH RPC demo 키 | 미해결 | Alchemy demo 키 사용 중. 운영용 키로 교체 필요 |
| GitLab CI/CD `PARTNER_HOST` | 미설정 | GitLab Variables에 `103.213.248.81` 추가 필요 |
| GitLab CI/CD `WIDGET_HOST` | 미설정 | GitLab Variables에 `103.213.248.135` 추가 필요 |
| admin-api 미배포 | 대기 | 서버 미정 (partner 서버 or 별도) |
| ~~SSH brute-force~~ | ✅ 해결 | bastion 도입 + UFW IP 제한 + password 인증 비활성화 |
| ~~운영 DB 데이터 불일치~~ | ✅ 해결 | blockchain_networks/currencies/hd_wallets/wallet_addresses/wallet_keys/nonce_tracker 정합성 확보 |

---

## 10. 모니터링 — Vigil (2026-07-17 설치)

전 서버 6대에 `vigil-agent` 설치 완료. 기존 Axim 공용 콜렉터에 **project=cryptoments**로 등록.

### 10.1 콜렉터 (공용 — cryptoments 인프라 외부)

| 항목 | 값 |
|------|-----|
| 대시보드 | `http://103.118.247.165:3500/` |
| Agent 접속 URL | `ws://103.118.247.165:3500/ws` |
| auth_token | `vigil-axim-2026-secure` (전 프로젝트 공용) |
| 프로젝트 | `cryptoments` (그 외 axim-wallet, torq, blockchain-monitor, infra 공존) |
| Agent 버전 | v0.4.2 (콜렉터 `/bin/vigil-agent` 셀프업데이트 채널 기준) |
| AI 조회 | 로컬 Mac `vigil-mcp` (`~/git/vigil/target/release/vigil-mcp --url http://103.118.247.165:3500`) |

### 10.2 서버별 에이전트 + 로그 소스

에이전트 공통: `/opt/vigil/vigil-agent`, 설정 `/opt/vigil/agent.toml`, systemd `vigil-agent.service`, group=prod.

| server_id | 로그 소스 (name → 파일) | 포맷 |
|-----------|------------------------|------|
| db-01 | mysql → `/var/log/mysql/error.log` / redis → `/var/log/redis/redis-server.log` | plain (default_level=info) |
| app-01 | open-api, scheduler → 각 `/opt/cryptoments/<svc>/logs/default.log`(info) + `error.log`(error, multiline) | plain + default_level |
| node-01 | blockchain-api, telegram-bot, telegram-bot-p2p → `packages/<svc>/logs/combined.log` | custom(winston `ts [level] msg`), multiline |
| node-02 | relayer-api, wallet-activator → `packages/<svc>/logs/combined.log` | custom(winston), multiline |
| partner | admin-api, partner-api → default.log(info)+error.log(error) / nginx → `/var/log/nginx/error.log` | plain + default_level |
| widget | nginx → `/var/log/nginx/error.log` / guide-ui → `/root/.pm2/logs/guide-ui-{out,error}.log` | plain + default_level |

> Spring `default.log`는 `HH:MM:SS 메시지` 레벨 없는 포맷이라 `default_level=info` 필수. `error.log`는 스택트레이스 전용이라 `default_level=error` + `multiline`. (레벨 미지정 시 카운트/푸시에서 제외되어 장애가 안 보임)
> pm2 기본 로그(`node-service/logs/*-out.log`)는 비어있고, 실제 로그는 winston `packages/<svc>/logs/combined.log`에 기록됨.

### 10.3 운영 명령어

```bash
# 상태/로그 (bastion에서)
ssh <서버> 'systemctl status vigil-agent'
ssh <서버> 'sudo journalctl -u vigil-agent --since "5 min ago" | grep "metrics sent"'

# 설정 변경 후 (서버/앱 재시작 불필요 — agent만 재시작)
ssh <서버> 'sudo systemctl restart vigil-agent'

# 에이전트 업그레이드 (콜렉터 셀프업데이트 채널)
curl -s http://103.118.247.165/bin/agent-update.sh | sudo COLLECTOR_URL=http://103.118.247.165 bash

# 콜렉터 헬스체크
curl -sf http://103.118.247.165:3500/api/v1/status
```
