# Docker Compose 生产部署 ## 快速开始 ```bash # 1. 配置环境变量 cp .env.example .env # 编辑 .env,填入真实的 API Key 和密码 # 有域名时设置 DOMAIN=your-domain.com # 2. 启动所有服务 docker compose up -d # 3. 查看状态 docker compose ps docker compose logs -f ``` ## 服务清单 | 服务 | 端口 | 说明 | |------|------|------| | caddy | 80 / 443 | 反向代理,自动 HTTPS | | gateway | 8080 (内部) | API 网关 | | ai-core | 8081 (内部) | AI 核心 | | memory-service | 8091 (内部) | 记忆检索 | | voice-service | 8093 (内部) | 语音识别 | | iot-debug-service | 8083 (内部) | IoT 调试 | | ethend | 9090 (外部) | 管理控制台 | | postgres | 5432 (内部) | 数据库 (pgvector/pg16) | | redis | 6379 (内部) | 缓存 | | qdrant | 6333 (内部) | 向量数据库 | | minio | 9000 (内部) | 对象存储 | | searxng | 8080 (内部) | 搜索引擎 | ## 架构分层 ``` Internet │ ▼ ┌────────────────────────────────────┐ │ Caddy (:80/:443) │ ← 反向代理层 │ TLS 自动签发 · WebSocket 路由 │ └────────────┬───────────────────────┘ │ ┌─────▼──────┐ │ Gateway │ ← API 网关层 │ :8080 │ 唯一后端入口 └──┬───┬───┬─┘ │ │ │ ┌────▼┐ ┌▼───▼──┐ ┌────────────┐ │AI │ │Memory │ │Voice/IoT/ │ ← 业务服务层 │Core │ │:8091 │ │ethend │ │:8081│ └───────┘ └────────────┘ └──┬──┘ │ ┌────▼───────────────────────────┐ │ Postgres · Redis · Qdrant · MinIO│ ← 基础设施层 └────────────────────────────────┘ ``` 12 个容器通过内部 Docker 网络通信(服务名即主机名)。仅 Caddy 和 ethend 向宿主机暴露端口。 --- ## 各服务详解 ### 反向代理层 **Caddy** — 唯一对外入口,监听 `${CADDY_HTTP_PORT:-80}` 和 `${CADDY_HTTPS_PORT:-443}`。 路由规则(由 [Caddyfile](../../Caddyfile) 定义): | 路径 | 转发目标 | 说明 | |------|----------|------| | `/ws/*` | gateway:8080 | WebSocket 实时推送 | | `/api/*` | gateway:8080 | REST API(含 X-Forwarded-For) | | 其他 | 内置响应 | 前端占位页 | - `DOMAIN` 非空时,Caddy 自动从 Let's Encrypt 签发 TLS 证书 - `DOMAIN` 为空时仅 HTTP(`:80` 模式) ### API 网关层 **Gateway** (`:8080`) — 核心枢纽,所有客户端请求经由此处。启动要求 PostgreSQL 和 Redis 均通过健康检查。 功能:JWT 认证、用户注册/登录、WebSocket Hub、限流、CORS、消息排队。 关键 env: - `JWT_SECRET` + `INTERNAL_SERVICE_TOKEN` — 缺失会导致 **panic** - `ADMIN_USERNAME` + `ADMIN_PASSWORD` — 首次启动时自动创建管理员账号 - `ALLOWED_ORIGINS` — 跨域白名单,需包含 ethend 端口和前端地址 ### 业务服务层 **AI-Core** (`:8081`) — LLM 编排核心:人设注入、多子会话调度、工具调用、后台思考。依赖 PostgreSQL 健康。 **Memory Service** (`:8091`) — 记忆 CRUD + 语义检索 + 衰减 + 自动提取。依赖 PostgreSQL 健康。 **Voice Service** (`:8093`) — DashScope 云 STT + whisper.cpp 本地 ASR。无启动依赖。 **IoT Debug Service** (`:8083`) — 8 个模拟智能家居设备。无启动依赖。 **ethend** (`:9090`) — 管理控制台 (Node.js Express):服务启停、日志监控、性能分析。端口直接暴露到宿主机(不经过 Caddy)。无启动依赖。 ### 基础设施层 **PostgreSQL** (`:5432`) — `pgvector/pgvector:pg16` 镜像,存储用户、会话、消息、记忆、IoT 设备状态。健康检查 `pg_isready` 每 10s 一次,5 次失败才判定不健康。 **Redis** (`:6379`) — `redis:7-alpine`,WebSocket 会话缓存、JWT 黑名单、限流计数器。健康检查 `redis-cli ping`。 **Qdrant** (`:6333-6334`) — 向量数据库,记忆语义检索。无健康检查(非启动依赖)。 **MinIO** (`:9000-9001`) — S3 兼容对象存储,存放用户上传的图片和文件。 --- ## 启动序列 ``` Phase 1 (并行) Phase 2 (并行) Phase 3 (并行) ┌──────────┐ ┌──────────────┐ ┌──────────────┐ │ postgres │─健康──▶ │ gateway │ │ voice-svc │ │ redis │─健康──▶ │ ai-core │ │ iot-debug │ │ qdrant │ │ memory-svc │ │ ethend │ │ minio │ │ caddy │ │ │ │ searxng │ └──────────────┘ └──────────────┘ └──────────┘ 等 PG/Redis 健康 无依赖,立即启动 总是启动 ``` - **Phase 1** — 基础设施 + 搜索引擎,无依赖,总是最先启动 - **Phase 2** — gateway / ai-core / memory-service 等待 PostgreSQL + Redis 健康检查通过;caddy 等待 gateway 容器启动 - **Phase 3** — voice-service / iot-debug-service / ethend 零依赖,立即启动 --- ## 运行时状态 ```bash $ docker compose ps NAME STATUS PORTS cyrene_caddy Up 0.0.0.0:80->80, 0.0.0.0:443->443 cyrene_gateway Up (healthy) 8080/tcp cyrene_ai_core Up 8081/tcp cyrene_memory_service Up 8091/tcp cyrene_voice_service Up 8093/tcp cyrene_iot_debug_service Up 8083/tcp cyrene_ethend Up 0.0.0.0:9090->9090/tcp cyrene_searxng Up 8080/tcp cyrene_postgres Up (healthy) 5432/tcp cyrene_redis Up (healthy) 6379/tcp cyrene_qdrant Up 6333-6334/tcp cyrene_minio Up 9000-9001/tcp ``` **对外可达**: | 地址 | 经过 | |------|------| | `http:///api/v1/chat` | Caddy → Gateway → AI-Core | | `ws:///ws` | Caddy → Gateway WebSocket | | `http://:9090` | 直连 ethend | **数据持久化** — 5 个 Docker 卷(`docker compose down` 不会删除,需加 `-v` 才清空): | 卷 | 内容 | |----|------| | `pg_data` | 所有业务数据(用户、会话、消息、记忆) | | `redis_data` | 会话缓存 | | `qdrant_data` | 向量索引 | | `minio_data` | 上传文件 | | `caddy_data` | TLS 证书 | --- ## 环境变量 所有变量在 `.env` 中配置,完整模板见 [.env.example](../../.env.example)。 ### 必填(服务启动 panic 若缺失) | 变量 | 说明 | |------|------| | `LLM_API_URL` | LLM API 地址 | | `LLM_API_KEY` | LLM API 密钥 | | `ADMIN_PASSWORD` | 管理员密码 | | `JWT_SECRET` | JWT 签名密钥 | | `INTERNAL_SERVICE_TOKEN` | 服务间通信认证 Token | | `POSTGRES_PASSWORD` | 数据库密码 | | `MINIO_SECRET_KEY` | MinIO 密钥 | ### 推荐配置 | 变量 | 默认值 | 说明 | |------|--------|------| | `LLM_MODEL` | `gpt-4o` | 主模型 | | `LLM_FALLBACK_MODEL` | `gpt-4o-mini` | 回退模型 | | `ADMIN_USERNAME` | `admin` | 管理员用户名 | | `ADMIN_NICKNAME` | `管理员` | AI 对管理员的称呼 | | `REGISTRATION_ENABLED` | `false` | 开放注册(生产建议关闭) | | `ALLOWED_ORIGINS` | `http://localhost:5173,...` | CORS 白名单 | | `ENABLE_BACKGROUND_THINKING` | `true` | 后台自主思考 | | `ACME_EMAIL` | — | Let's Encrypt 证书邮箱 | ## 域名与 HTTPS 在 `.env` 中设置 `DOMAIN` 和 `ACME_EMAIL`: ```bash # 无域名(仅 HTTP) DOMAIN= # 有域名(自动 HTTPS) DOMAIN=your-domain.com ACME_EMAIL=admin@your-domain.com ``` Caddy 在 `DOMAIN` 有值时自动从 Let's Encrypt 申请 TLS 证书,无需额外配置。 ## 常用命令 ```bash # 重新构建并启动单个服务 docker compose up -d --build gateway # 查看特定服务日志 docker compose logs -f ai-core # 停止所有服务 docker compose down # 停止并删除数据卷(危险) docker compose down -v ```