Files
news-project/DEVELOPMENT_DEPLOYMENT_GUIDE.md
hyungi 016682269a 🧹 Consolidate documentation
-  Created unified DEVELOPMENT_DEPLOYMENT_GUIDE.md
- 🗑️ Removed individual setup guides:
  - TECH_STACK.md
  - HOMEBREW_SETUP.md
  - SYNOLOGY_SETUP.md
  - DS1525_OPTIMIZED_SETUP.md
  - DEV_DEPLOY_PIPELINE.md
  - DOCKER_DEV_GUIDE.md

All content consolidated into single development-deployment guide
focused on Mac → DS1525+ (32GB RAM) workflow
2025-09-14 12:58:55 +09:00

20 KiB

🚀 개발-배포 통합 가이드 (Mac → DS1525+ 32GB)

📋 개요

Mac에서 개발하고 Synology DS1525+ (32GB RAM, 480GB SSD)에 배포하는 완전한 개발-배포 파이프라인 가이드입니다.


🔧 하드웨어 사양

개발 환경: Mac

  • MacBook Pro/Mac Mini (M1/M2 또는 Intel)
  • 메모리: 16GB 이상 권장
  • 저장공간: 100GB 이상 여유 공간

배포 환경: Synology DS1525+

  • CPU: AMD Ryzen R1600 (4코어 2.6GHz)
  • 메모리: 32GB RAM (대용량!)
  • 저장장치: 시놀로지 정품 2.5" SSD 480GB
  • 네트워크: 기가비트 이더넷 x4
  • OS: DSM 7.0+

🐳 Docker 기반 개발-배포 전략

아키텍처 호환성 해결

Mac (개발)              DS1525+ (배포)
├── M1/M2: arm64   →   ├── AMD Ryzen: amd64
├── Intel: amd64   →   └── Linux 컨테이너
└── Docker Desktop     └── Container Manager

핵심 해결책: 멀티 아키텍처 빌드

  • Mac에서 개발: arm64 또는 amd64
  • DS1525+에서 실행: amd64 보장
  • 크로스 플랫폼 빌드로 호환성 문제 완전 해결

🛠️ 1단계: Mac 개발 환경 설정

Docker Desktop 설치 및 최적화

# Homebrew로 Docker Desktop 설치
brew install --cask docker

# Docker Desktop 리소스 설정 (GUI)
# Memory: 8GB (16GB Mac 기준)
# CPUs: 4 cores
# Swap: 2GB
# Disk: 100GB

Docker Buildx 설정 (멀티 아키텍처 빌드용)

# 멀티 플랫폼 빌더 생성
docker buildx create --name multiarch --driver docker-container --use
docker buildx inspect --bootstrap

# 지원 플랫폼 확인
docker buildx ls

프로젝트 구조 설정

project/
├── docker/
│   ├── Dockerfile.dev         # Mac 개발용
│   ├── Dockerfile.prod        # DS1525+ 배포용
│   ├── docker-compose.dev.yml # 로컬 개발
│   └── docker-compose.prod.yml# DS1525+ 배포
├── scripts/
│   ├── dev-start.sh          # 개발 시작
│   ├── build-deploy.sh       # 빌드 & 배포
│   └── rollback.sh           # 롤백
├── src/
│   ├── backend/
│   └── frontend/
├── config/
│   ├── nginx/
│   ├── postgres/
│   └── monitoring/
└── environments/
    ├── .env.development
    └── .env.production

🏗️ 2단계: 개발용 Docker 설정

개발용 Dockerfile

# docker/Dockerfile.dev
FROM --platform=linux/amd64 python:3.11-slim

# 개발 도구 설치
RUN apt-get update && apt-get install -y \
    gcc g++ curl vim git \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Python 개발 의존성
COPY requirements.dev.txt .
RUN pip install --no-cache-dir -r requirements.dev.txt

# 환경 설정
ENV PYTHONPATH=/app
ENV PYTHONUNBUFFERED=1
ENV RELOAD=true

EXPOSE 8000 5678

# Hot Reload 개발 서버
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

개발용 Docker Compose

# docker/docker-compose.dev.yml
version: '3.8'

services:
  app:
    build:
      context: ..
      dockerfile: docker/Dockerfile.dev
    platform: linux/amd64  # DS1525+ 호환성 보장
    container_name: app-dev
    environment:
      - DEBUG=true
      - DATABASE_URL=postgresql://dev_user:dev_pass@postgres:5432/app_dev
      - REDIS_URL=redis://redis:6379/0
    volumes:
      - ../src:/app/src:cached  # Mac 성능 최적화
      - ../config:/app/config:cached
    ports:
      - "8000:8000"
      - "5678:5678"  # 디버깅 포트
    depends_on:
      - postgres
      - redis
    networks:
      - dev-network

  postgres:
    image: postgres:15-alpine
    platform: linux/amd64
    container_name: postgres-dev
    environment:
      POSTGRES_DB: app_dev
      POSTGRES_USER: dev_user
      POSTGRES_PASSWORD: dev_pass
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    networks:
      - dev-network

  redis:
    image: redis:7-alpine
    platform: linux/amd64
    container_name: redis-dev
    volumes:
      - redis_data:/data
    ports:
      - "6379:6379"
    networks:
      - dev-network

networks:
  dev-network:
    driver: bridge

volumes:
  postgres_data:
  redis_data:

개발 시작 스크립트

#!/bin/bash
# scripts/dev-start.sh

echo "🚀 개발 환경 시작"

# 개발 컨테이너 시작
docker-compose -f docker/docker-compose.dev.yml up -d

# 서비스 준비 대기
echo "⏳ 서비스 시작 대기..."
sleep 15

# 헬스체크
if curl -f http://localhost:8000/health > /dev/null 2>&1; then
    echo "✅ 개발 환경 준비 완료!"
    echo "🌐 애플리케이션: http://localhost:8000"
    echo "🗄️ 데이터베이스: localhost:5432"
    echo "🔴 Redis: localhost:6379"
else
    echo "❌ 서비스 시작 실패"
    docker-compose -f docker/docker-compose.dev.yml logs
fi

🏭 3단계: DS1525+ 배포 환경 설정

DS1525+ 32GB 최적화 구성

총 메모리: 32GB 할당 계획
├── DSM 시스템: 2GB
├── PostgreSQL: 4GB (고성능 DB)
├── Elasticsearch: 8GB (대용량 검색)
├── Redis: 2GB (대용량 캐시)
├── 애플리케이션: 4GB (멀티 워커)
├── 모니터링: 2GB (Prometheus + Grafana)
├── 시스템 캐시: 8GB
└── 여유 공간: 2GB

배포용 Dockerfile (최적화)

# docker/Dockerfile.prod
FROM --platform=$BUILDPLATFORM python:3.11-slim AS builder

ARG TARGETPLATFORM
ARG BUILDPLATFORM

WORKDIR /app

# 시스템 패키지 설치
RUN apt-get update && apt-get install -y \
    gcc g++ curl \
    && rm -rf /var/lib/apt/lists/*

# Python 의존성 설치
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 프로덕션 이미지
FROM python:3.11-slim AS production

WORKDIR /app

# 빌드된 패키지 복사
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin

# 애플리케이션 코드 복사
COPY src/ ./src/
COPY config/ ./config/

# 헬스체크 추가
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:8000/health || exit 1

EXPOSE 8000

CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]

DS1525+ 배포용 Docker Compose

# docker/docker-compose.prod.yml
version: '3.8'

services:
  app:
    build:
      context: ..
      dockerfile: docker/Dockerfile.prod
      platforms:
        - linux/amd64
    container_name: app-prod
    environment:
      - DATABASE_URL=postgresql://app_user:${DB_PASSWORD}@postgres:5432/app_prod
      - REDIS_URL=redis://redis:6379/0
      - WORKERS=4
    volumes:
      - /volume1/docker/app/logs:/app/logs
    ports:
      - "8000:8000"
    networks:
      - prod-network
    depends_on:
      - postgres
      - redis
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 4G
          cpus: '2.0'

  postgres:
    image: postgres:15-alpine
    platform: linux/amd64
    container_name: postgres-prod
    environment:
      POSTGRES_DB: app_prod
      POSTGRES_USER: app_user
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - /volume1/docker/app/data/postgres:/var/lib/postgresql/data
      - ../config/postgresql.conf:/etc/postgresql/postgresql.conf
    ports:
      - "5432:5432"
    networks:
      - prod-network
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 4G
          cpus: '2.0'
    command: postgres -c config_file=/etc/postgresql/postgresql.conf

  redis:
    image: redis:7-alpine
    platform: linux/amd64
    container_name: redis-prod
    command: redis-server --maxmemory 2gb --maxmemory-policy allkeys-lru
    volumes:
      - /volume1/docker/app/data/redis:/data
    ports:
      - "6379:6379"
    networks:
      - prod-network
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 2G
          cpus: '1.0'

  elasticsearch:
    image: elasticsearch:8.11.0
    platform: linux/amd64
    container_name: elasticsearch-prod
    environment:
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms4g -Xmx8g"
      - xpack.security.enabled=false
    volumes:
      - /volume1/docker/app/data/elasticsearch:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"
    networks:
      - prod-network
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 10G
          cpus: '2.0'

  nginx:
    image: nginx:alpine
    platform: linux/amd64
    container_name: nginx-prod
    volumes:
      - ../config/nginx/nginx.conf:/etc/nginx/nginx.conf
      - /volume1/docker/app/logs:/var/log/nginx
    ports:
      - "80:80"
      - "443:443"
    networks:
      - prod-network
    depends_on:
      - app
    restart: unless-stopped

  # 모니터링
  prometheus:
    image: prom/prometheus:latest
    platform: linux/amd64
    container_name: prometheus
    volumes:
      - ../config/prometheus.yml:/etc/prometheus/prometheus.yml
      - /volume1/docker/app/data/prometheus:/prometheus
    ports:
      - "9090:9090"
    networks:
      - prod-network
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 1G

  grafana:
    image: grafana/grafana:latest
    platform: linux/amd64
    container_name: grafana
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin123
    volumes:
      - /volume1/docker/app/data/grafana:/var/lib/grafana
    ports:
      - "3000:3000"
    networks:
      - prod-network
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 1G

networks:
  prod-network:
    driver: bridge

volumes:
  postgres_data:
  redis_data:
  elasticsearch_data:

🚀 4단계: 자동화 빌드 & 배포

통합 빌드-배포 스크립트

#!/bin/bash
# scripts/build-deploy.sh

set -e

# 설정
PROJECT_NAME="app"
NAS_HOST="192.168.1.100"
NAS_USER="admin"
VERSION=$(git describe --tags --always --dirty)

echo "🚀 빌드 & 배포 시작: $PROJECT_NAME v$VERSION"

# 1. 로컬 테스트
echo "🧪 로컬 테스트 실행..."
docker-compose -f docker/docker-compose.dev.yml up -d
sleep 30

if curl -f http://localhost:8000/health > /dev/null 2>&1; then
    echo "✅ 로컬 테스트 통과"
    docker-compose -f docker/docker-compose.dev.yml down
else
    echo "❌ 로컬 테스트 실패"
    docker-compose -f docker/docker-compose.dev.yml logs
    exit 1
fi

# 2. 멀티 아키텍처 빌드 (DS1525+ 호환)
echo "🔨 DS1525+ 호환 이미지 빌드..."
docker buildx build \
    --platform linux/amd64 \
    --tag $PROJECT_NAME:$VERSION \
    --tag $PROJECT_NAME:latest \
    --file docker/Dockerfile.prod \
    --load \
    .

# 3. DS1525+로 이미지 전송
echo "📤 DS1525+로 이미지 전송..."
docker save $PROJECT_NAME:latest | ssh $NAS_USER@$NAS_HOST "docker load"

# 4. 환경변수 동기화
echo "⚙️ 환경변수 동기화..."
scp environments/.env.production $NAS_USER@$NAS_HOST:/volume1/docker/$PROJECT_NAME/.env

# 5. DS1525+에서 배포 실행
echo "🚢 DS1525+ 배포 실행..."
ssh $NAS_USER@$NAS_HOST << EOF
    cd /volume1/docker/$PROJECT_NAME
    
    # 기존 컨테이너 백업 (롤백용)
    if docker-compose -f docker-compose.prod.yml ps | grep -q "Up"; then
        echo "📦 롤백용 백업 생성..."
        docker-compose -f docker-compose.prod.yml config > docker-compose.backup.yml
        docker images --format "{{.Repository}}:{{.Tag}}" | grep $PROJECT_NAME | head -1 > last_image.txt
    fi
    
    # 새 버전 배포
    docker-compose -f docker-compose.prod.yml down
    docker-compose -f docker-compose.prod.yml up -d
    
    # 이미지 정리
    docker image prune -f
EOF

# 6. 배포 후 헬스체크
echo "🏥 배포 후 헬스체크..."
sleep 60

for i in {1..10}; do
    if curl -f http://$NAS_HOST:8000/health > /dev/null 2>&1; then
        echo "✅ 배포 성공 및 헬스체크 통과"
        break
    fi
    
    if [ $i -eq 10 ]; then
        echo "❌ 헬스체크 실패 - 롤백 필요"
        exit 1
    fi
    
    echo "⏳ 헬스체크 재시도 ($i/10)..."
    sleep 10
done

# 7. 배포 완료 정보
echo ""
echo "🎉 배포 완료!"
echo "📊 배포 정보:"
echo "  - 프로젝트: $PROJECT_NAME"
echo "  - 버전: $VERSION"
echo "  - 대상: DS1525+ (32GB RAM, 480GB SSD)"
echo "  - 배포 시간: $(date)"
echo ""
echo "🔗 접속 링크:"
echo "  - 애플리케이션: http://$NAS_HOST"
echo "  - 모니터링: http://$NAS_HOST:3000 (admin/admin123)"
echo "  - Prometheus: http://$NAS_HOST:9090"

롤백 스크립트

#!/bin/bash
# scripts/rollback.sh

set -e

NAS_HOST="192.168.1.100"
NAS_USER="admin"
PROJECT_NAME="app"

echo "🔄 롤백 시작..."

ssh $NAS_USER@$NAS_HOST << EOF
    cd /volume1/docker/$PROJECT_NAME
    
    # 백업 파일 확인
    if [[ ! -f "docker-compose.backup.yml" ]] || [[ ! -f "last_image.txt" ]]; then
        echo "❌ 백업 파일을 찾을 수 없습니다"
        exit 1
    fi
    
    # 현재 컨테이너 중지
    docker-compose -f docker-compose.prod.yml down
    
    # 이전 설정으로 복원
    cp docker-compose.backup.yml docker-compose.prod.yml
    
    # 컨테이너 시작
    docker-compose -f docker-compose.prod.yml up -d
    
    echo "✅ 롤백 완료"
EOF

# 헬스체크
sleep 30
if curl -f http://$NAS_HOST:8000/health > /dev/null 2>&1; then
    echo "✅ 롤백 성공 및 헬스체크 통과"
else
    echo "❌ 롤백 후 헬스체크 실패"
    exit 1
fi

📊 5단계: DS1525+ 성능 최적화

PostgreSQL 최적화 (32GB 활용)

# config/postgresql.conf
# DS1525+ 32GB RAM 최적화

# 메모리 설정
shared_buffers = 2GB                    # 32GB의 6.25%
effective_cache_size = 16GB             # 32GB의 50%
maintenance_work_mem = 512MB
work_mem = 64MB

# AMD Ryzen 4코어 활용
max_worker_processes = 4
max_parallel_workers = 4
max_parallel_workers_per_gather = 2

# SSD 최적화
random_page_cost = 1.1
effective_io_concurrency = 200

# 연결 및 체크포인트
max_connections = 200
checkpoint_completion_target = 0.9
checkpoint_timeout = 15min
max_wal_size = 4GB

Nginx 설정 (고성능)

# config/nginx/nginx.conf
worker_processes 4;  # AMD Ryzen 4코어

events {
    worker_connections 2048;
    use epoll;
    multi_accept on;
}

http {
    upstream app {
        server app:8000;
        keepalive 32;
    }

    server {
        listen 80;
        server_name _;
        
        client_max_body_size 100M;
        
        # 정적 파일 캐싱
        location /static/ {
            expires 1y;
            add_header Cache-Control "public, immutable";
        }
        
        # API 프록시
        location / {
            proxy_pass http://app;
            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;
            
            # 연결 유지
            proxy_http_version 1.1;
            proxy_set_header Connection "";
        }
    }
}

🔧 6단계: 개발 워크플로우

일일 개발 루틴

# 개발 시작
./scripts/dev-start.sh

# 코드 변경 후 테스트
docker-compose -f docker/docker-compose.dev.yml logs -f app

# 개발 완료 후 배포
git add .
git commit -m "feat: 새로운 기능 추가"
git push origin main

# 배포 실행
./scripts/build-deploy.sh

# 개발 환경 정리
docker-compose -f docker/docker-compose.dev.yml down

브랜치별 배포 전략

# develop 브랜치 → 스테이징 자동 배포
git checkout develop
git merge feature/new-feature
git push origin develop

# main 브랜치 → 프로덕션 배포
git checkout main
git merge develop
git tag v1.2.0
git push origin main --tags
./scripts/build-deploy.sh

📈 7단계: 모니터링 및 유지보수

리소스 모니터링

# DS1525+ 리소스 확인
ssh admin@nas.local << 'EOF'
    echo "💾 메모리 사용량:"
    free -h
    
    echo "🐳 Docker 컨테이너 상태:"
    docker stats --no-stream
    
    echo "💿 디스크 사용량:"
    df -h /volume1
    
    echo "🔥 CPU 사용률:"
    top -bn1 | grep "Cpu(s)"
EOF

자동 백업 (480GB SSD 최적화)

#!/bin/bash
# scripts/backup.sh

BACKUP_DIR="/volume1/backup/app"
DATE=$(date +%Y%m%d_%H%M%S)

# 백업 디렉토리 생성
mkdir -p $BACKUP_DIR/{database,volumes,configs}

echo "💾 백업 시작: $DATE"

# 1. 데이터베이스 백업 (압축)
docker exec postgres-prod pg_dump -U app_user app_prod | gzip > $BACKUP_DIR/database/postgres_$DATE.sql.gz

# 2. 중요 볼륨만 백업 (용량 고려)
docker run --rm \
    -v app_postgres_data:/source:ro \
    -v $BACKUP_DIR/volumes:/backup \
    alpine tar czf /backup/postgres_$DATE.tar.gz -C /source .

# 3. 설정 파일 백업
tar czf $BACKUP_DIR/configs/configs_$DATE.tar.gz \
    /volume1/docker/app/docker-compose.prod.yml \
    /volume1/docker/app/.env \
    /volume1/docker/app/config/

# 4. 오래된 백업 정리 (SSD 수명 고려)
find $BACKUP_DIR -name "*.gz" -mtime +7 -delete
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete

echo "✅ 백업 완료: $DATE"

🎯 성능 벤치마크 (DS1525+ 32GB)

예상 성능 지표

🚀 DS1525+ 32GB 성능 (최적화 후)
├── 동시 사용자: 200명
├── 응답 시간: 50-100ms
├── 처리량: 2,000 req/sec
├── 메모리 사용률: 70-80%
├── CPU 사용률: 30-50%
└── 디스크 I/O: SSD 최적화

리소스 할당 현황

총 32GB 메모리 활용:
├── PostgreSQL: 4GB (12.5%)
├── Elasticsearch: 8GB (25%)
├── Redis: 2GB (6.25%)
├── 애플리케이션: 4GB (12.5%)
├── 모니터링: 2GB (6.25%)
├── 시스템 캐시: 8GB (25%)
├── DSM: 2GB (6.25%)
└── 여유: 2GB (6.25%)

🚨 문제 해결 가이드

일반적인 문제들

1. 아키텍처 호환성 오류

# 오류: exec format error
# 해결: 플랫폼 명시적 지정
docker run --platform linux/amd64 your-image

# 또는 Dockerfile에서
FROM --platform=linux/amd64 python:3.11-slim

2. 메모리 부족 (32GB에서는 드물지만)

# 메모리 사용량 확인
docker stats

# Elasticsearch 메모리 줄이기 (필요시)
ES_JAVA_OPTS="-Xms2g -Xmx4g"

3. 디스크 공간 부족 (480GB SSD)

# Docker 정리
docker system prune -a

# 로그 정리
find /volume1/docker/app/logs -name "*.log" -mtime +3 -delete

# 백업 정리
find /volume1/backup -mtime +30 -delete

📋 배포 체크리스트

배포 전 확인사항

✅ 체크리스트
- [ ] 로컬 테스트 통과
- [ ] 멀티 아키텍처 빌드 성공
- [ ] 환경변수 설정 완료
- [ ] 데이터베이스 마이그레이션 준비
- [ ] 헬스체크 엔드포인트 구현
- [ ] 백업 계획 수립
- [ ] 롤백 계획 준비
- [ ] 모니터링 설정 확인

배포 후 확인사항

✅ 배포 후 체크
- [ ] 애플리케이션 정상 동작
- [ ] 데이터베이스 연결 확인
- [ ] 캐시 동작 확인
- [ ] 검색 기능 확인
- [ ] 모니터링 대시보드 확인
- [ ] 로그 수집 확인
- [ ] 백업 자동화 확인

🎉 결론

DS1525+ 32GB의 압도적 장점

  • 충분한 성능: 32GB RAM으로 모든 서비스 여유롭게 운영
  • 24시간 안정성: 전력 효율적, 저소음 운영
  • 완전 자동화: 개발부터 배포까지 스크립트 기반
  • 확장 가능: 향후 서비스 확장 시에도 여유로움
  • 비용 효율: 클라우드 대비 월등한 비용 절약

개발-배포 파이프라인 완성

Mac 개발 (Hot Reload) 
    ↓
멀티 아키텍처 빌드 
    ↓
DS1525+ 자동 배포 
    ↓
실시간 모니터링 
    ↓
자동 백업

이제 개발에만 집중하고 배포는 스크립트가 알아서! 🚀