Files
trading/docker-compose.yml
kyu a9e805be05
trading/pipeline/head This commit looks good
feat: 자동매매 자본금 설정값 분리 (하드코딩 ₩1천만 제거)
손실%·포지션사이즈 기준 자본금을 .env TRADE_DEFAULT_CAPITAL로 설정 가능하게 분리.
- TRADE_SETTINGS['default_capital']을 env에서 로드(미설정 시 1천만)
- /trade/propose·/trade/scan의 하드코딩 Query default 제거 → 설정값 폴백
- docker-compose score-engine에 TRADE_DEFAULT_CAPITAL 전달
- 런타임 변경은 기존 POST /trade/settings 그대로 (재시작 시 env로 복귀)
- /performance/simulation의 capital은 what-if 입력값이라 유지

검증: 컨테이너 env 인식·/trade/settings 반영·런타임 override(5천만)·healthy 확인.
실잔고(키움) 자동연동은 계좌번호 미설정으로 별도 과제.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 15:45:46 +09:00

865 lines
26 KiB
YAML
Raw Permalink 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.
# ============================================================
# 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"
GEMINI_API_KEY: "${GEMINI_API_KEY:-}"
GEMINI_MODEL: "${GEMINI_MODEL:-gemini-2.5-pro}"
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:-}"
GEMINI_API_KEY: "${GEMINI_API_KEY:-}"
GEMINI_MODEL: "${GEMINI_MODEL:-gemini-2.5-pro}"
TRADE_DEFAULT_CAPITAL: "${TRADE_DEFAULT_CAPITAL:-10000000}"
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
# ── 메모리 튜닝 (32GB 호스트, 로컬 SSD 기준) ──
# shared_buffers 4GB (컨테이너 limit의 ~67% — Postgres hot cache)
# effective_cache_size 12GB (OS 페이지캐시 포함 추정 가용 캐시)
# work_mem 32MB (단일 sort/hash 메모리. max_conn × 동시쿼리 곱 주의)
# maintenance_work_mem 512MB (VACUUM/CREATE INDEX)
# random_page_cost 1.1 (SSD 비용 모델)
# effective_io_concurrency 200 (SSD)
# parallel_workers 병렬쿼리 활성화 (백테스트/학습용 큰 JOIN에 효과)
# WAL/checkpoint 쓰기 부하 줄이고 회복 시간 살짝 늘림 (적절 균형)
command: >
postgres
-c shared_buffers=4GB
-c effective_cache_size=12GB
-c work_mem=32MB
-c maintenance_work_mem=512MB
-c random_page_cost=1.1
-c effective_io_concurrency=200
-c max_parallel_workers_per_gather=4
-c max_parallel_workers=8
-c max_worker_processes=12
-c checkpoint_timeout=15min
-c max_wal_size=4GB
-c checkpoint_completion_target=0.9
-c wal_compression=on
-c log_min_duration_statement=1000
-c log_checkpoints=on
-c log_temp_files=10MB
-c jit=off
shm_size: 512mb
deploy:
resources:
limits:
cpus: "4.0"
memory: 12G
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: 1G
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: 1G
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: 1G
logging: *default-logging