이번 라운드 변경을 README/아키텍처/운영 가이드에 반영. - README: 기능표(오픈마켓 폴백·원가계측·다중병렬), 워크플로우 4-1 폴백 단계, 폴더구조 - architecture: 안티봇 대응(쿠팡 Akamai / ESM Cloudflare Turnstile 웜업·동적차단), 검색 원가 계측(CDP 실측·컴포넌트별 비용), 다중 상품 병렬, 유휴 브라우저 정리 - operations: 로그 항목(프리플라이트·웜업·IP회전·데드라인·유휴정리), 문제해결(407·Turnstile· Chrome 정리·느림/비쌈), 배포유의, 원가/몰별 DB 쿼리 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6.8 KiB
6.8 KiB
운영 가이드 — 실행 · 로그 · DB · 문제 해결
1. 사전 준비
필요한 것: Python 3.12+ (로컬은 3.14), PostgreSQL, Google Chrome(쿠팡 크롤링용)
설정 파일 (config/config.local.toml, git 미커밋)
cp config/config.local.toml.example config/config.local.toml
채워야 할 값:
| 섹션 | 값 |
|---|---|
[MainDBConfig] |
DB 접속(host/port/id/pw, name=lps_db) |
[NaverConfig].keys |
네이버 쇼핑 API 키(id/secret). 여러 개면 자동 로테이션 |
[OpenAIConfig] |
api_key (AI 판정·검색어 생성용) |
[DecodoConfig] |
프록시 정보(비워두면 프록시 미사용) |
한 파일에 설정+시크릿 통합 관리. 배포 시엔 이 파일을 마운트하거나, 환경별로 바뀌는 값(DB_HOST 등)만 환경변수로 덮어씁니다.
DB 준비: lps_db 생성 후 최초 실행 시 테이블 자동 생성.
createdb -h 127.0.0.1 -U postgres lps_db
psql -h 127.0.0.1 -U postgres -d lps_db -c "CREATE EXTENSION IF NOT EXISTS pgcrypto;"
2. 실행
API 서버 (요청 접수)
./run_local_server.sh # → http://localhost:9600/docs
워커 (실제 검색 수행) — 별도 터미널
python worker_main.py
# 로그를 실시간으로 보려면:
PYTHONUNBUFFERED=1 python worker_main.py
# 상품 여러 개를 동시에 검색(워커별 브라우저 세트 · 다른 IP):
WORKER_CONCURRENCY=3 python worker_main.py # 권장 2~3(로컬). Chrome 최대 4×N개
워커 실행 시 쿠팡 크롤링용 Chrome 창이 뜹니다(정상). 기동 로그에
DECODO 프리플라이트 OK — egress IP ...,AI: ON/OFF가 표시됩니다. 동시성 N이면 상품 N개가 진짜 병렬 처리됩니다(각 워커가 자기 프로필·프록시 IP 사용).
부하 테스트 (여러 상품 동시 검색 측정)
N=8 python loadtest.py # 상품 8개 제출→처리량·지연(p50/p95)·AI/DECODO/총비용 집계
워커 동시성만큼 병렬 처리됩니다(동시성 낮으면 큐에서 순차 대기 — 그게 부하 관측 포인트).
3. 로그 보는 법 (워커 터미널)
| 로그 | 의미 |
|---|---|
DECODO 프리플라이트 OK — egress IP ... |
시작 시 살아있는 프록시 포트 선점 성공(egress IP 표시) |
[warmup:gmarket] 챌린지 통과·쿠키 확보 |
시작 웜업 — 챌린지 미리 풀어 쿠키 선점(실 작업 웜) |
[naver] query='...' → N건 |
네이버 검색 결과 수 |
[coupang] query='...' → N건 (ip_req#K) |
쿠팡 결과 수 / 이 IP로 K번째 요청 |
[gmarket/auction/st11] query='...' → N건 |
오픈마켓 폴백 크롤 결과 수 |
[ai] 판정 N건 중 매칭 M건 |
AI 같은상품 선별 결과 |
[coupang][BOT-DETECTED] ... marker='...' |
봇 감지(마커별) → IP 회전 |
[gmarket] IP 회전 — 프록시 전송오류/봇 감지 |
프록시 죽음(407/터널) 또는 차단 → 새 IP |
[fallback:gmarket] 데드라인 15s 초과 → 스킵 |
폴백 크롤이 시간 상한 초과 → 그 몰만 스킵 |
[coupang] 유휴 120s 초과 → 브라우저 정리 |
유휴 브라우저 닫아 메모리 회수(다음 검색 때 재기동) |
[worker-0] done <id> / fail ... → DEAD |
작업 완료 / 실패 |
디버그 로그가 안 보이면
config.local.toml의[LogConfig] log_level = "debug"확인.
4. DB 조회 (유용한 쿼리)
psql -h 127.0.0.1 -U postgres -d lps_db
-- 큐 상태 요약 (1=대기 2=처리중 3=완료 4=실패)
SELECT status, count(*) FROM job GROUP BY status;
-- 최근 작업 결과
SELECT job_id, status, result->>'outcome' AS outcome,
result->'lowest'->>'price' AS lowest, result->'sources' AS sources
FROM job ORDER BY created_at DESC LIMIT 5;
-- 특정 상품의 최저가 이력(그래프 원본) + 몰별 스냅샷
SELECT triggered_at, naver_lowest, coupang_lowest, final_lowest, final_source, by_mall
FROM price_history WHERE product_code='T1' ORDER BY triggered_at;
-- 검색 원가(최근 완료 작업의 metrics)
SELECT job_id,
result->'metrics'->'cost'->>'total_usd' AS 총비용,
result->'metrics'->'cost'->>'proxy_usd' AS DECODO,
result->'metrics'->'crawl'->>'proxy_bytes' AS 전송바이트,
result->'metrics'->>'duration_ms' AS 소요ms
FROM job WHERE status=3 ORDER BY updated_at DESC LIMIT 5;
-- 봇 감지 패턴 (IP당 평균 몇 요청 만에 감지?)
SELECT avg(ip_request_no), count(*) FROM bot_detection;
-- 네거티브 캐시(없음으로 기록된 상품)
SELECT key, until, reason FROM search_negative ORDER BY created_at DESC;
5. 테스트
python -m pytest
⚠️ 워커가 실행 중이면 테스트가 깨집니다 — 워커가 같은
lps_db의 테스트 작업을 가로채기 때문. 테스트 전 워커를 멈추세요:pkill -f worker_main.py
6. 문제 해결
| 증상 | 원인 / 해결 |
|---|---|
| 포트 9600 사용 중 | lsof -ti:9600 | xargs kill 후 재실행 |
| 백그라운드 실행 시 로그 안 보임 | print 버퍼링 → PYTHONUNBUFFERED=1 붙여 실행 |
프리플라이트 실패/모든 크롤 실패 |
DECODO 프록시 문제 — 대시보드에서 잔여 트래픽·플랜·자격증명 확인(407=인증거부). 게이트 다운이면 네이버(직접)만 동작 |
| G마켓 결과 계속 0건 | Cloudflare Turnstile 미통과(나쁜 IP는 인터랙티브 체크박스) — 웜업 IP회전 재시도로 완화. 지연 부담이면 폴백 데드라인이 스킵 |
쿠팡 blocked=True(Access Denied 등) |
Akamai 차단 → 자동 IP 회전(감지 이력 bot_detection). 반복되면 프록시 IP 풀 확대 |
| Chrome이 계속 쌓임 | 유휴 정리(120s)가 닫음. 스파이크/이전 워커 잔여는 pkill -f "user-data-dir=/tmp/lps_" |
| AI 매칭이 0건 자주 발생 | 검색어 모호/스펙 불일치 → product_name/specification을 더 정확히 |
| 검색이 너무 느림/비쌈 | result.metrics로 소스별 시간·DECODO 바이트 확인. 대역폭이 대부분(오픈마켓 크롤) |
result.desc = LPS_JOB_NOT_FOUND |
존재하지 않거나 잘못된 job_id |
7. 배포 시 유의 (예정)
- Chrome을 headless로(서버엔 화면 없음) — 단 Cloudflare Turnstile은 headful이 더 잘 통과, 튜닝 필요
- Docker 이미지에 chromium 설치 필요
- 프록시(residential) 사실상 필수 — 클라우드 IP는 쉽게 차단됨. 포트(IP) 풀은 넉넉히(회전 여유)
- 워커 여러 개는 워커별 Chrome 프로필 분리 + 다른 프록시 IP(이미
WORKER_CONCURRENCY로 자동 처리) - 시크릿(config.local.toml)은 이미지에 굽지 말고 마운트 권장
- 비용: 대역폭이 원가의 대부분(오픈마켓 크롤) — 같은 상품 재크롤을 줄이는 TTL 캐시가 다음 절감 후보