diff --git a/.gitignore b/.gitignore index 5b2b1f56..24814953 100644 --- a/.gitignore +++ b/.gitignore @@ -291,4 +291,6 @@ uploads/ *.hwp *.hwpx -claude.md \ No newline at end of file +claude.md + +.cursor/mcp.json \ No newline at end of file diff --git a/deploy/customers/README.md b/deploy/customers/README.md new file mode 100644 index 00000000..8b55214b --- /dev/null +++ b/deploy/customers/README.md @@ -0,0 +1,115 @@ +# 고객사별 환경 변수 관리 + +## 개요 + +이 폴더는 각 고객사(업체)별 환경 변수 설정을 **참고용**으로 관리합니다. + +**중요:** 실제 비밀번호는 이 파일에 저장하지 마세요. 템플릿으로만 사용합니다. + +--- + +## 고객사 목록 + +| 고객사 | 파일 | 배포 형태 | 상태 | +| :--- | :--- | :--- | :--- | +| 스피폭스 | `spifox.env` | 온프레미스 (공장 서버) | 진행 중 | +| 엔키드 | `enkid.env` | 온프레미스 (공장 서버) | 예정 | + +--- + +## 신규 고객사 추가 절차 + +### 1단계: 환경 변수 파일 생성 + +```bash +# 기존 파일 복사 +cp spifox.env newcustomer.env + +# 수정 +nano newcustomer.env +``` + +필수 수정 항목: +- `COMPANY_CODE`: 고유한 회사 코드 (예: NEWCO) +- `SERVER_IP`: 고객사 서버 IP +- `DB_PASSWORD`: 고유한 비밀번호 +- `JWT_SECRET`: 고유한 시크릿 키 + +### 2단계: 데이터베이스에 회사 등록 + +```sql +-- company_info 테이블에 추가 +INSERT INTO company_info (company_code, company_name, status) +VALUES ('NEWCO', '신규고객사', 'ACTIVE'); +``` + +### 3단계: 관리자 계정 생성 + +```sql +-- user_info 테이블에 관리자 추가 +INSERT INTO user_info (user_id, user_name, company_code, role) +VALUES ('newco_admin', '신규고객사 관리자', 'NEWCO', 'COMPANY_ADMIN'); +``` + +### 4단계: 고객사 서버에 배포 + +```bash +# 고객사 서버에 SSH 접속 +ssh user@customer-server + +# 설치 폴더 생성 +sudo mkdir -p /opt/vexplor +cd /opt/vexplor + +# docker-compose.yml 복사 (deploy/onpremise/에서) +# .env 파일 복사 및 수정 + +# 서비스 시작 +docker compose up -d +``` + +--- + +## 환경 변수 설명 + +| 변수 | 설명 | 예시 | +| :--- | :--- | :--- | +| `COMPANY_CODE` | 회사 고유 코드 (멀티테넌시) | `SPIFOX`, `ENKID` | +| `SERVER_IP` | 서버의 실제 IP | `192.168.0.100` | +| `DB_PASSWORD` | DB 비밀번호 | (고객사별 고유) | +| `JWT_SECRET` | JWT 토큰 시크릿 | (고객사별 고유) | +| `IMAGE_TAG` | Docker 이미지 버전 | `latest`, `v1.0.0` | + +--- + +## 보안 주의사항 + +1. **비밀번호**: 이 폴더의 파일에는 실제 비밀번호를 저장하지 마세요 +2. **Git**: `.env` 파일이 커밋되지 않도록 `.gitignore` 확인 +3. **고객사별 격리**: 각 고객사는 별도 서버, 별도 DB로 완전 격리 +4. **키 관리**: JWT_SECRET은 고객사별로 반드시 다르게 설정 + +--- + +## 구조 다이어그램 + +``` +[Harbor (이미지 저장소)] + │ + │ docker pull + ↓ +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ 스피폭스 공장 │ │ 엔키드 공장 │ │ 신규 고객사 │ +│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │ +│ │ Vexplor │ │ │ │ Vexplor │ │ │ │ Vexplor │ │ +│ │ SPIFOX │ │ │ │ ENKID │ │ │ │ NEWCO │ │ +│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │ +│ │ │ │ │ │ +│ [독립 DB] │ │ [독립 DB] │ │ [독립 DB] │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ + +* 각 공장은 완전히 독립적으로 운영 +* 같은 Docker 이미지 사용, .env만 다름 +* 데이터는 절대 섞이지 않음 (물리적 격리) +``` + diff --git a/deploy/customers/enkid.env b/deploy/customers/enkid.env new file mode 100644 index 00000000..3a9e84df --- /dev/null +++ b/deploy/customers/enkid.env @@ -0,0 +1,36 @@ +# ============================================ +# 엔키드(ENKID) 공장 서버 환경 변수 +# ============================================ +# 이 파일을 엔키드 공장 서버의 /opt/vexplor/.env로 복사 + +# 회사 정보 +COMPANY_CODE=ENKID + +# 서버 정보 (실제 서버 IP로 변경 필요) +SERVER_IP=10.0.0.50 + +# 데이터베이스 +DB_USER=vexplor +DB_PASSWORD=enkid_secure_password_here +DB_NAME=vexplor +DB_PORT=5432 + +# 백엔드 +BACKEND_PORT=3001 +JWT_SECRET=enkid_jwt_secret_minimum_32_characters +JWT_EXPIRES_IN=24h +LOG_LEVEL=info + +# 프론트엔드 +FRONTEND_PORT=80 + +# Harbor 레지스트리 +HARBOR_USER=enkid_harbor_user +HARBOR_PASSWORD=enkid_harbor_password + +# 이미지 태그 +IMAGE_TAG=latest + +# Watchtower (1시간마다 업데이트 확인) +UPDATE_INTERVAL=3600 + diff --git a/deploy/customers/spifox.env b/deploy/customers/spifox.env new file mode 100644 index 00000000..ab7d6004 --- /dev/null +++ b/deploy/customers/spifox.env @@ -0,0 +1,36 @@ +# ============================================ +# 스피폭스(SPIFOX) 공장 서버 환경 변수 +# ============================================ +# 이 파일을 스피폭스 공장 서버의 /opt/vexplor/.env로 복사 + +# 회사 정보 +COMPANY_CODE=SPIFOX + +# 서버 정보 (실제 서버 IP로 변경 필요) +SERVER_IP=192.168.0.100 + +# 데이터베이스 +DB_USER=vexplor +DB_PASSWORD=spifox_secure_password_here +DB_NAME=vexplor +DB_PORT=5432 + +# 백엔드 +BACKEND_PORT=3001 +JWT_SECRET=spifox_jwt_secret_minimum_32_characters +JWT_EXPIRES_IN=24h +LOG_LEVEL=info + +# 프론트엔드 +FRONTEND_PORT=80 + +# Harbor 레지스트리 +HARBOR_USER=spifox_harbor_user +HARBOR_PASSWORD=spifox_harbor_password + +# 이미지 태그 +IMAGE_TAG=latest + +# Watchtower (1시간마다 업데이트 확인) +UPDATE_INTERVAL=3600 + diff --git a/deploy/onpremise/README.md b/deploy/onpremise/README.md new file mode 100644 index 00000000..76cad490 --- /dev/null +++ b/deploy/onpremise/README.md @@ -0,0 +1,321 @@ +# Vexplor 온프레미스(공장) 배포 가이드 + +## 개요 + +이 가이드는 Vexplor를 **공장 내부 서버(온프레미스)**에 배포하는 방법을 설명합니다. + +**Watchtower**를 사용하여 Harbor에 새 이미지가 푸시되면 **자동으로 업데이트**됩니다. + +--- + +## 사전 요구사항 + +### 서버 요구사항 + +| 항목 | 최소 사양 | 권장 사양 | +| :--- | :--- | :--- | +| OS | Ubuntu 20.04+ | Ubuntu 22.04 LTS | +| CPU | 4 Core | 8 Core | +| RAM | 8 GB | 16 GB | +| Disk | 50 GB | 100 GB SSD | +| Network | Harbor 접근 가능 | - | + +### 필수 소프트웨어 + +```bash +# Docker 설치 확인 +docker --version # 20.10 이상 + +# Docker Compose 설치 확인 +docker compose version # v2.0 이상 +``` + +--- + +## 1단계: 초기 설정 + +### 1.1 배포 폴더 생성 + +```bash +# 배포 폴더 생성 +sudo mkdir -p /opt/vexplor +cd /opt/vexplor + +# 파일 복사 (또는 git clone) +# deploy/onpremise/ 폴더의 내용을 복사 +``` + +### 1.2 환경 변수 설정 + +```bash +# 예제 파일 복사 +cp env.example .env + +# 편집 +nano .env +``` + +**필수 수정 항목:** + +```bash +# 서버 IP (이 서버의 실제 IP) +SERVER_IP=192.168.0.100 + +# 회사 코드 +COMPANY_CODE=SPIFOX + +# DB 비밀번호 (강력한 비밀번호 설정) +DB_PASSWORD=MySecurePassword123! + +# JWT 시크릿 (32자 이상) +JWT_SECRET=your-super-secret-jwt-key-minimum-32-chars + +# Harbor 인증 정보 +HARBOR_USER=your_username +HARBOR_PASSWORD=your_password +``` + +### 1.3 Harbor 레지스트리 로그인 + +Watchtower가 이미지를 당겨올 수 있도록 Docker 로그인이 필요합니다. + +```bash +# Harbor 로그인 +docker login harbor.wace.me + +# Username: (입력) +# Password: (입력) + +# 로그인 성공 확인 +cat ~/.docker/config.json +``` + +--- + +## 2단계: 서비스 실행 + +### 2.1 서비스 시작 + +```bash +cd /opt/vexplor + +# 이미지 다운로드 & 실행 +docker compose up -d + +# 상태 확인 +docker compose ps +``` + +### 2.2 정상 동작 확인 + +```bash +# 모든 컨테이너 Running 상태 확인 +docker compose ps + +# 로그 확인 +docker compose logs -f + +# 개별 서비스 로그 +docker compose logs -f backend +docker compose logs -f frontend +docker compose logs -f watchtower +``` + +### 2.3 웹 접속 테스트 + +``` +프론트엔드: http://SERVER_IP:80 +백엔드 API: http://SERVER_IP:3001/health +``` + +--- + +## 3단계: 자동 업데이트 확인 + +### Watchtower 동작 확인 + +```bash +# Watchtower 로그 확인 +docker compose logs -f watchtower +``` + +**정상 로그 예시:** + +``` +watchtower | time="2024-12-28T10:00:00+09:00" level=info msg="Checking for updates..." +watchtower | time="2024-12-28T10:00:05+09:00" level=info msg="Found new image harbor.wace.me/vexplor/vexplor-backend:latest" +watchtower | time="2024-12-28T10:00:10+09:00" level=info msg="Stopping container vexplor-backend" +watchtower | time="2024-12-28T10:00:15+09:00" level=info msg="Starting container vexplor-backend" +``` + +### 업데이트 주기 변경 + +```bash +# .env 파일에서 변경 +UPDATE_INTERVAL=3600 # 1시간마다 확인 + +# 변경 후 watchtower 재시작 +docker compose restart watchtower +``` + +--- + +## 운영 가이드 + +### 서비스 관리 명령어 + +```bash +# 모든 서비스 상태 확인 +docker compose ps + +# 모든 서비스 중지 +docker compose stop + +# 모든 서비스 시작 +docker compose start + +# 모든 서비스 재시작 +docker compose restart + +# 모든 서비스 삭제 (데이터 유지) +docker compose down + +# 모든 서비스 삭제 + 볼륨 삭제 (주의: 데이터 삭제됨!) +docker compose down -v +``` + +### 로그 확인 + +```bash +# 전체 로그 (실시간) +docker compose logs -f + +# 특정 서비스 로그 +docker compose logs -f backend +docker compose logs -f frontend +docker compose logs -f database + +# 최근 100줄만 +docker compose logs --tail=100 backend +``` + +### 수동 업데이트 (긴급 시) + +자동 업데이트를 기다리지 않고 즉시 업데이트하려면: + +```bash +# 최신 이미지 다운로드 +docker compose pull + +# 재시작 +docker compose up -d +``` + +### 특정 버전으로 롤백 + +```bash +# .env 파일에서 버전 지정 +IMAGE_TAG=v1.0.0 + +# 재시작 +docker compose up -d +``` + +--- + +## 백업 가이드 + +### DB 백업 + +```bash +# 백업 디렉토리 생성 +mkdir -p /opt/vexplor/backups + +# PostgreSQL 백업 +docker compose exec database pg_dump -U vexplor vexplor > /opt/vexplor/backups/backup_$(date +%Y%m%d_%H%M%S).sql +``` + +### 업로드 파일 백업 + +```bash +# 볼륨 위치 확인 +docker volume inspect vexplor_backend_uploads + +# 또는 직접 복사 +docker cp vexplor-backend:/app/uploads /opt/vexplor/backups/uploads_$(date +%Y%m%d) +``` + +### 자동 백업 스크립트 (Cron) + +```bash +# crontab 편집 +crontab -e + +# 매일 새벽 3시 DB 백업 +0 3 * * * docker compose -f /opt/vexplor/docker-compose.yml exec -T database pg_dump -U vexplor vexplor > /opt/vexplor/backups/backup_$(date +\%Y\%m\%d).sql +``` + +--- + +## 문제 해결 + +### 컨테이너가 시작되지 않음 + +```bash +# 로그 확인 +docker compose logs backend + +# 일반적인 원인: +# 1. 환경 변수 누락 → .env 파일 확인 +# 2. 포트 충돌 → netstat -tlnp | grep 3001 +# 3. 메모리 부족 → free -h +``` + +### DB 연결 실패 + +```bash +# DB 컨테이너 상태 확인 +docker compose logs database + +# DB 직접 접속 테스트 +docker compose exec database psql -U vexplor -d vexplor -c "SELECT 1" +``` + +### Watchtower가 업데이트하지 않음 + +```bash +# Watchtower 로그 확인 +docker compose logs watchtower + +# Harbor 인증 확인 +docker pull harbor.wace.me/vexplor/vexplor-backend:latest + +# 라벨 확인 (라벨이 있는 컨테이너만 업데이트) +docker inspect vexplor-backend | grep watchtower +``` + +### 디스크 공간 부족 + +```bash +# 사용하지 않는 이미지/컨테이너 정리 +docker system prune -a + +# 오래된 로그 정리 +docker compose logs --tail=0 backend # 로그 초기화 +``` + +--- + +## 보안 권장사항 + +1. **방화벽 설정**: 필요한 포트(80, 3001)만 개방 +2. **SSL/TLS**: Nginx 리버스 프록시 + Let's Encrypt 적용 권장 +3. **정기 백업**: 최소 주 1회 DB 백업 +4. **로그 모니터링**: 비정상 접근 감시 + +--- + +## 연락처 + +배포 관련 문의: [담당자 이메일] + diff --git a/deploy/onpremise/docker-compose.yml b/deploy/onpremise/docker-compose.yml new file mode 100644 index 00000000..f913384b --- /dev/null +++ b/deploy/onpremise/docker-compose.yml @@ -0,0 +1,155 @@ +# Vexplor 온프레미스(공장) 배포용 Docker Compose +# 사용법: docker-compose up -d + +version: '3.8' + +services: + # ============================================ + # 1. 데이터베이스 (PostgreSQL) + # ============================================ + database: + image: postgres:15-alpine + container_name: vexplor-db + environment: + POSTGRES_USER: ${DB_USER:-vexplor} + POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required} + POSTGRES_DB: ${DB_NAME:-vexplor} + TZ: Asia/Seoul + volumes: + - postgres_data:/var/lib/postgresql/data + - ./init-db:/docker-entrypoint-initdb.d # 초기화 스크립트 (선택) + ports: + - "${DB_PORT:-5432}:5432" + restart: always + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-vexplor}"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - vexplor-network + + # ============================================ + # 2. 백엔드 API (Node.js) + # ============================================ + backend: + image: harbor.wace.me/vexplor/vexplor-backend:${IMAGE_TAG:-latest} + container_name: vexplor-backend + environment: + NODE_ENV: production + PORT: 3001 + HOST: 0.0.0.0 + TZ: Asia/Seoul + # DB 연결 + DB_HOST: database + DB_PORT: 5432 + DB_USER: ${DB_USER:-vexplor} + DB_PASSWORD: ${DB_PASSWORD} + DB_NAME: ${DB_NAME:-vexplor} + # JWT + JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required} + JWT_EXPIRES_IN: ${JWT_EXPIRES_IN:-24h} + # 회사 코드 (온프레미스는 단일 회사) + DEFAULT_COMPANY_CODE: ${COMPANY_CODE:-SPIFOX} + # 로깅 + LOG_LEVEL: ${LOG_LEVEL:-info} + volumes: + - backend_uploads:/app/uploads + - backend_data:/app/data + - backend_logs:/app/logs + ports: + - "${BACKEND_PORT:-3001}:3001" + depends_on: + database: + condition: service_healthy + restart: always + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3001/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + networks: + - vexplor-network + labels: + - "com.centurylinklabs.watchtower.enable=true" + + # ============================================ + # 3. 프론트엔드 (Next.js) + # ============================================ + frontend: + image: harbor.wace.me/vexplor/vexplor-frontend:${IMAGE_TAG:-latest} + container_name: vexplor-frontend + environment: + NODE_ENV: production + PORT: 3000 + HOSTNAME: 0.0.0.0 + TZ: Asia/Seoul + # 백엔드 API URL (내부 통신) + NEXT_PUBLIC_API_URL: http://${SERVER_IP:-localhost}:${BACKEND_PORT:-3001}/api + ports: + - "${FRONTEND_PORT:-80}:3000" + depends_on: + - backend + restart: always + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + networks: + - vexplor-network + labels: + - "com.centurylinklabs.watchtower.enable=true" + + # ============================================ + # 4. Watchtower (자동 업데이트) + # ============================================ + watchtower: + image: containrrr/watchtower:latest + container_name: vexplor-watchtower + environment: + TZ: Asia/Seoul + # Harbor 레지스트리 인증 + REPO_USER: ${HARBOR_USER} + REPO_PASS: ${HARBOR_PASSWORD} + # 업데이트 설정 + WATCHTOWER_POLL_INTERVAL: ${UPDATE_INTERVAL:-300} # 5분마다 확인 (초 단위) + WATCHTOWER_CLEANUP: "true" # 이전 이미지 자동 삭제 + WATCHTOWER_INCLUDE_STOPPED: "true" # 중지된 컨테이너도 업데이트 + WATCHTOWER_ROLLING_RESTART: "true" # 순차 재시작 (다운타임 최소화) + WATCHTOWER_LABEL_ENABLE: "true" # 라벨이 있는 컨테이너만 업데이트 + # 업데이트 시간 제한 (선택: 새벽 2-4시만 업데이트) + # WATCHTOWER_SCHEDULE: "0 0 2 * * *" # cron 형식 (매일 새벽 2시) + # 알림 설정 (선택) + # WATCHTOWER_NOTIFICATIONS: slack + # WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL: ${SLACK_WEBHOOK_URL} + volumes: + - /var/run/docker.sock:/var/run/docker.sock + # Harbor 인증 정보 (docker login 후 생성됨) + - ~/.docker/config.json:/config.json:ro + restart: always + networks: + - vexplor-network + +# ============================================ +# 볼륨 정의 +# ============================================ +volumes: + postgres_data: + driver: local + backend_uploads: + driver: local + backend_data: + driver: local + backend_logs: + driver: local + +# ============================================ +# 네트워크 정의 +# ============================================ +networks: + vexplor-network: + driver: bridge + diff --git a/deploy/onpremise/env.example b/deploy/onpremise/env.example new file mode 100644 index 00000000..7ffc0d5b --- /dev/null +++ b/deploy/onpremise/env.example @@ -0,0 +1,65 @@ +# ============================================ +# Vexplor 온프레미스(공장) 환경 변수 +# ============================================ +# 사용법: 이 파일을 .env로 복사 후 값 수정 +# cp env.example .env + +# ============================================ +# 서버 정보 +# ============================================ +# 이 서버의 IP 주소 (프론트엔드가 백엔드 API 호출할 때 사용) +SERVER_IP=192.168.0.100 + +# ============================================ +# 회사 정보 +# ============================================ +# 이 공장의 회사 코드 (멀티테넌시용) +COMPANY_CODE=SPIFOX + +# ============================================ +# 데이터베이스 설정 +# ============================================ +DB_USER=vexplor +DB_PASSWORD=your_secure_password_here +DB_NAME=vexplor +DB_PORT=5432 + +# ============================================ +# 백엔드 설정 +# ============================================ +BACKEND_PORT=3001 +JWT_SECRET=your_jwt_secret_key_minimum_32_characters +JWT_EXPIRES_IN=24h +LOG_LEVEL=info + +# ============================================ +# 프론트엔드 설정 +# ============================================ +FRONTEND_PORT=80 + +# ============================================ +# Harbor 레지스트리 인증 +# ============================================ +# Watchtower가 이미지를 당겨올 때 사용 +HARBOR_USER=your_harbor_username +HARBOR_PASSWORD=your_harbor_password + +# ============================================ +# 이미지 태그 +# ============================================ +# latest 또는 특정 버전 (v1.0.0 등) +IMAGE_TAG=latest + +# ============================================ +# Watchtower 설정 +# ============================================ +# 업데이트 확인 주기 (초 단위) +# 300 = 5분, 3600 = 1시간, 86400 = 24시간 +UPDATE_INTERVAL=3600 + +# ============================================ +# 알림 설정 (선택) +# ============================================ +# Slack 웹훅 URL (업데이트 알림 받기) +# SLACK_WEBHOOK_URL=https://hooks.slack.com/services/xxx/xxx/xxx + diff --git a/deploy/onpremise/scripts/backup.sh b/deploy/onpremise/scripts/backup.sh new file mode 100644 index 00000000..1e3a65fd --- /dev/null +++ b/deploy/onpremise/scripts/backup.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# ============================================ +# Vexplor 백업 스크립트 +# Cron에 등록하여 정기 백업 가능 +# ============================================ + +set -e + +INSTALL_DIR="/opt/vexplor" +BACKUP_DIR="/opt/vexplor/backups" +DATE=$(date +%Y%m%d_%H%M%S) + +# 백업 디렉토리 생성 +mkdir -p $BACKUP_DIR + +echo "==========================================" +echo " Vexplor 백업 시작 - $DATE" +echo "==========================================" + +cd $INSTALL_DIR + +# 1. PostgreSQL 데이터베이스 백업 +echo "[1/3] 데이터베이스 백업..." +docker compose exec -T database pg_dump -U vexplor vexplor > "$BACKUP_DIR/db_$DATE.sql" +gzip "$BACKUP_DIR/db_$DATE.sql" +echo " → $BACKUP_DIR/db_$DATE.sql.gz" + +# 2. 업로드 파일 백업 +echo "[2/3] 업로드 파일 백업..." +docker cp vexplor-backend:/app/uploads "$BACKUP_DIR/uploads_$DATE" 2>/dev/null || echo " → 업로드 폴더 없음 (스킵)" +if [ -d "$BACKUP_DIR/uploads_$DATE" ]; then + tar -czf "$BACKUP_DIR/uploads_$DATE.tar.gz" -C "$BACKUP_DIR" "uploads_$DATE" + rm -rf "$BACKUP_DIR/uploads_$DATE" + echo " → $BACKUP_DIR/uploads_$DATE.tar.gz" +fi + +# 3. 환경 설정 백업 +echo "[3/3] 환경 설정 백업..." +cp "$INSTALL_DIR/.env" "$BACKUP_DIR/env_$DATE" +cp "$INSTALL_DIR/docker-compose.yml" "$BACKUP_DIR/docker-compose_$DATE.yml" +echo " → $BACKUP_DIR/env_$DATE" +echo " → $BACKUP_DIR/docker-compose_$DATE.yml" + +# 4. 오래된 백업 정리 (30일 이상) +echo "" +echo "[정리] 30일 이상 된 백업 삭제..." +find $BACKUP_DIR -type f -mtime +30 -delete 2>/dev/null || true + +# 완료 +echo "" +echo "==========================================" +echo " 백업 완료!" +echo "==========================================" +echo "" +echo "백업 위치: $BACKUP_DIR" +ls -lh $BACKUP_DIR | tail -10 + diff --git a/deploy/onpremise/scripts/install.sh b/deploy/onpremise/scripts/install.sh new file mode 100644 index 00000000..880dcbcc --- /dev/null +++ b/deploy/onpremise/scripts/install.sh @@ -0,0 +1,79 @@ +#!/bin/bash +# ============================================ +# Vexplor 온프레미스 초기 설치 스크립트 +# ============================================ + +set -e + +echo "==========================================" +echo " Vexplor 온프레미스 설치 스크립트" +echo "==========================================" + +# 색상 정의 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# 설치 경로 +INSTALL_DIR="/opt/vexplor" + +# 1. Docker 설치 확인 +echo -e "\n${YELLOW}[1/5] Docker 설치 확인...${NC}" +if ! command -v docker &> /dev/null; then + echo -e "${RED}Docker가 설치되어 있지 않습니다.${NC}" + echo "다음 명령어로 설치하세요:" + echo " curl -fsSL https://get.docker.com | sh" + echo " sudo usermod -aG docker \$USER" + exit 1 +fi +echo -e "${GREEN}Docker $(docker --version | cut -d' ' -f3)${NC}" + +# 2. Docker Compose 확인 +echo -e "\n${YELLOW}[2/5] Docker Compose 확인...${NC}" +if ! docker compose version &> /dev/null; then + echo -e "${RED}Docker Compose v2가 설치되어 있지 않습니다.${NC}" + exit 1 +fi +echo -e "${GREEN}$(docker compose version)${NC}" + +# 3. 설치 디렉토리 생성 +echo -e "\n${YELLOW}[3/5] 설치 디렉토리 생성...${NC}" +sudo mkdir -p $INSTALL_DIR +sudo chown $USER:$USER $INSTALL_DIR +echo -e "${GREEN}$INSTALL_DIR 생성 완료${NC}" + +# 4. 파일 복사 +echo -e "\n${YELLOW}[4/5] 설정 파일 복사...${NC}" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +cp "$SCRIPT_DIR/docker-compose.yml" "$INSTALL_DIR/" +cp "$SCRIPT_DIR/env.example" "$INSTALL_DIR/" + +if [ ! -f "$INSTALL_DIR/.env" ]; then + cp "$SCRIPT_DIR/env.example" "$INSTALL_DIR/.env" + echo -e "${YELLOW}[주의] .env 파일을 생성했습니다. 반드시 수정하세요!${NC}" +fi + +echo -e "${GREEN}파일 복사 완료${NC}" + +# 5. Harbor 로그인 안내 +echo -e "\n${YELLOW}[5/5] Harbor 레지스트리 로그인...${NC}" +if [ ! -f ~/.docker/config.json ] || ! grep -q "harbor.wace.me" ~/.docker/config.json 2>/dev/null; then + echo -e "${YELLOW}Harbor 로그인이 필요합니다:${NC}" + echo " docker login harbor.wace.me" +else + echo -e "${GREEN}Harbor 로그인 확인됨${NC}" +fi + +# 완료 메시지 +echo -e "\n==========================================" +echo -e "${GREEN} 설치 준비 완료!${NC}" +echo "==========================================" +echo "" +echo "다음 단계:" +echo " 1. 환경 변수 설정: nano $INSTALL_DIR/.env" +echo " 2. Harbor 로그인: docker login harbor.wace.me" +echo " 3. 서비스 시작: cd $INSTALL_DIR && docker compose up -d" +echo "" + diff --git a/deploy/onpremise/scripts/update.sh b/deploy/onpremise/scripts/update.sh new file mode 100644 index 00000000..77e7678b --- /dev/null +++ b/deploy/onpremise/scripts/update.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# ============================================ +# Vexplor 수동 업데이트 스크립트 +# Watchtower를 기다리지 않고 즉시 업데이트할 때 사용 +# ============================================ + +set -e + +INSTALL_DIR="/opt/vexplor" +cd $INSTALL_DIR + +echo "==========================================" +echo " Vexplor 수동 업데이트" +echo "==========================================" + +# 1. 현재 상태 백업 +echo "[1/4] 현재 설정 백업..." +docker compose config > "backup-config-$(date +%Y%m%d-%H%M%S).yml" + +# 2. 최신 이미지 다운로드 +echo "[2/4] 최신 이미지 다운로드..." +docker compose pull backend frontend + +# 3. 서비스 재시작 (롤링 업데이트) +echo "[3/4] 서비스 재시작..." +docker compose up -d --no-deps backend +sleep 10 # 백엔드가 완전히 뜰 때까지 대기 +docker compose up -d --no-deps frontend + +# 4. 상태 확인 +echo "[4/4] 상태 확인..." +sleep 5 +docker compose ps + +echo "" +echo "==========================================" +echo " 업데이트 완료!" +echo "==========================================" +echo "" +echo "로그 확인: docker compose logs -f" + diff --git a/k8s/ingress-nginx.yaml b/k8s/ingress-nginx.yaml deleted file mode 100644 index dfb551cd..00000000 --- a/k8s/ingress-nginx.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# Nginx Ingress Controller 설치 -# 단일 노드 클러스터용 설정 (NodePort 사용) -# -# 설치 명령어: -# kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.5/deploy/static/provider/baremetal/deploy.yaml -# -# 또는 이 파일로 커스텀 설치: -# kubectl apply -f k8s/ingress-nginx.yaml - -# NodePort를 80, 443으로 고정하는 패치용 설정 -apiVersion: v1 -kind: Service -metadata: - name: ingress-nginx-controller - namespace: ingress-nginx - labels: - app.kubernetes.io/name: ingress-nginx - app.kubernetes.io/instance: ingress-nginx - app.kubernetes.io/component: controller -spec: - type: NodePort - externalTrafficPolicy: Local - ipFamilyPolicy: SingleStack - ipFamilies: - - IPv4 - ports: - - name: http - port: 80 - protocol: TCP - targetPort: http - nodePort: 30080 - - name: https - port: 443 - protocol: TCP - targetPort: https - nodePort: 30443 - selector: - app.kubernetes.io/name: ingress-nginx - app.kubernetes.io/instance: ingress-nginx - app.kubernetes.io/component: controller - diff --git a/kubernetes-setup-guide.md b/kubernetes-setup-guide.md index 3d27b04c..8f785b46 100644 --- a/kubernetes-setup-guide.md +++ b/kubernetes-setup-guide.md @@ -12,29 +12,29 @@ ### 기존 서버 (참조용) -| 항목 | 값 | -|------|-----| -| IP | 211.115.91.170 | -| SSH 포트 | 12991 | -| 사용자 | geonhee | -| OS | Ubuntu 24.04.3 LTS | -| K8s 버전 | v1.28.0 | -| 컨테이너 런타임 | containerd 1.7.28 | +| 항목 | 값 | +| --------------- | ------------------ | +| IP | 211.115.91.170 | +| SSH 포트 | 12991 | +| 사용자 | geonhee | +| OS | Ubuntu 24.04.3 LTS | +| K8s 버전 | v1.28.0 | +| 컨테이너 런타임 | containerd 1.7.28 | ### 새 서버 (구축 완료) -| 항목 | 값 | -|------|-----| -| IP | 112.168.212.142 | -| SSH 포트 | 22 | -| 사용자 | wace | -| 호스트명 | waceserver | -| OS | Ubuntu 24.04.3 LTS | -| K8s 버전 | v1.28.15 | -| 컨테이너 런타임 | containerd 1.7.28 | -| 내부 IP | 10.10.0.74 | -| CPU | 20코어 | -| 메모리 | 31GB | +| 항목 | 값 | +| --------------- | ------------------ | +| IP | 112.168.212.142 | +| SSH 포트 | 22 | +| 사용자 | wace | +| 호스트명 | waceserver | +| OS | Ubuntu 24.04.3 LTS | +| K8s 버전 | v1.28.15 | +| 컨테이너 런타임 | containerd 1.7.28 | +| 내부 IP | 10.10.0.74 | +| CPU | 20코어 | +| 메모리 | 31GB | --- @@ -112,6 +112,7 @@ sudo kubeadm init --pod-network-cidr=10.244.0.0/16 ``` **출력 결과 (중요 정보)**: + - 클러스터 초기화 성공 - API 서버: https://10.10.0.74:6443 - 워커 노드 조인 토큰 생성됨 @@ -155,9 +156,9 @@ kubectl taint nodes --all node-role.kubernetes.io/control-plane- kubectl get nodes -o wide ``` -| NAME | STATUS | ROLES | VERSION | INTERNAL-IP | OS-IMAGE | CONTAINER-RUNTIME | -|------|--------|-------|---------|-------------|----------|-------------------| -| waceserver | Ready | control-plane | v1.28.15 | 10.10.0.74 | Ubuntu 24.04.3 LTS | containerd://1.7.28 | +| NAME | STATUS | ROLES | VERSION | INTERNAL-IP | OS-IMAGE | CONTAINER-RUNTIME | +| ---------- | ------ | ------------- | -------- | ----------- | ------------------ | ------------------- | +| waceserver | Ready | control-plane | v1.28.15 | 10.10.0.74 | Ubuntu 24.04.3 LTS | containerd://1.7.28 | ### 시스템 Pod 상태 @@ -166,15 +167,15 @@ kubectl get pods -n kube-system kubectl get pods -n kube-flannel ``` -| 컴포넌트 | 상태 | -|---------|------| -| etcd | ✅ Running | -| kube-apiserver | ✅ Running | +| 컴포넌트 | 상태 | +| ----------------------- | ---------- | +| etcd | ✅ Running | +| kube-apiserver | ✅ Running | | kube-controller-manager | ✅ Running | -| kube-scheduler | ✅ Running | -| kube-proxy | ✅ Running | -| coredns (x2) | ✅ Running | -| kube-flannel | ✅ Running | +| kube-scheduler | ✅ Running | +| kube-proxy | ✅ Running | +| coredns (x2) | ✅ Running | +| kube-flannel | ✅ Running | --- @@ -188,6 +189,7 @@ kubeadm join 10.10.0.74:6443 --token 4lfga6.luad9f367uxh0rlq \ ``` **토큰 만료 시 새 토큰 생성**: + ```bash kubeadm token create --print-join-command ``` @@ -271,11 +273,11 @@ k8s/ #### Gitea Repository Secrets 설정 필요 -| Secret 이름 | 설명 | -|------------|------| -| `HARBOR_USERNAME` | Harbor 사용자명 | -| `HARBOR_PASSWORD` | Harbor 비밀번호 | -| `KUBECONFIG` | base64 인코딩된 Kubernetes config | +| Secret 이름 | 설명 | +| ------------------- | --------------------------------- | +| `HARBOR_USERNAME` | Harbor 사용자명 | +| `HARBOR_PASSWORD` | Harbor 비밀번호 | +| `KUBECONFIG` | base64 인코딩된 Kubernetes config | ```bash # KUBECONFIG 생성 방법 (K8s 서버에서 실행) @@ -301,4 +303,3 @@ ssh -p 12991 geonhee@211.115.91.170 - [Kubernetes 공식 문서](https://kubernetes.io/docs/) - [kubeadm 설치 가이드](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/) - [Flannel 네트워크 플러그인](https://github.com/flannel-io/flannel) - diff --git a/디지털트윈 아키텍쳐_v3.png b/디지털트윈 아키텍쳐_v3.png new file mode 100644 index 00000000..b72e7549 Binary files /dev/null and b/디지털트윈 아키텍쳐_v3.png differ