# 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 ```yaml 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`): ```yaml 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`): ```yaml 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` ```yaml 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): ```yaml # 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 주요 테이블 ```sql -- 제조사 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) 설정 ```bash # 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) 설정 ```bash # 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) 설정 ```bash # 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 설정 ```bash # 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) ```bash #!/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 설정 ```bash # 매일 새벽 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. 다음 단계 1. **Server2, Server3 SSD 용량 확인** 2. **Server1 Docker 환경 구축** 3. **MongolCar Backend API 개발 (FastAPI)** 4. **MongolCar Frontend 개발 (Next.js)** 5. **Carmodoo Agent Docker화** 6. **DNS 설정 및 SSL 인증서 발급** --- *Generated by Claude Code - 2025-11-28* *Updated: 2025-01-04 - Grantech Docker 전환, AutonetSellCar Staging 추가, Nginx Proxy Manager 실제 설정 반영*