From 6b45adbbd292bdfeee6cb4b6371e8d07b702637e Mon Sep 17 00:00:00 2001 From: Blizzard Date: Sat, 18 Jul 2026 14:45:24 +0800 Subject: [PATCH] =?UTF-8?q?chore(deploy):=20=E4=B8=89=E6=9C=BA=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=20compose=20=E6=8B=86=E5=88=86=20+=20NATS=203=20?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E9=9B=86=E7=BE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 126=MinIO(已部署)、128=基础设施、132=应用。 - deploy/128-infra:NATS 3 节点集群(独立卷+cluster routes,暴露4222/4223/4224)+PG/Redis/ Milvus(自带etcd+自带内部minio,不复用126)/Neo4j/Jaeger;端口对局域网暴露、卷持久化。 - deploy/nats-cluster/nats{1,2,3}.conf:max_payload 一致 + jetstream + cluster。 - deploy/132-app:仅 gateway(3000:8080)+dispatcher+mcp-go+mcp-py,无 admin nginx;env 指向 126/128;mcp-go 补齐 MINIO env(本会话 blob 遗漏);NATS_STREAM_REPLICAS=3;.env.example 模板。 - docker-compose.prod.yml(单机):删 admin 服务(已内嵌)、mcp-go 补 MINIO env。 - 三个 compose 均 docker compose config 校验通过;.env 已 gitignore、.env.example 可提交。 Co-Authored-By: Claude Opus 4.8 --- deploy/128-infra/.env.example | 4 + deploy/128-infra/docker-compose.yml | 134 ++++++++++++++++++++++++++++ deploy/132-app/.env.example | 26 ++++++ deploy/132-app/docker-compose.yml | 80 +++++++++++++++++ deploy/nats-cluster/nats1.conf | 21 +++++ deploy/nats-cluster/nats2.conf | 20 +++++ deploy/nats-cluster/nats3.conf | 20 +++++ docker-compose.prod.yml | 16 ++-- 8 files changed, 313 insertions(+), 8 deletions(-) create mode 100644 deploy/128-infra/.env.example create mode 100644 deploy/128-infra/docker-compose.yml create mode 100644 deploy/132-app/.env.example create mode 100644 deploy/132-app/docker-compose.yml create mode 100644 deploy/nats-cluster/nats1.conf create mode 100644 deploy/nats-cluster/nats2.conf create mode 100644 deploy/nats-cluster/nats3.conf diff --git a/deploy/128-infra/.env.example b/deploy/128-infra/.env.example new file mode 100644 index 0000000..2d01bff --- /dev/null +++ b/deploy/128-infra/.env.example @@ -0,0 +1,4 @@ +# 基础设施机 128 的库密码。cp .env.example .env 后填写。 +# 这两个密码必须与 132 应用机 .env 里的 POSTGRES_PASSWORD / NEO4J_PASSWORD 一致。 +POSTGRES_PASSWORD=change-me-postgres +NEO4J_PASSWORD=change-me-neo4j diff --git a/deploy/128-infra/docker-compose.yml b/deploy/128-infra/docker-compose.yml new file mode 100644 index 0000000..8113b8a --- /dev/null +++ b/deploy/128-infra/docker-compose.yml @@ -0,0 +1,134 @@ +# sundynix-agentix · 基础设施机 192.168.100.128 +# +# cd deploy/128-infra && cp .env.example .env # 填库密码 +# docker compose up -d +# +# 说明: +# - 对象存储 MinIO 已单独部署在 192.168.100.126,本机不含业务 MinIO; +# 这里的 milvus-minio 仅供 Milvus 自己存段文件(内部网络,不对外)。 +# - 端口对局域网暴露(0.0.0.0),供应用机 192.168.100.132 连接。 +# ⚠️ Redis / Milvus / NATS 无认证,务必用防火墙只放行 132/126 网段,勿对公网开放。 +# - NATS 三节点集群(JetStream Raft):单机多容器,各自独立卷;对 132 暴露 4222/4223/4224。 +# 应用侧设 NATS_STREAM_REPLICAS=3,durable 流才有 quorum 容错。 +# - 全部卷持久化 + restart:unless-stopped,容器损坏/重启不丢数据。 + +services: + # ---------- NATS 3 节点集群 ---------- + nats1: + image: nats:2-alpine + command: ["-c", "/etc/nats/nats-server.conf"] + restart: unless-stopped + volumes: + - ../nats-cluster/nats1.conf:/etc/nats/nats-server.conf:ro + - nats1_data:/data/jetstream + ports: ["4222:4222", "8222:8222"] # 客户端 + 监控 + + nats2: + image: nats:2-alpine + command: ["-c", "/etc/nats/nats-server.conf"] + restart: unless-stopped + volumes: + - ../nats-cluster/nats2.conf:/etc/nats/nats-server.conf:ro + - nats2_data:/data/jetstream + ports: ["4223:4222"] + + nats3: + image: nats:2-alpine + command: ["-c", "/etc/nats/nats-server.conf"] + restart: unless-stopped + volumes: + - ../nats-cluster/nats3.conf:/etc/nats/nats-server.conf:ro + - nats3_data:/data/jetstream + ports: ["4224:4222"] + + # ---------- 关系库 / 缓存 ---------- + postgres: + image: postgres:16-alpine + restart: unless-stopped + environment: + POSTGRES_USER: sundynix + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?需在 .env 设置} + POSTGRES_DB: sundynix + volumes: ["pg_data:/var/lib/postgresql/data"] + ports: ["5432:5432"] + healthcheck: + test: ["CMD-SHELL", "pg_isready -U sundynix"] + interval: 5s + timeout: 5s + retries: 10 + + redis: + image: redis:7-alpine + restart: unless-stopped + volumes: ["redis_data:/data"] + ports: ["6379:6379"] + + # ---------- 向量库 Milvus(自带 etcd + 自带内部 minio,不复用 126)---------- + milvus-etcd: + image: quay.io/coreos/etcd:v3.5.14 + restart: unless-stopped + environment: + ETCD_AUTO_COMPACTION_MODE: revision + ETCD_AUTO_COMPACTION_RETENTION: "1000" + ETCD_QUOTA_BACKEND_BYTES: "4294967296" + ETCD_SNAPSHOT_COUNT: "50000" + command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd + volumes: ["milvus_etcd:/etcd"] + + milvus-minio: + image: minio/minio:RELEASE.2023-03-20T20-16-18Z + restart: unless-stopped + environment: + MINIO_ACCESS_KEY: minioadmin # Milvus 段存储专用,内部网络不对外 + MINIO_SECRET_KEY: minioadmin + command: minio server /minio_data + volumes: ["milvus_minio_data:/minio_data"] + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] + interval: 30s + timeout: 20s + retries: 3 + + milvus: + image: milvusdb/milvus:v2.4.13 + command: ["milvus", "run", "standalone"] + restart: unless-stopped + environment: + ETCD_ENDPOINTS: milvus-etcd:2379 + MINIO_ADDRESS: milvus-minio:9000 + volumes: ["milvus_data:/var/lib/milvus"] + ports: ["19530:19530"] # 供 132 的 mcp-go 连接 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"] + interval: 15s + timeout: 10s + retries: 10 + depends_on: [milvus-etcd, milvus-minio] + + # ---------- 图谱库 ---------- + neo4j: + image: neo4j:5-community + restart: unless-stopped + environment: + NEO4J_AUTH: neo4j/${NEO4J_PASSWORD:?需在 .env 设置} + volumes: ["neo4j_data:/data"] + ports: ["7687:7687", "7474:7474"] # bolt + 浏览器 + + # ---------- 链路追踪(可选,不需要可删)---------- + jaeger: + image: jaegertracing/all-in-one:1.60 + restart: unless-stopped + environment: + COLLECTOR_OTLP_ENABLED: "true" + ports: ["16686:16686", "4318:4318"] # UI + OTLP/HTTP 接收 + +volumes: + nats1_data: + nats2_data: + nats3_data: + pg_data: + redis_data: + milvus_etcd: + milvus_minio_data: + milvus_data: + neo4j_data: diff --git a/deploy/132-app/.env.example b/deploy/132-app/.env.example new file mode 100644 index 0000000..0fa1b5a --- /dev/null +++ b/deploy/132-app/.env.example @@ -0,0 +1,26 @@ +# 应用机 132 的密钥与凭据。cp .env.example .env 后填写(.env 不进 git)。 +# 三个应用服务共享 SUNDYNIX_SECRET_KEY,必须完全一致(否则跨服务密文解不开)。 + +# —— 引导密钥(生成:openssl rand -base64 32)—— +SUNDYNIX_SECRET_KEY=change-me-secret-32 +JWT_SECRET=change-me-jwt-32 + +# —— 库密码(须与 128-infra/.env 一致)—— +POSTGRES_PASSWORD=change-me-postgres +NEO4J_PASSWORD=change-me-neo4j + +# —— 对象存储(126 上的 MinIO 凭据)—— +MINIO_ACCESS_KEY=change-me-minio-access +MINIO_SECRET_KEY=change-me-minio-secret +MINIO_BUCKET=sundynix-docs + +# —— 管理员白名单:首次注册后拿 user.id 填这里,重启 gateway 生效(逗号分隔多个)—— +ADMIN_USER_IDS= + +# —— CORS:admin 同源免;桌面端/web 从 3000 跨源直连需填实际 origin(frp 外网域名等), +# 生产(APP_ENV=production)下缺省不放行任意源。多个逗号分隔。—— +CORS_ALLOW_ORIGIN= + +# —— 可选调参 —— +DISPATCHER_CONCURRENCY=8 +TASK_TOKEN_BUDGET=200000 diff --git a/deploy/132-app/docker-compose.yml b/deploy/132-app/docker-compose.yml new file mode 100644 index 0000000..a78aef4 --- /dev/null +++ b/deploy/132-app/docker-compose.yml @@ -0,0 +1,80 @@ +# sundynix-agentix · 应用机 192.168.100.132 +# +# cd deploy/132-app && cp .env.example .env # 填密钥/密码/MinIO 凭据 +# docker compose up -d --build # 在 132 本地构建并运行(Gitea 自动部署也走这条) +# +# 说明: +# - 对外只暴露 gateway:3000(frp 映射)。gateway 已内嵌 admin 控制台(同源 serve UI+API), +# 无需单独 admin nginx 容器。dispatcher/mcp-go/mcp-py 只走 NATS,无对外端口。 +# - 基础设施在 128(PG/NATS集群/Redis/Milvus/Neo4j),对象存储在 126(MinIO),env 全部指向它们。 +# - 构建上下文为仓库根(../..,因 Go 服务 replace ../sundynix-shared)。 +# - NATS_STREAM_REPLICAS=3:durable 流走 128 三节点集群,有 Raft quorum 容错。 + +services: + gateway: + build: { context: ../.., dockerfile: sundynix-gateway/Dockerfile } + image: sundynix/gateway:${TAG:-latest} + restart: unless-stopped + environment: + APP_ENV: production + GATEWAY_ADDR: ":8080" + NATS_URL: nats://192.168.100.128:4222,nats://192.168.100.128:4223,nats://192.168.100.128:4224 + NATS_STREAM_REPLICAS: "3" + POSTGRES_DSN: postgres://sundynix:${POSTGRES_PASSWORD:?}@192.168.100.128:5432/sundynix?sslmode=disable + REDIS_ADDR: 192.168.100.128:6379 + MINIO_ENDPOINT: 192.168.100.126:9000 + MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:?} + MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:?} + MINIO_BUCKET: ${MINIO_BUCKET:-sundynix-docs} + SUNDYNIX_SECRET_KEY: ${SUNDYNIX_SECRET_KEY:?} + JWT_SECRET: ${JWT_SECRET:?} + ADMIN_USER_IDS: ${ADMIN_USER_IDS:-} + CORS_ALLOW_ORIGIN: ${CORS_ALLOW_ORIGIN:-*} + OTEL_EXPORTER_OTLP_ENDPOINT: http://192.168.100.128:4318 + ports: ["3000:8080"] # frp 外网 → 132:3000 → 容器 8080 + + dispatcher: + build: { context: ../.., dockerfile: sundynix-dispatcher/Dockerfile } + image: sundynix/dispatcher:${TAG:-latest} + restart: unless-stopped + environment: + APP_ENV: production + NATS_URL: nats://192.168.100.128:4222,nats://192.168.100.128:4223,nats://192.168.100.128:4224 + NATS_STREAM_REPLICAS: "3" + SUNDYNIX_SECRET_KEY: ${SUNDYNIX_SECRET_KEY:?} + OTEL_EXPORTER_OTLP_ENDPOINT: http://192.168.100.128:4318 + DISPATCHER_CONCURRENCY: ${DISPATCHER_CONCURRENCY:-8} + TASK_TOKEN_BUDGET: ${TASK_TOKEN_BUDGET:-200000} + + mcp-go: + build: { context: ../.., dockerfile: sundynix-mcp-go/Dockerfile } + image: sundynix/mcp-go:${TAG:-latest} + restart: unless-stopped + environment: + APP_ENV: production + NATS_URL: nats://192.168.100.128:4222,nats://192.168.100.128:4223,nats://192.168.100.128:4224 + NATS_STREAM_REPLICAS: "3" + POSTGRES_DSN: postgres://sundynix:${POSTGRES_PASSWORD:?}@192.168.100.128:5432/sundynix?sslmode=disable + REDIS_ADDR: 192.168.100.128:6379 + MILVUS_ADDR: 192.168.100.128:19530 + NEO4J_URI: neo4j://192.168.100.128:7687 + NEO4J_USER: neo4j + NEO4J_PASS: ${NEO4J_PASSWORD:?} + # 报告源/产物 + KB 正文对象存储(本会话新加 blob)——指向 126,勿漏,否则 fallback localhost 连不上。 + MINIO_ENDPOINT: 192.168.100.126:9000 + MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:?} + MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:?} + MINIO_BUCKET: ${MINIO_BUCKET:-sundynix-docs} + SUNDYNIX_SECRET_KEY: ${SUNDYNIX_SECRET_KEY:?} + OTEL_EXPORTER_OTLP_ENDPOINT: http://192.168.100.128:4318 + + mcp-py: + build: { context: ../../sundynix-mcp-py, dockerfile: Dockerfile } + image: sundynix/mcp-py:${TAG:-latest} + restart: unless-stopped + environment: + NATS_URL: nats://192.168.100.128:4222,nats://192.168.100.128:4223,nats://192.168.100.128:4224 + # secure_sandbox(代码隔离执行)需访问宿主 Docker 拉一次性容器。安全敏感:等于把 132 宿主 + # Docker 暴露给该服务(可逃逸);不需要代码执行工具可注释掉这两行。mcp-py 无对外端口。 + volumes: + - /var/run/docker.sock:/var/run/docker.sock diff --git a/deploy/nats-cluster/nats1.conf b/deploy/nats-cluster/nats1.conf new file mode 100644 index 0000000..7021266 --- /dev/null +++ b/deploy/nats-cluster/nats1.conf @@ -0,0 +1,21 @@ +# NATS 集群节点 1 / 3(JetStream Raft)。单机多节点:三容器同宿主(128),各自独立卷。 +# 三节点的 max_payload 必须一致;server_name 唯一;routes 指向另外两节点的 cluster 端口。 +server_name: nats1 +port: 4222 +http_port: 8222 +max_payload: 8MB + +jetstream { + store_dir: "/data/jetstream" + max_memory_store: 1GB + max_file_store: 10GB +} + +cluster { + name: sundynix-cluster + listen: 0.0.0.0:6222 + routes: [ + nats-route://nats2:6222 + nats-route://nats3:6222 + ] +} diff --git a/deploy/nats-cluster/nats2.conf b/deploy/nats-cluster/nats2.conf new file mode 100644 index 0000000..ac90ec6 --- /dev/null +++ b/deploy/nats-cluster/nats2.conf @@ -0,0 +1,20 @@ +# NATS 集群节点 2 / 3(JetStream Raft)。max_payload 与其余节点一致;routes 指向 nats1/nats3。 +server_name: nats2 +port: 4222 +http_port: 8222 +max_payload: 8MB + +jetstream { + store_dir: "/data/jetstream" + max_memory_store: 1GB + max_file_store: 10GB +} + +cluster { + name: sundynix-cluster + listen: 0.0.0.0:6222 + routes: [ + nats-route://nats1:6222 + nats-route://nats3:6222 + ] +} diff --git a/deploy/nats-cluster/nats3.conf b/deploy/nats-cluster/nats3.conf new file mode 100644 index 0000000..67f60f3 --- /dev/null +++ b/deploy/nats-cluster/nats3.conf @@ -0,0 +1,20 @@ +# NATS 集群节点 3 / 3(JetStream Raft)。max_payload 与其余节点一致;routes 指向 nats1/nats2。 +server_name: nats3 +port: 4222 +http_port: 8222 +max_payload: 8MB + +jetstream { + store_dir: "/data/jetstream" + max_memory_store: 1GB + max_file_store: 10GB +} + +cluster { + name: sundynix-cluster + listen: 0.0.0.0:6222 + routes: [ + nats-route://nats1:6222 + nats-route://nats2:6222 + ] +} diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 2e3e95a..1edd8bf 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -5,7 +5,7 @@ # # 设计: # - 应用经服务名互连(postgres:5432 等),不依赖 127.0.0.1;基建端口不对宿主暴露(仅内网)。 -# - 仅暴露 admin 控制台(:${HTTP_PORT}) 与 gateway(:${GATEWAY_PORT},供桌面端直连) + Jaeger UI(可选)。 +# - 仅暴露 gateway(:${GATEWAY_PORT},内嵌 admin 控制台 + API + 供桌面端直连) + Jaeger UI(可选)。 # - 引导密钥从 .env 注入(SUNDYNIX_SECRET_KEY/JWT_SECRET/各库密码);模型 API key 仍在控制台配、加密存 PG。 # - APP_ENV=production → 强制校验密钥、锁 CORS、启用管理员白名单。 @@ -64,6 +64,11 @@ services: NEO4J_URI: neo4j://neo4j:7687 NEO4J_USER: neo4j NEO4J_PASS: ${NEO4J_PASSWORD:?} + # 报告源/产物 + KB 正文对象存储(blob)——与 gateway 同一 MinIO,勿漏否则 fallback localhost。 + MINIO_ENDPOINT: minio:9000 + MINIO_ACCESS_KEY: minioadmin + MINIO_SECRET_KEY: minioadmin + MINIO_BUCKET: sundynix-docs SUNDYNIX_SECRET_KEY: *secret-key OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4318 depends_on: @@ -83,13 +88,8 @@ services: depends_on: nats: { condition: service_started } - admin: - build: { context: sundynix-admin, dockerfile: Dockerfile } - image: sundynix/admin:${TAG:-latest} - restart: unless-stopped - ports: ["${HTTP_PORT:-80}:80"] # 运维控制台 + 反代网关 API(一个 URL 访问整套) - depends_on: - gateway: { condition: service_started } + # admin 运维控制台已内嵌进 gateway(go:embed,见 sundynix-gateway/internal/webui), + # 不再需要单独的 nginx 容器:访问 gateway:${GATEWAY_PORT} 即同时得到控制台 UI + API。 # ---------- 基础设施(端口不对宿主暴露,仅 compose 内网) ---------- nats: