o2o-negosium-original/docker-compose.yml
민헌 d4cee1caf7 feat(lps): API 스케일 파라미터를 .env 로 노출 — PROCESS_COUNT·커넥션 예산
주석 상태였던 PROCESS_COUNT/DB_CONNECTION_BUDGET 를 활성화하되 서버별
값은 .env(LPS_API_PROCESS_COUNT, LPS_DB_CONNECTION_BUDGET)로 조정.
기본 1/40 — API 는 단일 프로세스로도 ~1,100 RPS(실측)라 평시 충분,
부하 대비 시에만 상향. 풀 자동 산정이 예산 초과를 방지.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 12:02:17 +09:00

202 lines
9.0 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Negosium + Negodata 백엔드.
# DB 는 compose 에서 관리하지 않는다 — 각 backend 는 config.docker.toml 의 접속 정보대로
# 외부 PostgreSQL 에 연결한다(호스트에 떠 있는 로컬 postgres, 또는 따로 실행 중인 docker postgres).
# 컨테이너에서 호스트의 DB 에 접속할 때는 host.docker.internal 을 쓴다.
#
# docker compose up -d
# negosium 서버: http://localhost:9300/docs
# negosium 프론트: http://localhost:3300
# negodata 서버: http://localhost:9400/docs
# agent 서버: http://localhost:9500/docs
# anchoring 배치: 포트 없음 — 상주 스케줄러(격주 토 00:00 KST), docker logs anchoring 으로 확인
#
# DB 준비(최초 1회): postgres-init 의 SQL 을 대상 DB 에 적용한다.
# psql -h <host> -p <port> -U <user> -f postgres-init/00-init.sql (스키마 전체: negosium_db + 도메인·learning·anchoring schema)
# psql -h <host> -p <port> -U <user> -f postgres-init/temp-data.sql (임시 데이터 시드: admin / admin1234, company.users)
services:
negosium-backend:
build: ./backend
container_name: negosium-backend
environment:
APP_ENV: local
DB_HOST: host.docker.internal # 컨테이너→호스트 DB (config.local.toml의 127.0.0.1 override)
AGENT_BASE_URL: http://host.docker.internal:9500 # 컨테이너→호스트 agent (로컬 uvicorn :9500)
ports:
- "9300:9300"
# 컨테이너에서 호스트의 DB 로 접근 (config.docker.toml 의 host.docker.internal)
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped
negodata-backend:
build: ./negodata/backend
container_name: negodata-backend
environment:
APP_ENV: local
DB_HOST: host.docker.internal # 컨테이너→호스트 DB (config.local.toml의 127.0.0.1 override)
RELOAD: "1" # uvicorn --reload 활성 → 소스 저장 시 자동 재기동(재빌드 불필요)
SCHEDULER_ENABLED: "1" # 마감 크론 활성(단일 워커라 중복 없음). 운영 다중 워커면 1개 프로세스에서만 1
PYTHONUNBUFFERED: "1" # 컨테이너 로그 실시간 출력(stdout 버퍼링 끔)
volumes:
- ./negodata/backend:/app # 호스트 소스 = 컨테이너 코드. 이게 있어야 수정이 즉시 반영됨
ports:
- "9400:9400"
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped
# negodata 프론트 (Vite dev 서버).
negodata-front:
build: ./negodata/front
container_name: negodata-front
ports:
- "3000:3000"
volumes:
- ./negodata/front:/app
- /app/node_modules
restart: unless-stopped
# 협상 에이전트 (negosium_db 공유, learning 스키마 사용).
agent:
build: ./agent
container_name: negosium-agent
environment:
APP_ENV: local
DB_HOST: host.docker.internal # 컨테이너→호스트 DB (config.local.toml의 127.0.0.1 override)
ports:
- "9500:9500"
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped
# negosium 공급사 프론트 (프로덕션 빌드 정적 서빙, :3300). 브라우저가 backend(:9300)를 직접 호출.
negosium-front:
build: ./frontend
container_name: negosium-front
ports:
- "3300:3300"
restart: unless-stopped
# 앵커링 값 자동 조정 배치 (negosium_db 공유, 포트 없음 — 상주 스케줄러).
anchoring:
build: ./schedules/anchoring
container_name: anchoring
environment:
APP_ENV: local # config.{APP_ENV}.toml 선택 (local/dev/prod)
DB_HOST: host.docker.internal # 컨테이너→호스트 DB
REDIS_HOST: anchoring-redis
TZ: Asia/Seoul
volumes:
- ./schedules/anchoring/config.local.toml:/app/config.local.toml:ro # 시크릿은 마운트 — up 전에 파일 필요
depends_on:
- anchoring-redis
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped
logging: # 상주 배치 — 장기 운영 디스크 보호
driver: json-file
options:
max-size: "10m"
max-file: "5"
# 앵커링 조회 캐시 (anchoring 전용)
anchoring-redis:
image: redis:7-alpine
container_name: anchoring-redis
ports:
- "127.0.0.1:6380:6379" # 호스트 로컬만 개방 (무인증 Redis). 6380 = 호스트 redis(6379)와 충돌 회피
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
# ── LPS (인터넷 최저가 검색) ──────────────────────────────────
# API(요청 접수, lean) + 워커(크롤, 헤드풀 Chromium+Xvfb). DB 는 외부(host.docker.internal).
# 이미지엔 시크릿이 없다(example config 로 빌드) — 실값은 아래 env 로 주입.
# 시크릿 값은 리포 루트 .env 파일에 채운다(.env.example 참고, .env 는 미커밋).
lps-api:
build:
context: ./lps
dockerfile: Dockerfile
container_name: lps-api
environment:
APP_ENV: local
DB_HOST: host.docker.internal # 컨테이너→호스트 DB (example toml 의 127.0.0.1 override)
DB_USER: ${LPS_DB_USER:-postgres}
DB_PASSWORD: ${LPS_DB_PASSWORD:-postgres}
PYTHONUNBUFFERED: "1"
# 멀티코어: PROCESS_COUNT(uvicorn 워커=코어수)를 올리면 커넥션 풀은 자동 산정된다.
# (pool+overflow)×2엔진×PROCESS_COUNT ≤ DB_CONNECTION_BUDGET 를 config 가 스스로 보장.
# 값은 .env 에서 서버별로 조정(compose 수정 불필요). API 병목은 드묾 — 기본 1이면 충분,
# 부하테스트/대량 폴링 대비 시에만 코어 수만큼 상향(예: 4).
PROCESS_COUNT: ${LPS_API_PROCESS_COUNT:-1}
DB_CONNECTION_BUDGET: ${LPS_DB_CONNECTION_BUDGET:-40} # 전용 PG(max_connections≈100)면 90 근처로 상향
ports:
- "9600:9600"
extra_hosts:
- "host.docker.internal:host-gateway"
labels:
autoheal: "true" # HEALTHCHECK 실패 시 autoheal 이 재시작
restart: unless-stopped
logging:
driver: json-file
options: { max-size: "10m", max-file: "5" }
lps-worker:
build:
context: ./lps
dockerfile: Dockerfile.worker # Chromium + Xvfb (headless 는 안티봇에 탐지됨)
container_name: lps-worker
environment:
APP_ENV: local
DB_HOST: host.docker.internal
DB_USER: ${LPS_DB_USER:-postgres}
DB_PASSWORD: ${LPS_DB_PASSWORD:-postgres}
PYTHONUNBUFFERED: "1"
WORKER_CONCURRENCY: "1" # 상품 동시 검색 수(워커별 브라우저 세트, Chrome 4×N)
LPS_PROFILE_DIR: /profiles # Chrome 프로필을 영속 볼륨에 → 재시작해도 cf_clearance 유지(재웜업 회피)
# LPS_FALLBACKS: "gmarket,auction,st11" # 오픈마켓 폴백(기본 OFF — 켜기 전 라이브 스모크로 셀렉터 점검)
# LPS_JOB_DEADLINE_SEC: "300" # 잡 1건 처리 상한(행 방어) — 기본 300s
# ── 시크릿 주입(이미지엔 없음 — 필수). 리포 루트 .env 에 값 채움(.env.example 참고) ──
OPENAI_API_KEY: ${OPENAI_API_KEY:-} # 비면 AI 판정 OFF
NAVER_KEYS: ${NAVER_KEYS:-} # "id1:secret1,id2:secret2" — 비면 네이버 검색 실패
DECODO_HOST: ${DECODO_HOST:-} # DECODO 4종 비면 프록시 미사용(직접 연결)
DECODO_USERNAME: ${DECODO_USERNAME:-}
DECODO_PASSWORD: ${DECODO_PASSWORD:-}
DECODO_PORT_START: ${DECODO_PORT_START:-0}
DECODO_PORT_END: ${DECODO_PORT_END:-0}
DECODO_COST_PER_GB: ${DECODO_COST_PER_GB:-0}
volumes:
- lps-profiles:/profiles # Chrome 프로필(쿠키) 영속
extra_hosts:
- "host.docker.internal:host-gateway"
shm_size: "1gb" # Chrome 는 /dev/shm 을 많이 씀 — 부족하면 탭 크래시
stop_grace_period: 75s # graceful 종료 유예(LPS_SHUTDOWN_GRACE_SEC=60 + 정리 여유) — 기본 10s 면 하던 잡 마무리 전에 SIGKILL
labels:
autoheal: "true" # 하트비트 HEALTHCHECK 실패(행/좀비) 시 autoheal 이 재시작
restart: unless-stopped
logging:
driver: json-file
options: { max-size: "10m", max-file: "5" }
# HEALTHCHECK 실패 컨테이너 자동 재시작 — compose 의 restart 는 '프로세스 종료'만 다루고
# unhealthy 는 표시만 하므로, autoheal 라벨 붙은 컨테이너(lps-api/lps-worker)를 감시해 재시작한다.
# docker.sock 마운트 = 도커 제어 권한이므로 신뢰 환경에서만 사용.
autoheal:
image: willfarrell/autoheal:latest
container_name: autoheal
environment:
AUTOHEAL_CONTAINER_LABEL: autoheal
volumes:
- /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped
logging:
driver: json-file
options: { max-size: "10m", max-file: "5" }
volumes:
lps-profiles: