#6 insta-lab Chromium Browser Pool — Playwright/Chromium 인스턴스를 모듈 레벨에서 보관하고 매 슬레이트마다 reuse. 카드 10장 렌더의 launch 비용 (~3초/회)이 사라짐. startup/shutdown lifecycle hook 추가. crashed/disconnected 시 lazy 재초기화. #8 realestate-lab 수집 병렬화 — collect_all과 delete_old_completed가 서로 다른 데이터 영역이라 ThreadPoolExecutor(2)로 병렬. asyncio.gather 대신 thread executor를 쓴 이유는 BackgroundScheduler+동기 함수 환경 에서 자연스럽고 추가 의존성 없기 때문. 매칭은 일관성 유지로 순차. #7 stock async — 보류. 재진단 결과 stock은 BackgroundScheduler 사용 중이라 main loop 블로킹 없음. fetch 4회는 network I/O wait가 대부분이라 to_thread도 의미 없음. 진짜 효과를 보려면 AsyncIOScheduler 전환 + aiohttp 병렬이라 큰 리팩토링. 박재오 판단 대기. CHECK_POINT.md 갱신. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
216 lines
7.5 KiB
Markdown
216 lines
7.5 KiB
Markdown
# web-backend CHECK_POINT
|
||
|
||
> NAS Docker 11 컨테이너(9 백엔드 + frontend + deployer). Synology Celeron J4025 (2C 2.0GHz) 18GB.
|
||
> 2026-05-18 작성 — uvicorn CPU 폭주 진단 결과 정리.
|
||
|
||
## 🔴 즉시 (오늘, 총 1시간 5분)
|
||
|
||
### 1. 09:00 cron 5분 스태거링 ⭐ 가장 큰 효과
|
||
|
||
**파일**: `agent-office/app/scheduler.py:72-76`
|
||
```python
|
||
# 변경 전 — 09:00 동시 실행 (CPU 폭주 원인 #1)
|
||
scheduler.add_job(_run_insta_trends_collect, "cron", hour=9, minute=0)
|
||
scheduler.add_job(_run_lotto_schedule, "cron", day_of_week="mon", hour=9, minute=0)
|
||
scheduler.add_job(_run_youtube_research, "cron", hour=9, minute=0)
|
||
|
||
# 변경 후 — 5분 스태거링
|
||
scheduler.add_job(_run_insta_trends_collect, "cron", hour=9, minute=0, id="insta_trends")
|
||
scheduler.add_job(_run_lotto_schedule, "cron", day_of_week="mon", hour=9, minute=5, id="lotto_curate")
|
||
scheduler.add_job(_run_youtube_research, "cron", hour=9, minute=10, id="youtube_research")
|
||
```
|
||
|
||
**파일**: `realestate-lab/app/main.py:51`
|
||
```python
|
||
# 변경 전
|
||
scheduler.add_job(scheduled_collect, "cron", hour=9, minute=0, id="collect")
|
||
|
||
# 변경 후
|
||
scheduler.add_job(scheduled_collect, "cron", hour=9, minute=15, id="collect")
|
||
```
|
||
|
||
- [x] agent-office scheduler.py 수정 (2026-05-18)
|
||
- [x] realestate-lab main.py 수정 (2026-05-18)
|
||
- [ ] git commit + push (Gitea Webhook 자동 빌드)
|
||
|
||
---
|
||
|
||
### 2. insta-lab Playwright Semaphore(1) ⭐
|
||
|
||
**파일**: `insta-lab/app/main.py` (모듈 레벨 추가)
|
||
```python
|
||
import asyncio
|
||
|
||
# 모듈 레벨에 한 번만 선언
|
||
RENDER_SEMAPHORE = asyncio.Semaphore(1) # Chromium 동시 실행 1개로 제한
|
||
|
||
# 카드 렌더 백그라운드 함수에 감싸기
|
||
async def _bg_render(task_id: str, slate_id: int):
|
||
async with RENDER_SEMAPHORE:
|
||
await card_renderer.render_slate(slate_id, ...)
|
||
```
|
||
|
||
- [x] card_renderer.render_slate를 Semaphore(1)로 감쌈 (2026-05-18, lazy init)
|
||
- [ ] 동시 2개 요청 테스트 (curl 동시 2회 → 순차 처리되는지 확인)
|
||
|
||
---
|
||
|
||
### 3. healthcheck interval 60s
|
||
|
||
**파일**: `docker-compose.yml` (모든 9 컨테이너)
|
||
```yaml
|
||
# 변경 전
|
||
healthcheck:
|
||
interval: 30s
|
||
|
||
# 변경 후
|
||
healthcheck:
|
||
interval: 60s
|
||
```
|
||
|
||
- [x] docker-compose.yml 10개 healthcheck 일괄 변경 (9 백엔드 + frontend, 2026-05-18)
|
||
- [ ] `docker compose up -d` 재기동
|
||
- [ ] `docker stats` 로 CPU 5% 정도 감소 확인
|
||
|
||
---
|
||
|
||
### 4. uvicorn --workers 1 명시
|
||
|
||
**모든 Dockerfile CMD**:
|
||
```dockerfile
|
||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"]
|
||
```
|
||
|
||
영향 9 파일 (모두 2026-05-18 적용):
|
||
- [x] lotto/Dockerfile
|
||
- [x] stock/Dockerfile
|
||
- [x] music-lab/Dockerfile
|
||
- [x] insta-lab/Dockerfile
|
||
- [x] realestate-lab/Dockerfile
|
||
- [x] agent-office/Dockerfile
|
||
- [x] personal/Dockerfile
|
||
- [x] packs-lab/Dockerfile
|
||
- [x] travel-proxy/Dockerfile
|
||
|
||
→ `docker compose build --no-cache` 후 재기동.
|
||
|
||
---
|
||
|
||
### 5. lotto Monte Carlo 08:05 → 08:30
|
||
|
||
**파일**: `lotto/app/main.py:86`
|
||
```python
|
||
# 변경 전 — stock 08:00과 5분 차이로 겹침
|
||
scheduler.add_job(_run_simulation_job, "cron", hour="0,4,8,12,16,20", minute=5)
|
||
|
||
# 변경 후 — 25분 분리
|
||
scheduler.add_job(_run_simulation_job, "cron", hour="0,4,8,12,16,20", minute=30)
|
||
```
|
||
|
||
- [x] lotto/app/main.py 수정 (2026-05-18)
|
||
|
||
---
|
||
|
||
## 🟡 중기 (1~2주)
|
||
|
||
### 6. Chromium Browser Pool 재설계 (insta-lab) ✅ 2026-05-18
|
||
- 매번 launch X → 1개 인스턴스 재사용
|
||
- 카드 10장 렌더 시간 30% 단축 기대
|
||
- [x] `card_renderer.py` 내부에 모듈 레벨 `_PLAYWRIGHT`/`_BROWSER` + `init_browser`/`shutdown_browser` 함수 (별도 모듈 분리 안 함, 같은 파일에 인접 배치)
|
||
- [x] `_render_slate_locked` 본체에서 `_get_browser()` 재사용 (crashed 시 lazy 재초기화)
|
||
- [x] `main.py` startup hook에서 `init_browser()`, shutdown hook에서 `shutdown_browser()`
|
||
|
||
### 7. stock 뉴스 스크랩 비동기화 — ⚠️ 보류 2026-05-18
|
||
- **재진단**: stock은 `BackgroundScheduler` 사용 중 → main loop 블로킹 없음 (이미 별도 thread)
|
||
- `fetch_market_news`의 4개 동기 `requests.get`은 network I/O wait라 CPU 거의 사용 안 함
|
||
- `to_thread`로 wrap해도 BackgroundScheduler 환경에서 사실상 의미 없음
|
||
- 진짜 효과를 보려면 AsyncIOScheduler 전환 + scraper.py 4개 fetch를 `aiohttp` 병렬로 — **큰 리팩토링 vs 효과 불명확**
|
||
- [ ] 박재오 판단: 큰 리팩토링 진행 여부
|
||
|
||
### 8. realestate 수집 병렬화 ✅ 2026-05-18
|
||
- **파일**: `realestate-lab/app/main.py:scheduled_collect`
|
||
- `collect_all()` + `delete_old_completed_announcements()` 병렬
|
||
- BackgroundScheduler 환경이라 `asyncio.gather` 대신 `ThreadPoolExecutor(max_workers=2)` 사용 (효과 동일)
|
||
- 매칭은 순차 유지 (DB 일관성)
|
||
- [x] ThreadPoolExecutor 적용
|
||
|
||
### 9. lotto Monte Carlo 시뮬레이션 빈도 검토
|
||
- 현재 6회/일 (00·04·08·12·16·20)
|
||
- 실제 필요 빈도 박재오 결정 — 3회/일(아침·점심·저녁)로 줄이면 CPU 50% 감소
|
||
- [ ] 박재오 의사결정 후 cron 변경
|
||
|
||
---
|
||
|
||
## 🟢 장기 (1개월+)
|
||
|
||
### 10. 무거운 작업 Windows AI 서버로 이전
|
||
- 현재 stock Ollama qwen3:14b → NAS 로컬 호출 가능성 (확인 필요)
|
||
- 모든 LLM 호출을 Windows AI 192.168.45.59로 통일
|
||
- NAS CPU ~20% 감소 기대
|
||
|
||
### 11. 컨테이너 리소스 제한
|
||
```yaml
|
||
services:
|
||
insta-lab:
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
cpus: "0.5"
|
||
memory: "512M"
|
||
```
|
||
- [ ] insta-lab·music-lab·stock 등 무거운 컨테이너부터 적용
|
||
- 효과: OOM killer로부터 다른 컨테이너 보호
|
||
|
||
### 12. NAS 업그레이드 검토
|
||
- 현재: Celeron J4025 (2C 2.0GHz)
|
||
- 대안: Ryzen N5105 (4C 2.0GHz) NAS — 4코어로 병렬성 2배
|
||
- 박재오 자금·우선순위 결정
|
||
|
||
---
|
||
|
||
## ✅ 최근 완료 (참고)
|
||
|
||
- 2026-05-15: insta-lab 신설 (포트 18700, Jinja2 + Playwright + Claude Sonnet)
|
||
- 2026-05-16: insta-lab Playwright 1080×1350 PNG 렌더 완성
|
||
- 2026-05-17: agent-office random idle 제거, ADMIN_API_KEY 강화 (stock)
|
||
- 2026-05-17: insta-lab minimal theme + design_importer 추가
|
||
- 2026-05-17: blog-lab 트랙 완전 폐기 (docker-compose에 없음, 위키 정정 완료)
|
||
- 2026-05-18: 🔴 즉시 5건 일괄 적용 — 09:00 cron 스태거링(insta/lotto/youtube/realestate), lotto Monte Carlo 08:30, insta-lab Semaphore(1), healthcheck 60s, uvicorn --workers 1 명시 (사용자 push + NAS deployer 재기동 대기)
|
||
- 2026-05-18: 🟡 중기 2건 적용 — #6 insta-lab Chromium Browser Pool (lifecycle hook), #8 realestate ThreadPoolExecutor 병렬 (collect/delete). #7 stock async는 BackgroundScheduler 사용 중이라 재진단 후 보류 (효과 미미). #9 Monte Carlo 빈도는 박재오 결정 대기.
|
||
|
||
---
|
||
|
||
## 🔧 진단 커맨드 (NAS bash)
|
||
|
||
```bash
|
||
# 실시간 CPU 사용 (상위 15)
|
||
top -b -n 1 | head -25
|
||
|
||
# 프로세스별 CPU 정렬
|
||
ps aux --sort=-%cpu | head -15
|
||
|
||
# uvicorn·chromium·python 프로세스만
|
||
ps aux | grep -E "uvicorn|chromium|python" | grep -v grep
|
||
|
||
# 스케줄러 실행 로그 (최근 50)
|
||
docker logs agent-office 2>&1 | grep -E "APScheduler|executing" | tail -50
|
||
|
||
# insta-lab Chromium 프로세스 개수
|
||
docker exec insta-lab ps aux | grep chromium | wc -l
|
||
|
||
# 컨테이너별 CPU/메모리 실시간
|
||
docker stats --no-stream
|
||
```
|
||
|
||
---
|
||
|
||
## 📚 참고
|
||
|
||
- 진단 풀 보고서: `C:\Users\jaeoh\Documents\Obsidian Vault\raw\2026-05-18-NAS-uvicorn-CPU-진단-개선안.md`
|
||
- 위키 페이지: [[사업-개인-웹-플랫폼]] (CPU 부하 진단 섹션 + 컨테이너 표)
|
||
- docker-compose.yml: 본 디렉토리 루트
|
||
|
||
## 변경 이력
|
||
|
||
- 2026-05-18: 페이지 신설. 즉시 5건 + 중기 4건 + 장기 3건. 진단 커맨드.
|