Merge pull request 'add/kubernetes' (#5) from add/kubernetes into main
All checks were successful
Deploy vexplor / build-and-deploy (push) Successful in 3m16s
All checks were successful
Deploy vexplor / build-and-deploy (push) Successful in 3m16s
Reviewed-on: #5
This commit was merged in pull request #5.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -292,3 +292,5 @@ uploads/
|
||||
*.hwpx
|
||||
|
||||
claude.md
|
||||
|
||||
.cursor/mcp.json
|
||||
115
deploy/customers/README.md
Normal file
115
deploy/customers/README.md
Normal file
@@ -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만 다름
|
||||
* 데이터는 절대 섞이지 않음 (물리적 격리)
|
||||
```
|
||||
|
||||
36
deploy/customers/enkid.env
Normal file
36
deploy/customers/enkid.env
Normal file
@@ -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
|
||||
|
||||
36
deploy/customers/spifox.env
Normal file
36
deploy/customers/spifox.env
Normal file
@@ -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
|
||||
|
||||
321
deploy/onpremise/README.md
Normal file
321
deploy/onpremise/README.md
Normal file
@@ -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. **로그 모니터링**: 비정상 접근 감시
|
||||
|
||||
---
|
||||
|
||||
## 연락처
|
||||
|
||||
배포 관련 문의: [담당자 이메일]
|
||||
|
||||
155
deploy/onpremise/docker-compose.yml
Normal file
155
deploy/onpremise/docker-compose.yml
Normal file
@@ -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
|
||||
|
||||
65
deploy/onpremise/env.example
Normal file
65
deploy/onpremise/env.example
Normal file
@@ -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
|
||||
|
||||
57
deploy/onpremise/scripts/backup.sh
Normal file
57
deploy/onpremise/scripts/backup.sh
Normal file
@@ -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
|
||||
|
||||
79
deploy/onpremise/scripts/install.sh
Normal file
79
deploy/onpremise/scripts/install.sh
Normal file
@@ -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 ""
|
||||
|
||||
41
deploy/onpremise/scripts/update.sh
Normal file
41
deploy/onpremise/scripts/update.sh
Normal file
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
BIN
디지털트윈 아키텍쳐_v3.png
Normal file
BIN
디지털트윈 아키텍쳐_v3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1012 KiB |
Reference in New Issue
Block a user