o2o-negosium-original/docker-compose.yml
민헌 3a8d669970 feat(lps): P2 시크릿 env 주입 + Chrome 프로필 영속 볼륨
프로덕션에서 시크릿을 이미지에 굽지 않고 env 로 주입, 재시작 시 챌린지 쿠키 유지.

- server_configs: 시크릿 env override 추가(house 의 DB override 패턴 확장) —
  OPENAI_API_KEY/MODEL, DECODO_HOST/USERNAME/PASSWORD/COST_PER_GB, NAVER_KEYS="id:sec,...".
  로컬은 env 미설정 → config.local.toml 그대로.
- worker_main: Chrome 프로필 경로를 LPS_PROFILE_DIR(기본 /tmp)로 env 화 → 영속 볼륨 마운트 가능.
- docker-compose: lps-worker 에 lps-profiles 볼륨(/profiles) + 시크릿 env 주입 주석 예시.
- config.example: 프로덕션 env 주입 목록 문서화.

검증: env override 동작(OpenAI/DECODO/Naver), 프로필이 /profiles 볼륨에 Cookies 포함 저장(cf_clearance 유지). 89 tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 23:04:37 +09:00

165 lines
6.5 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).
lps-api:
build:
context: ./lps
dockerfile: Dockerfile
container_name: lps-api
environment:
APP_ENV: local
DB_HOST: host.docker.internal # 컨테이너→호스트 DB (config.local.toml의 127.0.0.1 override)
PYTHONUNBUFFERED: "1"
ports:
- "9600:9600"
extra_hosts:
- "host.docker.internal:host-gateway"
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
PYTHONUNBUFFERED: "1"
WORKER_CONCURRENCY: "1" # 상품 동시 검색 수(워커별 브라우저 세트, Chrome 4×N)
LPS_PROFILE_DIR: /profiles # Chrome 프로필을 영속 볼륨에 → 재시작해도 cf_clearance 유지(재웜업 회피)
# ── 프로덕션 시크릿 주입(이미지에 안 굽고 env 로). 아래 주석 해제 후 값 채우거나 secrets 매니저 연동 ──
# OPENAI_API_KEY: ${OPENAI_API_KEY}
# DECODO_USERNAME: ${DECODO_USERNAME}
# DECODO_PASSWORD: ${DECODO_PASSWORD}
# DECODO_HOST: ${DECODO_HOST}
# NAVER_KEYS: ${NAVER_KEYS} # "id1:secret1,id2:secret2"
volumes:
- lps-profiles:/profiles # Chrome 프로필(쿠키) 영속
extra_hosts:
- "host.docker.internal:host-gateway"
shm_size: "1gb" # Chrome 는 /dev/shm 을 많이 씀 — 부족하면 탭 크래시
restart: unless-stopped
logging:
driver: json-file
options: { max-size: "10m", max-file: "5" }
volumes:
lps-profiles: