🚀 배포용: PDF 뷰어 개선 및 서적별 UI 데본씽크 스타일 적용
✨ 주요 개선사항: - PDF API 500 에러 수정 (한글 파일명 UTF-8 인코딩 처리) - PDF 뷰어 기능 완전 구현 (PDF.js 통합, 네비게이션, 확대/축소) - 서적별 문서 그룹화 UI 데본씽크 스타일로 개선 - PDF Manager 페이지 서적별 보기 기능 추가 - Alpine.js 로드 순서 최적화로 JavaScript 에러 해결 🎨 UI/UX 개선: - 확장/축소 가능한 아코디언 스타일 서적 목록 - 간결하고 직관적인 데본씽크 스타일 인터페이스 - PDF 상태 표시 (HTML 연결, 서적 분류) - 반응형 디자인 및 부드러운 애니메이션 🔧 기술적 개선: - PDF.js 워커 설정 및 토큰 인증 처리 - 서적별 PDF 자동 그룹화 로직 - Alpine.js 컴포넌트 초기화 최적화
This commit is contained in:
33
scripts/backup.sh
Executable file
33
scripts/backup.sh
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Document Server 데이터베이스 백업 스크립트
|
||||
# 시놀로지 NAS 환경에서 사용
|
||||
|
||||
BACKUP_DIR="/volume1/docker/document-server/backups"
|
||||
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
||||
CONTAINER_NAME="document-server-db"
|
||||
|
||||
# 백업 디렉토리 생성
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
echo "🔄 데이터베이스 백업 시작: $TIMESTAMP"
|
||||
|
||||
# PostgreSQL 백업
|
||||
docker exec $CONTAINER_NAME pg_dump -U docuser -d document_db > "$BACKUP_DIR/document_db_$TIMESTAMP.sql"
|
||||
|
||||
# 압축
|
||||
gzip "$BACKUP_DIR/document_db_$TIMESTAMP.sql"
|
||||
|
||||
# 7일 이상 된 백업 파일 삭제
|
||||
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +7 -delete
|
||||
|
||||
echo "✅ 백업 완료: $BACKUP_DIR/document_db_$TIMESTAMP.sql.gz"
|
||||
|
||||
# 업로드 파일 백업 (선택사항)
|
||||
if [ "$1" = "--include-uploads" ]; then
|
||||
echo "🔄 업로드 파일 백업 시작..."
|
||||
tar -czf "$BACKUP_DIR/uploads_$TIMESTAMP.tar.gz" -C /volume1/docker/document-server uploads/
|
||||
echo "✅ 업로드 파일 백업 완료: $BACKUP_DIR/uploads_$TIMESTAMP.tar.gz"
|
||||
fi
|
||||
|
||||
echo "🎉 전체 백업 작업 완료"
|
||||
198
scripts/cleanup-for-production.sh
Executable file
198
scripts/cleanup-for-production.sh
Executable file
@@ -0,0 +1,198 @@
|
||||
#!/bin/bash
|
||||
|
||||
# =============================================================================
|
||||
# Document Server - 프로덕션 배포용 정리 스크립트
|
||||
# 테스트 파일, 개발용 데이터, 로그 파일 등을 정리합니다
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# 색상 정의
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
# 환경 설정
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
echo "=== 🧹 프로덕션 배포용 정리 시작 ==="
|
||||
echo ""
|
||||
|
||||
# 1. 테스트 파일 제거
|
||||
log_info "🗑️ 테스트 파일 제거 중..."
|
||||
|
||||
# 테스트 HTML 파일들
|
||||
TEST_FILES=(
|
||||
"test-document.html"
|
||||
"test-upload.html"
|
||||
"test.html"
|
||||
"cache-buster.html"
|
||||
"frontend/test-upload.html"
|
||||
"frontend/test.html"
|
||||
)
|
||||
|
||||
for file in "${TEST_FILES[@]}"; do
|
||||
if [ -f "$file" ]; then
|
||||
rm "$file"
|
||||
log_success "제거: $file"
|
||||
fi
|
||||
done
|
||||
|
||||
# 2. 개발용 이미지 파일 제거
|
||||
log_info "🖼️ 테스트 이미지 파일 제거 중..."
|
||||
|
||||
# RAF 이미지 파일들 (테스트용)
|
||||
find . -name "*.RAF_compressed.JPEG" -delete 2>/dev/null || true
|
||||
find . -name "*.RAF" -delete 2>/dev/null || true
|
||||
|
||||
log_success "테스트 이미지 파일 제거 완료"
|
||||
|
||||
# 3. 로그 파일 정리
|
||||
log_info "📝 로그 파일 정리 중..."
|
||||
|
||||
LOG_FILES=(
|
||||
"backend.log"
|
||||
"frontend.log"
|
||||
)
|
||||
|
||||
for file in "${LOG_FILES[@]}"; do
|
||||
if [ -f "$file" ]; then
|
||||
rm "$file"
|
||||
log_success "제거: $file"
|
||||
fi
|
||||
done
|
||||
|
||||
# 4. Python 캐시 파일 정리
|
||||
log_info "🐍 Python 캐시 파일 정리 중..."
|
||||
|
||||
find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
|
||||
find . -name "*.pyc" -delete 2>/dev/null || true
|
||||
find . -name "*.pyo" -delete 2>/dev/null || true
|
||||
|
||||
log_success "Python 캐시 파일 정리 완료"
|
||||
|
||||
# 5. 개발용 업로드 파일 정리
|
||||
log_info "📁 개발용 업로드 파일 정리 중..."
|
||||
|
||||
# 백엔드 uploads 디렉토리
|
||||
if [ -d "backend/uploads" ]; then
|
||||
rm -rf backend/uploads/documents/* 2>/dev/null || true
|
||||
rm -rf backend/uploads/thumbnails/* 2>/dev/null || true
|
||||
log_success "백엔드 업로드 파일 정리 완료"
|
||||
fi
|
||||
|
||||
# 프론트엔드 uploads 디렉토리
|
||||
if [ -d "frontend/uploads" ]; then
|
||||
rm -rf frontend/uploads/* 2>/dev/null || true
|
||||
log_success "프론트엔드 업로드 파일 정리 완료"
|
||||
fi
|
||||
|
||||
# 루트 uploads 디렉토리
|
||||
if [ -d "uploads" ]; then
|
||||
rm -rf uploads/documents/* 2>/dev/null || true
|
||||
rm -rf uploads/pdfs/* 2>/dev/null || true
|
||||
rm -rf uploads/thumbnails/* 2>/dev/null || true
|
||||
log_success "루트 업로드 파일 정리 완료"
|
||||
fi
|
||||
|
||||
# 6. 가상환경 제거 (프로덕션에서는 Docker 사용)
|
||||
log_info "🐍 가상환경 제거 중..."
|
||||
|
||||
if [ -d "backend/venv" ]; then
|
||||
rm -rf backend/venv
|
||||
log_success "가상환경 제거 완료"
|
||||
fi
|
||||
|
||||
# 7. 개발용 설정 파일 정리
|
||||
log_info "⚙️ 개발용 설정 파일 정리 중..."
|
||||
|
||||
# 환경 변수 파일들 (프로덕션에서 새로 생성)
|
||||
DEV_CONFIG_FILES=(
|
||||
".env"
|
||||
".env.local"
|
||||
".env.development"
|
||||
"backend/.env"
|
||||
)
|
||||
|
||||
for file in "${DEV_CONFIG_FILES[@]}"; do
|
||||
if [ -f "$file" ]; then
|
||||
rm "$file"
|
||||
log_success "제거: $file"
|
||||
fi
|
||||
done
|
||||
|
||||
# 8. 불필요한 문서 파일 정리
|
||||
log_info "📚 개발용 문서 파일 정리 중..."
|
||||
|
||||
DEV_DOCS=(
|
||||
"VIEWER_REFACTORING.md"
|
||||
)
|
||||
|
||||
for file in "${DEV_DOCS[@]}"; do
|
||||
if [ -f "$file" ]; then
|
||||
rm "$file"
|
||||
log_success "제거: $file"
|
||||
fi
|
||||
done
|
||||
|
||||
# 9. 빈 디렉토리 정리
|
||||
log_info "📂 빈 디렉토리 정리 중..."
|
||||
|
||||
find . -type d -empty -delete 2>/dev/null || true
|
||||
|
||||
# 10. 권한 정리
|
||||
log_info "🔐 파일 권한 정리 중..."
|
||||
|
||||
# 스크립트 파일 실행 권한 확인
|
||||
chmod +x scripts/*.sh 2>/dev/null || true
|
||||
|
||||
# 설정 파일 권한 설정
|
||||
find . -name "*.conf" -exec chmod 644 {} \; 2>/dev/null || true
|
||||
find . -name "*.yml" -exec chmod 644 {} \; 2>/dev/null || true
|
||||
find . -name "*.yaml" -exec chmod 644 {} \; 2>/dev/null || true
|
||||
|
||||
log_success "파일 권한 정리 완료"
|
||||
|
||||
# 11. 정리 결과 요약
|
||||
echo ""
|
||||
echo "=== 📊 정리 결과 ==="
|
||||
|
||||
# 현재 디렉토리 크기
|
||||
TOTAL_SIZE=$(du -sh . | cut -f1)
|
||||
echo "전체 크기: $TOTAL_SIZE"
|
||||
|
||||
# 주요 디렉토리 크기
|
||||
echo ""
|
||||
echo "주요 디렉토리:"
|
||||
du -sh backend frontend scripts nginx 2>/dev/null | while read size dir; do
|
||||
echo " $dir: $size"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=== ✅ 정리 완료 ==="
|
||||
echo ""
|
||||
echo "🚀 이제 프로덕션 배포 준비가 완료되었습니다!"
|
||||
echo ""
|
||||
echo "다음 단계:"
|
||||
echo "1. NAS에 업로드"
|
||||
echo "2. ./scripts/deploy-synology.sh 실행"
|
||||
echo "3. 배포 완료!"
|
||||
|
||||
log_success "프로덕션 정리 스크립트 완료"
|
||||
399
scripts/deploy-synology.sh
Executable file
399
scripts/deploy-synology.sh
Executable file
@@ -0,0 +1,399 @@
|
||||
#!/bin/bash
|
||||
|
||||
# =============================================================================
|
||||
# Document Server - Synology DS1525+ 최적화 배포 스크립트
|
||||
#
|
||||
# 하드웨어 사양:
|
||||
# - CPU: AMD Ryzen R1600 (4코어/8스레드)
|
||||
# - RAM: 32GB DDR4 ECC
|
||||
# - SSD: 읽기/쓰기 캐시 활성화
|
||||
# - Storage: Volume1(SSD), Volume2(HDD)
|
||||
# =============================================================================
|
||||
|
||||
set -e # 에러 발생 시 스크립트 중단
|
||||
|
||||
# 색상 정의
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 로그 함수
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 환경 변수 설정
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
COMPOSE_FILE="docker-compose.synology.yml"
|
||||
|
||||
# 환경 변수 설정 확인
|
||||
ENV_FILE="$PROJECT_DIR/.env.synology"
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
log_info "환경 변수 파일이 없습니다. 설정을 시작합니다..."
|
||||
"$SCRIPT_DIR/setup-env.sh"
|
||||
fi
|
||||
|
||||
# 환경 변수 로드
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
log_info "환경 변수 파일을 로드합니다: $ENV_FILE"
|
||||
set -a # 자동으로 export
|
||||
source "$ENV_FILE"
|
||||
set +a
|
||||
else
|
||||
log_error "환경 변수 파일을 찾을 수 없습니다"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "🚀 Synology DS1525+ 최적화 배포 시작"
|
||||
log_info "📁 프로젝트 디렉토리: $PROJECT_DIR"
|
||||
|
||||
# 1. 시스템 요구사항 확인
|
||||
log_info "🔍 시스템 요구사항 확인 중..."
|
||||
|
||||
# Docker 및 Docker Compose 확인
|
||||
if ! command -v docker &> /dev/null; then
|
||||
log_error "Docker가 설치되지 않았습니다."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
log_error "Docker Compose가 설치되지 않았습니다."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 메모리 확인 (최소 16GB 권장)
|
||||
TOTAL_MEM=$(free -g | awk '/^Mem:/{print $2}')
|
||||
if [ "$TOTAL_MEM" -lt 16 ]; then
|
||||
log_warning "메모리가 ${TOTAL_MEM}GB입니다. 최소 16GB를 권장합니다."
|
||||
fi
|
||||
|
||||
log_success "시스템 요구사항 확인 완료"
|
||||
|
||||
# 2. 디렉토리 구조 생성
|
||||
log_info "📂 디렉토리 구조 생성 중..."
|
||||
|
||||
# SSD 디렉토리 (성능 최우선) - Volume3
|
||||
SSD_DIRS=(
|
||||
"/volume3/docker/document-server/database"
|
||||
"/volume3/docker/document-server/redis"
|
||||
"/volume3/docker/document-server/logs"
|
||||
"/volume3/docker/document-server/logs/nginx"
|
||||
"/volume3/docker/document-server/config"
|
||||
"/volume3/docker/document-server/nginx/conf.d"
|
||||
"/volume3/docker/document-server/nginx/cache"
|
||||
"/volume3/docker/document-server/cache"
|
||||
)
|
||||
|
||||
# HDD 디렉토리 (대용량 저장) - Volume1
|
||||
HDD_DIRS=(
|
||||
"/volume1/document-storage/uploads"
|
||||
"/volume1/document-storage/documents"
|
||||
"/volume1/document-storage/thumbnails"
|
||||
"/volume1/document-storage/backups"
|
||||
"/volume1/document-storage/archives"
|
||||
)
|
||||
|
||||
# SSD 디렉토리 생성
|
||||
for dir in "${SSD_DIRS[@]}"; do
|
||||
if [ ! -d "$dir" ]; then
|
||||
sudo mkdir -p "$dir"
|
||||
log_info "SSD 디렉토리 생성: $dir"
|
||||
fi
|
||||
done
|
||||
|
||||
# HDD 디렉토리 생성
|
||||
for dir in "${HDD_DIRS[@]}"; do
|
||||
if [ ! -d "$dir" ]; then
|
||||
sudo mkdir -p "$dir"
|
||||
log_info "HDD 디렉토리 생성: $dir"
|
||||
fi
|
||||
done
|
||||
|
||||
# 권한 설정
|
||||
sudo chown -R 1000:1000 /volume3/docker/document-server/
|
||||
sudo chown -R 1000:1000 /volume1/document-storage/
|
||||
|
||||
log_success "디렉토리 구조 생성 완료"
|
||||
|
||||
# 3. 설정 파일 복사
|
||||
log_info "⚙️ 설정 파일 생성 중..."
|
||||
|
||||
# PostgreSQL 설정 (32GB RAM 최적화)
|
||||
cat > /volume3/docker/document-server/config/postgresql.synology.conf << 'EOF'
|
||||
# PostgreSQL 설정 - Synology DS1525+ 32GB RAM 최적화
|
||||
|
||||
# 메모리 설정 (32GB RAM 기준)
|
||||
shared_buffers = 8GB # RAM의 25%
|
||||
effective_cache_size = 24GB # RAM의 75%
|
||||
work_mem = 512MB # 복잡한 쿼리용 (증가)
|
||||
maintenance_work_mem = 4GB # 인덱스 구축용 (증가)
|
||||
|
||||
# 체크포인트 설정 (SSD 최적화)
|
||||
checkpoint_completion_target = 0.9
|
||||
wal_buffers = 128MB # WAL 버퍼 (증가)
|
||||
checkpoint_timeout = 15min
|
||||
max_wal_size = 4GB
|
||||
min_wal_size = 1GB
|
||||
|
||||
# SSD 최적화
|
||||
random_page_cost = 1.1 # SSD 환경
|
||||
effective_io_concurrency = 200 # SSD 동시 I/O
|
||||
seq_page_cost = 1.0
|
||||
|
||||
# 병렬 처리 (4코어/8스레드 최적화)
|
||||
max_worker_processes = 8
|
||||
max_parallel_workers_per_gather = 4
|
||||
max_parallel_workers = 8
|
||||
max_parallel_maintenance_workers = 4
|
||||
|
||||
# 연결 설정
|
||||
max_connections = 200
|
||||
shared_preload_libraries = 'pg_stat_statements'
|
||||
|
||||
# 로깅 설정
|
||||
log_min_duration_statement = 1000 # 1초 이상 쿼리 로깅
|
||||
log_checkpoints = on
|
||||
log_connections = on
|
||||
log_disconnections = on
|
||||
log_lock_waits = on
|
||||
|
||||
# 자동 VACUUM 설정
|
||||
autovacuum = on
|
||||
autovacuum_max_workers = 4
|
||||
autovacuum_naptime = 30s
|
||||
EOF
|
||||
|
||||
# Nginx 설정 (SSD 캐시 최적화)
|
||||
cat > /volume3/docker/document-server/nginx/conf.d/default.conf << 'EOF'
|
||||
# Nginx 설정 - SSD 캐시 최적화
|
||||
|
||||
# 업스트림 백엔드
|
||||
upstream backend {
|
||||
server backend:8000;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
# 캐시 존 정의 (SSD에 저장)
|
||||
proxy_cache_path /var/cache/nginx/documents
|
||||
levels=1:2
|
||||
keys_zone=documents:100m
|
||||
max_size=2g
|
||||
inactive=60m
|
||||
use_temp_path=off;
|
||||
|
||||
proxy_cache_path /var/cache/nginx/api
|
||||
levels=1:2
|
||||
keys_zone=api:50m
|
||||
max_size=500m
|
||||
inactive=10m
|
||||
use_temp_path=off;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
# 클라이언트 설정
|
||||
client_max_body_size 500M;
|
||||
client_body_timeout 300s;
|
||||
client_header_timeout 300s;
|
||||
|
||||
# Gzip 압축
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types
|
||||
text/plain
|
||||
text/css
|
||||
text/xml
|
||||
text/javascript
|
||||
application/javascript
|
||||
application/xml+rss
|
||||
application/json;
|
||||
|
||||
# 정적 파일 (SSD에서 서빙)
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
|
||||
# 정적 파일 캐시
|
||||
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
|
||||
# 업로드된 문서 (HDD에서 서빙, SSD 캐시)
|
||||
location /uploads/ {
|
||||
alias /usr/share/nginx/html/uploads/;
|
||||
|
||||
# 문서 캐시 (자주 접근하는 문서는 SSD에 캐시)
|
||||
proxy_cache documents;
|
||||
proxy_cache_valid 200 60m;
|
||||
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
|
||||
add_header X-Cache-Status $upstream_cache_status;
|
||||
|
||||
expires 1h;
|
||||
}
|
||||
|
||||
# API 요청
|
||||
location /api/ {
|
||||
proxy_pass http://backend;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# API 응답 캐시 (GET 요청만)
|
||||
proxy_cache api;
|
||||
proxy_cache_methods GET HEAD;
|
||||
proxy_cache_valid 200 5m;
|
||||
proxy_cache_bypass $http_pragma $http_authorization;
|
||||
add_header X-Cache-Status $upstream_cache_status;
|
||||
|
||||
# 타임아웃 설정
|
||||
proxy_connect_timeout 30s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
# 헬스체크
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
log_success "설정 파일 생성 완료"
|
||||
|
||||
# 4. 환경 변수 파일 생성
|
||||
log_info "🔐 환경 변수 설정 중..."
|
||||
|
||||
cat > "$PROJECT_DIR/.env.synology" << EOF
|
||||
# Synology DS1525+ 배포 환경 변수
|
||||
DB_PASSWORD=$DB_PASSWORD
|
||||
SECRET_KEY=$SECRET_KEY
|
||||
ADMIN_EMAIL=$ADMIN_EMAIL
|
||||
ADMIN_PASSWORD=$ADMIN_PASSWORD
|
||||
DOMAIN_NAME=$DOMAIN_NAME
|
||||
|
||||
# 성능 최적화 설정
|
||||
POSTGRES_SHARED_BUFFERS=8GB
|
||||
POSTGRES_EFFECTIVE_CACHE_SIZE=24GB
|
||||
REDIS_MAXMEMORY=8gb
|
||||
|
||||
# 경로 설정
|
||||
SSD_PATH=/volume1/docker/document-server
|
||||
HDD_PATH=/volume2/document-storage
|
||||
EOF
|
||||
|
||||
log_success "환경 변수 설정 완료"
|
||||
|
||||
# 5. Docker Compose 배포
|
||||
log_info "🐳 Docker 컨테이너 배포 중..."
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# 기존 컨테이너 중지 및 제거 (있는 경우)
|
||||
if docker-compose -f "$COMPOSE_FILE" ps -q | grep -q .; then
|
||||
log_warning "기존 컨테이너를 중지합니다..."
|
||||
docker-compose -f "$COMPOSE_FILE" down
|
||||
fi
|
||||
|
||||
# 이미지 빌드 및 컨테이너 시작
|
||||
log_info "이미지 빌드 중..."
|
||||
docker-compose -f "$COMPOSE_FILE" build --no-cache
|
||||
|
||||
log_info "컨테이너 시작 중..."
|
||||
docker-compose -f "$COMPOSE_FILE" up -d
|
||||
|
||||
# 6. 서비스 상태 확인
|
||||
log_info "🔍 서비스 상태 확인 중..."
|
||||
|
||||
# 컨테이너 시작 대기
|
||||
sleep 30
|
||||
|
||||
# 헬스체크
|
||||
services=("database" "redis" "backend" "nginx")
|
||||
for service in "${services[@]}"; do
|
||||
if docker-compose -f "$COMPOSE_FILE" ps "$service" | grep -q "Up"; then
|
||||
log_success "$service 서비스 정상 실행 중"
|
||||
else
|
||||
log_error "$service 서비스 실행 실패"
|
||||
docker-compose -f "$COMPOSE_FILE" logs "$service"
|
||||
fi
|
||||
done
|
||||
|
||||
# 7. 백업 스크립트 설정
|
||||
log_info "💾 백업 스크립트 설정 중..."
|
||||
|
||||
cat > /volume1/docker/document-server/backup.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# 자동 백업 스크립트
|
||||
|
||||
BACKUP_DIR="/volume2/document-storage/backups"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
# 데이터베이스 백업
|
||||
docker exec document-server-db pg_dump -U docuser document_db > "$BACKUP_DIR/db_backup_$DATE.sql"
|
||||
|
||||
# 설정 파일 백업
|
||||
tar -czf "$BACKUP_DIR/config_backup_$DATE.tar.gz" /volume1/docker/document-server/config/
|
||||
|
||||
# 7일 이상 된 백업 파일 삭제
|
||||
find "$BACKUP_DIR" -name "*.sql" -mtime +7 -delete
|
||||
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +7 -delete
|
||||
|
||||
echo "백업 완료: $DATE"
|
||||
EOF
|
||||
|
||||
chmod +x /volume1/docker/document-server/backup.sh
|
||||
|
||||
log_success "백업 스크립트 설정 완료"
|
||||
|
||||
# 8. 배포 완료 정보 출력
|
||||
log_success "🎉 Synology DS1525+ 배포 완료!"
|
||||
|
||||
echo ""
|
||||
echo "=== 배포 정보 ==="
|
||||
echo "🌐 웹 인터페이스: http://localhost:24100"
|
||||
echo "🔧 API 서버: http://localhost:24102"
|
||||
echo "🗄️ 데이터베이스: localhost:24101"
|
||||
echo "💾 Redis: localhost:24103"
|
||||
echo ""
|
||||
echo "=== 관리자 계정 ==="
|
||||
echo "📧 이메일: $ADMIN_EMAIL"
|
||||
echo "🔑 비밀번호: $ADMIN_PASSWORD"
|
||||
echo ""
|
||||
echo "=== 스토리지 구성 ==="
|
||||
echo "💿 SSD (성능): /volume1/docker/document-server/"
|
||||
echo "💾 HDD (용량): /volume2/document-storage/"
|
||||
echo ""
|
||||
echo "=== 자동 백업 ==="
|
||||
echo "📅 매일 새벽 2시 자동 백업 (Synology 작업 스케줄러에서 설정)"
|
||||
echo "📂 백업 위치: /volume2/document-storage/backups/"
|
||||
echo ""
|
||||
echo "=== 모니터링 명령어 ==="
|
||||
echo "docker-compose -f $COMPOSE_FILE ps"
|
||||
echo "docker-compose -f $COMPOSE_FILE logs -f"
|
||||
echo "docker stats"
|
||||
|
||||
log_info "배포 스크립트 실행 완료"
|
||||
249
scripts/monitor-synology.sh
Executable file
249
scripts/monitor-synology.sh
Executable file
@@ -0,0 +1,249 @@
|
||||
#!/bin/bash
|
||||
|
||||
# =============================================================================
|
||||
# Document Server - Synology DS1525+ 모니터링 스크립트
|
||||
# 시스템 리소스 및 서비스 상태 모니터링
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# 색상 정의
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 로그 함수
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 환경 설정
|
||||
COMPOSE_FILE="docker-compose.synology.yml"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
echo "=== 📊 Synology DS1525+ Document Server 모니터링 ==="
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
echo ""
|
||||
|
||||
# 1. 시스템 리소스 확인
|
||||
log_info "🖥️ 시스템 리소스 상태"
|
||||
|
||||
# CPU 사용률
|
||||
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | awk -F'%' '{print $1}')
|
||||
echo -e "CPU 사용률: ${CYAN}${CPU_USAGE}%${NC}"
|
||||
|
||||
# 메모리 사용률 (32GB 기준)
|
||||
MEMORY_INFO=$(free -h | grep "Mem:")
|
||||
TOTAL_MEM=$(echo $MEMORY_INFO | awk '{print $2}')
|
||||
USED_MEM=$(echo $MEMORY_INFO | awk '{print $3}')
|
||||
AVAILABLE_MEM=$(echo $MEMORY_INFO | awk '{print $7}')
|
||||
MEM_PERCENT=$(free | grep "Mem:" | awk '{printf "%.1f", ($3/$2) * 100.0}')
|
||||
|
||||
echo -e "메모리: ${CYAN}${USED_MEM}${NC}/${CYAN}${TOTAL_MEM}${NC} (${CYAN}${MEM_PERCENT}%${NC}) | 사용 가능: ${GREEN}${AVAILABLE_MEM}${NC}"
|
||||
|
||||
# 디스크 사용률
|
||||
echo -e "\n${BLUE}💾 디스크 사용률:${NC}"
|
||||
df -h /volume1 /volume2 | grep -E "(volume1|volume2)" | while read line; do
|
||||
USAGE=$(echo $line | awk '{print $5}' | sed 's/%//')
|
||||
MOUNT=$(echo $line | awk '{print $6}')
|
||||
USED=$(echo $line | awk '{print $3}')
|
||||
TOTAL=$(echo $line | awk '{print $2}')
|
||||
|
||||
if [ "$USAGE" -gt 90 ]; then
|
||||
echo -e " ${RED}${MOUNT}${NC}: ${RED}${USED}${NC}/${TOTAL} (${RED}${USAGE}%${NC}) ⚠️"
|
||||
elif [ "$USAGE" -gt 80 ]; then
|
||||
echo -e " ${YELLOW}${MOUNT}${NC}: ${YELLOW}${USED}${NC}/${TOTAL} (${YELLOW}${USAGE}%${NC})"
|
||||
else
|
||||
echo -e " ${GREEN}${MOUNT}${NC}: ${CYAN}${USED}${NC}/${TOTAL} (${GREEN}${USAGE}%${NC})"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
# 2. Docker 컨테이너 상태
|
||||
log_info "🐳 Docker 컨테이너 상태"
|
||||
|
||||
if [ -f "$COMPOSE_FILE" ]; then
|
||||
# 컨테이너 상태 확인
|
||||
CONTAINERS=$(docker-compose -f "$COMPOSE_FILE" ps --format "table {{.Name}}\t{{.State}}\t{{.Ports}}")
|
||||
echo "$CONTAINERS"
|
||||
|
||||
echo ""
|
||||
|
||||
# 각 서비스별 상태 확인
|
||||
SERVICES=("database" "redis" "backend" "nginx")
|
||||
for service in "${SERVICES[@]}"; do
|
||||
STATUS=$(docker-compose -f "$COMPOSE_FILE" ps -q "$service" 2>/dev/null)
|
||||
if [ -n "$STATUS" ]; then
|
||||
HEALTH=$(docker inspect --format='{{.State.Health.Status}}' $(docker-compose -f "$COMPOSE_FILE" ps -q "$service") 2>/dev/null || echo "no-healthcheck")
|
||||
if [ "$HEALTH" = "healthy" ]; then
|
||||
log_success "$service: 정상 (healthy)"
|
||||
elif [ "$HEALTH" = "unhealthy" ]; then
|
||||
log_error "$service: 비정상 (unhealthy)"
|
||||
else
|
||||
log_warning "$service: 헬스체크 없음"
|
||||
fi
|
||||
else
|
||||
log_error "$service: 실행 중이지 않음"
|
||||
fi
|
||||
done
|
||||
else
|
||||
log_error "Docker Compose 파일을 찾을 수 없습니다: $COMPOSE_FILE"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# 3. 리소스 사용량 (컨테이너별)
|
||||
log_info "📈 컨테이너별 리소스 사용량"
|
||||
|
||||
if command -v docker &> /dev/null; then
|
||||
# Docker stats 정보 (1회성)
|
||||
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}" | head -10
|
||||
else
|
||||
log_error "Docker가 설치되지 않았습니다"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# 4. 네트워크 연결 상태
|
||||
log_info "🌐 네트워크 연결 상태"
|
||||
|
||||
# 포트 확인
|
||||
PORTS=("24100:nginx" "24101:database" "24102:backend" "24103:redis")
|
||||
for port_info in "${PORTS[@]}"; do
|
||||
PORT=$(echo $port_info | cut -d: -f1)
|
||||
SERVICE=$(echo $port_info | cut -d: -f2)
|
||||
|
||||
if netstat -tuln | grep -q ":$PORT "; then
|
||||
log_success "$SERVICE (포트 $PORT): 리스닝 중"
|
||||
else
|
||||
log_error "$SERVICE (포트 $PORT): 리스닝하지 않음"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
# 5. 로그 파일 크기 확인
|
||||
log_info "📝 로그 파일 상태"
|
||||
|
||||
LOG_DIRS=(
|
||||
"/volume1/docker/document-server/logs"
|
||||
"/volume1/docker/document-server/logs/nginx"
|
||||
)
|
||||
|
||||
for log_dir in "${LOG_DIRS[@]}"; do
|
||||
if [ -d "$log_dir" ]; then
|
||||
LOG_SIZE=$(du -sh "$log_dir" 2>/dev/null | cut -f1)
|
||||
echo -e " ${CYAN}${log_dir}${NC}: ${LOG_SIZE}"
|
||||
|
||||
# 큰 로그 파일 경고 (1GB 이상)
|
||||
LOG_SIZE_MB=$(du -sm "$log_dir" 2>/dev/null | cut -f1)
|
||||
if [ "$LOG_SIZE_MB" -gt 1024 ]; then
|
||||
log_warning "로그 디렉토리가 1GB를 초과했습니다: $log_dir"
|
||||
fi
|
||||
else
|
||||
log_warning "로그 디렉토리가 존재하지 않습니다: $log_dir"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
# 6. 데이터베이스 연결 테스트
|
||||
log_info "🗄️ 데이터베이스 연결 테스트"
|
||||
|
||||
if docker-compose -f "$COMPOSE_FILE" ps -q database >/dev/null 2>&1; then
|
||||
DB_STATUS=$(docker-compose -f "$COMPOSE_FILE" exec -T database pg_isready -U docuser -d document_db 2>/dev/null)
|
||||
if echo "$DB_STATUS" | grep -q "accepting connections"; then
|
||||
log_success "PostgreSQL: 연결 가능"
|
||||
|
||||
# 데이터베이스 크기 확인
|
||||
DB_SIZE=$(docker-compose -f "$COMPOSE_FILE" exec -T database psql -U docuser -d document_db -t -c "SELECT pg_size_pretty(pg_database_size('document_db'));" 2>/dev/null | xargs)
|
||||
echo -e " 데이터베이스 크기: ${CYAN}${DB_SIZE}${NC}"
|
||||
else
|
||||
log_error "PostgreSQL: 연결 실패"
|
||||
fi
|
||||
else
|
||||
log_error "데이터베이스 컨테이너가 실행 중이지 않습니다"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# 7. Redis 연결 테스트
|
||||
log_info "💾 Redis 연결 테스트"
|
||||
|
||||
if docker-compose -f "$COMPOSE_FILE" ps -q redis >/dev/null 2>&1; then
|
||||
REDIS_STATUS=$(docker-compose -f "$COMPOSE_FILE" exec -T redis redis-cli ping 2>/dev/null)
|
||||
if [ "$REDIS_STATUS" = "PONG" ]; then
|
||||
log_success "Redis: 연결 가능"
|
||||
|
||||
# Redis 메모리 사용량
|
||||
REDIS_MEMORY=$(docker-compose -f "$COMPOSE_FILE" exec -T redis redis-cli info memory | grep "used_memory_human" | cut -d: -f2 | tr -d '\r')
|
||||
echo -e " 메모리 사용량: ${CYAN}${REDIS_MEMORY}${NC}"
|
||||
else
|
||||
log_error "Redis: 연결 실패"
|
||||
fi
|
||||
else
|
||||
log_error "Redis 컨테이너가 실행 중이지 않습니다"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# 8. 백업 상태 확인
|
||||
log_info "💾 백업 상태 확인"
|
||||
|
||||
BACKUP_DIR="/volume2/document-storage/backups"
|
||||
if [ -d "$BACKUP_DIR" ]; then
|
||||
BACKUP_COUNT=$(find "$BACKUP_DIR" -name "*.sql" -mtime -1 | wc -l)
|
||||
LATEST_BACKUP=$(find "$BACKUP_DIR" -name "*.sql" -type f -printf '%T@ %p\n' 2>/dev/null | sort -n | tail -1 | cut -d' ' -f2- | xargs basename 2>/dev/null || echo "없음")
|
||||
|
||||
echo -e " 백업 디렉토리: ${CYAN}${BACKUP_DIR}${NC}"
|
||||
echo -e " 최근 24시간 백업: ${CYAN}${BACKUP_COUNT}개${NC}"
|
||||
echo -e " 최신 백업: ${CYAN}${LATEST_BACKUP}${NC}"
|
||||
|
||||
if [ "$BACKUP_COUNT" -eq 0 ]; then
|
||||
log_warning "최근 24시간 내 백업이 없습니다"
|
||||
fi
|
||||
else
|
||||
log_error "백업 디렉토리가 존재하지 않습니다: $BACKUP_DIR"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# 9. 권장 사항
|
||||
log_info "💡 권장 사항"
|
||||
|
||||
# 메모리 사용률이 높은 경우
|
||||
if [ "${MEM_PERCENT%.*}" -gt 80 ]; then
|
||||
log_warning "메모리 사용률이 높습니다 (${MEM_PERCENT}%). 모니터링이 필요합니다."
|
||||
fi
|
||||
|
||||
# 디스크 사용률 확인
|
||||
HIGH_DISK_USAGE=$(df /volume1 /volume2 | awk 'NR>1 {gsub(/%/, "", $5); if ($5 > 85) print $6 " (" $5 "%)"}')
|
||||
if [ -n "$HIGH_DISK_USAGE" ]; then
|
||||
log_warning "디스크 사용률이 높은 볼륨: $HIGH_DISK_USAGE"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== 모니터링 완료 ==="
|
||||
echo "다음 명령어로 실시간 모니터링 가능:"
|
||||
echo " watch -n 5 '$0'"
|
||||
echo " docker-compose -f $COMPOSE_FILE logs -f"
|
||||
echo " docker stats"
|
||||
58
scripts/restore.sh
Executable file
58
scripts/restore.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Document Server 데이터베이스 복원 스크립트
|
||||
# 시놀로지 NAS 환경에서 사용
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "사용법: $0 <백업파일명>"
|
||||
echo "예시: $0 document_db_20241201_143000.sql.gz"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BACKUP_FILE="$1"
|
||||
BACKUP_DIR="/volume1/docker/document-server/backups"
|
||||
CONTAINER_NAME="document-server-db"
|
||||
|
||||
if [ ! -f "$BACKUP_DIR/$BACKUP_FILE" ]; then
|
||||
echo "❌ 백업 파일을 찾을 수 없습니다: $BACKUP_DIR/$BACKUP_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "⚠️ 주의: 현재 데이터베이스의 모든 데이터가 삭제됩니다!"
|
||||
read -p "계속하시겠습니까? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "복원이 취소되었습니다."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🔄 데이터베이스 복원 시작..."
|
||||
|
||||
# 압축 해제 (필요한 경우)
|
||||
if [[ $BACKUP_FILE == *.gz ]]; then
|
||||
echo "📦 백업 파일 압축 해제 중..."
|
||||
gunzip -c "$BACKUP_DIR/$BACKUP_FILE" > "/tmp/restore_temp.sql"
|
||||
SQL_FILE="/tmp/restore_temp.sql"
|
||||
else
|
||||
SQL_FILE="$BACKUP_DIR/$BACKUP_FILE"
|
||||
fi
|
||||
|
||||
# 기존 데이터베이스 삭제 및 재생성
|
||||
echo "🗑️ 기존 데이터베이스 삭제 중..."
|
||||
docker exec $CONTAINER_NAME psql -U docuser -d postgres -c "DROP DATABASE IF EXISTS document_db;"
|
||||
docker exec $CONTAINER_NAME psql -U docuser -d postgres -c "CREATE DATABASE document_db;"
|
||||
|
||||
# 백업 복원
|
||||
echo "📥 데이터베이스 복원 중..."
|
||||
docker exec -i $CONTAINER_NAME psql -U docuser -d document_db < "$SQL_FILE"
|
||||
|
||||
# 임시 파일 정리
|
||||
if [ -f "/tmp/restore_temp.sql" ]; then
|
||||
rm "/tmp/restore_temp.sql"
|
||||
fi
|
||||
|
||||
echo "✅ 데이터베이스 복원 완료"
|
||||
echo "🔄 백엔드 서비스 재시작 중..."
|
||||
docker restart document-server-backend
|
||||
|
||||
echo "🎉 복원 작업 완료"
|
||||
257
scripts/setup-env.sh
Executable file
257
scripts/setup-env.sh
Executable file
@@ -0,0 +1,257 @@
|
||||
#!/bin/bash
|
||||
|
||||
# =============================================================================
|
||||
# Document Server - 환경 변수 설정 스크립트
|
||||
# 대화형으로 환경 변수를 설정하고 .env 파일을 생성합니다
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# 색상 정의
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 로그 함수
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
# 보안 키 생성 함수
|
||||
generate_secure_key() {
|
||||
openssl rand -base64 32 | tr -d "=+/" | cut -c1-32
|
||||
}
|
||||
|
||||
generate_jwt_key() {
|
||||
openssl rand -base64 64 | tr -d "=+/" | cut -c1-64
|
||||
}
|
||||
|
||||
generate_password() {
|
||||
openssl rand -base64 16 | tr -d "=+/" | cut -c1-12
|
||||
}
|
||||
|
||||
# 환경 설정
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
ENV_FILE="$PROJECT_DIR/.env.synology"
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
echo "=== 🔧 Document Server 환경 변수 설정 ==="
|
||||
echo ""
|
||||
|
||||
# 기존 .env 파일 확인
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
log_warning "기존 환경 변수 파일이 있습니다: $ENV_FILE"
|
||||
echo ""
|
||||
read -p "기존 설정을 덮어쓰시겠습니까? (y/N): " -n 1 -r
|
||||
echo ""
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
log_info "기존 설정을 유지합니다"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 기존 파일 백업
|
||||
cp "$ENV_FILE" "$ENV_FILE.backup.$(date +%Y%m%d_%H%M%S)"
|
||||
log_info "기존 파일을 백업했습니다"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "환경 변수를 설정합니다. 엔터를 누르면 기본값/자동생성값을 사용합니다."
|
||||
echo ""
|
||||
|
||||
# 1. 데이터베이스 비밀번호
|
||||
echo -e "${CYAN}1. 데이터베이스 비밀번호${NC}"
|
||||
DEFAULT_DB_PASSWORD=$(generate_password)
|
||||
echo " 기본값: $DEFAULT_DB_PASSWORD (자동생성)"
|
||||
read -p " 입력: " DB_PASSWORD
|
||||
DB_PASSWORD=${DB_PASSWORD:-$DEFAULT_DB_PASSWORD}
|
||||
|
||||
# 2. JWT 시크릿 키
|
||||
echo ""
|
||||
echo -e "${CYAN}2. JWT 시크릿 키 (보안용)${NC}"
|
||||
DEFAULT_SECRET_KEY=$(generate_jwt_key)
|
||||
echo " 기본값: ${DEFAULT_SECRET_KEY:0:20}... (자동생성)"
|
||||
read -p " 입력: " SECRET_KEY
|
||||
SECRET_KEY=${SECRET_KEY:-$DEFAULT_SECRET_KEY}
|
||||
|
||||
# 3. 관리자 이메일
|
||||
echo ""
|
||||
echo -e "${CYAN}3. 관리자 이메일${NC}"
|
||||
DEFAULT_ADMIN_EMAIL="admin@document-server.local"
|
||||
echo " 기본값: $DEFAULT_ADMIN_EMAIL"
|
||||
read -p " 입력: " ADMIN_EMAIL
|
||||
ADMIN_EMAIL=${ADMIN_EMAIL:-$DEFAULT_ADMIN_EMAIL}
|
||||
|
||||
# 4. 관리자 비밀번호
|
||||
echo ""
|
||||
echo -e "${CYAN}4. 관리자 비밀번호${NC}"
|
||||
DEFAULT_ADMIN_PASSWORD=$(generate_password)
|
||||
echo " 기본값: $DEFAULT_ADMIN_PASSWORD (자동생성)"
|
||||
read -p " 입력: " ADMIN_PASSWORD
|
||||
ADMIN_PASSWORD=${ADMIN_PASSWORD:-$DEFAULT_ADMIN_PASSWORD}
|
||||
|
||||
# 5. 도메인 이름
|
||||
echo ""
|
||||
echo -e "${CYAN}5. 도메인 이름 (외부 접속용)${NC}"
|
||||
DEFAULT_DOMAIN="localhost"
|
||||
echo " 기본값: $DEFAULT_DOMAIN"
|
||||
echo " 예시: mydomain.com, nas.mydomain.com"
|
||||
read -p " 입력: " DOMAIN_NAME
|
||||
DOMAIN_NAME=${DOMAIN_NAME:-$DEFAULT_DOMAIN}
|
||||
|
||||
# 6. 외부 포트 (선택사항)
|
||||
echo ""
|
||||
echo -e "${CYAN}6. 외부 포트 (기본: 24100)${NC}"
|
||||
DEFAULT_PORT="24100"
|
||||
echo " 기본값: $DEFAULT_PORT"
|
||||
read -p " 입력: " EXTERNAL_PORT
|
||||
EXTERNAL_PORT=${EXTERNAL_PORT:-$DEFAULT_PORT}
|
||||
|
||||
# .env 파일 생성
|
||||
echo ""
|
||||
log_info "환경 변수 파일 생성 중..."
|
||||
|
||||
cat > "$ENV_FILE" << EOF
|
||||
# =============================================================================
|
||||
# Document Server - Synology DS1525+ 환경 변수
|
||||
# 생성일: $(date '+%Y-%m-%d %H:%M:%S')
|
||||
# =============================================================================
|
||||
|
||||
# 데이터베이스 설정
|
||||
DB_PASSWORD=$DB_PASSWORD
|
||||
POSTGRES_PASSWORD=$DB_PASSWORD
|
||||
|
||||
# 보안 설정
|
||||
SECRET_KEY=$SECRET_KEY
|
||||
JWT_SECRET_KEY=$SECRET_KEY
|
||||
|
||||
# 관리자 계정
|
||||
ADMIN_EMAIL=$ADMIN_EMAIL
|
||||
ADMIN_PASSWORD=$ADMIN_PASSWORD
|
||||
|
||||
# 네트워크 설정
|
||||
DOMAIN_NAME=$DOMAIN_NAME
|
||||
EXTERNAL_PORT=$EXTERNAL_PORT
|
||||
|
||||
# CORS 설정 (도메인에 따라 자동 설정)
|
||||
ALLOWED_ORIGINS=http://localhost:$EXTERNAL_PORT,http://$DOMAIN_NAME:$EXTERNAL_PORT
|
||||
|
||||
# 성능 최적화 설정 (DS1525+ 32GB RAM)
|
||||
POSTGRES_SHARED_BUFFERS=8GB
|
||||
POSTGRES_EFFECTIVE_CACHE_SIZE=24GB
|
||||
POSTGRES_WORK_MEM=512MB
|
||||
POSTGRES_MAINTENANCE_WORK_MEM=4GB
|
||||
|
||||
# Redis 설정
|
||||
REDIS_MAXMEMORY=8gb
|
||||
REDIS_MAXMEMORY_POLICY=allkeys-lru
|
||||
|
||||
# 로그 레벨
|
||||
LOG_LEVEL=INFO
|
||||
DEBUG=false
|
||||
|
||||
# 파일 업로드 설정
|
||||
MAX_FILE_SIZE=500000000
|
||||
UPLOAD_DIR=/app/uploads
|
||||
|
||||
# 백업 설정
|
||||
BACKUP_RETENTION_DAYS=30
|
||||
AUTO_BACKUP_ENABLED=true
|
||||
|
||||
# 모니터링 설정
|
||||
HEALTH_CHECK_INTERVAL=30s
|
||||
HEALTH_CHECK_TIMEOUT=10s
|
||||
HEALTH_CHECK_RETRIES=3
|
||||
|
||||
# 스토리지 경로 (Synology 최적화)
|
||||
SSD_PATH=/volume3/docker/document-server
|
||||
HDD_PATH=/volume1/document-storage
|
||||
|
||||
# 타임존 설정
|
||||
TZ=Asia/Seoul
|
||||
EOF
|
||||
|
||||
# 파일 권한 설정 (보안)
|
||||
chmod 600 "$ENV_FILE"
|
||||
|
||||
log_success "환경 변수 파일이 생성되었습니다: $ENV_FILE"
|
||||
|
||||
# 설정 요약 출력
|
||||
echo ""
|
||||
echo "=== 📋 설정 요약 ==="
|
||||
echo -e "데이터베이스 비밀번호: ${CYAN}$DB_PASSWORD${NC}"
|
||||
echo -e "관리자 이메일: ${CYAN}$ADMIN_EMAIL${NC}"
|
||||
echo -e "관리자 비밀번호: ${CYAN}$ADMIN_PASSWORD${NC}"
|
||||
echo -e "도메인: ${CYAN}$DOMAIN_NAME${NC}"
|
||||
echo -e "포트: ${CYAN}$EXTERNAL_PORT${NC}"
|
||||
echo ""
|
||||
|
||||
# 보안 정보 저장
|
||||
SECURITY_INFO_FILE="/volume1/document-storage/backups/security-info-$(date +%Y%m%d_%H%M%S).txt"
|
||||
mkdir -p "$(dirname "$SECURITY_INFO_FILE")" 2>/dev/null || true
|
||||
|
||||
cat > "$SECURITY_INFO_FILE" << EOF
|
||||
Document Server 보안 정보
|
||||
생성일: $(date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
=== 관리자 계정 ===
|
||||
이메일: $ADMIN_EMAIL
|
||||
비밀번호: $ADMIN_PASSWORD
|
||||
|
||||
=== 데이터베이스 ===
|
||||
사용자: docuser
|
||||
비밀번호: $DB_PASSWORD
|
||||
|
||||
=== 접속 정보 ===
|
||||
웹 인터페이스: http://$DOMAIN_NAME:$EXTERNAL_PORT
|
||||
API 문서: http://$DOMAIN_NAME:$((EXTERNAL_PORT + 2))/docs
|
||||
|
||||
=== 중요 안내 ===
|
||||
- 이 파일은 안전한 곳에 보관하세요
|
||||
- 비밀번호는 정기적으로 변경하세요
|
||||
- 외부 접속 시 HTTPS 사용을 권장합니다
|
||||
EOF
|
||||
|
||||
chmod 600 "$SECURITY_INFO_FILE" 2>/dev/null || true
|
||||
|
||||
log_success "보안 정보가 저장되었습니다: $SECURITY_INFO_FILE"
|
||||
|
||||
# 다음 단계 안내
|
||||
echo ""
|
||||
echo "=== 🚀 다음 단계 ==="
|
||||
echo "1. 배포 실행:"
|
||||
echo " ${CYAN}./scripts/deploy-synology.sh${NC}"
|
||||
echo ""
|
||||
echo "2. 상태 확인:"
|
||||
echo " ${CYAN}./scripts/monitor-synology.sh${NC}"
|
||||
echo ""
|
||||
echo "3. 웹 접속:"
|
||||
echo " ${CYAN}http://$DOMAIN_NAME:$EXTERNAL_PORT${NC}"
|
||||
echo ""
|
||||
|
||||
# SSL 설정 권장사항
|
||||
if [ "$DOMAIN_NAME" != "localhost" ]; then
|
||||
echo "=== 🔒 보안 권장사항 ==="
|
||||
echo "외부 도메인을 사용하시는 경우 SSL 인증서 설정을 권장합니다:"
|
||||
echo ""
|
||||
echo "1. Let's Encrypt 인증서 발급:"
|
||||
echo " ${CYAN}certbot certonly --webroot -w /volume2/document-storage/documents -d $DOMAIN_NAME${NC}"
|
||||
echo ""
|
||||
echo "2. Nginx SSL 설정 추가"
|
||||
echo "3. 방화벽에서 HTTPS(443) 포트 개방"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
log_success "환경 변수 설정이 완료되었습니다!"
|
||||
303
scripts/update-synology.sh
Executable file
303
scripts/update-synology.sh
Executable file
@@ -0,0 +1,303 @@
|
||||
#!/bin/bash
|
||||
|
||||
# =============================================================================
|
||||
# Document Server - Synology 업데이트 스크립트
|
||||
# Git을 통한 무중단 업데이트 및 롤백 지원
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# 색상 정의
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 로그 함수
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 환경 설정
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
COMPOSE_FILE="docker-compose.synology.yml"
|
||||
BACKUP_DIR="/volume2/document-storage/backups"
|
||||
UPDATE_LOG="/volume1/docker/document-server/logs/update.log"
|
||||
|
||||
# 업데이트 모드 설정
|
||||
UPDATE_MODE="${1:-safe}" # safe, force, rollback
|
||||
|
||||
log_info "🔄 Document Server 업데이트 시작 (모드: $UPDATE_MODE)"
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') - 업데이트 시작: $UPDATE_MODE" >> "$UPDATE_LOG"
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# 현재 상태 확인
|
||||
log_info "📊 현재 상태 확인 중..."
|
||||
|
||||
# Git 상태 확인
|
||||
if [ ! -d ".git" ]; then
|
||||
log_error "Git 저장소가 아닙니다. Git 클론으로 설치해주세요."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 현재 커밋 해시 저장 (롤백용)
|
||||
CURRENT_COMMIT=$(git rev-parse HEAD)
|
||||
CURRENT_BRANCH=$(git branch --show-current)
|
||||
echo "이전 커밋: $CURRENT_COMMIT" >> "$UPDATE_LOG"
|
||||
|
||||
log_info "현재 브랜치: $CURRENT_BRANCH"
|
||||
log_info "현재 커밋: ${CURRENT_COMMIT:0:8}"
|
||||
|
||||
# 컨테이너 상태 확인
|
||||
if docker-compose -f "$COMPOSE_FILE" ps -q | grep -q .; then
|
||||
CONTAINERS_RUNNING=true
|
||||
log_info "컨테이너가 실행 중입니다"
|
||||
else
|
||||
CONTAINERS_RUNNING=false
|
||||
log_warning "컨테이너가 실행 중이지 않습니다"
|
||||
fi
|
||||
|
||||
# 롤백 모드
|
||||
if [ "$UPDATE_MODE" = "rollback" ]; then
|
||||
log_warning "🔙 롤백 모드 실행"
|
||||
|
||||
# 마지막 성공한 커밋으로 롤백
|
||||
LAST_SUCCESS=$(tail -n 20 "$UPDATE_LOG" | grep "업데이트 성공" | tail -n 1 | awk '{print $6}')
|
||||
|
||||
if [ -z "$LAST_SUCCESS" ]; then
|
||||
log_error "롤백할 커밋을 찾을 수 없습니다"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "롤백 대상 커밋: $LAST_SUCCESS"
|
||||
|
||||
# 롤백 실행
|
||||
git reset --hard "$LAST_SUCCESS"
|
||||
|
||||
# 컨테이너 재시작
|
||||
if [ "$CONTAINERS_RUNNING" = true ]; then
|
||||
log_info "컨테이너 재시작 중..."
|
||||
docker-compose -f "$COMPOSE_FILE" down
|
||||
docker-compose -f "$COMPOSE_FILE" up -d --build
|
||||
fi
|
||||
|
||||
log_success "롤백 완료: $LAST_SUCCESS"
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') - 롤백 완료: $LAST_SUCCESS" >> "$UPDATE_LOG"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 업데이트 가능 여부 확인
|
||||
log_info "🔍 업데이트 확인 중..."
|
||||
|
||||
# 원격 저장소에서 최신 정보 가져오기
|
||||
git fetch origin
|
||||
|
||||
# 업데이트 가능한 커밋 수 확인
|
||||
COMMITS_BEHIND=$(git rev-list --count HEAD..origin/$CURRENT_BRANCH)
|
||||
|
||||
if [ "$COMMITS_BEHIND" -eq 0 ]; then
|
||||
log_success "이미 최신 버전입니다"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log_info "업데이트 가능한 커밋: $COMMITS_BEHIND개"
|
||||
|
||||
# 변경사항 미리보기
|
||||
log_info "📝 변경사항 미리보기:"
|
||||
git log --oneline HEAD..origin/$CURRENT_BRANCH | head -10
|
||||
|
||||
# Safe 모드에서 사용자 확인
|
||||
if [ "$UPDATE_MODE" = "safe" ]; then
|
||||
echo ""
|
||||
read -p "업데이트를 진행하시겠습니까? (y/N): " -n 1 -r
|
||||
echo ""
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
log_info "업데이트가 취소되었습니다"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# 백업 생성
|
||||
log_info "💾 백업 생성 중..."
|
||||
|
||||
BACKUP_TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_NAME="pre_update_${BACKUP_TIMESTAMP}"
|
||||
|
||||
# 데이터베이스 백업
|
||||
if [ "$CONTAINERS_RUNNING" = true ]; then
|
||||
docker-compose -f "$COMPOSE_FILE" exec -T database pg_dump -U docuser document_db > "$BACKUP_DIR/db_${BACKUP_NAME}.sql"
|
||||
log_success "데이터베이스 백업 완료"
|
||||
fi
|
||||
|
||||
# 설정 파일 백업
|
||||
tar -czf "$BACKUP_DIR/config_${BACKUP_NAME}.tar.gz" \
|
||||
/volume1/docker/document-server/config/ \
|
||||
.env.synology 2>/dev/null || true
|
||||
|
||||
log_success "설정 파일 백업 완료"
|
||||
|
||||
# Git 업데이트 실행
|
||||
log_info "📥 코드 업데이트 중..."
|
||||
|
||||
# 로컬 변경사항 임시 저장 (있는 경우)
|
||||
if ! git diff --quiet; then
|
||||
log_warning "로컬 변경사항을 임시 저장합니다"
|
||||
git stash push -m "Auto-stash before update $BACKUP_TIMESTAMP"
|
||||
fi
|
||||
|
||||
# 업데이트 실행
|
||||
git pull origin "$CURRENT_BRANCH"
|
||||
|
||||
NEW_COMMIT=$(git rev-parse HEAD)
|
||||
log_success "코드 업데이트 완료: ${NEW_COMMIT:0:8}"
|
||||
|
||||
# Docker 이미지 업데이트 확인
|
||||
log_info "🐳 Docker 이미지 업데이트 확인 중..."
|
||||
|
||||
# Dockerfile이나 requirements.txt 변경 확인
|
||||
NEED_REBUILD=false
|
||||
|
||||
if git diff --name-only "$CURRENT_COMMIT" "$NEW_COMMIT" | grep -E "(Dockerfile|requirements.txt|pyproject.toml|package.json)" > /dev/null; then
|
||||
NEED_REBUILD=true
|
||||
log_info "의존성 변경 감지 - 이미지 재빌드 필요"
|
||||
fi
|
||||
|
||||
# 컨테이너 업데이트
|
||||
if [ "$CONTAINERS_RUNNING" = true ]; then
|
||||
log_info "🔄 서비스 업데이트 중..."
|
||||
|
||||
if [ "$NEED_REBUILD" = true ]; then
|
||||
log_info "이미지 재빌드 중..."
|
||||
|
||||
# 무중단 업데이트를 위한 단계별 재시작
|
||||
docker-compose -f "$COMPOSE_FILE" build --no-cache backend
|
||||
docker-compose -f "$COMPOSE_FILE" up -d --no-deps backend
|
||||
|
||||
# 헬스체크 대기
|
||||
log_info "백엔드 헬스체크 대기 중..."
|
||||
sleep 30
|
||||
|
||||
# Nginx 업데이트 (필요시)
|
||||
if git diff --name-only "$CURRENT_COMMIT" "$NEW_COMMIT" | grep -E "(nginx|frontend)" > /dev/null; then
|
||||
docker-compose -f "$COMPOSE_FILE" build --no-cache nginx
|
||||
docker-compose -f "$COMPOSE_FILE" up -d --no-deps nginx
|
||||
fi
|
||||
else
|
||||
log_info "설정 파일만 업데이트 - 재시작 중..."
|
||||
docker-compose -f "$COMPOSE_FILE" restart backend nginx
|
||||
fi
|
||||
|
||||
# 서비스 상태 확인
|
||||
sleep 10
|
||||
|
||||
# 헬스체크
|
||||
HEALTH_CHECK_FAILED=false
|
||||
|
||||
# 백엔드 헬스체크
|
||||
if ! curl -f http://localhost:24102/health > /dev/null 2>&1; then
|
||||
log_error "백엔드 헬스체크 실패"
|
||||
HEALTH_CHECK_FAILED=true
|
||||
fi
|
||||
|
||||
# 프론트엔드 헬스체크
|
||||
if ! curl -f http://localhost:24100/ > /dev/null 2>&1; then
|
||||
log_error "프론트엔드 헬스체크 실패"
|
||||
HEALTH_CHECK_FAILED=true
|
||||
fi
|
||||
|
||||
# 헬스체크 실패 시 롤백
|
||||
if [ "$HEALTH_CHECK_FAILED" = true ]; then
|
||||
log_error "헬스체크 실패 - 자동 롤백 실행"
|
||||
|
||||
# 이전 커밋으로 롤백
|
||||
git reset --hard "$CURRENT_COMMIT"
|
||||
|
||||
# 컨테이너 롤백
|
||||
docker-compose -f "$COMPOSE_FILE" down
|
||||
docker-compose -f "$COMPOSE_FILE" up -d --build
|
||||
|
||||
log_error "업데이트 실패 - 이전 버전으로 롤백됨"
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') - 업데이트 실패 (롤백): $NEW_COMMIT -> $CURRENT_COMMIT" >> "$UPDATE_LOG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "서비스 업데이트 완료"
|
||||
else
|
||||
log_info "컨테이너가 실행 중이지 않아 서비스 업데이트를 건너뜁니다"
|
||||
fi
|
||||
|
||||
# 데이터베이스 마이그레이션 확인
|
||||
log_info "🗄️ 데이터베이스 마이그레이션 확인 중..."
|
||||
|
||||
if git diff --name-only "$CURRENT_COMMIT" "$NEW_COMMIT" | grep -E "(migrations|models)" > /dev/null; then
|
||||
log_warning "데이터베이스 스키마 변경 감지"
|
||||
|
||||
if [ "$CONTAINERS_RUNNING" = true ]; then
|
||||
log_info "마이그레이션 실행 중..."
|
||||
docker-compose -f "$COMPOSE_FILE" exec -T backend python -m alembic upgrade head || true
|
||||
log_success "마이그레이션 완료"
|
||||
else
|
||||
log_warning "컨테이너가 실행 중이지 않아 마이그레이션을 건너뜁니다"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 업데이트 완료
|
||||
log_success "🎉 업데이트 완료!"
|
||||
|
||||
echo ""
|
||||
echo "=== 업데이트 정보 ==="
|
||||
echo "이전 커밋: ${CURRENT_COMMIT:0:8}"
|
||||
echo "새 커밋: ${NEW_COMMIT:0:8}"
|
||||
echo "업데이트된 커밋 수: $COMMITS_BEHIND"
|
||||
echo "백업 위치: $BACKUP_DIR/*_${BACKUP_NAME}.*"
|
||||
echo ""
|
||||
|
||||
# 변경사항 요약
|
||||
echo "=== 주요 변경사항 ==="
|
||||
git log --oneline "$CURRENT_COMMIT".."$NEW_COMMIT" | head -5
|
||||
|
||||
echo ""
|
||||
echo "=== 서비스 상태 ==="
|
||||
if [ "$CONTAINERS_RUNNING" = true ]; then
|
||||
docker-compose -f "$COMPOSE_FILE" ps
|
||||
echo ""
|
||||
echo "🌐 웹 인터페이스: http://localhost:24100"
|
||||
echo "🔧 API 문서: http://localhost:24102/docs"
|
||||
fi
|
||||
|
||||
# 성공 로그 기록
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') - 업데이트 성공: $CURRENT_COMMIT -> $NEW_COMMIT" >> "$UPDATE_LOG"
|
||||
|
||||
# 정리 작업
|
||||
log_info "🧹 정리 작업 중..."
|
||||
|
||||
# 오래된 백업 파일 정리 (30일 이상)
|
||||
find "$BACKUP_DIR" -name "pre_update_*" -mtime +30 -delete 2>/dev/null || true
|
||||
|
||||
# Docker 이미지 정리
|
||||
docker system prune -f > /dev/null 2>&1 || true
|
||||
|
||||
log_success "업데이트 프로세스 완료"
|
||||
|
||||
# 모니터링 실행 제안
|
||||
echo ""
|
||||
echo "💡 업데이트 후 시스템 상태를 확인하려면:"
|
||||
echo " ./scripts/monitor-synology.sh"
|
||||
echo ""
|
||||
echo "🔙 문제가 있으면 롤백하려면:"
|
||||
echo " ./scripts/update-synology.sh rollback"
|
||||
Reference in New Issue
Block a user