This commit is contained in:
hiondal
2025-06-15 13:52:26 +00:00
commit 6a5c411800
53 changed files with 15785 additions and 0 deletions
+480
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+9
View File
@@ -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
+217
View File
@@ -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)"
+257
View File
@@ -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)"
+103
View File
@@ -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
+72
View File
@@ -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"]
+103
View File
@@ -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"
+130
View File
@@ -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: 제거
+39
View File
@@ -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
+21
View File
@@ -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: ""
+17
View File
@@ -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
+280
View File
@@ -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 환경 설정이 완료되었습니다!"