6d3b0bacc0
- 19개 마이크로서비스 (news-collector, score-engine, ta-engine, dart-collector, aux-signal, us-market, graph-engine, telegram-bot, dashboard-api, kis-api 등) - 가치투자 스코어링 + 10공식 앙상블 보팅 (매직포뮬러/F-Score/Altman/PEG/ 모멘텀/Beneish/GP-A/G-Score/Amihud/BAB) - 뉴스 수집→형태소→임베딩→중복제거→AI분석 파이프라인 - 기술적분석 + GAT 그래프신경망 + 미증시 동조 시그널 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
831 lines
24 KiB
YAML
831 lines
24 KiB
YAML
|
|
|
|
# ============================================================
|
|
# Trading AI System
|
|
# GPU 0: RTX 3060 12GB → Ollama (EXAONE 3.5 추론)
|
|
# GPU 1: RTX 3070 8GB → Ollama (bge-m3 임베딩)
|
|
# ============================================================
|
|
|
|
x-logging: &default-logging
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "${LOG_MAX_SIZE:-100m}"
|
|
max-file: "${LOG_MAX_FILE:-10}"
|
|
|
|
x-restart: &restart
|
|
restart: unless-stopped
|
|
|
|
networks:
|
|
trading-net:
|
|
driver: bridge
|
|
ipam:
|
|
config:
|
|
- subnet: "${SUBNET:-172.30.0.0/16}"
|
|
gateway: "${GATEWAY:-172.30.0.1}"
|
|
|
|
volumes:
|
|
redis-data:
|
|
qdrant-data:
|
|
ollama-data:
|
|
n8n-data:
|
|
bareun-data:
|
|
pgdata:
|
|
|
|
services:
|
|
|
|
# ── Phase 1: 네이버 뉴스 수집기 ──────────────────────────
|
|
news-collector:
|
|
build:
|
|
context: ./news-collector
|
|
dockerfile: Dockerfile
|
|
container_name: trading-news-collector
|
|
restart: unless-stopped
|
|
init: true
|
|
ports:
|
|
- "8787:8787"
|
|
environment:
|
|
REDIS_HOST: redis
|
|
REDIS_PASSWORD: "${REDIS_PASSWORD}"
|
|
POSTGRES_HOST: "${POSTGRES_HOST}"
|
|
POSTGRES_PORT: "${POSTGRES_PORT}"
|
|
POSTGRES_DB: "${POSTGRES_DB}"
|
|
POSTGRES_USER: "${POSTGRES_USER}"
|
|
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
|
BAREUN_API_URL: "http://bareunaapi:5757"
|
|
OLLAMA_URL: "http://ollama:11434"
|
|
QDRANT_URL: "http://qdrant:6333"
|
|
TZ: "Asia/Seoul"
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.16
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
bareunaapi:
|
|
condition: service_healthy
|
|
postgres:
|
|
condition: service_healthy
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "2.0"
|
|
memory: 2G
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:8787/health')\" 2>/dev/null || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 5
|
|
start_period: 30s
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "100m"
|
|
max-file: "5"
|
|
|
|
# ── Phase 2: 한국투자증권 주가 API ──────────────────────
|
|
kis-api:
|
|
build:
|
|
context: ./kis-api
|
|
dockerfile: Dockerfile
|
|
container_name: trading-kis-api
|
|
restart: unless-stopped
|
|
init: true
|
|
ports:
|
|
- "8585:8585"
|
|
environment:
|
|
KIWOOM_APP_KEY: "${KIWOOM_APP_KEY}"
|
|
KIWOOM_SECRET_KEY: "${KIWOOM_SECRET_KEY}"
|
|
KIWOOM_BASE_URL: "${KIWOOM_BASE_URL:-https://api.kiwoom.com}"
|
|
REDIS_HOST: redis
|
|
REDIS_PASSWORD: "${REDIS_PASSWORD}"
|
|
POSTGRES_HOST: "${POSTGRES_HOST}"
|
|
POSTGRES_PORT: "${POSTGRES_PORT}"
|
|
POSTGRES_DB: "${POSTGRES_DB}"
|
|
POSTGRES_USER: "${POSTGRES_USER}"
|
|
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
|
TZ: "Asia/Seoul"
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.18
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
postgres:
|
|
condition: service_healthy
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "2.0"
|
|
memory: 1G
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:8585/health')\" 2>/dev/null || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 5
|
|
start_period: 30s
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "50m"
|
|
max-file: "5"
|
|
|
|
# ── Phase 2.5: 기술적 분석 엔진 ──────────────────────────
|
|
ta-engine:
|
|
build:
|
|
context: ./ta-engine
|
|
dockerfile: Dockerfile
|
|
container_name: trading-ta-engine
|
|
restart: unless-stopped
|
|
init: true
|
|
ports:
|
|
- "8484:8484"
|
|
environment:
|
|
REDIS_HOST: redis
|
|
REDIS_PASSWORD: "${REDIS_PASSWORD}"
|
|
POSTGRES_HOST: "${POSTGRES_HOST}"
|
|
POSTGRES_PORT: "${POSTGRES_PORT}"
|
|
POSTGRES_DB: "${POSTGRES_DB}"
|
|
POSTGRES_USER: "${POSTGRES_USER}"
|
|
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
|
OLLAMA_URL: "http://ollama:11434"
|
|
TZ: "Asia/Seoul"
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.23
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
postgres:
|
|
condition: service_healthy
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "2.0"
|
|
memory: 1G
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:8484/health')\" 2>/dev/null || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 5
|
|
start_period: 30s
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "50m"
|
|
max-file: "5"
|
|
|
|
# ── Phase 3: 종목 점수 엔진 ─────────────────────────────
|
|
score-engine:
|
|
build:
|
|
context: ./score-engine
|
|
dockerfile: Dockerfile
|
|
container_name: trading-score-engine
|
|
restart: unless-stopped
|
|
init: true
|
|
ports:
|
|
- "8686:8686"
|
|
environment:
|
|
REDIS_HOST: redis
|
|
REDIS_PASSWORD: "${REDIS_PASSWORD}"
|
|
POSTGRES_HOST: "${POSTGRES_HOST}"
|
|
POSTGRES_PORT: "${POSTGRES_PORT}"
|
|
POSTGRES_DB: "${POSTGRES_DB}"
|
|
POSTGRES_USER: "${POSTGRES_USER}"
|
|
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
|
TELEGRAM_BOT_TOKEN: "${TELEGRAM_BOT_TOKEN:-}"
|
|
TELEGRAM_CHAT_ID: "${TELEGRAM_CHAT_ID:-}"
|
|
ECOS_API_KEY: "${ECOS_API_KEY:-}"
|
|
TZ: "Asia/Seoul"
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.19
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
ta-engine:
|
|
condition: service_healthy
|
|
postgres:
|
|
condition: service_healthy
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "2.0"
|
|
memory: 1G
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:8686/health')\" 2>/dev/null || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 5
|
|
start_period: 30s
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "50m"
|
|
max-file: "5"
|
|
|
|
# ── Phase 3.7: 텔레그램 양방향 봇 ───────────────────────
|
|
telegram-bot:
|
|
build:
|
|
context: ./telegram-bot
|
|
dockerfile: Dockerfile
|
|
container_name: trading-telegram-bot
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_HOST: "${POSTGRES_HOST}"
|
|
POSTGRES_PORT: "${POSTGRES_PORT}"
|
|
POSTGRES_DB: "${POSTGRES_DB}"
|
|
POSTGRES_USER: "${POSTGRES_USER}"
|
|
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
|
TELEGRAM_BOT_TOKEN: "${TELEGRAM_BOT_TOKEN}"
|
|
TELEGRAM_CHAT_ID: "${TELEGRAM_CHAT_ID}"
|
|
OLLAMA_URL: "http://ollama:11434"
|
|
EXAONE_MODEL: "exaone3.5:7.8b"
|
|
TZ: "Asia/Seoul"
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.26
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "1.0"
|
|
memory: 512M
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "50m"
|
|
max-file: "5"
|
|
|
|
# ── Phase 3.6: 보조 시그널 (컨센서스/기관수급/매크로) ──
|
|
aux-signal:
|
|
build:
|
|
context: ./aux-signal
|
|
dockerfile: Dockerfile
|
|
container_name: trading-aux-signal
|
|
restart: unless-stopped
|
|
init: true
|
|
ports:
|
|
- "8282:8282"
|
|
environment:
|
|
POSTGRES_HOST: "${POSTGRES_HOST}"
|
|
POSTGRES_PORT: "${POSTGRES_PORT}"
|
|
POSTGRES_DB: "${POSTGRES_DB}"
|
|
POSTGRES_USER: "${POSTGRES_USER}"
|
|
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
|
ECOS_API_KEY: "${ECOS_API_KEY:-}"
|
|
TZ: "Asia/Seoul"
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.25
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "2.0"
|
|
memory: 1G
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:8282/health')\" 2>/dev/null || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 5
|
|
start_period: 60s
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "50m"
|
|
max-file: "5"
|
|
|
|
# ── Phase 3.5: 미국증시 동조 시그널 ─────────────────────
|
|
us-market:
|
|
build:
|
|
context: ./us-market
|
|
dockerfile: Dockerfile
|
|
container_name: trading-us-market
|
|
restart: unless-stopped
|
|
init: true
|
|
ports:
|
|
- "8383:8383"
|
|
environment:
|
|
POSTGRES_HOST: "${POSTGRES_HOST}"
|
|
POSTGRES_PORT: "${POSTGRES_PORT}"
|
|
POSTGRES_DB: "${POSTGRES_DB}"
|
|
POSTGRES_USER: "${POSTGRES_USER}"
|
|
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
|
FINNHUB_API_KEY: "${FINNHUB_API_KEY:-}"
|
|
ALPHAVANTAGE_API_KEY: "${ALPHAVANTAGE_API_KEY:-}"
|
|
TZ: "Asia/Seoul"
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.24
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "2.0"
|
|
memory: 1G
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:8383/health')\" 2>/dev/null || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 5
|
|
start_period: 60s
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "50m"
|
|
max-file: "5"
|
|
|
|
# ── Phase 5: 그래프 신경망 (GAT) ────────────────────────
|
|
graph-engine:
|
|
build:
|
|
context: ./graph-engine
|
|
dockerfile: Dockerfile
|
|
container_name: trading-graph-engine
|
|
restart: unless-stopped
|
|
init: true
|
|
ports:
|
|
- "9090:9090"
|
|
environment:
|
|
POSTGRES_HOST: "${POSTGRES_HOST}"
|
|
POSTGRES_PORT: "${POSTGRES_PORT}"
|
|
POSTGRES_DB: "${POSTGRES_DB}"
|
|
POSTGRES_USER: "${POSTGRES_USER}"
|
|
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
|
GRAPH_MODEL_DIR: "/mnt/nas/models/graph"
|
|
TZ: "Asia/Seoul"
|
|
volumes:
|
|
- /mnt/nas/models:/mnt/nas/models
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.27
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "4.0"
|
|
memory: 4G
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:9090/health')\" 2>/dev/null || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 5
|
|
start_period: 90s
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "50m"
|
|
max-file: "5"
|
|
|
|
# ── Phase 4: 대시보드 API ───────────────────────────────
|
|
dashboard-api:
|
|
build:
|
|
context: ./dashboard-api
|
|
dockerfile: Dockerfile
|
|
container_name: trading-dashboard-api
|
|
restart: unless-stopped
|
|
init: true
|
|
ports:
|
|
- "8989:8989"
|
|
environment:
|
|
REDIS_HOST: redis
|
|
REDIS_PASSWORD: "${REDIS_PASSWORD}"
|
|
POSTGRES_HOST: "${POSTGRES_HOST}"
|
|
POSTGRES_PORT: "${POSTGRES_PORT}"
|
|
POSTGRES_DB: "${POSTGRES_DB}"
|
|
POSTGRES_USER: "${POSTGRES_USER}"
|
|
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
|
JWT_SECRET: "${JWT_SECRET}"
|
|
JWT_EXPIRE_DAYS: "${JWT_EXPIRE_DAYS}"
|
|
ADMIN_EMAILS: "${ADMIN_EMAILS}"
|
|
TRUSTED_ORIGINS: "${TRUSTED_ORIGINS}"
|
|
TZ: "Asia/Seoul"
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.22
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
postgres:
|
|
condition: service_healthy
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "2.0"
|
|
memory: 1G
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "true"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 15s
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "50m"
|
|
max-file: "5"
|
|
|
|
# ──────────────────────────────────────────
|
|
# DART 공시 수집기 172.30.0.17
|
|
# ──────────────────────────────────────────
|
|
dart-collector:
|
|
build:
|
|
context: ./dart-collector
|
|
dockerfile: Dockerfile
|
|
container_name: trading-dart-collector
|
|
restart: unless-stopped
|
|
init: true
|
|
ports:
|
|
- "8888:8888"
|
|
environment:
|
|
DART_API_KEY: "${DART_API_KEY}"
|
|
REDIS_HOST: redis
|
|
REDIS_PASSWORD: "${REDIS_PASSWORD}"
|
|
POSTGRES_HOST: "${POSTGRES_HOST}"
|
|
POSTGRES_PORT: "${POSTGRES_PORT}"
|
|
POSTGRES_DB: "${POSTGRES_DB}"
|
|
POSTGRES_USER: "${POSTGRES_USER}"
|
|
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
|
BAREUN_API_URL: "http://bareunaapi:5757"
|
|
OLLAMA_URL: "http://ollama:11434"
|
|
QDRANT_URL: "http://qdrant:6333"
|
|
TZ: "Asia/Seoul"
|
|
N8N_EDITOR_BASE_URL: "https://n8.kyleyang.co.kr"
|
|
WEBHOOK_URL: "https://n8.kyleyang.co.kr"
|
|
N8N_HOST: "0.0.0.0"
|
|
N8N_LISTEN_ADDRESS: "0.0.0.0"
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.17
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
bareunaapi:
|
|
condition: service_healthy
|
|
postgres:
|
|
condition: service_healthy
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "2.0"
|
|
memory: 2G
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:8888/health')\" 2>/dev/null || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 5
|
|
start_period: 60s
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "100m"
|
|
max-file: "10"
|
|
|
|
# ──────────────────────────────────────────
|
|
# ──────────────────────────────────────────
|
|
# PostgreSQL 172.30.0.9
|
|
# ──────────────────────────────────────────
|
|
postgres:
|
|
image: postgres:16
|
|
container_name: trading-postgres
|
|
<<: *restart
|
|
ports:
|
|
- "55432:5432"
|
|
environment:
|
|
POSTGRES_USER: "${POSTGRES_USER}"
|
|
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
|
POSTGRES_DB: "${POSTGRES_DB}"
|
|
PGDATA: /var/lib/postgresql/data
|
|
TZ: "Asia/Seoul"
|
|
volumes:
|
|
- pgdata:/var/lib/postgresql/data
|
|
- ./init-db.sql:/docker-entrypoint-initdb.d/init-db.sql:ro
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.9
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "2.0"
|
|
memory: 2G
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 30s
|
|
logging: *default-logging
|
|
|
|
# Redis 172.30.0.10
|
|
# ──────────────────────────────────────────
|
|
redis:
|
|
image: redis:7.2-alpine
|
|
container_name: trading-redis
|
|
<<: *restart
|
|
command: >
|
|
redis-server
|
|
--requirepass ${REDIS_PASSWORD}
|
|
--maxmemory ${REDIS_MAX_MEMORY:-2gb}
|
|
--maxmemory-policy ${REDIS_MAXMEMORY_POLICY:-allkeys-lru}
|
|
--save 900 1
|
|
--save 300 10
|
|
--appendonly yes
|
|
--appendfsync everysec
|
|
--tcp-keepalive 300
|
|
ports:
|
|
- "6379:6379"
|
|
volumes:
|
|
- redis-data:/data
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.10
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "2.0"
|
|
memory: 1G
|
|
healthcheck:
|
|
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 10s
|
|
logging: *default-logging
|
|
|
|
# ──────────────────────────────────────────
|
|
# Qdrant 172.30.0.11
|
|
# ──────────────────────────────────────────
|
|
qdrant:
|
|
image: qdrant/qdrant:v1.9.2
|
|
container_name: trading-qdrant
|
|
<<: *restart
|
|
ports:
|
|
- "6333:6333"
|
|
- "6334:6334"
|
|
volumes:
|
|
- qdrant-data:/qdrant/storage
|
|
- ./qdrant-config/config.yaml:/qdrant/config/production.yaml:ro
|
|
environment:
|
|
QDRANT__SERVICE__HTTP_PORT: 6333
|
|
QDRANT__SERVICE__GRPC_PORT: 6334
|
|
QDRANT__LOG_LEVEL: INFO
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.11
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "4.0"
|
|
memory: 512M
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "true"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 10s
|
|
logging: *default-logging
|
|
|
|
# ──────────────────────────────────────────
|
|
# Bareun 형태소 엔진 172.30.0.15
|
|
# ──────────────────────────────────────────
|
|
bareun:
|
|
image: bareunai/bareun:latest
|
|
container_name: trading-bareun
|
|
<<: *restart
|
|
ports:
|
|
- "5656:5656"
|
|
- "9902:9902"
|
|
volumes:
|
|
- bareun-data:/bareun/var
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.15
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "4.0"
|
|
memory: 1500M
|
|
reservations:
|
|
memory: 512M
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "curl -sf http://localhost:9902/health || exit 0"]
|
|
interval: 20s
|
|
timeout: 10s
|
|
retries: 5
|
|
start_period: 30s
|
|
logging: *default-logging
|
|
|
|
# ──────────────────────────────────────────
|
|
# 바른 API (FastAPI 래퍼) 172.30.0.12
|
|
# ──────────────────────────────────────────
|
|
bareunaapi:
|
|
build:
|
|
context: ./bareunaapi
|
|
dockerfile: Dockerfile
|
|
container_name: trading-bareunaapi
|
|
<<: *restart
|
|
ports:
|
|
- "5757:5757"
|
|
environment:
|
|
BAREUN_API_KEY: "${BAREUN_API_KEY}"
|
|
BAREUN_SERVER_HOST: bareun
|
|
BAREUN_SERVER_PORT: 5656
|
|
REDIS_HOST: redis
|
|
REDIS_PORT: 6379
|
|
REDIS_PASSWORD: "${REDIS_PASSWORD}"
|
|
REDIS_DB: 1
|
|
NEWS_DEDUP_TTL: "${NEWS_DEDUP_TTL:-86400}"
|
|
LOG_LEVEL: INFO
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.12
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
bareun:
|
|
condition: service_started
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "4.0"
|
|
memory: 512M
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:5757/health"]
|
|
interval: 15s
|
|
timeout: 10s
|
|
retries: 5
|
|
start_period: 40s
|
|
logging: *default-logging
|
|
|
|
# ──────────────────────────────────────────
|
|
# Ollama 추론+임베딩 (GPU 0+1) 172.30.0.13
|
|
# GPU 0: RTX 3060 12GB → EXAONE 3.5 7.8B 추론
|
|
# GPU 1: RTX 3070 8GB → bge-m3 임베딩
|
|
# ──────────────────────────────────────────
|
|
ollama:
|
|
image: ollama/ollama:latest
|
|
container_name: trading-ollama
|
|
<<: *restart
|
|
ports:
|
|
- "11434:11434"
|
|
volumes:
|
|
- ollama-data:/root/.ollama
|
|
environment:
|
|
OLLAMA_NUM_PARALLEL: "${OLLAMA_NUM_PARALLEL:-4}"
|
|
OLLAMA_MAX_LOADED_MODELS: "2"
|
|
OLLAMA_FLASH_ATTENTION: "1"
|
|
CUDA_VISIBLE_DEVICES: "0,1"
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.13
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "8.0"
|
|
memory: 24G
|
|
reservations:
|
|
devices:
|
|
- driver: nvidia
|
|
device_ids: ["0", "1"]
|
|
capabilities: [gpu]
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "true"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 10s
|
|
logging: *default-logging
|
|
|
|
# ──────────────────────────────────────────
|
|
# n8n 워크플로우 172.30.0.20
|
|
# ──────────────────────────────────────────
|
|
n8n:
|
|
image: n8nio/n8n:latest
|
|
container_name: trading-n8n
|
|
<<: *restart
|
|
ports:
|
|
- "5678:5678"
|
|
environment:
|
|
N8N_BASIC_AUTH_ACTIVE: "true"
|
|
N8N_BASIC_AUTH_USER: "${N8N_BASIC_AUTH_USER}"
|
|
N8N_BASIC_AUTH_PASSWORD: "${N8N_BASIC_AUTH_PASSWORD}"
|
|
N8N_ENCRYPTION_KEY: "${N8N_ENCRYPTION_KEY}"
|
|
WEBHOOK_URL: "${N8N_WEBHOOK_URL}"
|
|
N8N_LOG_LEVEL: info
|
|
N8N_METRICS: "true"
|
|
# DB - NAS PostgreSQL
|
|
DB_TYPE: postgresdb
|
|
DB_POSTGRESDB_HOST: "${POSTGRES_HOST}"
|
|
DB_POSTGRESDB_PORT: "${POSTGRES_PORT:-5432}"
|
|
DB_POSTGRESDB_DATABASE: "${POSTGRES_DB}"
|
|
DB_POSTGRESDB_USER: "${POSTGRES_USER}"
|
|
DB_POSTGRESDB_PASSWORD: "${POSTGRES_PASSWORD}"
|
|
DB_POSTGRESDB_SCHEMA: n8n
|
|
DB_POSTGRESDB_POOL_SIZE: "10"
|
|
DB_POSTGRESDB_CONNECT_TIMEOUT: "30000"
|
|
# Redis Queue
|
|
QUEUE_BULL_REDIS_HOST: redis
|
|
QUEUE_BULL_REDIS_PORT: 6379
|
|
QUEUE_BULL_REDIS_PASSWORD: "${REDIS_PASSWORD}"
|
|
QUEUE_BULL_REDIS_DB: 0
|
|
EXECUTIONS_MODE: queue
|
|
N8N_GRACEFUL_SHUTDOWN_TIMEOUT: 30
|
|
TZ: "${TZ:-Asia/Seoul}"
|
|
GENERIC_TIMEZONE: "${TZ:-Asia/Seoul}"
|
|
# 내부 서비스 주소 (워크플로우에서 사용)
|
|
BAREUNAAPI_URL: "http://bareunaapi:5757"
|
|
OLLAMA_URL: "http://ollama:11434"
|
|
QDRANT_URL: "http://qdrant:6333"
|
|
REDIS_PASSWORD: "${REDIS_PASSWORD}"
|
|
POSTGRES_HOST: "${POSTGRES_HOST}"
|
|
POSTGRES_DB: "${POSTGRES_DB}"
|
|
POSTGRES_USER: "${POSTGRES_USER}"
|
|
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
|
NEWS_SIMILARITY_THRESHOLD: "${NEWS_SIMILARITY_THRESHOLD:-0.92}"
|
|
TELEGRAM_BOT_TOKEN: "${TELEGRAM_BOT_TOKEN:-}"
|
|
TELEGRAM_CHAT_ID: "${TELEGRAM_CHAT_ID:-}"
|
|
N8N_SECURE_COOKIE: "false"
|
|
# 실행 이력 자동 정리 (느린 n8n 해결)
|
|
EXECUTIONS_DATA_PRUNE: "true"
|
|
EXECUTIONS_DATA_MAX_AGE: 168
|
|
EXECUTIONS_DATA_PRUNE_MAX_COUNT: 10000
|
|
volumes:
|
|
- n8n-data:/home/node/.n8n
|
|
- /mnt/nas:/mnt/nas
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.20
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
qdrant:
|
|
condition: service_healthy
|
|
bareunaapi:
|
|
condition: service_healthy
|
|
postgres:
|
|
condition: service_healthy
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "4.0"
|
|
memory: 1G
|
|
healthcheck:
|
|
test: ["CMD", "wget", "-qO-", "http://localhost:5678/healthz"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 5
|
|
start_period: 60s
|
|
logging: *default-logging
|
|
|
|
# ──────────────────────────────────────────
|
|
# n8n Worker 172.30.0.21
|
|
# ──────────────────────────────────────────
|
|
n8n-worker:
|
|
image: n8nio/n8n:latest
|
|
container_name: trading-n8n-worker
|
|
<<: *restart
|
|
command: worker
|
|
environment:
|
|
N8N_ENCRYPTION_KEY: "${N8N_ENCRYPTION_KEY}"
|
|
DB_TYPE: postgresdb
|
|
DB_POSTGRESDB_HOST: "${POSTGRES_HOST}"
|
|
DB_POSTGRESDB_PORT: "${POSTGRES_PORT:-5432}"
|
|
DB_POSTGRESDB_DATABASE: "${POSTGRES_DB}"
|
|
DB_POSTGRESDB_USER: "${POSTGRES_USER}"
|
|
DB_POSTGRESDB_PASSWORD: "${POSTGRES_PASSWORD}"
|
|
DB_POSTGRESDB_SCHEMA: n8n
|
|
DB_POSTGRESDB_POOL_SIZE: "5"
|
|
DB_POSTGRESDB_CONNECT_TIMEOUT: "30000"
|
|
QUEUE_BULL_REDIS_HOST: redis
|
|
QUEUE_BULL_REDIS_PORT: 6379
|
|
QUEUE_BULL_REDIS_PASSWORD: "${REDIS_PASSWORD}"
|
|
QUEUE_BULL_REDIS_DB: 0
|
|
EXECUTIONS_MODE: queue
|
|
TZ: "${TZ:-Asia/Seoul}"
|
|
GENERIC_TIMEZONE: "${TZ:-Asia/Seoul}"
|
|
BAREUNAAPI_URL: "http://bareunaapi:5757"
|
|
OLLAMA_URL: "http://ollama:11434"
|
|
QDRANT_URL: "http://qdrant:6333"
|
|
REDIS_PASSWORD: "${REDIS_PASSWORD}"
|
|
volumes:
|
|
- n8n-data:/home/node/.n8n
|
|
- /mnt/nas:/mnt/nas
|
|
networks:
|
|
trading-net:
|
|
ipv4_address: 172.30.0.21
|
|
depends_on:
|
|
- n8n
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "4.0"
|
|
memory: 512M
|
|
logging: *default-logging
|