Operation Ollama-First v5.0 / Phase 10 + Phase 12 收尾 docker-compose.mcp.yml — 4+3 容器 MCP stack - postgres-mcp (port 3001): Claude 直連 momo_pro DB read-only RBAC - mcp-omnisearch (3003): Tavily 主 + Exa 備(取代 Gemini Grounding) 避開 Brave(2026-02 取消免費 tier) - firecrawl-self (3002): 自建爬蟲,SPA 反爬蟲 - filesystem-mcp (3004): 跨主機檔案 read-only 護欄 #2 落地(Owen v5.0 鐵律 / ADR-033): firecrawl-self mem_limit:2g + cpus:1.5 PLAYWRIGHT_BROWSER_POOL_MAX=3 chrome-reaper sidecar 每小時清 Chrome zombies 安全設計: - 全部 127.0.0.1 暴露(不外網) - read-only volume mount(filesystem 只能讀) - postgres-mcp RBAC mcp_readonly role 限 SELECT 6 熱表 - API key 全走 env var 不寫死 ADR-031 — MCP 自建 Stack 治理決策 - 取代 Gemini Grounding 唯一通路(多供應商策略) - 預期 70%+ grounding 流量走免費 Tavily - 188 主機資源 +4-5GB RAM 可控 - Migration Plan:6 步驟(含 Tavily/Exa key 申請 + mcp_readonly role 預建) 啟用前置(待統帥): 1. .env 加 TAVILY_API_KEY / EXA_API_KEY / MCP_POSTGRES_PASSWORD / FIRECRAWL_AUTH_KEY 2. momo-db 建 mcp_readonly role + GRANT SELECT 3. ssh wooo@110 → ssh ollama@188 → docker compose -f docker-compose.mcp.yml up -d Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
183 lines
8.2 KiB
YAML
183 lines
8.2 KiB
YAML
# =============================================================================
|
||
# Operation Ollama-First v5.0 / Phase 10 — MCP 自建 Stack
|
||
# =============================================================================
|
||
# 部署位置:188 主機,與既有 momo-pro 容器共存
|
||
# 啟動方式:docker compose -f docker-compose.mcp.yml up -d
|
||
# 啟用前置:
|
||
# 1. .env 加 TAVILY_API_KEY(Tavily 1000 free credits/月)
|
||
# 2. .env 加 EXA_API_KEY(Exa 備援)
|
||
# 3. 確認 188 防火牆 allow 172.x docker bridge 對外(firecrawl 抓網頁)
|
||
#
|
||
# 4 個 MCP server 對應 ADR-031 (Phase 10) 規格:
|
||
# - postgres-mcp: Claude 直連 momo_pro DB(read-only RBAC)
|
||
# - mcp-omnisearch: 取代 Gemini Grounding,Tavily 主 + Exa 備
|
||
# - firecrawl-self: 自建爬蟲(含 Owen v5.0 護欄 #2 mem_limit:2g + chrome-reaper)
|
||
# - filesystem-mcp: 跨主機檔案操作
|
||
#
|
||
# 護欄一覽(ADR-033):
|
||
# #2 Firecrawl mem_limit:2g + chrome-reaper sidecar + 1.8GB 告警
|
||
#
|
||
# 部署後驗收(給統帥):
|
||
# curl http://localhost:3001/health # postgres-mcp
|
||
# curl http://localhost:3002/health # firecrawl
|
||
# curl http://localhost:3003/health # omnisearch
|
||
# curl http://localhost:3004/health # filesystem
|
||
# =============================================================================
|
||
|
||
services:
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────
|
||
# postgres-mcp: Claude 直連 momo_pro DB(read-only)
|
||
# ─────────────────────────────────────────────────────────────────────────
|
||
postgres-mcp:
|
||
image: mcp/postgres:latest
|
||
container_name: momo-mcp-postgres
|
||
restart: unless-stopped
|
||
init: true
|
||
ports:
|
||
- "127.0.0.1:3001:3000" # 僅 localhost 暴露(避免外網直連 DB)
|
||
environment:
|
||
- POSTGRES_HOST=momo-db
|
||
- POSTGRES_PORT=5432
|
||
- POSTGRES_USER=mcp_readonly # 須在 momo-db 預先建 read-only role
|
||
- POSTGRES_PASSWORD=${MCP_POSTGRES_PASSWORD}
|
||
- POSTGRES_DB=momo_pro
|
||
# RBAC:限制 SELECT 到熱表
|
||
- ALLOWED_TABLES=ai_insights,ai_calls,mcp_calls,daily_sales_snapshot,competitor_prices,products
|
||
networks:
|
||
- momo-shared
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
memory: 256m
|
||
healthcheck:
|
||
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
|
||
interval: 30s
|
||
timeout: 5s
|
||
retries: 3
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────
|
||
# mcp-omnisearch: 統一搜尋(Tavily 主 + Exa 備)
|
||
# ─────────────────────────────────────────────────────────────────────────
|
||
mcp-omnisearch:
|
||
image: ghcr.io/spences10/mcp-omnisearch:latest
|
||
container_name: momo-mcp-omnisearch
|
||
restart: unless-stopped
|
||
init: true
|
||
ports:
|
||
- "127.0.0.1:3003:3000"
|
||
environment:
|
||
# 排除 Brave(2026-02 已取消免費 tier)
|
||
- TAVILY_API_KEY=${TAVILY_API_KEY} # 1000 free credits/月(主)
|
||
- EXA_API_KEY=${EXA_API_KEY} # 1000 free credits/月(備援)
|
||
- SEARCH_PROVIDER_ORDER=tavily,exa # fallback 順序
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
memory: 512m
|
||
healthcheck:
|
||
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
|
||
interval: 30s
|
||
timeout: 5s
|
||
retries: 3
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────
|
||
# firecrawl-self: 自建爬蟲(含 Owen v5.0 護欄 #2)
|
||
# ─────────────────────────────────────────────────────────────────────────
|
||
firecrawl-self:
|
||
image: firecrawl/firecrawl:latest
|
||
container_name: momo-mcp-firecrawl
|
||
restart: unless-stopped
|
||
init: true
|
||
ports:
|
||
- "127.0.0.1:3002:3002"
|
||
environment:
|
||
- REDIS_URL=redis://firecrawl-redis:6379
|
||
- PLAYWRIGHT_MICROSERVICE_URL=http://firecrawl-playwright:3000
|
||
- PLAYWRIGHT_BROWSER_POOL_MAX=3 # ⭐ 護欄 #2:瀏覽器池上限
|
||
- SCRAPE_TIMEOUT_MS=30000
|
||
- BULL_AUTH_KEY=${FIRECRAWL_AUTH_KEY:-momo-internal-only}
|
||
depends_on:
|
||
- firecrawl-redis
|
||
- firecrawl-playwright
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
memory: 2g # ⭐ Owen v5.0 護欄 #2 硬上限
|
||
cpus: '1.5'
|
||
healthcheck:
|
||
test: ["CMD", "wget", "-qO-", "http://localhost:3002/health"]
|
||
interval: 30s
|
||
timeout: 10s
|
||
retries: 3
|
||
start_period: 60s
|
||
|
||
firecrawl-redis:
|
||
image: redis:7-alpine
|
||
container_name: momo-mcp-firecrawl-redis
|
||
restart: unless-stopped
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
memory: 128m
|
||
|
||
firecrawl-playwright:
|
||
image: firecrawl/playwright:latest
|
||
container_name: momo-mcp-firecrawl-playwright
|
||
restart: unless-stopped
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
memory: 1.5g
|
||
cpus: '1.0'
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────
|
||
# chrome-reaper: 護欄 #2 — 每小時清 Chrome 殘留
|
||
# ─────────────────────────────────────────────────────────────────────────
|
||
chrome-reaper:
|
||
image: alpine:3
|
||
container_name: momo-mcp-chrome-reaper
|
||
restart: unless-stopped
|
||
command: |
|
||
sh -c '
|
||
apk add --no-cache docker-cli;
|
||
while true; do
|
||
echo "[reaper] $(date) cleaning Chrome zombies...";
|
||
docker exec momo-mcp-firecrawl-playwright \
|
||
sh -c "pkill -f \"chrome.*--type=zygote\" 2>/dev/null || true;
|
||
pkill -f \"chrome.*--type=renderer\" 2>/dev/null || true" \
|
||
2>/dev/null || true;
|
||
sleep 3600;
|
||
done
|
||
'
|
||
volumes:
|
||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────
|
||
# filesystem-mcp: 跨主機檔案操作(限本地)
|
||
# ─────────────────────────────────────────────────────────────────────────
|
||
filesystem-mcp:
|
||
image: mcp/filesystem:latest
|
||
container_name: momo-mcp-filesystem
|
||
restart: unless-stopped
|
||
init: true
|
||
ports:
|
||
- "127.0.0.1:3004:3000"
|
||
environment:
|
||
- ALLOWED_PATHS=/data,/logs # 限制存取範圍
|
||
volumes:
|
||
- ./data:/data:ro # ⚠️ ro 唯讀,避免 LLM 改檔
|
||
- ./logs:/logs:ro
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
memory: 128m
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# 與既有 momo-pro 共用 network(讓 postgres-mcp 連 momo-db)
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
networks:
|
||
momo-shared:
|
||
external: true
|
||
name: momo-pro_default # 既有 docker-compose.yml 的 default network
|