Synology DS1525+ 최적화 배포 환경 완성: 32GB RAM/SSD캐시 최적화, 자동 배포 스크립트, 모니터링 도구

This commit is contained in:
Hyungi Ahn
2025-09-04 11:25:59 +09:00
parent f49edaf06b
commit 164dcd879d
5 changed files with 1227 additions and 0 deletions

387
scripts/deploy-synology.sh Executable file
View File

@@ -0,0 +1,387 @@
#!/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-optimized.yml"
# 기본 환경 변수
export DB_PASSWORD="${DB_PASSWORD:-$(openssl rand -base64 32)}"
export SECRET_KEY="${SECRET_KEY:-$(openssl rand -base64 64)}"
export ADMIN_EMAIL="${ADMIN_EMAIL:-admin@document-server.local}"
export ADMIN_PASSWORD="${ADMIN_PASSWORD:-$(openssl rand -base64 16)}"
export DOMAIN_NAME="${DOMAIN_NAME:-localhost}"
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 디렉토리 (성능 최우선)
SSD_DIRS=(
"/volume1/docker/document-server/database"
"/volume1/docker/document-server/redis"
"/volume1/docker/document-server/logs"
"/volume1/docker/document-server/logs/nginx"
"/volume1/docker/document-server/config"
"/volume1/docker/document-server/nginx/conf.d"
"/volume1/docker/document-server/nginx/cache"
"/volume1/docker/document-server/cache"
)
# HDD 디렉토리 (대용량 저장)
HDD_DIRS=(
"/volume2/document-storage/uploads"
"/volume2/document-storage/documents"
"/volume2/document-storage/thumbnails"
"/volume2/document-storage/backups"
"/volume2/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 /volume1/docker/document-server/
sudo chown -R 1000:1000 /volume2/document-storage/
log_success "디렉토리 구조 생성 완료"
# 3. 설정 파일 복사
log_info "⚙️ 설정 파일 생성 중..."
# PostgreSQL 설정 (32GB RAM 최적화)
cat > /volume1/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 > /volume1/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
View 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-optimized.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"