# AutonetSellCar.com 배포 가이드 이 문서는 시스템 아키텍처와 코드 수정부터 운영 배포까지의 전체 과정을 설명합니다. > **중요**: 배포 전 반드시 [PRODUCTION_VALUES.md](./PRODUCTION_VALUES.md)의 값을 확인하세요! > 반복되는 오류 해결은 [TROUBLESHOOTING.md](./TROUBLESHOOTING.md)를 참고하세요. --- ## 1. 시스템 아키텍처 개요 ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ AutonetSellCar.com 시스템 아키텍처 │ └─────────────────────────────────────────────────────────────────────────────────┘ ┌─────────────────┐ │ 인터넷 │ │ 59.14.158.123 │ └────────┬────────┘ │ ┌──────────────────┼──────────────────┐ │ │ │ ▼ ▼ ▼ autonetsellcar.com autonetsellcar.com autonetsellcar.com / /api /uploads │ │ │ └──────────────────┼──────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ 서버1 (192.168.0.201) │ │ 인프라 서버 │ │ ┌─────────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │ │ Nginx Proxy │ │ PostgreSQL │ │ Redis │ │ Portainer/Grafana │ │ │ │ Manager │ │ :5432 │ │ :6379 │ │ Prometheus │ │ │ │ :80/:443 │ │ │ │ │ │ │ │ │ │ │ │ DB: autonet │ │ │ │ │ │ │ └────────┬────────┘ └──────┬──────┘ └──────┬──────┘ └─────────────────────┘ │ │ │ │ │ │ └───────────┼──────────────────┼────────────────┼──────────────────────────────────┘ │ │ │ │ └────────┬───────┘ │ │ ▼ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ 서버2 (192.168.0.202) │ │ 애플리케이션 서버 │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ Production Environment │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ │ │ │ │ autonet-frontend │ │ autonet-backend │ │ carmodoo-agent │ │ │ │ │ │ Next.js 14 │───▶│ FastAPI │───▶│ Playwright │ │ │ │ │ │ :3000 │ │ :8000 │ │ PDF/Spec 조회 │ │ │ │ │ └──────────────────┘ └────────┬─────────┘ └──────────────────┘ │ │ │ │ │ │ │ │ │ │ PostgreSQL/Redis │ │ │ │ └──────────────────────────────────────┼───┼──▶ 서버1 │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ Staging Environment │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ │ │ frontend-staging │ │ backend-staging │ │ │ │ │ │ :3001 │ │ :8001 │ │ │ │ │ └──────────────────┘ └──────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ 서버4 (개발 PC) │ │ │ │ D:\Workspace\claudeCode\AutonetSellCar.com\ │ │ ├── frontend/ (Next.js) │ │ ├── backend/ (FastAPI) │ │ └── carmodoo-agent/ │ │ │ │ 로컬 테스트: localhost:3000 / localhost:8000 │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` --- ## 2. 서버별 역할 및 구성 ### 서버1 (192.168.0.201) - 인프라 서버 | 서비스 | 포트 | 역할 | |--------|------|------| | Nginx Proxy Manager | 80, 443, 81 | 리버스 프록시, SSL 인증서 관리 | | PostgreSQL | 5432 | 메인 데이터베이스 (autonet) | | Redis | 6379 | 캐시, 세션 관리 | | Portainer | 9000 | Docker 관리 UI | | Prometheus | 9090 | 메트릭 수집 | | Grafana | 3000 | 모니터링 대시보드 | ### 서버2 (192.168.0.202) - 애플리케이션 서버 | 컨테이너 | 포트 | 역할 | |----------|------|------| | autonet-frontend | 3000 | Next.js 프론트엔드 (운영) | | autonet-backend | 8000 | FastAPI 백엔드 (운영) | | carmodoo-agent | - | Playwright 기반 PDF/스펙 조회 | | autonet-frontend-staging | 3001 | 스테이징 프론트엔드 | | autonet-backend-staging | 8001 | 스테이징 백엔드 | --- ## 3. 도메인 및 라우팅 설정 ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Nginx Proxy Manager 설정 │ └─────────────────────────────────────────────────────────────────────────────────┘ autonetsellcar.com (SSL: Let's Encrypt) │ ├── / ──▶ 192.168.0.202:3000 (Frontend) │ ├── /api/* ──▶ 192.168.0.202:8000 (Backend) │ └── Custom Location: /api │ Forward: http://192.168.0.202:8000 │ └── /uploads/* ──▶ 192.168.0.202:8000 (Backend Static) └── Custom Location: /uploads Forward: http://192.168.0.202:8000 설정 방법: ┌─────────────────────────────────────────────────────────────────────────┐ │ 1. Nginx Proxy Manager (http://192.168.0.201:81) 접속 │ │ 2. Proxy Hosts > autonetsellcar.com 선택 │ │ 3. Custom Locations 탭에서 /api, /uploads 추가 │ │ - Location: /api │ │ - Scheme: http │ │ - Forward Hostname: 192.168.0.202 │ │ - Forward Port: 8000 │ └─────────────────────────────────────────────────────────────────────────┘ ``` --- ## 4. PostgreSQL 설정 ### 4.1 서버1 PostgreSQL Docker 컨테이너 ```bash # PostgreSQL 컨테이너 정보 확인 docker exec postgres-primary env | grep POSTGRES # 출력: # POSTGRES_USER=admin # POSTGRES_PASSWORD=roskfl@1122 # POSTGRES_DB=mongolcar (컨테이너 기본 DB, AutonetSellCar는 autonet DB 사용!) ``` ### 4.2 데이터베이스 생성 ```bash # autonet 데이터베이스 생성 docker exec -it postgres-primary psql -U admin -d postgres -c "CREATE DATABASE autonet;" # 비밀번호 재설정 (필요시) docker exec -it postgres-primary psql -U admin -d postgres -c "ALTER USER admin PASSWORD 'roskfl@1122';" ``` ### 4.3 외부 접속 설정 확인 ```bash # pg_hba.conf 확인 (외부 접속 허용) docker exec postgres-primary cat /var/lib/postgresql/data/pg_hba.conf | grep -v "^#" | grep -v "^$" # 필수 설정: # host all all all scram-sha-256 ``` ### 4.4 백엔드 PostgreSQL 연결 설정 **backend/.env:** ```env # Database USE_SQLITE=False DB_HOST=192.168.0.201 DB_PORT=5432 DB_NAME=autonet DB_USER=admin DB_PASSWORD=roskfl@1122 ``` **backend/app/config.py:** ```python @property def DATABASE_URL(self) -> str: if self.USE_SQLITE: base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) db_path = os.path.join(base_dir, "autonet.db") return f"sqlite:///{db_path}" # URL-encode password for special characters like @ # etc from urllib.parse import quote_plus encoded_password = quote_plus(self.DB_PASSWORD) return f"postgresql://{self.DB_USER}:{encoded_password}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}" ``` ### 4.5 SQLite → PostgreSQL 마이그레이션 ```bash # 개발 서버(서버4)에서 실행 cd D:\Workspace\claudeCode\AutonetSellCar.com\backend # psycopg2 설치 venv\Scripts\pip.exe install psycopg2-binary # 마이그레이션 스크립트 실행 venv\Scripts\python.exe migrate_to_postgres.py ``` **마이그레이션 스크립트 주요 기능:** - PostgreSQL에 테이블 자동 생성 (SQLAlchemy 모델 기반) - SQLite boolean (0/1) → PostgreSQL boolean (true/false) 변환 - FK 제약조건 임시 비활성화 후 데이터 이전 - Sequence 자동 리셋 --- ## 5. Docker Compose 설정 ### 5.1 Docker Compose v2 설치 (서버2) ```bash # Docker 공식 저장소 추가 sudo apt-get update sudo apt-get install ca-certificates curl gnupg -y sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # Docker Compose 플러그인 설치 sudo apt-get update sudo apt-get install docker-compose-plugin -y # 버전 확인 docker compose version ``` ### 5.2 docker-compose.production.yml ```yaml services: frontend: build: ./frontend container_name: autonet-frontend ports: - "3000:3000" environment: - NODE_ENV=production - NEXT_PUBLIC_API_URL=https://autonetsellcar.com depends_on: - backend networks: - autonet-production-network backend: build: ./backend container_name: autonet-backend ports: - "8000:8000" environment: - ENV=production env_file: - ./backend/.env volumes: - ./backend/uploads:/app/uploads networks: - autonet-production-network carmodoo-agent: build: ./agent container_name: carmodoo-agent volumes: - ./agent/.env:/app/.env:ro networks: - autonet-production-network networks: autonet-production-network: driver: bridge ``` ### 5.3 주요 Docker 명령어 ```bash # 전체 시작 docker compose -f docker-compose.production.yml up -d # 특정 서비스 재빌드 (캐시 없이) docker compose -f docker-compose.production.yml build backend --no-cache docker compose -f docker-compose.production.yml up -d backend # 로그 확인 docker logs autonet-backend --tail 50 -f # 컨테이너 상태 docker ps # 전체 중지 및 제거 docker compose -f docker-compose.production.yml down # 네트워크/볼륨 문제 시 docker rm -f autonet-backend autonet-frontend carmodoo-agent docker network rm autonet-production-network docker compose -f docker-compose.production.yml up -d ``` --- ## 6. 배포 흐름 ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ 배포 파이프라인 │ └─────────────────────────────────────────────────────────────────────────────────┘ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ 1. 개발 │ ───▶ │ 2. 커밋 │ ───▶ │ 3. 스테이징 │ ───▶ │ 4. 운영 │ │ (서버4) │ │ & Push │ │ 테스트 │ │ 배포 │ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ ▼ ▼ ▼ ▼ 코드 수정 git commit Docker 빌드 promote 로컬 테스트 git push staging :3001/:8001 :3000/:8000 ``` ### Step 1: 개발 (서버4) ```bash # 로컬 개발 서버 실행 cd backend && venv\Scripts\activate uvicorn app.main:app --reload --port 8000 cd frontend npm run dev # 테스트: http://localhost:3000 ``` ### Step 2: Git Commit & Push ```bash git status git add . git commit -m "feat: 기능 설명" git push staging main ``` ### Step 3: 스테이징 테스트 ```bash # 서버2 SSH 접속 후 cd /opt/autonet/staging docker compose -f docker-compose.staging.yml build --no-cache docker compose -f docker-compose.staging.yml up -d # 테스트 URL # Frontend: http://192.168.0.202:3001 # Backend: http://192.168.0.202:8001/docs ``` ### Step 4: 운영 배포 ```bash # 서버2에서 cd /opt/autonet/production docker compose -f docker-compose.production.yml build --no-cache docker compose -f docker-compose.production.yml up -d # 또는 스크립트 사용 /opt/autonet/scripts/deploy.sh promote # 운영 URL # https://autonetsellcar.com # https://autonetsellcar.com/api/docs ``` --- ## 7. 디렉토리 구조 ``` 서버2 (/opt/autonet/) │ ├── git/ │ └── autonet.git/ # Bare Git Repository │ └── hooks/ │ └── post-receive # 자동 배포 훅 │ ├── staging/ # 스테이징 환경 │ ├── frontend/ │ ├── backend/ │ │ └── .env # 스테이징 환경변수 │ └── docker-compose.staging.yml │ ├── production/ # 운영 환경 │ ├── frontend/ │ ├── backend/ │ │ ├── .env # 운영 환경변수 (PostgreSQL) │ │ ├── requirements.txt # psycopg2-binary 포함 │ │ └── app/ │ │ └── config.py # URL 인코딩 적용 │ ├── agent/ │ │ └── .env # Carmodoo 로그인 정보 │ └── docker-compose.production.yml │ ├── releases/ # 롤백용 백업 │ ├── 20241230_140000/ │ └── 20241230_150000/ │ └── scripts/ ├── deploy-staging.sh └── deploy.sh ``` --- ## 8. 환경변수 설정 ### 운영 서버 backend/.env ```env # Database (PostgreSQL) USE_SQLITE=False DB_HOST=192.168.0.201 DB_PORT=5432 DB_NAME=autonet DB_USER=admin DB_PASSWORD=roskfl@1122 # Redis REDIS_HOST=192.168.0.201 REDIS_PORT=6379 REDIS_PASSWORD= # JWT SECRET_KEY=your-secret-key-change-in-production # App DEBUG=False # Azure Translator AZURE_TRANSLATOR_KEY=your-azure-key AZURE_TRANSLATOR_REGION=southeastasia # Email (SMTP) SMTP_HOST=smtp.gmail.com SMTP_PORT=587 SMTP_USER=autonetsellcar@gmail.com SMTP_PASSWORD=your-app-password SMTP_FROM_EMAIL=autonetsellcar@gmail.com SMTP_FROM_NAME=AutonetSellCar # Verification VERIFICATION_CODE_EXPIRE_MINUTES=10 EMAIL_VERIFICATION_REQUIRED=True ``` ### 운영 서버 agent/.env ```env # Carmodoo 딜러 포탈 로그인 정보 CARMODOO_USER_ID=01033315258 CARMODOO_PASSWORD=alskfl@1122 # Backend API 연결 (Docker 네트워크 내부) API_SERVER_URL=http://autonet-backend:8000/api ``` > ⚠️ **중요**: carmodoo-agent는 이 환경변수가 없으면 로그인 실패로 계속 재시작됩니다. --- ## 9. Git 기반 배포가 필요한 이유 ### 9.1 문제 상황 (Git 미사용 시) ``` [서버4 - 로컬 개발] [서버2 - 운영 서버] 소스코드 A 소스코드 A │ │ │ 직접 수정 → 소스코드 B (예: 번역 수정) │ │ └────── docker build ───────────┘ │ ▼ 소스코드 A로 덮어씀 (B 변경사항 사라짐!) ``` **원인**: - 서버2에서 직접 코드를 수정함 - 서버4에서 `docker build`를 하면 서버4의 소스코드로 컨테이너가 다시 빌드됨 - 서버2에서 수정한 내용이 서버4에는 없으므로 → **변경사항이 사라짐** ### 9.2 해결 방법 (Git 기반 배포) ``` [서버4 - 로컬 개발] [서버2 - 운영 서버] 소스코드 수정 │ git commit │ git push staging ────────────────→ Git Bare Repository │ post-receive hook (자동) │ ▼ 소스코드 복사 + docker build │ ▼ 스테이징 컨테이너 실행 │ 검증 후 deploy.sh promote │ ▼ 운영 컨테이너 실행 ``` ### 9.3 핵심 원칙 | 이전 (문제) | 현재 (해결) | |------------|------------| | 서버2에서 직접 수정 | **서버4에서만 수정** | | 수정 후 바로 docker build | git commit → git push | | 변경 이력 없음 | Git으로 버전 관리 | | 롤백 불가 | `deploy.sh rollback`으로 롤백 가능 | ### 9.4 올바른 배포 워크플로우 ```bash # 1. 서버4에서 코드 수정 (로컬 개발) code frontend/src/app/page.tsx # 2. 로컬 테스트 npm run dev # 3. Git 커밋 git add . git commit -m "fix: 번역 수정" # 4. 스테이징 배포 (자동 - post-receive hook) git push staging main # 5. 스테이징 검증 # http://192.168.0.202:3001 (frontend) # http://192.168.0.202:8001/docs (backend) # 6. 운영 승격 ssh damon@192.168.0.202 "/opt/autonet/scripts/deploy.sh promote" ``` > ⚠️ **중요**: 서버2에서 직접 코드를 수정하지 마세요! 모든 변경은 서버4에서 Git을 통해 배포해야 합니다. --- ## 10. 문제 해결 ### PostgreSQL 연결 오류 ```bash # 에러: could not translate host name "1122@192.168.0.201" # 원인: 비밀번호에 @ 문자가 URL 구분자로 인식됨 # 해결: config.py에서 URL 인코딩 적용 from urllib.parse import quote_plus encoded_password = quote_plus(self.DB_PASSWORD) ``` ### Docker 네트워크 충돌 ```bash # 에러: network was found but has incorrect label docker compose -f docker-compose.production.yml down docker network rm autonet-production-network docker compose -f docker-compose.production.yml up -d ``` ### 컨테이너 이름 충돌 ```bash # 에러: container name is already in use docker rm -f autonet-backend autonet-frontend carmodoo-agent docker compose -f docker-compose.production.yml up -d ``` ### psycopg2 모듈 없음 ```bash # requirements.txt에 추가 psycopg2-binary # PostgreSQL production # 또는 직접 추가 echo "psycopg2-binary" >> /opt/autonet/production/backend/requirements.txt docker compose -f docker-compose.production.yml build backend --no-cache ``` ### Mixed Content 에러 ``` # 에러: Mixed Content - HTTPS 페이지에서 HTTP API 호출 # 해결: Frontend 빌드 시 HTTPS API URL 사용 NEXT_PUBLIC_API_URL=https://autonetsellcar.com ``` ### carmodoo-agent 계속 재시작 ```bash # 에러 확인 docker logs carmodoo-agent --tail 20 # 에러: User ID and password are required # 원인: agent/.env 파일 없음 또는 환경변수 미설정 # 해결: # 1. .env 파일 생성 cat > /opt/autonet/production/agent/.env << 'EOF' CARMODOO_USER_ID=01033315258 CARMODOO_PASSWORD=alskfl@1122 API_SERVER_URL=http://autonet-backend:8000/api EOF # 2. 컨테이너 재생성 (.env 마운트 적용) docker stop carmodoo-agent && docker rm carmodoo-agent docker run -d --name carmodoo-agent \ --restart unless-stopped \ --network autonet-production-network \ -v /opt/autonet/production/agent/.env:/app/.env:ro \ production-carmodoo-agent ``` --- ## 11. 빠른 참조 명령어 ``` ╔═══════════════════════════════════════════════════════════════════════════════╗ ║ 개발 (서버4) ║ ╠═══════════════════════════════════════════════════════════════════════════════╣ ║ git status # 변경 파일 확인 ║ ║ git add . # 스테이징 ║ ║ git commit -m "message" # 커밋 ║ ║ git push staging main # 서버2로 푸시 ║ ╚═══════════════════════════════════════════════════════════════════════════════╝ ╔═══════════════════════════════════════════════════════════════════════════════╗ ║ 운영 (서버2) ║ ╠═══════════════════════════════════════════════════════════════════════════════╣ ║ docker ps # 실행 중인 컨테이너 ║ ║ docker logs autonet-backend --tail 50 # 백엔드 로그 ║ ║ docker compose -f docker-compose.production.yml up -d # 시작 ║ ║ docker compose -f docker-compose.production.yml down # 중지 ║ ║ docker compose -f docker-compose.production.yml build --no-cache # 재빌드 ║ ╚═══════════════════════════════════════════════════════════════════════════════╝ ╔═══════════════════════════════════════════════════════════════════════════════╗ ║ PostgreSQL (서버1) ║ ╠═══════════════════════════════════════════════════════════════════════════════╣ ║ docker exec -it postgres-primary psql -U admin -d autonet # DB 접속 ║ ║ \dt # 테이블 목록 ║ ║ \q # 종료 ║ ╚═══════════════════════════════════════════════════════════════════════════════╝ ``` --- ## 12. 체크리스트 ### 최초 서버 설정 - [ ] 서버1: PostgreSQL 컨테이너 실행 중 - [ ] 서버1: autonet 데이터베이스 생성 - [ ] 서버1: 외부 접속 허용 (pg_hba.conf) - [ ] 서버1: Nginx Proxy Manager SSL 설정 - [ ] 서버1: Custom Location (/api, /uploads) 설정 - [ ] 서버2: Docker Compose v2 설치 - [ ] 서버2: backend/.env PostgreSQL 설정 - [ ] 서버2: requirements.txt에 psycopg2-binary 추가 - [ ] 서버2: config.py URL 인코딩 적용 ### 배포 전 - [ ] 로컬 테스트 완료 - [ ] git status로 변경 파일 확인 - [ ] 커밋 메시지 작성 ### 배포 후 - [ ] docker ps로 컨테이너 상태 확인 - [ ] docker logs로 에러 확인 - [ ] https://autonetsellcar.com 접속 테스트 - [ ] API 응답 확인 (/api/docs) --- **최종 업데이트**: 2024-12-31