Files
beaver_project/部署指南.md
steven_li b3767dd4ab feat(outlook): 添加 Outlook MCP 集成支持并优化分页功能
- 新增 NANO_OUTLOOK_MCP_URL 和 NANO_OUTLOOK_MCP_SERVER_ID 环境变量配置
- 实现 Outlook 邮件和日历的分页查询功能,添加安全参数验证
- 为 app-instance 创建脚本添加 Outlook MCP 服务器 ID 参数
- 更新前端 Outlook 页面实现邮件列表和日历事件的分页浏览
- 添加 Git 忽略文件配置和 Docker 挂载路径修复

BREAKING CHANGE: Outlook 集成现在需要配置 MCP URL 和服务器 ID 环境变量
2026-03-16 17:01:58 +08:00

619 lines
13 KiB
Markdown
Raw 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.

# nano_project 本机一步步部署指南
这份文档适合第一次在本机把整个项目跑起来的人,目标是:
- 在一台 `Linux``WSL2 Ubuntu` 机器上
-`Docker` 跑完整链路
- 最后能在浏览器里注册账号,并自动创建你的专属实例
这套项目当前的推荐本机测试方式是:
- `auth-portal`
- `authz-service`
- `deploy-control`
- `router-proxy`
- `app-instance`
全部一起跑。
如果你只单独跑某个前端页面,页面能打开,但注册、登录、创建实例这些核心能力不一定会通。
---
## 0. 先说前提
### 适合的环境
推荐:
- Linux
- WSL2 Ubuntu
不推荐直接按这份文档在纯 Windows 命令行里照抄,因为这里依赖:
- Docker
- Bash 脚本
- Docker Socket 挂载
- 宿主机目录挂载
### 你需要先装好的工具
- `docker`
- `git`
- `curl`
- `openssl`
- `python3`
先检查:
```bash
docker --version
docker ps
python3 --version
openssl version
curl --version
```
如果 `docker ps` 报错,先把 Docker 启动起来。
---
## 1. 进入项目根目录
```bash
cd /home/ivan/xuan/nano_project
```
你执行完以后,建议顺手确认一下当前目录:
```bash
pwd
```
你应该看到:
```text
/home/ivan/xuan/nano_project
```
---
## 2. 准备一套本机测试变量
### 为什么这里用 `127.0.0.1.nip.io`
因为这是最省事的本机测试域名方案。
它的作用是:
- `alice.127.0.0.1.nip.io`
- 自动解析到 `127.0.0.1`
这样 `router-proxy` 就能按子域名区分不同实例。
### 直接复制执行
```bash
export PROJECT_ROOT=/home/ivan/xuan/nano_project
export NANO_NET=nano-instance-edge
export NANO_DEPLOY_TOKEN="$(openssl rand -hex 32)"
export NANO_AUTHZ_INTERNAL_TOKEN="$(openssl rand -hex 32)"
export NANO_SERVER_IP=127.0.0.1
export NANO_BASE_DOMAIN=127.0.0.1.nip.io
export NANO_PROVIDER=openai
export NANO_MODEL=openai/gpt-5
export NANO_API_KEY='把这里换成你自己的模型 API Key'
export NANO_API_BASE=''
export NANO_AUTHZ_URL='http://nano-authz-service:19090'
export NANO_OUTLOOK_MCP_URL=''
export NANO_OUTLOOK_MCP_SERVER_ID='outlook_mcp'
export NANO_DEPLOY_URL='http://nano-deploy-control:8090'
```
### 这里每个变量大概是干什么的
- `PROJECT_ROOT`
- 仓库根目录
- `NANO_NET`
- 所有容器共用的 Docker 网络
- `NANO_DEPLOY_TOKEN`
- `auth-portal` / `authz-service``deploy-control` 时的鉴权 token
- `NANO_AUTHZ_INTERNAL_TOKEN`
- AuthZ 内部接口 token
- `NANO_BASE_DOMAIN`
- 实例基础域名
- `NANO_PROVIDER`
- 新实例默认模型提供商
- `NANO_MODEL`
- 新实例默认模型
- `NANO_API_KEY`
- 新实例默认模型 API Key
- `NANO_API_BASE`
- 自定义模型网关地址,没有就留空
- `NANO_AUTHZ_URL`
- 容器网络内访问 AuthZ 的地址
- `NANO_DEPLOY_URL`
- 容器网络内访问 deploy-control 的地址
- `NANO_OUTLOOK_MCP_URL`
- 可选;如果你有独立 Outlook MCP 服务,可以在这里填
- `NANO_OUTLOOK_MCP_SERVER_ID`
- Outlook MCP 默认 server id当前推荐固定 `outlook_mcp`
### 一个特别重要的提醒
`NANO_API_KEY` 不能空着。
如果这里不填,新用户注册时虽然页面可能能走到一半,但自动创建 `app-instance` 时大概率失败,因为实例配置里需要 `APP_INSTANCE_API_KEY`
---
## 3. 创建运行目录
```bash
mkdir -p \
"$PROJECT_ROOT/authz-service/runtime/data" \
"$PROJECT_ROOT/app-instance/runtime/instances" \
"$PROJECT_ROOT/app-instance/runtime/registry" \
"$PROJECT_ROOT/router-proxy/runtime/conf.d"
```
这一步的作用是给下面几个东西留持久化空间:
- AuthZ 数据
- 实例注册表
- 每个用户实例的配置目录
- router-proxy 生成出来的路由文件
---
## 4. 构建镜像
第一次构建会比较久,正常情况要等几分钟。
```bash
cd "$PROJECT_ROOT"
docker build -t nano/app-instance:latest app-instance
docker build -t nano/authz-service:latest authz-service
docker build -t nano/deploy-control:latest deploy-control
docker build -t nano/auth-portal:latest auth-portal/src
```
如果中间有某个镜像失败,不要继续往下跑,先把失败那一步修掉。
常见失败原因:
- Docker 没启动
- 网络拉镜像失败
- 你的本机磁盘空间不够
---
## 5. 创建共享 Docker 网络
```bash
docker network inspect "$NANO_NET" >/dev/null 2>&1 || docker network create "$NANO_NET"
```
执行完后可确认:
```bash
docker network ls | grep "$NANO_NET"
```
应该能看到:
```text
nano-instance-edge
```
---
## 6. 启动统一入口代理 `router-proxy`
```bash
cd "$PROJECT_ROOT"
PROXY_NETWORK_NAME="$NANO_NET" \
PROXY_HTTP_PORT=8088 \
./router-proxy/start-proxy.sh --replace
```
启动后,统一入口走:
```text
http://<你的实例slug>.127.0.0.1.nip.io:8088
```
例如:
```text
http://alice.127.0.0.1.nip.io:8088
```
---
## 7. 启动 `authz-service`
```bash
docker rm -f nano-authz-service >/dev/null 2>&1 || true
docker run -d \
--name nano-authz-service \
--restart unless-stopped \
--network "$NANO_NET" \
-p 19090:19090 \
-v "$PROJECT_ROOT/authz-service/runtime/data:/var/lib/authz-service/data" \
-e AUTHZ_ISSUER="$NANO_AUTHZ_URL" \
-e AUTHZ_INTERNAL_TOKEN="$NANO_AUTHZ_INTERNAL_TOKEN" \
-e DEPLOY_API_BASE_URL="$NANO_DEPLOY_URL" \
-e DEPLOY_API_TOKEN="$NANO_DEPLOY_TOKEN" \
nano/authz-service:latest
```
### 这里有个很关键的坑
`AUTHZ_ISSUER` 这里不能写成:
```text
http://127.0.0.1:19090
```
因为后面新创建的 `app-instance` 也是通过 Docker 网络去访问 AuthZ 的。
所以这里要写容器网络里可访问的地址:
```text
http://nano-authz-service:19090
```
---
## 8. 启动 `deploy-control`
这一步最容易配错的点是挂载目录。
一定要注意:
- `app-instance``router-proxy` 的宿主机路径,要按原路径挂进容器
- 不能偷懒挂到容器里的另一个短路径比如 `/app-instance`
- 同时要把 `APP_INSTANCE_DIR``ROUTER_PROXY_DIR` 也明确传进去
直接执行:
```bash
docker rm -f nano-deploy-control >/dev/null 2>&1 || true
docker run -d \
--name nano-deploy-control \
--restart unless-stopped \
--network "$NANO_NET" \
-p 8090:8090 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$PROJECT_ROOT/app-instance:$PROJECT_ROOT/app-instance" \
-v "$PROJECT_ROOT/router-proxy:$PROJECT_ROOT/router-proxy" \
-e APP_INSTANCE_DIR="$PROJECT_ROOT/app-instance" \
-e ROUTER_PROXY_DIR="$PROJECT_ROOT/router-proxy" \
-e DEPLOY_CONTROL_API_TOKEN="$NANO_DEPLOY_TOKEN" \
-e APP_INSTANCE_IMAGE="nano/app-instance:latest" \
-e APP_INSTANCE_NETWORK_NAME="$NANO_NET" \
-e APP_INSTANCE_PROVIDER="$NANO_PROVIDER" \
-e APP_INSTANCE_MODEL="$NANO_MODEL" \
-e APP_INSTANCE_API_KEY="$NANO_API_KEY" \
-e APP_INSTANCE_API_BASE="$NANO_API_BASE" \
-e DEFAULT_AUTHZ_BASE_URL="$NANO_AUTHZ_URL" \
-e DEFAULT_AUTHZ_OUTLOOK_MCP_URL="$NANO_OUTLOOK_MCP_URL" \
-e DEFAULT_OUTLOOK_MCP_SERVER_ID="$NANO_OUTLOOK_MCP_SERVER_ID" \
-e DEPLOY_PUBLIC_SCHEME="http" \
-e DEPLOY_PUBLIC_BASE_DOMAIN="$NANO_BASE_DOMAIN" \
-e DEPLOY_PUBLIC_PORT="8088" \
-e DEPLOY_AUTO_START_PROXY="1" \
nano/deploy-control:latest
```
### 这一步在做什么
`deploy-control` 会负责:
- 收到“创建实例”的请求
- 调用 `app-instance/create-instance.sh`
- 通过 Docker 创建对应用户实例
- 刷新 `router-proxy`
---
## 9. 启动 `auth-portal`
```bash
docker rm -f nano-auth-portal >/dev/null 2>&1 || true
docker run -d \
--name nano-auth-portal \
--restart unless-stopped \
--network "$NANO_NET" \
-p 3081:3081 \
-e AUTHZ_API_BASE_URL="$NANO_AUTHZ_URL" \
-e DEPLOY_API_BASE_URL="$NANO_DEPLOY_URL" \
-e DEPLOY_API_TOKEN="$NANO_DEPLOY_TOKEN" \
nano/auth-portal:latest
```
这个页面就是用户看到的登录/注册入口。
---
## 10. 做健康检查
### 先检查接口
```bash
curl http://127.0.0.1:19090/healthz
curl http://127.0.0.1:8090/healthz
curl -I http://127.0.0.1:3081
```
你应该大致看到:
- `authz-service` 返回健康 JSON
- `deploy-control` 返回健康 JSON
- `auth-portal` 返回 `HTTP/1.1 200 OK`
### 再看容器状态
```bash
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
```
你至少应该能看到这些容器:
- `nano-authz-service`
- `nano-deploy-control`
- `nano-auth-portal`
- `nano-router-proxy`
### 再看一下代理日志
```bash
docker logs --tail=50 nano-router-proxy
```
如果这一步没有明显报错,就可以开始浏览器测试了。
---
## 11. 浏览器首次测试
打开:
```text
http://127.0.0.1:3081/register
```
然后按顺序操作:
1. 注册一个新账号
2. 注册成功后,系统会自动创建一个你的专属实例
3. 浏览器应该跳到你的实例地址
跳转目标一般长这样:
```text
http://你的slug.127.0.0.1.nip.io:8088
```
例如:
```text
http://alice.127.0.0.1.nip.io:8088
```
---
## 12. 确认实例真的被创建出来了
```bash
cd "$PROJECT_ROOT/app-instance"
./list-instances.sh
./list-instances.sh --json
```
你应该能看到类似:
- `instance_id`
- `instance_slug`
- `container_name`
- `public_url`
以及对应的 `app-instance-<slug>` 容器。
你还可以继续查:
```bash
docker ps --format 'table {{.Names}}\t{{.Status}}' | grep app-instance
```
---
## 13. 如果你只是想单独看前端页面
如果你只是想看 `auth-portal` 页面样子,不跑全链路,也可以单独启动它的前端开发模式:
```bash
cd /home/ivan/xuan/nano_project/auth-portal/src
npm install
npm run dev
```
然后打开:
```text
http://127.0.0.1:3081
```
但是要注意:
- 这只能看页面
- 注册、登录、创建实例这些动作是否成功,仍然取决于 `authz-service``deploy-control` 有没有另外启动
---
## 14. 一键排错命令
如果你感觉“不对劲”,先跑这几条:
```bash
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
docker logs --tail=100 nano-authz-service
docker logs --tail=100 nano-deploy-control
docker logs --tail=100 nano-auth-portal
docker logs --tail=100 nano-router-proxy
curl http://127.0.0.1:19090/healthz
curl http://127.0.0.1:8090/healthz
curl -I http://127.0.0.1:3081
```
如果是实例创建失败,再加两条:
```bash
cd "$PROJECT_ROOT/app-instance"
./list-instances.sh --json
docker ps --format 'table {{.Names}}\t{{.Status}}' | grep app-instance
```
---
## 15. 最常见的坑
### 1. API Key 没填
现象:
- 注册页面提交后创建实例失败
原因:
- `APP_INSTANCE_API_KEY` 没有有效值
### 2. Docker 没启动
现象:
- `deploy-control` 无法创建实例
-`docker ps` 本身就报错
### 3. `AUTHZ_ISSUER` 写成了 `127.0.0.1`
错误写法:
```text
http://127.0.0.1:19090
```
正确写法:
```text
http://nano-authz-service:19090
```
原因:
- 新实例容器里访问不到宿主机自己的 `127.0.0.1:19090`
### 4. `deploy-control` 的路径挂载写错
错误思路:
- 把宿主机的 `app-instance` 挂到容器里的 `/app-instance`
正确思路:
- 宿主机原路径挂进去
- 并设置:
- `APP_INSTANCE_DIR="$PROJECT_ROOT/app-instance"`
- `ROUTER_PROXY_DIR="$PROJECT_ROOT/router-proxy"`
### 5. `nip.io` 解析失败
如果实例跳转地址打不开,先试:
```bash
ping 127.0.0.1.nip.io
```
如果你本地网络把 `nip.io` 拦了,这套子域名测试方式就会失效。
### 6. 端口被占用
默认会用到这些端口:
- `3081`
- `8090`
- `19090`
- `8088`
你可以先查:
```bash
ss -ltnp | grep -E '3081|8090|19090|8088'
```
---
## 16. 如果你要重新来一遍
如果你只是想“重新部署这四个基础容器”,可以先停掉它们:
```bash
docker rm -f \
nano-auth-portal \
nano-authz-service \
nano-deploy-control \
nano-router-proxy 2>/dev/null || true
```
如果你还想把旧实例容器也一起清掉,再额外处理 `app-instance-*`
注意:
- 不要在你还需要旧数据的时候乱删 `runtime/`
- `authz-service/runtime/data``app-instance/runtime/instances` 里都有持久化数据
---
## 17. 本机部署成功后的结果应该是什么
如果整个流程正常,最后你会得到:
- 一个可以打开的注册页:
- `http://127.0.0.1:3081/register`
- 一个统一实例入口代理:
- `http://<slug>.127.0.0.1.nip.io:8088`
- 一个能自动创建用户专属容器的部署控制面
- 一份实例注册表:
- `app-instance/runtime/registry/instances.json`
---
## 18. 你下一步最建议做什么
第一次建议这样测:
1. 用全新用户名注册一个测试账号
2. 确认浏览器跳到了你的实例 URL
3. 再执行 `./app-instance/list-instances.sh --json`
4. 确认注册表里真的有这条实例记录
如果你后面想要,我还可以继续补两份文档:
- `服务器部署指南.md`
- 面向公网服务器、固定 IP、长期运行
- `常见报错排查.md`
- 专门收集 502、超时、实例起不来、MCP 鉴权失败这类问题