# Beaver Project 本机部署指南 这份文档用于在一台 Linux 或 WSL2 Ubuntu 机器上跑完整链路: - `auth-portal` - `authz-service` - `deploy-control` - `router-proxy` - 自动创建出来的 `app-instance` 目标结果: - 浏览器能打开 `http://127.0.0.1:3081/register` - 注册账号后自动创建专属实例 - 浏览器跳转到 `http://.127.0.0.1.nip.io:8088` 如果你只单独启动某个前端页面,页面可以打开,但注册、登录、创建实例这些动作不一定能通。 ## 0. 前提 推荐环境: - Linux - WSL2 Ubuntu 需要工具: - `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/beaver_project pwd ``` 预期目录: ```text /home/ivan/xuan/beaver_project ``` ## 2. 准备本机测试变量 本机测试推荐用 `127.0.0.1.nip.io`。例如: ```text alice.127.0.0.1.nip.io -> 127.0.0.1 ``` 这样 `router-proxy` 可以按子域名区分不同实例。 直接执行: ```bash export PROJECT_ROOT=/home/ivan/xuan/beaver_project export BEAVER_NET=beaver-instance-edge export BEAVER_PROXY_CONTAINER_NAME=beaver-router-proxy export BEAVER_DEPLOY_TOKEN="$(openssl rand -hex 32)" export BEAVER_AUTHZ_INTERNAL_TOKEN="$(openssl rand -hex 32)" export BEAVER_BASE_DOMAIN=127.0.0.1.nip.io export BEAVER_AUTHZ_URL='http://beaver-authz-service:19090' export BEAVER_DEPLOY_URL='http://beaver-deploy-control:8090' export BEAVER_OUTLOOK_MCP_URL='' export BEAVER_OUTLOOK_MCP_SERVER_ID='outlook_mcp' ``` 变量说明: | 变量 | 作用 | | --- | --- | | `PROJECT_ROOT` | 仓库根目录 | | `BEAVER_NET` | 所有容器共用的 Docker network | | `BEAVER_DEPLOY_TOKEN` | `auth-portal` / `authz-service` 调 `deploy-control` 的 token | | `BEAVER_AUTHZ_INTERNAL_TOKEN` | AuthZ 内部接口 token | | `BEAVER_BASE_DOMAIN` | 新实例的基域名 | | `BEAVER_AUTHZ_URL` | 容器网络内访问 AuthZ 的地址 | | `BEAVER_DEPLOY_URL` | 容器网络内访问 deploy-control 的地址 | | `BEAVER_OUTLOOK_MCP_URL` | 可选 Outlook MCP HTTP 地址 | | `BEAVER_OUTLOOK_MCP_SERVER_ID` | Outlook MCP server id,默认 `outlook_mcp` | `BEAVER_AUTHZ_URL` 和 `BEAVER_DEPLOY_URL` 必须带协议头。正确写法: ```text http://beaver-authz-service:19090 http://beaver-deploy-control:8090 ``` 错误写法: ```text beaver-authz-service:19090 beaver-deploy-control:8090 127.0.0.1:19090 127.0.0.1:8090 ``` 如果漏了 `http://`,注册页可能报: ```text 502: Request URL is missing an 'http://' or 'https://' protocol. ``` 如果你改了 shell 里的变量,已经运行的容器不会自动更新。改完这些变量后,至少要重建: - `beaver-authz-service` - `beaver-auth-portal` ## 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 beaver/app-instance:latest app-instance docker build -t beaver/authz-service:latest authz-service docker build -t beaver/deploy-control:latest deploy-control docker build -t beaver/auth-portal:latest auth-portal/src ``` 如果某个镜像构建失败,先修构建错误,不要继续往下跑。 ## 5. 创建共享 Docker 网络 ```bash docker network inspect "$BEAVER_NET" >/dev/null 2>&1 || docker network create "$BEAVER_NET" docker network ls | grep "$BEAVER_NET" ``` 预期能看到: ```text beaver-instance-edge ``` ## 6. 启动 router-proxy ```bash cd "$PROJECT_ROOT" PROXY_NETWORK_NAME="$BEAVER_NET" \ PROXY_CONTAINER_NAME="$BEAVER_PROXY_CONTAINER_NAME" \ PROXY_HTTP_PORT=8088 \ ./router-proxy/start-proxy.sh --replace ``` 实例统一入口: ```text http://.127.0.0.1.nip.io:8088 ``` 示例: ```text http://alice.127.0.0.1.nip.io:8088 ``` ## 7. 启动 authz-service ```bash docker rm -f beaver-authz-service >/dev/null 2>&1 || true docker run -d \ --name beaver-authz-service \ --restart unless-stopped \ --network "$BEAVER_NET" \ -p 19090:19090 \ -v "$PROJECT_ROOT/authz-service/runtime/data:/var/lib/authz-service/data" \ -e AUTHZ_ISSUER="$BEAVER_AUTHZ_URL" \ -e AUTHZ_INTERNAL_TOKEN="$BEAVER_AUTHZ_INTERNAL_TOKEN" \ -e DEPLOY_API_BASE_URL="$BEAVER_DEPLOY_URL" \ -e DEPLOY_API_TOKEN="$BEAVER_DEPLOY_TOKEN" \ beaver/authz-service:latest ``` 重点: - `AUTHZ_ISSUER` 在当前部署里要写 `http://beaver-authz-service:19090` - 不要写 `http://127.0.0.1:19090` - 新创建的 `app-instance` 容器要通过 Docker network 访问 AuthZ 检查关键环境变量: ```bash docker inspect beaver-authz-service --format '{{range .Config.Env}}{{println .}}{{end}}' \ | egrep '^(AUTHZ_ISSUER|DEPLOY_API_BASE_URL)=' ``` ## 8. 启动 deploy-control `deploy-control` 会挂载 Docker socket,再创建新的 `app-instance` 容器。这里最容易错的是路径挂载: - 要把宿主机真实路径按原路径挂进容器。 - 不要把 `app-instance` 挂到容器里的 `/app-instance` 这种短路径。 - `APP_INSTANCE_DIR` 和 `ROUTER_PROXY_DIR` 要和挂载路径一致。 直接执行: ```bash docker rm -f beaver-deploy-control >/dev/null 2>&1 || true docker run -d \ --name beaver-deploy-control \ --restart unless-stopped \ --network "$BEAVER_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 PROXY_CONTAINER_NAME="$BEAVER_PROXY_CONTAINER_NAME" \ -e PROXY_NETWORK_NAME="$BEAVER_NET" \ -e DEPLOY_CONTROL_API_TOKEN="$BEAVER_DEPLOY_TOKEN" \ -e APP_INSTANCE_IMAGE="beaver/app-instance:latest" \ -e APP_INSTANCE_NETWORK_NAME="$BEAVER_NET" \ -e DEFAULT_AUTHZ_BASE_URL="$BEAVER_AUTHZ_URL" \ -e DEFAULT_AUTHZ_OUTLOOK_MCP_URL="$BEAVER_OUTLOOK_MCP_URL" \ -e DEFAULT_OUTLOOK_MCP_SERVER_ID="$BEAVER_OUTLOOK_MCP_SERVER_ID" \ -e DEPLOY_PUBLIC_SCHEME="http" \ -e DEPLOY_PUBLIC_BASE_DOMAIN="$BEAVER_BASE_DOMAIN" \ -e DEPLOY_PUBLIC_PORT="8088" \ -e DEPLOY_AUTO_START_PROXY="1" \ beaver/deploy-control:latest ``` 当前版本创建实例时会传 `--skip-provider-config`,也就是先不写 provider、model 或 API key。注册成功后,`auth-portal` 会进入模型配置引导页,再调用 `deploy-control /api/instances/configure-provider` 写入该实例的 `config.json` 并重启容器。 ## 9. 启动 auth-portal ```bash docker rm -f beaver-auth-portal >/dev/null 2>&1 || true docker run -d \ --name beaver-auth-portal \ --restart unless-stopped \ --network "$BEAVER_NET" \ -p 3081:3081 \ -e AUTHZ_API_BASE_URL="$BEAVER_AUTHZ_URL" \ -e DEPLOY_API_BASE_URL="$BEAVER_DEPLOY_URL" \ -e DEPLOY_API_TOKEN="$BEAVER_DEPLOY_TOKEN" \ beaver/auth-portal:latest ``` 检查关键环境变量: ```bash docker inspect beaver-auth-portal --format '{{range .Config.Env}}{{println .}}{{end}}' \ | egrep '^(AUTHZ_API_BASE_URL|DEPLOY_API_BASE_URL)=' ``` ## 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 docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' docker logs --tail=50 beaver-router-proxy ``` 至少应该看到这些容器: - `beaver-authz-service` - `beaver-deploy-control` - `beaver-auth-portal` - `beaver-router-proxy` ## 11. 浏览器首次测试 打开: ```text http://127.0.0.1:3081/register ``` 预期流程: 1. 注册一个新账号。 2. Portal 创建不含模型凭证的实例。 3. 页面进入模型配置引导。 4. 填 provider、model、API key 后确认,或暂时跳过。 5. 浏览器跳到你的实例地址。 跳转目标示例: ```text http://alice.127.0.0.1.nip.io:8088 ``` ## 12. 确认实例已创建 ```bash cd "$PROJECT_ROOT/app-instance" ./list-instances.sh ./list-instances.sh --json docker ps --format 'table {{.Names}}\t{{.Status}}' | grep app-instance ``` 注册表里应包含: - `instance_id` - `instance_slug` - `container_name` - `public_url` - `instance_host` ## 13. 只看 auth-portal 页面 如果只想看 Portal 页面,不跑全链路: ```bash cd /home/ivan/xuan/beaver_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 beaver-authz-service docker logs --tail=100 beaver-deploy-control docker logs --tail=100 beaver-auth-portal docker logs --tail=100 beaver-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 ``` 排查 URL 变量: ```bash docker inspect beaver-authz-service --format '{{range .Config.Env}}{{println .}}{{end}}' \ | egrep '^(AUTHZ_ISSUER|DEPLOY_API_BASE_URL)=' docker inspect beaver-auth-portal --format '{{range .Config.Env}}{{println .}}{{end}}' \ | egrep '^(AUTHZ_API_BASE_URL|DEPLOY_API_BASE_URL)=' ``` 它们都必须是完整 URL,不能是空字符串,也不能是裸 `host:port`。 ## 15. 常见问题 ### 注册页报 URL 缺少协议 现象: ```text 502: Request URL is missing an 'http://' or 'https://' protocol. ``` 优先检查: - `beaver-authz-service` 里的 `DEPLOY_API_BASE_URL` - `beaver-auth-portal` 里的 `AUTHZ_API_BASE_URL` - `beaver-auth-portal` 里的 `DEPLOY_API_BASE_URL` 如果你只是改了当前 shell 变量,但没有重建容器,旧值还会继续生效。 ### `AUTHZ_ISSUER` 写成了 `127.0.0.1` 错误: ```text http://127.0.0.1:19090 ``` 正确: ```text http://beaver-authz-service:19090 ``` 原因是 `app-instance` 容器里的 `127.0.0.1` 指向它自己。 ### deploy-control 路径挂载写错 错误思路: ```text 宿主机 app-instance -> 容器 /app-instance ``` 正确思路: ```text $PROJECT_ROOT/app-instance -> $PROJECT_ROOT/app-instance $PROJECT_ROOT/router-proxy -> $PROJECT_ROOT/router-proxy ``` 因为 `deploy-control` 会通过宿主机 Docker socket 再创建新容器,传给 Docker 的 bind mount 源路径必须是宿主机真实路径。 ### `nip.io` 解析失败 检查: ```bash ping 127.0.0.1.nip.io ``` 如果本地网络屏蔽了 `nip.io`,子域名测试会失败。可以临时换成本机 hosts 或正式域名。 ### 端口被占用 默认端口: - `3081` - `8088` - `8090` - `19090` 检查: ```bash ss -ltnp | grep -E '3081|8088|8090|19090' ``` ## 16. 重新部署基础容器 只重建基础四个容器: ```bash docker rm -f \ beaver-auth-portal \ beaver-authz-service \ beaver-deploy-control \ beaver-router-proxy 2>/dev/null || true ``` 这不会自动删除实例数据。如果你还需要旧账号、旧实例或模型配置,不要删除 `runtime/` 目录。