# 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://<IP>/api/v1/chat` | Caddy → Gateway → AI-Core |
| `ws://<IP>/ws` | Caddy → Gateway WebSocket |
| `http://<IP>: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
```
