release
This commit is contained in:
@@ -0,0 +1,480 @@
|
||||
# 카카오맵 리뷰 분석 서비스 (교육용)
|
||||
|
||||
AI 기반 리뷰 분석을 통해 소상공인의 고객 피드백 관리를 지원하는 RESTful API 서비스입니다.
|
||||
|
||||
## ⚠️ 중요 법적 경고사항
|
||||
|
||||
**이 서비스는 교육 목적으로만 제작되었습니다.**
|
||||
|
||||
### 법적 위험
|
||||
- 카카오 이용약관 위반 → 계정 정지, 법적 조치
|
||||
- 개인정보보호법(PIPA) 위반 → 과태료 최대 3억원
|
||||
- 저작권 및 데이터베이스권 침해 → 손해배상
|
||||
- 업무방해죄 → 5년 이하 징역 또는 1천500만원 이하 벌금
|
||||
|
||||
### 합법적 대안
|
||||
- **카카오 공식 API 활용** ([developers.kakao.com](https://developers.kakao.com))
|
||||
- 점주 대상 자체 가게 관리 서비스 개발
|
||||
- 사용자 동의 기반 데이터 수집 앱
|
||||
- 카카오와 정식 파트너십 체결
|
||||
|
||||
**실제 서비스 사용을 금지합니다!**
|
||||
|
||||
---
|
||||
|
||||
## 📋 프로젝트 개요
|
||||
|
||||
### 주요 기능
|
||||
- 🔍 **카카오맵 리뷰 분석**: HTML 파싱 및 Selenium 기반 동적 수집
|
||||
- 🤖 **AI 기반 피드백**: Claude API 연동을 통한 맞춤형 개선 방안 제시
|
||||
- 📊 **감정 분석**: 긍정/부정/중립 감정 분류 및 통계
|
||||
- 💾 **Vector DB 연동**: 유사 케이스 검색 및 템플릿 활용
|
||||
- 🚀 **RESTful API 제공**: FastAPI 기반의 완전한 API 서비스
|
||||
- 📚 **Swagger UI 지원**: 자동 생성되는 API 문서
|
||||
- ☸️ **Kubernetes 배포**: 완전한 컨테이너 오케스트레이션 지원
|
||||
- 🔧 **환경변수 설정**: ConfigMap과 Secret을 통한 유연한 설정 관리
|
||||
|
||||
### 기술 스택
|
||||
- **Backend**: Python 3.11, FastAPI, aiohttp
|
||||
- **Web Scraping**: Selenium, BeautifulSoup4, Chrome WebDriver
|
||||
- **AI/ML**: Claude AI API, Vector DB (Chroma)
|
||||
- **Database**: PostgreSQL (향후), Vector DB
|
||||
- **Container**: Docker, Multi-stage build
|
||||
- **Orchestration**: Kubernetes (AKS)
|
||||
- **Registry**: Azure Container Registry (ACR)
|
||||
- **Documentation**: Swagger UI, ReDoc
|
||||
|
||||
## 🏗️ 프로젝트 구조
|
||||
|
||||
```
|
||||
review-api/review/
|
||||
├── app/ # 애플리케이션 소스
|
||||
│ ├── main.py # 메인 애플리케이션
|
||||
│ └── requirements.txt # Python 의존성
|
||||
├── deployment/ # 배포 관련 파일
|
||||
│ ├── container/ # 컨테이너 이미지 빌드
|
||||
│ │ ├── Dockerfile # 서비스 이미지 빌드
|
||||
│ │ └── Dockerfile-base # 베이스 이미지 빌드
|
||||
│ └── manifests/ # Kubernetes 매니페스트
|
||||
│ ├── configmap.yaml # 환경 설정
|
||||
│ ├── secret.yaml # 민감 정보 (API 키)
|
||||
│ ├── deployment.yaml # 애플리케이션 배포
|
||||
│ ├── service.yaml # 서비스 노출
|
||||
│ └── ingress.yaml # 외부 접근
|
||||
├── build-base.sh # 베이스 이미지 빌드 스크립트
|
||||
├── build.sh # 서비스 이미지 빌드 스크립트
|
||||
├── create-imagepullsecret.sh # ACR 인증 설정 스크립트
|
||||
├── setup.sh # 로컬 환경 설정 스크립트
|
||||
└── README.md # 프로젝트 문서
|
||||
```
|
||||
|
||||
## 🚀 빠른 시작
|
||||
|
||||
### 1. 로컬 개발 환경 설정
|
||||
|
||||
```bash
|
||||
# 저장소 클론 (review-api/review 디렉토리로 이동)
|
||||
cd review-api/review
|
||||
|
||||
# 환경 설정 스크립트 실행
|
||||
chmod +x setup.sh
|
||||
./setup.sh
|
||||
|
||||
# 가상환경 활성화
|
||||
source venv/bin/activate
|
||||
|
||||
# 애플리케이션 실행
|
||||
python app/main.py
|
||||
```
|
||||
|
||||
### 2. 로컬 웹 브라우저 접속
|
||||
|
||||
```bash
|
||||
# 애플리케이션이 정상 실행된 후 아래 URL로 접속
|
||||
```
|
||||
|
||||
- **메인 페이지**: http://localhost:19000
|
||||
- **Swagger UI**: http://localhost:19000/docs
|
||||
- **ReDoc**: http://localhost:19000/redoc
|
||||
- **헬스체크**: http://localhost:19000/health
|
||||
- **진단 정보**: http://localhost:19000/diagnostic
|
||||
- **법적 경고**: http://localhost:19000/legal-warning
|
||||
|
||||
### 3. API 테스트 (교육용)
|
||||
|
||||
```bash
|
||||
# 기본 리뷰 분석 (교육용 - 실제 사용 금지)
|
||||
curl -X POST "http://localhost:19000/analyze" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"store_id": "501745730",
|
||||
"days_limit": 7,
|
||||
"max_time": 300
|
||||
}'
|
||||
|
||||
# 환경 설정 확인
|
||||
curl "http://localhost:19000/config"
|
||||
|
||||
# 법적 경고사항 확인
|
||||
curl "http://localhost:19000/legal-warning"
|
||||
```
|
||||
|
||||
## 🐳 Docker 컨테이너 실행
|
||||
|
||||
### 베이스 이미지 빌드
|
||||
|
||||
```bash
|
||||
# ACR에 베이스 이미지 빌드 및 푸시
|
||||
./build-base.sh latest acrdigitalgarage03 rg-digitalgarage-03
|
||||
```
|
||||
|
||||
### 서비스 이미지 빌드
|
||||
|
||||
```bash
|
||||
# ACR에 서비스 이미지 빌드 및 푸시
|
||||
./build.sh latest acrdigitalgarage03 rg-digitalgarage-03
|
||||
```
|
||||
|
||||
### 로컬 Docker 실행
|
||||
|
||||
```bash
|
||||
# 컨테이너 실행
|
||||
docker run -p 19000:19000 \
|
||||
-e APP_TITLE="카카오맵 리뷰 분석 API" \
|
||||
-e PORT=19000 \
|
||||
kakao-review-api:latest
|
||||
|
||||
# 백그라운드 실행
|
||||
docker run -d -p 19000:19000 \
|
||||
--name kakao-review-api \
|
||||
-e PORT=19000 \
|
||||
kakao-review-api:latest
|
||||
```
|
||||
|
||||
## ☸️ Kubernetes 배포
|
||||
|
||||
### 1. ACR Image Pull Secret 생성
|
||||
|
||||
```bash
|
||||
# Image Pull Secret 생성
|
||||
./create-imagepullsecret.sh acrdigitalgarage03 rg-digitalgarage-03
|
||||
```
|
||||
|
||||
### 2. Kubernetes 리소스 배포
|
||||
|
||||
```bash
|
||||
# ConfigMap 및 Secret 적용
|
||||
kubectl apply -f deployment/manifests/configmap.yaml
|
||||
kubectl apply -f deployment/manifests/secret.yaml
|
||||
|
||||
# 애플리케이션 배포
|
||||
kubectl apply -f deployment/manifests/deployment.yaml
|
||||
kubectl apply -f deployment/manifests/service.yaml
|
||||
kubectl apply -f deployment/manifests/ingress.yaml
|
||||
```
|
||||
|
||||
### 3. 배포 상태 확인
|
||||
|
||||
```bash
|
||||
# Pod 상태 확인
|
||||
kubectl get pods -l app=kakao-review-api
|
||||
|
||||
# 서비스 상태 확인
|
||||
kubectl get svc kakao-review-api-service
|
||||
|
||||
# Ingress 상태 확인
|
||||
kubectl get ingress kakao-review-api-ingress
|
||||
|
||||
# 로그 확인
|
||||
kubectl logs -l app=kakao-review-api -f
|
||||
```
|
||||
|
||||
### 4. 🌐 외부 브라우저에서 접속하기
|
||||
|
||||
#### Ingress 주소 확인 방법
|
||||
|
||||
```bash
|
||||
# 1. Ingress 설정된 호스트 확인
|
||||
kubectl get ingress kakao-review-api-ingress -o jsonpath='{.spec.rules[0].host}'
|
||||
|
||||
# 2. Ingress External IP 확인 (LoadBalancer 타입인 경우)
|
||||
kubectl get ingress kakao-review-api-ingress
|
||||
|
||||
# 3. Ingress Controller의 External IP 확인
|
||||
kubectl get svc -n ingress-nginx ingress-nginx-controller
|
||||
|
||||
# 4. 현재 설정된 ingress 주소 확인
|
||||
INGRESS_HOST=$(kubectl get ingress kakao-review-api-ingress -o jsonpath='{.spec.rules[0].host}')
|
||||
echo "🌐 Review API URL: http://${INGRESS_HOST}"
|
||||
```
|
||||
|
||||
#### 브라우저 접속 주소
|
||||
|
||||
현재 설정된 주소로 접속하세요:
|
||||
|
||||
```bash
|
||||
# 현재 설정된 기본 주소 (환경에 따라 다를 수 있음)
|
||||
INGRESS_URL="http://kakao-review-api.20.249.191.180.nip.io"
|
||||
echo "브라우저에서 접속: ${INGRESS_URL}"
|
||||
```
|
||||
|
||||
**주요 접속 페이지:**
|
||||
- **🏠 메인 페이지**: http://kakao-review-api.20.249.191.180.nip.io
|
||||
- **📖 Swagger UI**: http://kakao-review-api.20.249.191.180.nip.io/docs
|
||||
- **📄 ReDoc**: http://kakao-review-api.20.249.191.180.nip.io/redoc
|
||||
- **❤️ 헬스체크**: http://kakao-review-api.20.249.191.180.nip.io/health
|
||||
- **🔧 진단 정보**: http://kakao-review-api.20.249.191.180.nip.io/diagnostic
|
||||
- **⚠️ 법적 경고**: http://kakao-review-api.20.249.191.180.nip.io/legal-warning
|
||||
|
||||
#### 접속 테스트
|
||||
|
||||
```bash
|
||||
# API 접속 테스트
|
||||
curl "http://kakao-review-api.20.249.191.180.nip.io/health"
|
||||
|
||||
# 설정 정보 확인
|
||||
curl "http://kakao-review-api.20.249.191.180.nip.io/config"
|
||||
|
||||
# Swagger UI 접속 확인
|
||||
curl -I "http://kakao-review-api.20.249.191.180.nip.io/docs"
|
||||
|
||||
# 법적 경고 확인
|
||||
curl "http://kakao-review-api.20.249.191.180.nip.io/legal-warning"
|
||||
```
|
||||
|
||||
## ⚙️ 환경 설정
|
||||
|
||||
### 환경 변수
|
||||
|
||||
| 변수명 | 기본값 | 설명 |
|
||||
|--------|--------|------|
|
||||
| `APP_TITLE` | `카카오맵 리뷰 분석 API` | 애플리케이션 제목 |
|
||||
| `APP_VERSION` | `1.0.1` | 애플리케이션 버전 |
|
||||
| `HOST` | `0.0.0.0` | 서버 호스트 |
|
||||
| `PORT` | `19000` | 서버 포트 |
|
||||
| `DEFAULT_MAX_TIME` | `300` | 기본 최대 스크롤 시간(초) |
|
||||
| `DEFAULT_DAYS_LIMIT` | `60` | 기본 날짜 제한(일) |
|
||||
| `MAX_DAYS_LIMIT` | `365` | 최대 날짜 제한(일) |
|
||||
| `CHROME_OPTIONS` | `--headless...` | Chrome 브라우저 옵션 |
|
||||
| `LEGAL_WARNING_ENABLED` | `true` | 법적 경고 표시 여부 |
|
||||
| `CONTACT_EMAIL` | `admin@example.com` | 연락처 이메일 |
|
||||
|
||||
### Chrome 및 Selenium 설정
|
||||
|
||||
현재 서비스는 Chrome WebDriver를 사용합니다:
|
||||
|
||||
```bash
|
||||
# Chrome 설치 확인
|
||||
google-chrome --version
|
||||
|
||||
# ChromeDriver 설치 확인
|
||||
chromedriver --version
|
||||
|
||||
# Selenium 테스트
|
||||
python -c "from selenium import webdriver; print('Selenium 설치됨')"
|
||||
```
|
||||
|
||||
## 📊 API 엔드포인트
|
||||
|
||||
### 주요 엔드포인트
|
||||
|
||||
| Method | Endpoint | 설명 |
|
||||
|--------|----------|------|
|
||||
| `GET` | `/` | 메인 페이지 |
|
||||
| `GET` | `/docs` | Swagger UI 문서 |
|
||||
| `GET` | `/health` | 헬스체크 |
|
||||
| `GET` | `/diagnostic` | 시스템 진단 정보 |
|
||||
| `POST` | `/analyze` | 리뷰 분석 수행 (교육용) |
|
||||
| `GET` | `/config` | 환경 설정 확인 |
|
||||
| `GET` | `/legal-warning` | 법적 경고사항 |
|
||||
|
||||
### 리뷰 분석 API 예시 (교육용)
|
||||
|
||||
```json
|
||||
POST /analyze
|
||||
{
|
||||
"store_id": "501745730",
|
||||
"days_limit": 7,
|
||||
"max_time": 300
|
||||
}
|
||||
```
|
||||
|
||||
**응답:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "분석이 성공적으로 완료되었습니다.",
|
||||
"store_info": {
|
||||
"id": "501745730",
|
||||
"name": "맛있는 식당",
|
||||
"category": "한식",
|
||||
"rating": "4.2",
|
||||
"review_count": "1,234"
|
||||
},
|
||||
"reviews": [...],
|
||||
"analysis_date": "2024-06-12T10:30:00",
|
||||
"total_reviews": 25,
|
||||
"execution_time": 45.2
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 개발 및 확장
|
||||
|
||||
### 로컬 개발
|
||||
|
||||
```bash
|
||||
# 개발 모드로 실행 (자동 재시작)
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 19000 --reload
|
||||
|
||||
# 의존성 추가
|
||||
pip install 새패키지명
|
||||
pip freeze > app/requirements.txt
|
||||
|
||||
# 코드 포맷팅
|
||||
black app/main.py
|
||||
flake8 app/main.py
|
||||
```
|
||||
|
||||
### Ingress 호스트 변경
|
||||
|
||||
현재 환경에 맞게 Ingress 호스트를 변경하려면:
|
||||
|
||||
```bash
|
||||
# 1. 현재 External IP 확인
|
||||
kubectl get svc -n ingress-nginx ingress-nginx-controller
|
||||
|
||||
# 2. deployment/manifests/ingress.yaml 파일에서 host 수정
|
||||
# 예: kakao-review-api.{YOUR_EXTERNAL_IP}.nip.io
|
||||
|
||||
# 3. 변경사항 적용
|
||||
kubectl apply -f deployment/manifests/ingress.yaml
|
||||
|
||||
# 4. 새로운 주소 확인
|
||||
kubectl get ingress kakao-review-api-ingress
|
||||
```
|
||||
|
||||
## 🐛 문제 해결
|
||||
|
||||
### 일반적인 문제
|
||||
|
||||
**1. Chrome WebDriver 관련 문제**
|
||||
```bash
|
||||
# Chrome 설치 상태 확인
|
||||
docker run --rm kakao-review-api:latest google-chrome --version
|
||||
|
||||
# ChromeDriver 버전 확인
|
||||
docker run --rm kakao-review-api:latest chromedriver --version
|
||||
|
||||
# 헤드리스 모드 테스트
|
||||
curl -X POST "http://localhost:19000/analyze" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"store_id": "test", "days_limit": 1, "max_time": 30}'
|
||||
```
|
||||
|
||||
**2. Kubernetes 배포 실패**
|
||||
```bash
|
||||
# Pod 로그 확인 (Chrome 에러 확인)
|
||||
kubectl logs -l app=kakao-review-api
|
||||
|
||||
# ConfigMap 확인
|
||||
kubectl get configmap kakao-review-api-config -o yaml
|
||||
|
||||
# 리소스 사용량 확인 (Chrome이 메모리를 많이 사용함)
|
||||
kubectl top pods -l app=kakao-review-api
|
||||
```
|
||||
|
||||
**3. Ingress 접속 실패**
|
||||
```bash
|
||||
# Ingress Controller 상태 확인
|
||||
kubectl get pods -n ingress-nginx
|
||||
|
||||
# Ingress 규칙 확인
|
||||
kubectl describe ingress kakao-review-api-ingress
|
||||
|
||||
# Service 연결 확인
|
||||
kubectl get endpoints kakao-review-api-service
|
||||
|
||||
# 타임아웃 설정 확인 (Chrome 분석은 시간이 오래 걸림)
|
||||
kubectl get ingress kakao-review-api-ingress -o yaml | grep timeout
|
||||
```
|
||||
|
||||
**4. 포트 관련 문제**
|
||||
- 로컬 개발: 19000번 포트 사용
|
||||
- Docker 컨테이너: 19000번 포트로 실행
|
||||
- Kubernetes: 19000번 포트로 실행 (Service에서 80번으로 노출, Ingress를 통해 외부 접근)
|
||||
|
||||
**5. 법적 문제 회피**
|
||||
```bash
|
||||
# 법적 경고 항상 확인
|
||||
curl "http://kakao-review-api.20.249.191.180.nip.io/legal-warning"
|
||||
|
||||
# 교육용임을 명시하는 환경변수 설정
|
||||
export LEGAL_WARNING_ENABLED=true
|
||||
export EDUCATIONAL_USE_ONLY=true
|
||||
```
|
||||
|
||||
## 🎯 성능 최적화
|
||||
|
||||
### Chrome 최적화 설정
|
||||
|
||||
```yaml
|
||||
# deployment.yaml에서 Chrome 최적화를 위한 리소스 설정
|
||||
resources:
|
||||
requests:
|
||||
memory: "1Gi" # Chrome은 메모리를 많이 사용
|
||||
cpu: "500m"
|
||||
limits:
|
||||
memory: "2Gi" # Chrome 분석을 위한 충분한 메모리
|
||||
cpu: "1000m"
|
||||
```
|
||||
|
||||
### 타임아웃 설정
|
||||
|
||||
```yaml
|
||||
# ingress.yaml에서 긴 분석 시간을 고려한 타임아웃
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "1800" # 30분
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "1800" # 30분
|
||||
```
|
||||
|
||||
## 📈 향후 확장 계획
|
||||
|
||||
- [ ] **Claude AI 연동**: 리뷰 분석 및 개선 방안 자동 생성
|
||||
- [ ] **Vector DB 구축**: 유사 케이스 검색 및 템플릿 관리
|
||||
- [ ] **PostgreSQL 연동**: 분석 결과 영구 저장
|
||||
- [ ] **다중 플랫폼 지원**: 네이버, 구글 등 추가 플랫폼 (합법적 방법으로)
|
||||
- [ ] **실시간 분석**: WebSocket 기반 실시간 피드백
|
||||
- [ ] **사용자 인증**: JWT 기반 인증 시스템
|
||||
- [ ] **대시보드**: 분석 결과 시각화
|
||||
- [ ] **알림 시스템**: 새로운 리뷰 알림 기능
|
||||
- [ ] **감정 분석 고도화**: 더 정확한 감정 분류
|
||||
- [ ] **비즈니스 통찰**: AI 기반 개선 방안 제시
|
||||
|
||||
## ⚖️ 법적 준수 사항
|
||||
|
||||
### 필수 확인 사항
|
||||
|
||||
1. **교육용 목적으로만 사용**
|
||||
2. **실제 서비스 환경에서 사용 금지**
|
||||
3. **카카오 이용약관 준수**
|
||||
4. **개인정보보호법 준수**
|
||||
5. **저작권 침해 금지**
|
||||
|
||||
### 권장 대안
|
||||
|
||||
1. **카카오 공식 API 사용**
|
||||
2. **자체 플랫폼 개발**
|
||||
3. **사용자 동의 기반 수집**
|
||||
4. **정식 파트너십 체결**
|
||||
|
||||
---
|
||||
|
||||
## 📞 지원 및 문의
|
||||
|
||||
- **이슈 리포트**: GitHub Issues
|
||||
- **기술 문의**: 개발팀 Slack
|
||||
- **법적 문의**: 법무팀
|
||||
- **API 문서**: Swagger UI에서 상세 확인
|
||||
|
||||
---
|
||||
|
||||
**⚠️ 재고지: 이 서비스는 교육용으로만 제작되었으며, 실제 운영 환경에서의 사용을 엄격히 금지합니다.**
|
||||
+1626
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
fastapi==0.104.1
|
||||
uvicorn[standard]==0.24.0
|
||||
beautifulsoup4==4.12.2
|
||||
selenium==4.15.2
|
||||
webdriver-manager==4.0.1
|
||||
pydantic==2.5.0
|
||||
lxml==4.9.3
|
||||
python-multipart==0.0.6
|
||||
python-dotenv==1.0.0
|
||||
Executable
+217
@@ -0,0 +1,217 @@
|
||||
#!/bin/bash
|
||||
# build-base.sh - Base Image 빌드 스크립트
|
||||
|
||||
set -e
|
||||
|
||||
# 변수 설정
|
||||
BASE_IMAGE_NAME="kakao-review-api-base"
|
||||
BASE_IMAGE_TAG="${1:-latest}"
|
||||
ACR_NAME="${2:-acrdigitalgarage03}" # ACR 이름 (예: acrdigitalgarage01)
|
||||
RESOURCE_GROUP="${3:-rg-digitalgarage-03}" # 리소스 그룹
|
||||
|
||||
# ACR URL 자동 구성
|
||||
if [ -n "${ACR_NAME}" ]; then
|
||||
REGISTRY="${ACR_NAME}.azurecr.io"
|
||||
FULL_BASE_IMAGE_NAME="${REGISTRY}/${BASE_IMAGE_NAME}:${BASE_IMAGE_TAG}"
|
||||
else
|
||||
FULL_BASE_IMAGE_NAME="${BASE_IMAGE_NAME}:${BASE_IMAGE_TAG}"
|
||||
fi
|
||||
|
||||
# 고정된 Dockerfile 경로
|
||||
BASE_DOCKERFILE_PATH="deployment/container/Dockerfile-base"
|
||||
BUILD_CONTEXT="."
|
||||
|
||||
echo "====================================================="
|
||||
echo " 카카오맵 리뷰 분석 API Base Image 빌드"
|
||||
echo "====================================================="
|
||||
echo "Base 이미지명: ${FULL_BASE_IMAGE_NAME}"
|
||||
if [ -n "${ACR_NAME}" ]; then
|
||||
echo "ACR 이름: ${ACR_NAME}"
|
||||
echo "리소스 그룹: ${RESOURCE_GROUP}"
|
||||
fi
|
||||
echo "빌드 시작: $(date)"
|
||||
echo ""
|
||||
|
||||
# 사용법 표시 함수
|
||||
show_usage() {
|
||||
echo "사용법:"
|
||||
echo " $0 [BASE_IMAGE_TAG] [ACR_NAME] [RESOURCE_GROUP]"
|
||||
echo ""
|
||||
echo "파라미터:"
|
||||
echo " BASE_IMAGE_TAG: Base 이미지 태그 (기본값: latest)"
|
||||
echo " ACR_NAME : Azure Container Registry 이름"
|
||||
echo " RESOURCE_GROUP: Azure 리소스 그룹"
|
||||
echo ""
|
||||
echo "예시:"
|
||||
echo " $0 v1.0.0 # 로컬 빌드만"
|
||||
echo " $0 v1.0.0 acrdigitalgarage01 rg-digitalgarage-03 # ACR 빌드 + 푸시"
|
||||
echo ""
|
||||
echo "주의: Base Image는 자주 빌드할 필요가 없습니다."
|
||||
echo " Chrome 업데이트나 기본 패키지 변경 시에만 재빌드하세요."
|
||||
}
|
||||
|
||||
# ACR 로그인 함수
|
||||
acr_login() {
|
||||
local acr_name="$1"
|
||||
local resource_group="$2"
|
||||
|
||||
echo "🔐 Azure Container Registry 로그인 중..."
|
||||
|
||||
if ! command -v az &> /dev/null; then
|
||||
echo "❌ Azure CLI (az)가 설치되지 않았습니다."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "❌ jq가 설치되지 않았습니다."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! az account show &> /dev/null; then
|
||||
echo "❌ Azure에 로그인되지 않았습니다."
|
||||
echo "로그인 명령: az login"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local credential_json
|
||||
credential_json=$(az acr credential show --name "${acr_name}" --resource-group "${resource_group}" 2>/dev/null)
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "❌ ACR credential 조회 실패"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local username
|
||||
local password
|
||||
|
||||
username=$(echo "${credential_json}" | jq -r '.username')
|
||||
password=$(echo "${credential_json}" | jq -r '.passwords[0].value')
|
||||
|
||||
if [ -z "${username}" ] || [ -z "${password}" ] || [ "${username}" == "null" ] || [ "${password}" == "null" ]; then
|
||||
echo "❌ ACR credential 파싱 실패"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🔐 Docker 로그인 실행 중..."
|
||||
echo "${password}" | docker login "${REGISTRY}" -u "${username}" --password-stdin
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ ACR 로그인 성공!"
|
||||
return 0
|
||||
else
|
||||
echo "❌ ACR 로그인 실패"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 파라미터 검증
|
||||
if [ "$1" == "--help" ] || [ "$1" == "-h" ]; then
|
||||
show_usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -n "${ACR_NAME}" ] && [ -z "${RESOURCE_GROUP}" ]; then
|
||||
echo "❌ ACR_NAME이 제공된 경우 RESOURCE_GROUP도 필요합니다."
|
||||
echo ""
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 필수 파일 확인
|
||||
echo "📁 필수 파일 확인 중..."
|
||||
|
||||
if [ ! -f "${BASE_DOCKERFILE_PATH}" ]; then
|
||||
echo "❌ ${BASE_DOCKERFILE_PATH} 파일을 찾을 수 없습니다."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Base Dockerfile 확인 완료"
|
||||
echo "📄 Base Dockerfile: ${BASE_DOCKERFILE_PATH}"
|
||||
|
||||
# ACR 로그인 수행
|
||||
if [ -n "${ACR_NAME}" ] && [ -n "${RESOURCE_GROUP}" ]; then
|
||||
echo ""
|
||||
acr_login "${ACR_NAME}" "${RESOURCE_GROUP}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Docker 빌드
|
||||
echo "🔨 Base Image 빌드 시작... (시간이 다소 소요됩니다)"
|
||||
echo "명령어: docker build -t \"${FULL_BASE_IMAGE_NAME}\" -f \"${BASE_DOCKERFILE_PATH}\" \"${BUILD_CONTEXT}\""
|
||||
|
||||
docker build -t "${FULL_BASE_IMAGE_NAME}" -f "${BASE_DOCKERFILE_PATH}" "${BUILD_CONTEXT}"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Base Image 빌드 완료!"
|
||||
echo "이미지명: ${FULL_BASE_IMAGE_NAME}"
|
||||
|
||||
# 이미지 정보 표시
|
||||
echo ""
|
||||
echo "📊 Base Image 정보:"
|
||||
docker images "${FULL_BASE_IMAGE_NAME}" --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}"
|
||||
|
||||
# latest 태그 추가 생성
|
||||
if [ "${BASE_IMAGE_TAG}" != "latest" ] && [ -n "${REGISTRY}" ]; then
|
||||
echo ""
|
||||
echo "🏷️ latest 태그 생성 중..."
|
||||
docker tag "${FULL_BASE_IMAGE_NAME}" "${REGISTRY}/${BASE_IMAGE_NAME}:latest"
|
||||
echo "✅ latest 태그 생성 완료: ${REGISTRY}/${BASE_IMAGE_NAME}:latest"
|
||||
fi
|
||||
|
||||
# ACR 푸시
|
||||
if [ -n "${ACR_NAME}" ]; then
|
||||
echo ""
|
||||
echo "🚀 ACR에 Base Image 푸시 중..."
|
||||
|
||||
echo "📤 푸시 중: ${FULL_BASE_IMAGE_NAME}"
|
||||
docker push "${FULL_BASE_IMAGE_NAME}"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Base Image 푸시 성공"
|
||||
|
||||
if [ "${BASE_IMAGE_TAG}" != "latest" ]; then
|
||||
echo "📤 푸시 중: ${REGISTRY}/${BASE_IMAGE_NAME}:latest"
|
||||
docker push "${REGISTRY}/${BASE_IMAGE_NAME}:latest"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ latest 태그 푸시 성공"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "❌ Base Image 푸시 실패"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🎉 Base Image 빌드 완료!"
|
||||
echo ""
|
||||
echo "📋 완료된 작업:"
|
||||
echo " ✅ Base Image 빌드: ${FULL_BASE_IMAGE_NAME}"
|
||||
if [ "${BASE_IMAGE_TAG}" != "latest" ] && [ -n "${REGISTRY}" ]; then
|
||||
echo " ✅ latest 태그: ${REGISTRY}/${BASE_IMAGE_NAME}:latest"
|
||||
fi
|
||||
if [ -n "${ACR_NAME}" ]; then
|
||||
echo " ✅ ACR 푸시 완료"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🧪 테스트 명령어:"
|
||||
echo " docker run --rm ${FULL_BASE_IMAGE_NAME} google-chrome --version"
|
||||
|
||||
echo ""
|
||||
echo "📝 다음 단계:"
|
||||
echo " 이제 Service Image를 빌드하세요:"
|
||||
if [ -n "${ACR_NAME}" ]; then
|
||||
echo " ./scripts/build.sh v1.0.0 ${ACR_NAME} ${RESOURCE_GROUP}"
|
||||
else
|
||||
echo " ./scripts/build.sh v1.0.0"
|
||||
fi
|
||||
|
||||
else
|
||||
echo "❌ Base Image 빌드 실패!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🏁 Base Image 빌드 프로세스 완료 - $(date)"
|
||||
Executable
+257
@@ -0,0 +1,257 @@
|
||||
#!/bin/bash
|
||||
# build.sh - Service Image 빌드 스크립트 (Base Image 활용)
|
||||
|
||||
set -e
|
||||
|
||||
# 변수 설정
|
||||
IMAGE_NAME="kakao-review-api"
|
||||
IMAGE_TAG="${1:-latest}"
|
||||
ACR_NAME="${2:-acrdigitalgarage03}" # ACR 이름 (예: acrdigitalgarage01)
|
||||
RESOURCE_GROUP="${3:-rg-digitalgarage-03}" # 리소스 그룹
|
||||
BASE_IMAGE_TAG="${4:-latest}" # Base Image 태그
|
||||
|
||||
# ACR URL 자동 구성
|
||||
if [ -n "${ACR_NAME}" ]; then
|
||||
REGISTRY="${ACR_NAME}.azurecr.io"
|
||||
FULL_IMAGE_NAME="${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
|
||||
BASE_IMAGE="${REGISTRY}/kakao-review-api-base:${BASE_IMAGE_TAG}"
|
||||
else
|
||||
FULL_IMAGE_NAME="${IMAGE_NAME}:${IMAGE_TAG}"
|
||||
BASE_IMAGE="kakao-review-api-base:${BASE_IMAGE_TAG}"
|
||||
fi
|
||||
|
||||
# 고정된 Dockerfile 경로
|
||||
DOCKERFILE_PATH="deployment/container/Dockerfile"
|
||||
BUILD_CONTEXT="."
|
||||
|
||||
echo "====================================================="
|
||||
echo " 카카오맵 리뷰 분석 API Service Image 빌드"
|
||||
echo "====================================================="
|
||||
echo "Service 이미지명: ${FULL_IMAGE_NAME}"
|
||||
echo "Base 이미지: ${BASE_IMAGE}"
|
||||
if [ -n "${ACR_NAME}" ]; then
|
||||
echo "ACR 이름: ${ACR_NAME}"
|
||||
echo "리소스 그룹: ${RESOURCE_GROUP}"
|
||||
fi
|
||||
echo "빌드 시작: $(date)"
|
||||
echo ""
|
||||
|
||||
# 사용법 표시 함수
|
||||
show_usage() {
|
||||
echo "사용법:"
|
||||
echo " $0 [IMAGE_TAG] [ACR_NAME] [RESOURCE_GROUP] [BASE_IMAGE_TAG]"
|
||||
echo ""
|
||||
echo "파라미터:"
|
||||
echo " IMAGE_TAG : Service 이미지 태그 (기본값: latest)"
|
||||
echo " ACR_NAME : Azure Container Registry 이름"
|
||||
echo " RESOURCE_GROUP: Azure 리소스 그룹"
|
||||
echo " BASE_IMAGE_TAG: Base 이미지 태그 (기본값: latest)"
|
||||
echo ""
|
||||
echo "예시:"
|
||||
echo " $0 v1.0.0 # 로컬 빌드만"
|
||||
echo " $0 v1.0.0 acrdigitalgarage01 rg-digitalgarage-03 # ACR 빌드 + 푸시"
|
||||
echo " $0 v1.0.0 acrdigitalgarage01 rg-digitalgarage-03 v2.0.0 # 특정 Base Image 사용"
|
||||
echo ""
|
||||
echo "전제조건:"
|
||||
echo " Base Image가 먼저 빌드되어 있어야 합니다:"
|
||||
echo " ./scripts/build-base.sh ${BASE_IMAGE_TAG} [ACR_NAME] [RESOURCE_GROUP]"
|
||||
}
|
||||
|
||||
# ACR 로그인 함수
|
||||
acr_login() {
|
||||
local acr_name="$1"
|
||||
local resource_group="$2"
|
||||
|
||||
echo "🔐 Azure Container Registry 로그인 중..."
|
||||
|
||||
if ! command -v az &> /dev/null; then
|
||||
echo "❌ Azure CLI (az)가 설치되지 않았습니다."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "❌ jq가 설치되지 않았습니다."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! az account show &> /dev/null; then
|
||||
echo "❌ Azure에 로그인되지 않았습니다."
|
||||
echo "로그인 명령: az login"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local credential_json
|
||||
credential_json=$(az acr credential show --name "${acr_name}" --resource-group "${resource_group}" 2>/dev/null)
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "❌ ACR credential 조회 실패"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local username
|
||||
local password
|
||||
|
||||
username=$(echo "${credential_json}" | jq -r '.username')
|
||||
password=$(echo "${credential_json}" | jq -r '.passwords[0].value')
|
||||
|
||||
if [ -z "${username}" ] || [ -z "${password}" ] || [ "${username}" == "null" ] || [ "${password}" == "null" ]; then
|
||||
echo "❌ ACR credential 파싱 실패"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🔐 Docker 로그인 실행 중..."
|
||||
echo "${password}" | docker login "${REGISTRY}" -u "${username}" --password-stdin
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ ACR 로그인 성공!"
|
||||
return 0
|
||||
else
|
||||
echo "❌ ACR 로그인 실패"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 파라미터 검증
|
||||
if [ "$1" == "--help" ] || [ "$1" == "-h" ]; then
|
||||
show_usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -n "${ACR_NAME}" ] && [ -z "${RESOURCE_GROUP}" ]; then
|
||||
echo "❌ ACR_NAME이 제공된 경우 RESOURCE_GROUP도 필요합니다."
|
||||
echo ""
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 법적 경고
|
||||
echo "⚠️ 중요 법적 경고사항 ⚠️"
|
||||
echo "이 이미지는 교육 목적으로만 사용하세요!"
|
||||
echo "실제 서비스 사용 시 법적 책임을 질 수 있습니다."
|
||||
echo ""
|
||||
|
||||
# 필수 파일 확인
|
||||
echo "📁 필수 파일 확인 중..."
|
||||
|
||||
if [ ! -f "app/main.py" ]; then
|
||||
echo "❌ app/main.py 파일을 찾을 수 없습니다."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "app/requirements.txt" ]; then
|
||||
echo "❌ app/requirements.txt 파일을 찾을 수 없습니다."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${DOCKERFILE_PATH}" ]; then
|
||||
echo "❌ ${DOCKERFILE_PATH} 파일을 찾을 수 없습니다."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ 모든 필수 파일이 확인되었습니다."
|
||||
echo "📄 Dockerfile: ${DOCKERFILE_PATH}"
|
||||
echo "🏗️ 빌드 컨텍스트: ${BUILD_CONTEXT}"
|
||||
|
||||
# Base Image 존재 확인
|
||||
echo ""
|
||||
echo "🔍 Base Image 확인 중..."
|
||||
if docker image inspect "${BASE_IMAGE}" >/dev/null 2>&1; then
|
||||
echo "✅ Base Image 확인됨: ${BASE_IMAGE}"
|
||||
else
|
||||
echo "❌ Base Image를 찾을 수 없습니다: ${BASE_IMAGE}"
|
||||
echo ""
|
||||
echo "Base Image를 먼저 빌드하세요:"
|
||||
if [ -n "${ACR_NAME}" ]; then
|
||||
echo " ./scripts/build-base.sh ${BASE_IMAGE_TAG} ${ACR_NAME} ${RESOURCE_GROUP}"
|
||||
else
|
||||
echo " ./scripts/build-base.sh ${BASE_IMAGE_TAG}"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ACR 로그인 수행
|
||||
if [ -n "${ACR_NAME}" ] && [ -n "${RESOURCE_GROUP}" ]; then
|
||||
echo ""
|
||||
acr_login "${ACR_NAME}" "${RESOURCE_GROUP}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Docker 빌드
|
||||
echo "🔨 Service Image 빌드 시작... (빠른 빌드 예상)"
|
||||
echo "명령어: docker build --build-arg BASE_IMAGE=\"${BASE_IMAGE}\" -t \"${FULL_IMAGE_NAME}\" -f \"${DOCKERFILE_PATH}\" \"${BUILD_CONTEXT}\""
|
||||
|
||||
docker build --build-arg BASE_IMAGE="${BASE_IMAGE}" -t "${FULL_IMAGE_NAME}" -f "${DOCKERFILE_PATH}" "${BUILD_CONTEXT}"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Service Image 빌드 완료!"
|
||||
echo "이미지명: ${FULL_IMAGE_NAME}"
|
||||
|
||||
# 이미지 정보 표시
|
||||
echo ""
|
||||
echo "📊 Service Image 정보:"
|
||||
docker images "${FULL_IMAGE_NAME}" --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}"
|
||||
|
||||
# latest 태그 추가 생성
|
||||
if [ "${IMAGE_TAG}" != "latest" ] && [ -n "${REGISTRY}" ]; then
|
||||
echo ""
|
||||
echo "🏷️ latest 태그 생성 중..."
|
||||
docker tag "${FULL_IMAGE_NAME}" "${REGISTRY}/${IMAGE_NAME}:latest"
|
||||
echo "✅ latest 태그 생성 완료: ${REGISTRY}/${IMAGE_NAME}:latest"
|
||||
fi
|
||||
|
||||
# ACR 푸시
|
||||
if [ -n "${ACR_NAME}" ]; then
|
||||
echo ""
|
||||
echo "🚀 ACR에 Service Image 푸시 중..."
|
||||
|
||||
echo "📤 푸시 중: ${FULL_IMAGE_NAME}"
|
||||
docker push "${FULL_IMAGE_NAME}"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Service Image 푸시 성공"
|
||||
|
||||
if [ "${IMAGE_TAG}" != "latest" ]; then
|
||||
echo "📤 푸시 중: ${REGISTRY}/${IMAGE_NAME}:latest"
|
||||
docker push "${REGISTRY}/${IMAGE_NAME}:latest"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ latest 태그 푸시 성공"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "❌ Service Image 푸시 실패"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🎉 Service Image 빌드 완료!"
|
||||
echo ""
|
||||
echo "📋 완료된 작업:"
|
||||
echo " ✅ Service Image 빌드: ${FULL_IMAGE_NAME}"
|
||||
echo " ✅ 사용된 Base Image: ${BASE_IMAGE}"
|
||||
if [ "${IMAGE_TAG}" != "latest" ] && [ -n "${REGISTRY}" ]; then
|
||||
echo " ✅ latest 태그: ${REGISTRY}/${IMAGE_NAME}:latest"
|
||||
fi
|
||||
if [ -n "${ACR_NAME}" ]; then
|
||||
echo " ✅ ACR 푸시 완료"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🧪 테스트 명령어:"
|
||||
echo " docker run -p 8000:8000 ${FULL_IMAGE_NAME}"
|
||||
echo " curl http://localhost:8000/health"
|
||||
|
||||
if [ -n "${ACR_NAME}" ]; then
|
||||
echo ""
|
||||
echo "🔍 ACR 이미지 확인:"
|
||||
echo " az acr repository show-tags --name ${ACR_NAME} --repository ${IMAGE_NAME}"
|
||||
fi
|
||||
|
||||
else
|
||||
echo "❌ Service Image 빌드 실패!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🏁 Service Image 빌드 프로세스 완료 - $(date)"
|
||||
Executable
+103
@@ -0,0 +1,103 @@
|
||||
#!/bin/bash
|
||||
# create-image-pull-secret.sh - ACR Image Pull Secret 생성 스크립트
|
||||
|
||||
set -e
|
||||
|
||||
# 변수 설정
|
||||
ACR_NAME="${1:-acrdigitalgarage03}"
|
||||
RESOURCE_GROUP="${2:-rg-digitalgarage-03}"
|
||||
SECRET_NAME="${3:-acr-secret}"
|
||||
|
||||
echo "====================================================="
|
||||
echo " ACR Image Pull Secret 생성"
|
||||
echo "====================================================="
|
||||
|
||||
# 사용법 표시 함수
|
||||
show_usage() {
|
||||
echo "사용법:"
|
||||
echo " $0 [ACR_NAME] [RESOURCE_GROUP] [SECRET_NAME]"
|
||||
echo ""
|
||||
echo "파라미터:"
|
||||
echo " ACR_NAME : Azure Container Registry 이름 (필수)"
|
||||
echo " RESOURCE_GROUP: Azure 리소스 그룹 (필수)"
|
||||
echo " SECRET_NAME : Secret 이름 (기본값: acr-secret)"
|
||||
echo ""
|
||||
echo "예시:"
|
||||
echo " $0 acrdigitalgarage01 rg-digitalgarage-03"
|
||||
echo " $0 acrdigitalgarage01 rg-digitalgarage-03 acr-prod-secret"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 파라미터 검증
|
||||
if [ "$1" == "--help" ] || [ "$1" == "-h" ]; then
|
||||
show_usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z "${ACR_NAME}" ] || [ -z "${RESOURCE_GROUP}" ]; then
|
||||
echo "❌ ACR_NAME과 RESOURCE_GROUP는 필수 파라미터입니다."
|
||||
echo ""
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ACR 정보 설정
|
||||
REGISTRY_URL="${ACR_NAME}.azurecr.io"
|
||||
|
||||
echo ""
|
||||
echo "📋 ACR 정보:"
|
||||
echo " ACR 이름: ${ACR_NAME}"
|
||||
echo " 레지스트리 URL: ${REGISTRY_URL}"
|
||||
echo " 리소스 그룹: ${RESOURCE_GROUP}"
|
||||
echo " Secret 이름: ${SECRET_NAME}"
|
||||
|
||||
# ACR credential 조회
|
||||
echo ""
|
||||
echo "🔑 ACR credential 조회 중..."
|
||||
|
||||
credential_json=$(az acr credential show --name "${ACR_NAME}" --resource-group "${RESOURCE_GROUP}" 2>/dev/null)
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "❌ ACR credential 조회 실패"
|
||||
echo "확인 사항:"
|
||||
echo " - ACR 이름: ${ACR_NAME}"
|
||||
echo " - 리소스 그룹: ${RESOURCE_GROUP}"
|
||||
echo " - Azure 권한"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# JSON에서 username과 password 추출
|
||||
username=$(echo "${credential_json}" | jq -r '.username')
|
||||
password=$(echo "${credential_json}" | jq -r '.passwords[0].value')
|
||||
|
||||
if [ -z "${username}" ] || [ -z "${password}" ] || [ "${username}" == "null" ] || [ "${password}" == "null" ]; then
|
||||
echo "❌ ACR credential 파싱 실패"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ ACR credential 조회 성공"
|
||||
echo " 사용자명: ${username}"
|
||||
echo " 비밀번호: ${password:0:10}..."
|
||||
|
||||
# 기존 Secret 삭제 (있는 경우)
|
||||
if kubectl get secret "${SECRET_NAME}" &> /dev/null; then
|
||||
echo ""
|
||||
echo "🗑️ 기존 Secret 삭제 중..."
|
||||
kubectl delete secret "${SECRET_NAME}"
|
||||
fi
|
||||
|
||||
# Image Pull Secret 생성
|
||||
echo ""
|
||||
echo "🔐 Image Pull Secret 생성 중..."
|
||||
|
||||
kubectl create secret docker-registry "${SECRET_NAME}" \
|
||||
--docker-server="${REGISTRY_URL}" \
|
||||
--docker-username="${username}" \
|
||||
--docker-password="${password}"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Image Pull Secret 생성 성공!"
|
||||
else
|
||||
echo "❌ Image Pull Secret 생성 실패"
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,72 @@
|
||||
# deployment/container/Dockerfile
|
||||
# Service Image - Base Image 위에 애플리케이션만 추가 (AKS 최적화)
|
||||
ARG BASE_IMAGE=kakao-review-api-base:latest
|
||||
FROM ${BASE_IMAGE}
|
||||
|
||||
# 메타데이터
|
||||
LABEL maintainer="admin@example.com"
|
||||
LABEL version="1.0.1"
|
||||
LABEL description="카카오맵 리뷰 분석 API - Service Image (AKS 최적화)"
|
||||
|
||||
# root로 전환 (패키지 설치용)
|
||||
USER root
|
||||
|
||||
# 🔧 Chrome 실행을 위한 환경 변수 설정 (VM 환경과 동일)
|
||||
ENV HOME=/home/appuser \
|
||||
WDM_LOCAL=1 \
|
||||
WDM_LOG_LEVEL=0 \
|
||||
CHROME_BIN=/usr/bin/google-chrome \
|
||||
CHROMEDRIVER_BIN=/usr/local/bin/chromedriver \
|
||||
DISPLAY=:99 \
|
||||
DBUS_SESSION_BUS_ADDRESS=/dev/null
|
||||
|
||||
# Python 의존성 파일 복사 및 설치
|
||||
COPY app/requirements.txt /app/
|
||||
RUN pip install --no-cache-dir -r /app/requirements.txt
|
||||
|
||||
# 애플리케이션 소스 복사
|
||||
COPY app/main.py /app/
|
||||
|
||||
# 🔧 Chrome 실행을 위한 디렉토리 및 권한 설정 (VM 환경과 동일)
|
||||
RUN mkdir -p /home/appuser/.cache/selenium \
|
||||
&& mkdir -p /home/appuser/.wdm \
|
||||
&& mkdir -p /home/appuser/.local/share \
|
||||
&& mkdir -p /tmp/chrome-user-data \
|
||||
&& mkdir -p /tmp/.wdm \
|
||||
&& chown -R appuser:appuser /home/appuser \
|
||||
&& chown -R appuser:appuser /tmp/chrome-user-data \
|
||||
&& chown -R appuser:appuser /tmp/.wdm \
|
||||
&& chmod -R 755 /home/appuser \
|
||||
&& chmod -R 777 /tmp/chrome-user-data \
|
||||
&& chmod -R 777 /tmp/.wdm
|
||||
|
||||
# /app 디렉토리 권한 설정
|
||||
RUN chown -R appuser:appuser /app
|
||||
|
||||
# 🔧 ChromeDriver 접근 권한 확인 및 테스트
|
||||
RUN echo "=== ChromeDriver 정보 ===" \
|
||||
&& ls -la /usr/local/bin/chromedriver \
|
||||
&& chromedriver --version \
|
||||
&& echo "=== Chrome 정보 ===" \
|
||||
&& google-chrome --version \
|
||||
&& echo "=== 권한 테스트 ===" \
|
||||
&& su - appuser -c "chromedriver --version" || echo "appuser ChromeDriver 접근 확인"
|
||||
|
||||
# 🔧 간단한 Selenium import 테스트만 수행
|
||||
RUN python3 -c "from selenium import webdriver; from selenium.webdriver.chrome.options import Options; from selenium.webdriver.chrome.service import Service; print('✅ Selenium 모듈 import 성공')"
|
||||
|
||||
# 🔧 컨테이너 내 사용자를 appuser로 변경 (하지만 Chrome은 root 권한 필요)
|
||||
USER appuser
|
||||
|
||||
# 작업 디렉토리 설정
|
||||
WORKDIR /app
|
||||
|
||||
# 포트 노출 (review 서비스용)
|
||||
EXPOSE 19000
|
||||
|
||||
# 🔧 헬스체크 (개선된 타임아웃)
|
||||
HEALTHCHECK --interval=30s --timeout=15s --start-period=60s --retries=3 \
|
||||
CMD curl -f http://localhost:19000/health || exit 1
|
||||
|
||||
# 🔧 애플리케이션 실행 (로그 레벨 설정)
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "19000", "--log-level", "info"]
|
||||
@@ -0,0 +1,103 @@
|
||||
# deployment/container/Dockerfile-base
|
||||
FROM python:3.11-slim
|
||||
|
||||
# 메타데이터
|
||||
LABEL maintainer="admin@example.com"
|
||||
LABEL description="카카오맵 리뷰 분석 API - Base Image with Chrome (AKS 최적화)"
|
||||
LABEL version="base-1.0.2"
|
||||
|
||||
# 환경 변수 설정
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
CHROME_BIN=/usr/bin/google-chrome \
|
||||
CHROMEDRIVER_BIN=/usr/local/bin/chromedriver
|
||||
|
||||
# 🔧 필수 패키지 설치 (VM setup.sh와 동일한 패키지들)
|
||||
RUN apt-get update && apt-get install -y \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
unzip \
|
||||
wget \
|
||||
curl \
|
||||
ca-certificates \
|
||||
fonts-liberation \
|
||||
libasound2 \
|
||||
libatk-bridge2.0-0 \
|
||||
libdrm2 \
|
||||
libxcomposite1 \
|
||||
libxdamage1 \
|
||||
libxrandr2 \
|
||||
libgbm1 \
|
||||
libxss1 \
|
||||
libnss3 \
|
||||
libxext6 \
|
||||
libxfixes3 \
|
||||
libxi6 \
|
||||
libxrender1 \
|
||||
libcairo-gobject2 \
|
||||
libgtk-3-0 \
|
||||
libgdk-pixbuf2.0-0 \
|
||||
libgtk-3-dev \
|
||||
libgconf-2-4 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 🔧 Google Chrome 설치 (VM setup.sh와 동일한 방식)
|
||||
RUN wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \
|
||||
&& echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y google-chrome-stable \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Chrome 설치 확인
|
||||
RUN google-chrome --version
|
||||
|
||||
# 🔧 ChromeDriver 설치 (VM setup.sh와 동일한 방식)
|
||||
RUN CHROME_VERSION=$(google-chrome --version | grep -oP '\d+\.\d+\.\d+\.\d+' | cut -d'.' -f1) \
|
||||
&& echo "Chrome 주 버전: $CHROME_VERSION" \
|
||||
&& CHROMEDRIVER_VERSION=$(curl -s "https://googlechromelabs.github.io/chrome-for-testing/LATEST_RELEASE_$CHROME_VERSION") \
|
||||
&& echo "ChromeDriver 버전: $CHROMEDRIVER_VERSION" \
|
||||
&& wget -q "https://storage.googleapis.com/chrome-for-testing-public/$CHROMEDRIVER_VERSION/linux64/chromedriver-linux64.zip" -O /tmp/chromedriver.zip \
|
||||
&& unzip -q /tmp/chromedriver.zip -d /tmp/ \
|
||||
&& mv /tmp/chromedriver-linux64/chromedriver /usr/local/bin/chromedriver \
|
||||
&& chmod +x /usr/local/bin/chromedriver \
|
||||
&& rm -rf /tmp/chromedriver* \
|
||||
&& chromedriver --version
|
||||
|
||||
# Chrome 및 ChromeDriver 최종 확인
|
||||
RUN echo "=== Chrome 정보 ===" \
|
||||
&& google-chrome --version \
|
||||
&& echo "=== ChromeDriver 정보 ===" \
|
||||
&& chromedriver --version \
|
||||
&& echo "=== 설치 경로 확인 ===" \
|
||||
&& ls -la /usr/bin/google-chrome* \
|
||||
&& ls -la /usr/local/bin/chromedriver
|
||||
|
||||
# 🔧 Chrome 실행을 위한 디렉토리 생성
|
||||
RUN mkdir -p /tmp/chrome-user-data \
|
||||
&& mkdir -p /home/appuser/.cache/selenium \
|
||||
&& mkdir -p /home/appuser/.wdm \
|
||||
&& mkdir -p /home/appuser/.local/share \
|
||||
&& chmod 777 /tmp/chrome-user-data
|
||||
|
||||
# 🔧 비root 사용자 생성 (하지만 Chrome은 root로 실행)
|
||||
RUN groupadd -r appuser && useradd -r -g appuser -d /home/appuser -s /bin/bash appuser \
|
||||
&& mkdir -p /home/appuser \
|
||||
&& chown -R appuser:appuser /home/appuser
|
||||
|
||||
# 작업 디렉토리 생성
|
||||
WORKDIR /app
|
||||
RUN chown appuser:appuser /app
|
||||
|
||||
# pip 업그레이드
|
||||
RUN pip install --no-cache-dir --upgrade pip
|
||||
|
||||
# Chrome 실행 테스트 (빌드 시 검증)
|
||||
RUN google-chrome --headless --no-sandbox --disable-dev-shm-usage --virtual-time-budget=1000 --run-all-compositor-stages-before-draw data:text/html,\<html\>\<body\>\<h1\>Test\</h1\>\</body\>\</html\> || echo "Chrome 테스트 완료"
|
||||
|
||||
# 포트 노출
|
||||
EXPOSE 8000
|
||||
|
||||
# 기본 명령어 (오버라이드 가능)
|
||||
CMD ["python", "--version"]
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# deployment/manifests/configmap.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: kakao-review-api-config
|
||||
data:
|
||||
# 애플리케이션 설정
|
||||
APP_TITLE: "카카오맵 리뷰 분석 API"
|
||||
APP_VERSION: "1.0.1"
|
||||
APP_DESCRIPTION: "교육 목적 전용 - 실제 서비스 사용 금지"
|
||||
|
||||
# 서버 설정
|
||||
HOST: "0.0.0.0"
|
||||
PORT: "8000"
|
||||
WORKERS: "1"
|
||||
LOG_LEVEL: "info"
|
||||
|
||||
# API 기본값 설정 (AKS 환경에 최적화)
|
||||
DEFAULT_MAX_TIME: "300"
|
||||
DEFAULT_DAYS_LIMIT: "60"
|
||||
MAX_DAYS_LIMIT: "365"
|
||||
MIN_MAX_TIME: "60"
|
||||
MAX_MAX_TIME: "600"
|
||||
|
||||
# 🔧 Chrome 브라우저 설정 (AKS 환경 최적화)
|
||||
CHROME_OPTIONS: |
|
||||
--headless=new
|
||||
--no-sandbox
|
||||
--disable-dev-shm-usage
|
||||
--disable-gpu
|
||||
--disable-software-rasterizer
|
||||
--window-size=1920,1080
|
||||
--disable-extensions
|
||||
--disable-plugins
|
||||
--disable-usb-keyboard-detect
|
||||
--no-first-run
|
||||
--no-default-browser-check
|
||||
--disable-logging
|
||||
--log-level=3
|
||||
--disable-background-timer-throttling
|
||||
--disable-backgrounding-occluded-windows
|
||||
--disable-renderer-backgrounding
|
||||
--disable-features=TranslateUI,VizDisplayCompositor
|
||||
--disable-ipc-flooding-protection
|
||||
--memory-pressure-off
|
||||
--max_old_space_size=4096
|
||||
--no-zygote
|
||||
--disable-setuid-sandbox
|
||||
--disable-background-networking
|
||||
--disable-default-apps
|
||||
--disable-sync
|
||||
--metrics-recording-only
|
||||
--safebrowsing-disable-auto-update
|
||||
--disable-prompt-on-repost
|
||||
--disable-hang-monitor
|
||||
--disable-client-side-phishing-detection
|
||||
--disable-component-update
|
||||
--disable-domain-reliability
|
||||
--user-data-dir=/tmp/chrome-user-data
|
||||
--data-path=/tmp/chrome-user-data
|
||||
--disk-cache-dir=/tmp/chrome-cache
|
||||
--aggressive-cache-discard
|
||||
--disable-web-security
|
||||
--allow-running-insecure-content
|
||||
--disable-blink-features=AutomationControlled
|
||||
|
||||
# 스크롤링 설정 (AKS 환경에 맞게 조정)
|
||||
SCROLL_CHECK_INTERVAL: "5"
|
||||
SCROLL_NO_CHANGE_LIMIT: "6"
|
||||
SCROLL_WAIT_TIME_SHORT: "2.0"
|
||||
SCROLL_WAIT_TIME_LONG: "3.0"
|
||||
|
||||
# 법적 경고 메시지
|
||||
LEGAL_WARNING_ENABLED: "true"
|
||||
CONTACT_EMAIL: "admin@example.com"
|
||||
|
||||
# 건강 체크 설정
|
||||
HEALTH_CHECK_TIMEOUT: "10"
|
||||
@@ -0,0 +1,130 @@
|
||||
# deployment/manifests/deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: kakao-review-api
|
||||
labels:
|
||||
app: kakao-review-api
|
||||
version: v1
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: kakao-review-api
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: kakao-review-api
|
||||
version: v1
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: acr-secret
|
||||
containers:
|
||||
- name: api
|
||||
image: acrdigitalgarage03.azurecr.io/kakao-review-api:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 19000
|
||||
name: http
|
||||
|
||||
# 🔧 ConfigMap 환경 변수
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: kakao-review-api-config
|
||||
|
||||
# 🔧 Secret 환경 변수
|
||||
env:
|
||||
- name: EXTERNAL_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: kakao-review-api-secret
|
||||
key: EXTERNAL_API_KEY
|
||||
- name: DB_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: kakao-review-api-secret
|
||||
key: DB_USERNAME
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: kakao-review-api-secret
|
||||
key: DB_PASSWORD
|
||||
- name: JWT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: kakao-review-api-secret
|
||||
key: JWT_SECRET
|
||||
# 🔧 Chrome/ChromeDriver 환경 변수 (VM과 동일)
|
||||
- name: WDM_LOCAL
|
||||
value: "/tmp/.wdm"
|
||||
- name: WDM_LOG_LEVEL
|
||||
value: "0"
|
||||
- name: CHROME_BIN
|
||||
value: "/usr/bin/google-chrome"
|
||||
- name: CHROMEDRIVER_BIN
|
||||
value: "/usr/local/bin/chromedriver"
|
||||
- name: DISPLAY
|
||||
value: ":99"
|
||||
- name: DBUS_SESSION_BUS_ADDRESS
|
||||
value: "/dev/null"
|
||||
|
||||
# 🔧 리소스 제한 (Chrome 실행에 충분한 리소스)
|
||||
resources:
|
||||
requests:
|
||||
memory: "2Gi"
|
||||
cpu: "1000m"
|
||||
limits:
|
||||
memory: "4Gi"
|
||||
cpu: "2000m"
|
||||
|
||||
# 🔧 헬스 체크 (타임아웃 증가)
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 19000
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 15
|
||||
failureThreshold: 5
|
||||
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 19000
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 10
|
||||
failureThreshold: 5
|
||||
|
||||
# 🔧 간소화된 보안 컨텍스트 (AKS 호환)
|
||||
securityContext:
|
||||
runAsNonRoot: false
|
||||
runAsUser: 0
|
||||
allowPrivilegeEscalation: true
|
||||
readOnlyRootFilesystem: false
|
||||
capabilities:
|
||||
add:
|
||||
- SYS_ADMIN
|
||||
drop: []
|
||||
|
||||
# 🔧 볼륨 마운트 (Chrome 실행 최적화)
|
||||
volumeMounts:
|
||||
- name: tmp-volume
|
||||
mountPath: /tmp
|
||||
- name: dev-shm
|
||||
mountPath: /dev/shm
|
||||
|
||||
# 🔧 볼륨 정의 (간소화)
|
||||
volumes:
|
||||
- name: tmp-volume
|
||||
emptyDir: {}
|
||||
- name: dev-shm
|
||||
emptyDir:
|
||||
medium: Memory
|
||||
sizeLimit: 2Gi
|
||||
|
||||
restartPolicy: Always
|
||||
|
||||
# 🔧 Pod 레벨 보안 설정 제거 (AKS 호환을 위해)
|
||||
# securityContext: 제거
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# deployment/manifests/ingress.yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: kakao-review-api-ingress
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
|
||||
# 🔧 타임아웃 설정 (Chrome 분석 시간 고려)
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "1800"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "1800"
|
||||
nginx.ingress.kubernetes.io/client-body-timeout: "1800"
|
||||
nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
|
||||
# 🔧 CORS 설정 (필요시)
|
||||
nginx.ingress.kubernetes.io/enable-cors: "true"
|
||||
nginx.ingress.kubernetes.io/cors-allow-origin: "*"
|
||||
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, OPTIONS"
|
||||
nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
# 🔧 환경에 맞게 호스트명 수정 필요
|
||||
- host: kakao-review-api.20.249.191.180.nip.io
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: kakao-review-api-service
|
||||
port:
|
||||
number: 80
|
||||
# 🔧 TLS 설정 (HTTPS 필요시 주석 해제)
|
||||
# tls:
|
||||
# - hosts:
|
||||
# - kakao-review-api.example.com
|
||||
# secretName: kakao-review-api-tls
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# deployment/manifests/secret.yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: kakao-review-api-secret
|
||||
type: Opaque
|
||||
data:
|
||||
# 🔧 현재 사용하지 않지만 향후 확장을 위한 플레이스홀더
|
||||
# 실제 값 설정 시: echo -n "your-value" | base64
|
||||
|
||||
# API 키 (향후 카카오 공식 API 연동용)
|
||||
EXTERNAL_API_KEY: ""
|
||||
|
||||
# 데이터베이스 연결 정보 (향후 DB 연동용)
|
||||
DB_USERNAME: ""
|
||||
DB_PASSWORD: ""
|
||||
DB_HOST: ""
|
||||
|
||||
# JWT 시크릿 (향후 인증 기능용)
|
||||
JWT_SECRET: ""
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# deployment/manifests/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: kakao-review-api-service
|
||||
labels:
|
||||
app: kakao-review-api
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 19000
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: kakao-review-api
|
||||
|
||||
Executable
+280
@@ -0,0 +1,280 @@
|
||||
#!/bin/bash
|
||||
# setup.sh - Review Analysis API 환경 설정 스크립트
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔍 Review Analysis API 환경 설정 시작..."
|
||||
|
||||
# 시스템 정보 확인
|
||||
echo "📊 시스템 정보:"
|
||||
echo " - OS: $(lsb_release -d | cut -f2)"
|
||||
echo " - Python: $(python3 --version 2>/dev/null || echo 'Python3 미설치')"
|
||||
echo " - pip: $(pip3 --version 2>/dev/null || echo 'pip3 미설치')"
|
||||
|
||||
# 필수 패키지 설치
|
||||
echo ""
|
||||
echo "📦 필수 패키지 설치 중..."
|
||||
sudo apt update
|
||||
sudo apt install -y python3-pip python3-venv unzip wget curl jq
|
||||
|
||||
# Chrome 브라우저 설치 (리뷰 분석용)
|
||||
echo ""
|
||||
echo "🌐 Chrome 브라우저 설치 확인 중..."
|
||||
if ! command -v google-chrome &> /dev/null; then
|
||||
echo "Chrome 브라우저 설치 중..."
|
||||
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
|
||||
echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee /etc/apt/sources.list.d/google-chrome.list
|
||||
sudo apt update
|
||||
sudo apt install -y google-chrome-stable
|
||||
echo "✅ Chrome 브라우저 설치 완료"
|
||||
else
|
||||
CHROME_VERSION=$(google-chrome --version)
|
||||
echo "✅ Chrome 브라우저 이미 설치됨: ${CHROME_VERSION}"
|
||||
fi
|
||||
|
||||
# ChromeDriver 설치
|
||||
echo ""
|
||||
echo "🚗 ChromeDriver 설치 중..."
|
||||
if [ -f "/usr/local/bin/chromedriver" ]; then
|
||||
echo "기존 ChromeDriver 제거 중..."
|
||||
sudo rm -f /usr/local/bin/chromedriver
|
||||
fi
|
||||
|
||||
CHROME_VERSION=$(google-chrome --version | grep -oP '\d+\.\d+\.\d+\.\d+' | cut -d'.' -f1)
|
||||
echo "Chrome 주 버전: ${CHROME_VERSION}"
|
||||
|
||||
CHROMEDRIVER_VERSION=$(curl -s "https://googlechromelabs.github.io/chrome-for-testing/LATEST_RELEASE_${CHROME_VERSION}")
|
||||
echo "ChromeDriver 버전: ${CHROMEDRIVER_VERSION}"
|
||||
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
cd "$TEMP_DIR"
|
||||
|
||||
wget -q "https://storage.googleapis.com/chrome-for-testing-public/${CHROMEDRIVER_VERSION}/linux64/chromedriver-linux64.zip"
|
||||
unzip -q chromedriver-linux64.zip
|
||||
sudo mv chromedriver-linux64/chromedriver /usr/local/bin/
|
||||
sudo chmod +x /usr/local/bin/chromedriver
|
||||
|
||||
cd - > /dev/null
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
||||
echo "✅ ChromeDriver 설치 완료: $(chromedriver --version)"
|
||||
|
||||
# Python 버전 확인
|
||||
PYTHON_VERSION=$(python3 -c "import sys; print('.'.join(map(str, sys.version_info[:2])))")
|
||||
echo ""
|
||||
echo "📍 Python 버전: ${PYTHON_VERSION}"
|
||||
|
||||
if [ "$(echo "${PYTHON_VERSION} < 3.8" | bc)" -eq 1 ]; then
|
||||
echo "⚠️ Python 3.8 이상이 권장됩니다."
|
||||
fi
|
||||
|
||||
# Python 가상환경 설정
|
||||
echo ""
|
||||
echo "🐍 Python 가상환경 설정 중..."
|
||||
if [ ! -d "venv" ]; then
|
||||
python3 -m venv venv
|
||||
echo "✅ 가상환경 생성 완료"
|
||||
else
|
||||
echo "✅ 기존 가상환경 발견"
|
||||
fi
|
||||
|
||||
# 가상환경 활성화 및 라이브러리 설치
|
||||
echo "📚 Python 라이브러리 설치 중..."
|
||||
source venv/bin/activate
|
||||
|
||||
# pip 업그레이드
|
||||
pip install --upgrade pip
|
||||
|
||||
# 필요한 라이브러리 설치
|
||||
if [ -f "app/requirements.txt" ]; then
|
||||
pip install -r app/requirements.txt
|
||||
echo "✅ requirements.txt에서 라이브러리 설치 완료"
|
||||
else
|
||||
echo "⚠️ app/requirements.txt 파일을 찾을 수 없습니다."
|
||||
echo "🔧 기본 라이브러리들을 직접 설치합니다..."
|
||||
pip install fastapi uvicorn beautifulsoup4 selenium webdriver-manager pydantic python-multipart lxml
|
||||
fi
|
||||
|
||||
# 데이터 디렉토리 생성
|
||||
echo ""
|
||||
echo "📁 데이터 디렉토리 설정 중..."
|
||||
mkdir -p data
|
||||
chmod 755 data
|
||||
echo "✅ 데이터 디렉토리 생성: $(pwd)/data"
|
||||
|
||||
# 환경변수 파일 생성 (예시)
|
||||
echo ""
|
||||
echo "⚙️ 환경변수 파일 생성 중..."
|
||||
cat > .env << EOF
|
||||
# 애플리케이션 설정
|
||||
APP_TITLE=카카오맵 리뷰 분석 API
|
||||
APP_VERSION=1.0.1
|
||||
APP_DESCRIPTION=교육 목적 전용 - 실제 서비스 사용 금지
|
||||
|
||||
# 서버 설정
|
||||
HOST=0.0.0.0
|
||||
PORT=19000
|
||||
LOG_LEVEL=info
|
||||
|
||||
# API 기본값 설정
|
||||
DEFAULT_MAX_TIME=300
|
||||
DEFAULT_DAYS_LIMIT=60
|
||||
MAX_DAYS_LIMIT=365
|
||||
MIN_MAX_TIME=60
|
||||
MAX_MAX_TIME=600
|
||||
|
||||
# Chrome 브라우저 설정
|
||||
CHROME_OPTIONS=--headless=new --no-sandbox --disable-dev-shm-usage --disable-gpu --window-size=1920,1080
|
||||
SCROLL_CHECK_INTERVAL=5
|
||||
SCROLL_NO_CHANGE_LIMIT=6
|
||||
SCROLL_WAIT_TIME_SHORT=2.0
|
||||
SCROLL_WAIT_TIME_LONG=3.0
|
||||
|
||||
# 법적 경고 메시지
|
||||
LEGAL_WARNING_ENABLED=true
|
||||
CONTACT_EMAIL=admin@example.com
|
||||
|
||||
# 건강 체크 설정
|
||||
HEALTH_CHECK_TIMEOUT=10
|
||||
|
||||
# AI API 설정 (향후 Claude API 연동용)
|
||||
# CLAUDE_API_KEY=your_claude_api_key_here
|
||||
|
||||
# 데이터베이스 설정 (향후 PostgreSQL 연동용)
|
||||
# DB_HOST=localhost
|
||||
# DB_PORT=5432
|
||||
# DB_NAME=review_analysis
|
||||
# DB_USERNAME=your_db_username
|
||||
# DB_PASSWORD=your_db_password
|
||||
EOF
|
||||
echo "✅ .env 파일 생성 완료"
|
||||
|
||||
# 네트워크 연결 테스트
|
||||
echo ""
|
||||
echo "🌐 네트워크 연결 테스트 중..."
|
||||
|
||||
# 카카오맵 연결 테스트
|
||||
if curl -s --connect-timeout 5 "https://place.map.kakao.com" > /dev/null; then
|
||||
echo "✅ 카카오맵 서버 연결 가능"
|
||||
else
|
||||
echo "❌ 카카오맵 서버 연결 실패"
|
||||
echo " 인터넷 연결 상태를 확인해주세요."
|
||||
fi
|
||||
|
||||
# Selenium 및 Chrome 테스트
|
||||
echo ""
|
||||
echo "🧪 Selenium 및 Chrome 테스트 중..."
|
||||
source venv/bin/activate
|
||||
python3 -c "
|
||||
try:
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.chrome.options import Options
|
||||
from selenium.webdriver.chrome.service import Service
|
||||
print('✅ Selenium 모듈 import 성공')
|
||||
|
||||
options = Options()
|
||||
options.add_argument('--headless')
|
||||
options.add_argument('--no-sandbox')
|
||||
options.add_argument('--disable-dev-shm-usage')
|
||||
|
||||
try:
|
||||
service = Service('/usr/local/bin/chromedriver')
|
||||
driver = webdriver.Chrome(service=service, options=options)
|
||||
driver.get('data:text/html,<html><body><h1>Test</h1></body></html>')
|
||||
title = driver.title
|
||||
driver.quit()
|
||||
print('✅ Chrome WebDriver 테스트 성공')
|
||||
except Exception as e:
|
||||
print(f'❌ Chrome WebDriver 테스트 실패: {e}')
|
||||
|
||||
except ImportError as e:
|
||||
print(f'❌ Selenium import 실패: {e}')
|
||||
except Exception as e:
|
||||
print(f'❌ 테스트 중 오류: {e}')
|
||||
"
|
||||
|
||||
# Docker 설치 확인 (선택사항)
|
||||
echo ""
|
||||
echo "🐳 Docker 설치 확인 중..."
|
||||
if command -v docker &> /dev/null; then
|
||||
DOCKER_VERSION=$(docker --version)
|
||||
echo "✅ Docker 설치됨: ${DOCKER_VERSION}"
|
||||
|
||||
# Docker 권한 확인
|
||||
if docker ps &> /dev/null; then
|
||||
echo "✅ Docker 권한 확인됨"
|
||||
else
|
||||
echo "⚠️ Docker 권한 없음. 다음 명령어로 권한을 추가하세요:"
|
||||
echo " sudo usermod -aG docker $USER"
|
||||
echo " newgrp docker"
|
||||
fi
|
||||
else
|
||||
echo "⚠️ Docker가 설치되지 않음 (컨테이너 배포 시 필요)"
|
||||
echo " 설치 방법: https://docs.docker.com/engine/install/ubuntu/"
|
||||
fi
|
||||
|
||||
# 방화벽 설정 확인
|
||||
echo ""
|
||||
echo "🔥 방화벽 설정 확인 중..."
|
||||
if command -v ufw &> /dev/null; then
|
||||
UFW_STATUS=$(sudo ufw status | head -n1 | awk '{print $2}')
|
||||
if [ "${UFW_STATUS}" = "active" ]; then
|
||||
echo "🔥 UFW 방화벽 활성화됨"
|
||||
if sudo ufw status | grep -q "19000"; then
|
||||
echo "✅ 포트 19000 방화벽 규칙 확인됨"
|
||||
else
|
||||
echo "⚠️ 포트 19000 방화벽 규칙 없음"
|
||||
echo " 다음 명령어로 포트를 열어주세요:"
|
||||
echo " sudo ufw allow 19000"
|
||||
fi
|
||||
else
|
||||
echo "✅ UFW 방화벽 비활성화됨"
|
||||
fi
|
||||
else
|
||||
echo "✅ UFW 방화벽 미설치"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🎉 환경 설정 완료!"
|
||||
echo ""
|
||||
echo "📋 다음 단계:"
|
||||
echo "1. 가상환경 활성화:"
|
||||
echo " source venv/bin/activate"
|
||||
echo ""
|
||||
echo "2. 환경변수 설정:"
|
||||
echo " export DATA_DIR=\"./data\""
|
||||
echo ""
|
||||
echo "3. 애플리케이션 실행:"
|
||||
echo " python app/main.py"
|
||||
echo " 또는"
|
||||
echo " uvicorn app.main:app --host 0.0.0.0 --port 19000 --reload"
|
||||
echo ""
|
||||
echo "4. 웹 브라우저에서 접속:"
|
||||
echo " http://localhost:19000 (메인 페이지)"
|
||||
echo " http://localhost:19000/docs (Swagger UI)"
|
||||
echo " http://localhost:19000/health (헬스체크)"
|
||||
echo ""
|
||||
echo "5. API 테스트:"
|
||||
echo " curl -X POST \"http://localhost:19000/analyze\" \\"
|
||||
echo " -H \"Content-Type: application/json\" \\"
|
||||
echo " -d '{\"store_id\":\"501745730\",\"days_limit\":7,\"max_time\":300}'"
|
||||
echo ""
|
||||
echo "⚠️ 중요 법적 경고사항:"
|
||||
echo " 이 서비스는 교육 목적으로만 사용하세요!"
|
||||
echo " 실제 웹사이트 크롤링은 법적 위험이 있습니다."
|
||||
echo " 카카오 공식 API를 사용하는 것을 권장합니다: developers.kakao.com"
|
||||
echo ""
|
||||
echo "💡 문제 발생 시:"
|
||||
echo " - 로그 확인: tail -f 로그파일"
|
||||
echo " - 환경변수 확인: cat .env"
|
||||
echo " - Chrome 확인: google-chrome --version"
|
||||
echo " - ChromeDriver 확인: chromedriver --version"
|
||||
echo ""
|
||||
echo "🔧 설정 파일 위치:"
|
||||
echo " - 환경변수: $(pwd)/.env"
|
||||
echo " - 데이터 저장: $(pwd)/data/"
|
||||
echo " - 애플리케이션: $(pwd)/app/"
|
||||
|
||||
deactivate 2>/dev/null || true
|
||||
echo ""
|
||||
echo "✅ Review Analysis API 환경 설정이 완료되었습니다!"
|
||||
Reference in New Issue
Block a user