Files
AutonetSellCar/SERVER_INFRASTRUCTURE_PLAN.md
AutonetSellCar Deploy 1c45840c70 fix: Add Korean fonts to Docker image for PDF capture
- 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>
2026-01-04 13:20:23 +09:00

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. 다음 단계

  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 실제 설정 반영