- Add fonts-noto-cjk and fontconfig to backend Dockerfile - Fixes garbled Korean text in performance check PDFs - Update SERVER_INFRASTRUCTURE_PLAN.md with actual infrastructure 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
22 KiB
22 KiB
MongolCar 서버 인프라 구성 계획
1. 서버 현황
| 서버 | IP | CPU | RAM | SSD | 상태 |
|---|---|---|---|---|---|
| Server1 | 192.168.0.201 | Ryzen 7700 | 64GB | 2TB | 신규 설치 |
| Server2 | 192.168.0.202 | Ryzen 7700 | 64GB | 확인 필요 | 정리 필요 |
| Server3 | 192.168.0.203 | Ryzen 7700 | 64GB | 확인 필요 | 정리 필요 |
2. 운영 사이트
| 도메인 | 용도 |
|---|---|
| www.autonetsellcar.com | 몽골 중고차 수출 플랫폼 (MongolCar) |
| www.grantech.kr | Grantech 기업 사이트 |
| www.cylinx.kr | Cylinx 기업 사이트 |
3. 권장 아키텍처: Master-Worker 구성
3.1 서버 역할 분배
┌─────────────────────────────────────────────────────────────────────┐
│ Server1 (192.168.0.201) │
│ [ MASTER NODE ] │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Nginx │ │ PostgreSQL │ │ Redis │ │
│ │ Proxy │ │ (Primary) │ │ (Primary) │ │
│ │ Manager │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Portainer │ │ Grafana │ │ Prometheus │ │
│ │ (Docker) │ │ (Monitoring)│ │ (Metrics) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ Server2 (192.168.0.202) │
│ [ WORKER NODE 1 ] │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ www.autonetsellcar.com (MongolCar) │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Next.js │ │ FastAPI │ │ Carmodoo │ │ │
│ │ │ Frontend │ │ Backend │ │ Agent │ │ │
│ │ │ :3000 │ │ :8000 │ │ (Cron Job) │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ PostgreSQL │ │ Redis │ │
│ │ (Replica) │ │ (Replica) │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ Server3 (192.168.0.203) │
│ [ WORKER NODE 2 ] │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ www.grantech.kr │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Next.js │ │ FastAPI │ │ │
│ │ │ Frontend │ │ Backend │ │ │
│ │ │ :3001 │ │ :8001 │ │ │
│ │ └─────────────┘ └─────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ www.cylinx.kr │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Next.js │ │ FastAPI │ │ │
│ │ │ Frontend │ │ Backend │ │ │
│ │ │ :3002 │ │ :8002 │ │ │
│ │ └─────────────┘ └─────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ PostgreSQL │ │ Redis │ │
│ │ (Replica) │ │ (Replica) │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
4. 네트워크 구성
4.1 외부 접근 (인터넷 → 서버)
인터넷
│
▼
┌─────────────────┐
│ 공유기/방화벽 │
│ (Port Forward) │
└─────────────────┘
│
├── 80/443 ──→ Server1:80/443 (Nginx Proxy Manager)
│
└── 22 ──→ Server1:22 (SSH - VPN 권장)
4.2 내부 네트워크
| 용도 | 포트 | 서버 |
|---|---|---|
| SSH | 22 | 모든 서버 |
| PostgreSQL | 5432 | Server1 (Primary) |
| Redis | 6379 | Server1 (Primary) |
| Nginx Proxy Manager | 80, 443, 81 | Server1 |
| Portainer | 9000 | Server1 |
| Grafana | 3100 | Server1 |
| Prometheus | 9090 | Server1 |
| MongolCar Frontend | 3000 | Server2 |
| MongolCar Backend | 8000 | Server2 |
| Grantech Frontend | 3001 | Server3 |
| Grantech Backend | 8001 | Server3 |
| Cylinx Frontend | 3002 | Server3 |
| Cylinx Backend | 8002 | Server3 |
5. Docker 컨테이너 구성
5.1 Server1 (Master) - docker-compose.yml
version: '3.8'
services:
# Reverse Proxy
nginx-proxy-manager:
image: 'jc21/nginx-proxy-manager:latest'
container_name: nginx-proxy-manager
restart: unless-stopped
ports:
- '80:80'
- '443:443'
- '81:81' # Admin UI
volumes:
- ./data/nginx/data:/data
- ./data/nginx/letsencrypt:/etc/letsencrypt
# Database
postgres:
image: postgres:16-alpine
container_name: postgres-primary
restart: unless-stopped
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: mongolcar
ports:
- '5432:5432'
volumes:
- ./data/postgres:/var/lib/postgresql/data
- ./init-db:/docker-entrypoint-initdb.d
# Cache
redis:
image: redis:7-alpine
container_name: redis-primary
restart: unless-stopped
ports:
- '6379:6379'
volumes:
- ./data/redis:/data
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
# Docker Management
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
restart: unless-stopped
ports:
- '9000:9000'
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data/portainer:/data
# Monitoring
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
ports:
- '9090:9090'
volumes:
- ./config/prometheus.yml:/etc/prometheus/prometheus.yml
- ./data/prometheus:/prometheus
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
ports:
- '3100:3000'
environment:
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
volumes:
- ./data/grafana:/var/lib/grafana
networks:
default:
name: master-network
5.2 Server2 (AutonetSellCar) - 실제 구성 (2025-01-04 적용)
위치: /opt/autonet/production/ 및 /opt/autonet/staging/
Production (docker-compose.production.yml):
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
args:
- NEXT_PUBLIC_API_URL=https://autonetsellcar.com
container_name: autonet-frontend
ports:
- "3000:3000"
environment:
- NODE_ENV=production
depends_on:
- backend
restart: unless-stopped
networks:
- mongolcar-network
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: autonet-backend
ports:
- "8000:8000"
env_file:
- ./backend/.env
volumes:
- /opt/autonet/production/backend/uploads:/app/uploads
restart: unless-stopped
networks:
- mongolcar-network
networks:
mongolcar-network:
external: true
Staging (docker-compose.staging.yml):
services:
frontend-staging:
build:
context: ./frontend
dockerfile: Dockerfile
args:
- NEXT_PUBLIC_API_URL=https://staging.autonetsellcar.com
container_name: autonet-frontend-staging
ports:
- "3001:3000"
depends_on:
- backend-staging
restart: unless-stopped
networks:
- mongolcar-network
backend-staging:
build:
context: ./backend
dockerfile: Dockerfile
container_name: autonet-backend-staging
ports:
- "8001:8000"
env_file:
- ./backend/.env # DB_NAME=autonet_staging
volumes:
- /opt/autonet/production/backend/uploads:/app/uploads
restart: unless-stopped
networks:
- mongolcar-network
networks:
mongolcar-network:
external: true
Server2 컨테이너 현황:
| 컨테이너 | 포트 | 도메인 | DB | 상태 |
|---|---|---|---|---|
| autonet-frontend | 3000 | autonetsellcar.com | - | ✅ 운영중 |
| autonet-backend | 8000 | (내부) | autonet | ✅ 운영중 |
| autonet-frontend-staging | 3001 | staging.autonetsellcar.com | - | ✅ 운영중 |
| autonet-backend-staging | 8001 | (내부) | autonet_staging | ✅ 운영중 |
5.3 Server3 (Grantech & Nextcloud) - 실제 구성 (2025-01-04 적용)
위치: /home/damon/sites/grantech/docker-compose.yml
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: grantech-frontend
ports:
- "3001:3001"
environment:
- NODE_ENV=production
depends_on:
- backend
restart: unless-stopped
networks:
- grantech-network
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: grantech-backend
ports:
- "8001:8001"
env_file:
- ./backend/.env
volumes:
- ./backend/uploads:/app/uploads
restart: unless-stopped
networks:
- grantech-network
networks:
grantech-network:
driver: bridge
Nextcloud (별도 docker-compose):
# Nextcloud - cloud.grantech.kr
services:
nextcloud:
image: nextcloud:latest
container_name: nextcloud
ports:
- "8080:80"
restart: unless-stopped
nextcloud-db:
image: mariadb:10.6
container_name: nextcloud-db
restart: unless-stopped
Server3 컨테이너 현황:
| 컨테이너 | 포트 | 도메인 | 상태 |
|---|---|---|---|
| grantech-frontend | 3001 | grantech.kr | ✅ 운영중 |
| grantech-backend | 8001 | api.grantech.kr | ✅ 운영중 |
| nextcloud | 8080 | cloud.grantech.kr | ✅ 운영중 |
| nextcloud-db | 3306 | (내부) | ✅ 운영중 |
6. 데이터베이스 설계
6.1 PostgreSQL 데이터베이스 분리
| 데이터베이스 | 용도 | Redis DB | 상태 |
|---|---|---|---|
autonet |
AutonetSellCar Production | 0 | ✅ 운영중 |
autonet_staging |
AutonetSellCar Staging | 0 | ✅ 운영중 |
grantech |
Grantech 서비스 | 1 | ✅ 운영중 |
cylinx |
Cylinx 서비스 (예정) | 2 | ⏳ 미구축 |
6.2 MongolCar 주요 테이블
-- 제조사
CREATE TABLE car_makers (
id SERIAL PRIMARY KEY,
code VARCHAR(10) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
name_en VARCHAR(100),
created_at TIMESTAMP DEFAULT NOW()
);
-- 모델
CREATE TABLE car_models (
id SERIAL PRIMARY KEY,
code VARCHAR(10) NOT NULL,
maker_id INTEGER REFERENCES car_makers(id),
name VARCHAR(100) NOT NULL,
name_en VARCHAR(100),
UNIQUE(code, maker_id)
);
-- 차량
CREATE TABLE cars (
id SERIAL PRIMARY KEY,
source VARCHAR(50) NOT NULL DEFAULT 'carmodoo',
source_id VARCHAR(50) NOT NULL,
source_key TEXT, -- encrypted key
maker_id INTEGER REFERENCES car_makers(id),
model_id INTEGER REFERENCES car_models(id),
car_name VARCHAR(200),
year INTEGER,
month INTEGER,
mileage INTEGER,
price_krw BIGINT,
price_usd DECIMAL(12,2),
fuel VARCHAR(20),
transmission VARCHAR(20),
color VARCHAR(50),
displacement INTEGER,
car_number VARCHAR(20),
seize_count INTEGER DEFAULT 0,
collateral_count INTEGER DEFAULT 0,
check_num VARCHAR(50),
dealer_name VARCHAR(100),
dealer_phone VARCHAR(50),
shop_name VARCHAR(100),
status VARCHAR(20) DEFAULT 'active',
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
synced_at TIMESTAMP,
UNIQUE(source, source_id)
);
-- 차량 이미지
CREATE TABLE car_images (
id SERIAL PRIMARY KEY,
car_id INTEGER REFERENCES cars(id) ON DELETE CASCADE,
url VARCHAR(500),
local_path VARCHAR(500),
is_main BOOLEAN DEFAULT FALSE,
sort_order INTEGER DEFAULT 0
);
-- 차량 옵션
CREATE TABLE car_options (
id SERIAL PRIMARY KEY,
car_id INTEGER REFERENCES cars(id) ON DELETE CASCADE,
option_name VARCHAR(100)
);
-- 사용자 (바이어)
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
name VARCHAR(100),
phone VARCHAR(50),
country VARCHAR(50) DEFAULT 'Mongolia',
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT NOW()
);
-- 문의
CREATE TABLE inquiries (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
car_id INTEGER REFERENCES cars(id),
message TEXT,
status VARCHAR(20) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT NOW()
);
-- 인덱스
CREATE INDEX idx_cars_maker ON cars(maker_id);
CREATE INDEX idx_cars_model ON cars(model_id);
CREATE INDEX idx_cars_price ON cars(price_krw);
CREATE INDEX idx_cars_year ON cars(year);
CREATE INDEX idx_cars_status ON cars(status);
7. Nginx Proxy Manager 설정
관리 UI: http://192.168.0.201:81
7.1 Proxy Hosts 설정 (실제 운영중 - 2025-01-04)
| Domain | Scheme | Forward Host | Forward Port | SSL | 상태 |
|---|---|---|---|---|---|
| autonetsellcar.com | http | 192.168.0.202 | 3000 | Let's Encrypt | ✅ 운영중 |
| staging.autonetsellcar.com | http | 192.168.0.202 | 3001 | Let's Encrypt | ✅ 운영중 |
| grantech.kr | http | 192.168.0.203 | 3001 | Let's Encrypt | ✅ 운영중 |
| api.grantech.kr | http | 192.168.0.203 | 8001 | Let's Encrypt | ✅ 운영중 |
| cloud.grantech.kr | http | 192.168.0.203 | 8080 | Let's Encrypt | ✅ 운영중 |
| cylinx.kr | http | 192.168.0.203 | 3002 | Let's Encrypt | ⏳ 예정 |
| api.cylinx.kr | http | 192.168.0.203 | 8002 | Let's Encrypt | ⏳ 예정 |
| portainer.local | http | 192.168.0.201 | 9000 | - | ✅ 내부용 |
| grafana.local | http | 192.168.0.201 | 3100 | - | ✅ 내부용 |
참고: AutonetSellCar Backend API는 별도 프록시 없이 Frontend에서 직접 호출 (같은 도메인, Next.js rewrites 사용)
8. 설치 순서
Phase 1: Server1 (Master) 설정
# 1. Docker 설치
sudo apt update && sudo apt upgrade -y
sudo apt install -y docker.io docker-compose
sudo systemctl enable docker
sudo usermod -aG docker $USER
# 2. 디렉토리 구조 생성
mkdir -p ~/master/{data,config,init-db}
mkdir -p ~/master/data/{nginx,postgres,redis,portainer,prometheus,grafana}
# 3. docker-compose.yml 작성 및 실행
cd ~/master
# docker-compose.yml 파일 생성
docker-compose up -d
# 4. PostgreSQL 초기 DB 생성
docker exec -it postgres-primary psql -U admin -c "CREATE DATABASE grantech;"
docker exec -it postgres-primary psql -U admin -c "CREATE DATABASE cylinx;"
Phase 2: Server2 (MongolCar) 설정
# 1. Docker 설치 (동일)
# 2. 프로젝트 디렉토리 구조
mkdir -p ~/mongolcar/{frontend,backend,carmodoo-agent,data,logs}
# 3. 코드 배포 및 실행
cd ~/mongolcar
# docker-compose.yml 파일 생성
docker-compose up -d
Phase 3: Server3 (Grantech & Cylinx) 설정
# 1. Docker 설치 (동일)
# 2. 프로젝트 디렉토리 구조
mkdir -p ~/sites/{grantech,cylinx}
mkdir -p ~/sites/grantech/{frontend,backend}
mkdir -p ~/sites/cylinx/{frontend,backend}
# 3. 코드 배포 및 실행
cd ~/sites
# docker-compose.yml 파일 생성
docker-compose up -d
Phase 4: DNS 및 SSL 설정
# 1. 도메인 DNS A 레코드 설정 (공인 IP로)
# autonetsellcar.com → 공인IP
# grantech.kr → 공인IP
# cylinx.kr → 공인IP
# 2. 공유기 포트포워딩
# 80 → 192.168.0.201:80
# 443 → 192.168.0.201:443
# 3. Nginx Proxy Manager에서 SSL 인증서 발급
# http://192.168.0.201:81 접속
# Proxy Hosts 추가 및 Let's Encrypt SSL 설정
9. 백업 전략
9.1 자동 백업 스크립트 (Server1)
#!/bin/bash
# /home/user/backup.sh
BACKUP_DIR="/backup/$(date +%Y%m%d)"
mkdir -p $BACKUP_DIR
# PostgreSQL 백업
docker exec postgres-primary pg_dumpall -U admin > $BACKUP_DIR/postgres_all.sql
# Redis 백업
docker exec redis-primary redis-cli -a $REDIS_PASSWORD BGSAVE
cp ~/master/data/redis/dump.rdb $BACKUP_DIR/
# 7일 이상 된 백업 삭제
find /backup -type d -mtime +7 -exec rm -rf {} \;
9.2 Cron 설정
# 매일 새벽 3시 백업
0 3 * * * /home/user/backup.sh
10. 모니터링 항목
10.1 Prometheus 수집 대상
- Node Exporter (각 서버 시스템 메트릭)
- PostgreSQL Exporter
- Redis Exporter
- Docker Container 메트릭
- Nginx 메트릭
10.2 Grafana 대시보드
- 서버 리소스 (CPU, Memory, Disk, Network)
- 컨테이너 상태
- 데이터베이스 연결/쿼리 성능
- API 응답시간
- 에러율
11. 보안 체크리스트
- SSH 키 기반 인증 설정
- 방화벽 (UFW) 설정
- Fail2ban 설치
- 불필요한 포트 차단
- PostgreSQL 외부 접근 제한
- Redis 비밀번호 설정
- HTTPS 강제 리다이렉트
- 환경변수로 비밀정보 관리
- 정기 보안 업데이트
12. 예상 리소스 사용량
| 서버 | CPU 예상 | RAM 예상 | Disk 예상 |
|---|---|---|---|
| Server1 (Master) | 10-20% | 8-12GB | 100GB+ |
| Server2 (MongolCar) | 20-40% | 8-16GB | 500GB+ (이미지) |
| Server3 (Sites) | 10-30% | 4-8GB | 50GB |
13. 다음 단계
- Server2, Server3 SSD 용량 확인
- Server1 Docker 환경 구축
- MongolCar Backend API 개발 (FastAPI)
- MongolCar Frontend 개발 (Next.js)
- Carmodoo Agent Docker화
- DNS 설정 및 SSL 인증서 발급
Generated by Claude Code - 2025-11-28 Updated: 2025-01-04 - Grantech Docker 전환, AutonetSellCar Staging 추가, Nginx Proxy Manager 실제 설정 반영