Files
beaver_project/部署指南.md
2026-06-03 12:06:34 +08:00

24 KiB
Raw Blame History

Beaver Project 本机部署指南

这份文档用于在一台 Linux 或 WSL2 Ubuntu 机器上跑完整链路:

  • auth-portal
  • authz-service
  • deploy-control
  • router-proxy
  • 自动创建出来的 app-instance

目标结果:

  • 浏览器能打开 http://127.0.0.1:3081/register
  • 注册账号后自动创建专属实例
  • 浏览器跳转到 http://<slug>.localhost:8088

如果你只单独启动某个前端页面,页面可以打开,但注册、登录、创建实例这些动作不一定能通。

0. 前提

推荐环境:

  • Linux
  • WSL2 Ubuntu

需要工具:

  • docker
  • git
  • curl
  • openssl
  • python3

检查:

docker --version
docker ps
python3 --version
openssl version
curl --version

如果 docker ps 报错,先启动 Docker。

1. 进入项目根目录

cd /home/ivan/xuan/beaver_project
pwd

预期目录:

/home/ivan/xuan/beaver_project

2. 准备本机测试变量

本机测试推荐用 localhost 子域名。例如:

alice.localhost -> 127.0.0.1 / ::1

这样 router-proxy 可以按子域名区分不同实例。

注意:

  • *.localhost 只适合在部署机器本机浏览器里测试。
  • 如果别的电脑访问 alice.localhost,它会指向那台电脑自己,不会指向 Beaver 服务器。
  • 局域网、服务器或正式环境必须把 BEAVER_BASE_DOMAIN 改成客户端能解析到 Beaver 服务器的域名,例如 apps.example.com
  • 旧版文档使用过 127.0.0.1.nip.io。它本来应该按域名里的 IP 解析到 127.0.0.1,但在部分 VPN、代理、DNS 网关或内网安全设备环境下会被改写到 198.18.0.0/15 这类假 IP 段,导致浏览器连不上本机服务。

直接执行:

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=localhost

export BEAVER_AUTHZ_URL='http://beaver-authz-service:19090'
export BEAVER_DEPLOY_URL='http://beaver-deploy-control:8090'

export BEAVER_MINIO_ROOT_USER='beaver-minio-admin'
export BEAVER_MINIO_ROOT_PASSWORD="$(openssl rand -hex 32)"
export BEAVER_USER_FILES_BUCKET='beaver-user-files'
export BEAVER_USER_FILES_MINIO_ENDPOINT='beaver-minio:9000'
export BEAVER_USER_FILES_MAX_UPLOAD_BYTES=$((5 * 1024 * 1024 * 1024))

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-servicedeploy-control 的 token
BEAVER_AUTHZ_INTERNAL_TOKEN AuthZ 内部接口 token
BEAVER_BASE_DOMAIN 新实例的基域名;本机测试用 localhost,服务器部署用真实域名
BEAVER_AUTHZ_URL 容器网络内访问 AuthZ 的地址
BEAVER_DEPLOY_URL 容器网络内访问 deploy-control 的地址
BEAVER_MINIO_ROOT_USER / BEAVER_MINIO_ROOT_PASSWORD 只给 provisioning 组件使用的 MinIO 管理凭据
BEAVER_USER_FILES_BUCKET 用户文件系统共用 bucket默认 beaver-user-files
BEAVER_USER_FILES_MINIO_ENDPOINT 容器网络内访问 MinIO API 的地址
BEAVER_USER_FILES_MAX_UPLOAD_BYTES 用户文件系统上传上限,默认 5GB聊天附件和 workspace 上传仍保留当前小文件限制
BEAVER_OUTLOOK_MCP_URL 可选 Outlook MCP HTTP 地址
BEAVER_OUTLOOK_MCP_SERVER_ID Outlook MCP server id默认 outlook_mcp

如果接入外部正式 MinIO不需要启动本地 beaver-minio。把上面的 MinIO 变量改成正式服务即可:

export BEAVER_MINIO_ROOT_USER='<minio-admin-access-key>'
export BEAVER_MINIO_ROOT_PASSWORD='<minio-admin-secret-key>'
export BEAVER_USER_FILES_BUCKET='beaver-user-files-formal-test'
export BEAVER_USER_FILES_MINIO_ENDPOINT='10.6.80.98:19000'

注意:

  • BEAVER_USER_FILES_MINIO_ENDPOINT 是给 Python MinIO SDK 用的 S3 API endpoint格式是 host:port,不要带 http://
  • 操作员用 mccurl 验证时才写成 http://10.6.80.98:19000
  • MinIO Console 端口不是 S3 API 端口。例如 19001 如果返回 MinIO Console 页面,就不能填进 USER_FILES_MINIO_ENDPOINT
  • 这个管理员账号只给 AuthZ provisioning 使用,用于创建共享 bucket、scoped user 和 scoped policy不要暴露给前端或普通用户。

BEAVER_AUTHZ_URLBEAVER_DEPLOY_URL 必须带协议头。正确写法:

http://beaver-authz-service:19090
http://beaver-deploy-control:8090

错误写法:

beaver-authz-service:19090
beaver-deploy-control:8090
127.0.0.1:19090
127.0.0.1:8090

如果漏了 http://,注册页可能报:

502: Request URL is missing an 'http://' or 'https://' protocol.

如果你改了 shell 里的变量,已经运行的容器不会自动更新。改完这些变量后,至少要重建:

  • beaver-authz-service
  • beaver-auth-portal

如果改的是 BEAVER_BASE_DOMAIN,还要重启 beaver-deploy-control。这个变量只影响之后新创建的实例;已经创建过的实例 URL 已经写入 app-instance/runtime/registry/instances.json,不会自动改成新域名。

非本机访问怎么配置域名

如果 Beaver 部署在服务器上,而用户从其他机器访问,不要使用 localhost。推荐准备一个真实域名,并把通配子域名解析到服务器,例如:

portal.example.com      -> Beaver 服务器
*.apps.example.com      -> Beaver 服务器

然后使用:

export BEAVER_BASE_DOMAIN=apps.example.com

新实例 URL 会变成:

http://alice.apps.example.com:8088

如果外层 Nginx/Caddy/负载均衡已经把 https://*.apps.example.com 转发到 router-proxy,正式环境通常还会把 DEPLOY_PUBLIC_SCHEME 改为 https,并把 DEPLOY_PUBLIC_PORT 改成 443

3. 创建运行目录

mkdir -p \
  "$PROJECT_ROOT/authz-service/runtime/data" \
  "$PROJECT_ROOT/minio/runtime/data" \
  "$PROJECT_ROOT/app-instance/runtime/instances" \
  "$PROJECT_ROOT/app-instance/runtime/registry" \
  "$PROJECT_ROOT/router-proxy/runtime/conf.d"

这些目录保存:

  • AuthZ 数据
  • MinIO 对象数据
  • 实例注册表
  • 每个用户实例的配置和数据
  • router-proxy 生成的路由文件

4. 构建镜像

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 网络

docker network inspect "$BEAVER_NET" >/dev/null 2>&1 || docker network create "$BEAVER_NET"
docker network ls | grep "$BEAVER_NET"

预期能看到:

beaver-instance-edge

6. 启动 router-proxy

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

实例统一入口:

http://<slug>.localhost:8088

示例:

http://alice.localhost:8088

这里的 localhost 示例只表示本机测试。服务器部署时应替换为上面配置的真实基域名,例如:

http://alice.apps.example.com:8088

7. 启动 MinIO

MinIO 是用户文件系统的后端实现细节。用户和前端不会看到 bucket、access key 或 prefixBeaver 只通过 /api/user-files/* 暴露个人智能体文件系统。

如果使用外部正式 MinIO可以跳过本节本地 MinIO 容器启动,直接进入 authz-service 启动步骤。

docker rm -f beaver-minio >/dev/null 2>&1 || true

docker run -d \
  --name beaver-minio \
  --restart unless-stopped \
  --network "$BEAVER_NET" \
  -p 9000:9000 \
  -p 9001:9001 \
  -v "$PROJECT_ROOT/minio/runtime/data:/data" \
  -e MINIO_ROOT_USER="$BEAVER_MINIO_ROOT_USER" \
  -e MINIO_ROOT_PASSWORD="$BEAVER_MINIO_ROOT_PASSWORD" \
  minio/minio:latest server /data --console-address ":9001"

用户文件采用共享 bucket + 用户 namespace

bucket: beaver-user-files
namespace: users/{backend_id}
example object: users/alice/uploads/report.pdf

每个 backend/user 会由 AuthZ provisioning 生成 scoped MinIO 凭据policy 只允许访问自己的 users/{backend_id}/* prefix。

用户文件路径必须使用相对的虚拟根路径,例如 uploads/input.txtoutputs/report.mdshared/profile.jsontasks/<task_id>/draft.md/uploads/input.txt 这类 leading-slash absolute-style path 会被拒绝,不会再被规范化成当前用户的 uploads/input.txt

用户文件上传由 Beaver 后端代理到 MinIO不暴露 bucket、prefix 或凭据。当前默认允许最大 5GB 的用户文件上传,业务上限由 app-instance 后端环境变量 BEAVER_USER_FILES_MAX_UPLOAD_BYTES 控制;反向代理默认 client_max_body_size 已提高到 5GB。MinIO 本身支持大对象和 multipart 上传,但 agent 对超大文件的读取/处理能力仍需要按具体任务另行验证。

8. 启动 authz-service

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" \
  -e USER_FILES_MINIO_PROVISIONING_ENABLED=1 \
  -e USER_FILES_MINIO_ENDPOINT="$BEAVER_USER_FILES_MINIO_ENDPOINT" \
  -e USER_FILES_MINIO_PUBLIC_ENDPOINT="$BEAVER_USER_FILES_MINIO_ENDPOINT" \
  -e USER_FILES_MINIO_ADMIN_ACCESS_KEY="$BEAVER_MINIO_ROOT_USER" \
  -e USER_FILES_MINIO_ADMIN_SECRET_KEY="$BEAVER_MINIO_ROOT_PASSWORD" \
  -e USER_FILES_MINIO_BUCKET="$BEAVER_USER_FILES_BUCKET" \
  -e USER_FILES_MINIO_SECURE=0 \
  beaver/authz-service:latest

重点:

  • AUTHZ_ISSUER 在当前部署里要写 http://beaver-authz-service:19090
  • 不要写 http://127.0.0.1:19090
  • 新创建的 app-instance 容器要通过 Docker network 访问 AuthZ
  • USER_FILES_MINIO_* 只用于 AuthZ provisioning 创建 bucket、用户、policy并把 scoped settings 存入 AuthZ普通用户不会接触这些配置。

检查关键环境变量:

docker inspect beaver-authz-service --format '{{range .Config.Env}}{{println .}}{{end}}' \
  | egrep '^(AUTHZ_ISSUER|DEPLOY_API_BASE_URL|USER_FILES_MINIO_)='

9. 启动 deploy-control

deploy-control 会挂载 Docker socket再创建新的 app-instance 容器。这里最容易错的是路径挂载:

  • 要把宿主机真实路径按原路径挂进容器。
  • 不要把 app-instance 挂到容器里的 /app-instance 这种短路径。
  • APP_INSTANCE_DIRROUTER_PROXY_DIR 要和挂载路径一致。

直接执行:

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_INTERNAL_TOKEN="$BEAVER_AUTHZ_INTERNAL_TOKEN" \
  -e DEFAULT_AUTHZ_OUTLOOK_MCP_URL="$BEAVER_OUTLOOK_MCP_URL" \
  -e DEFAULT_OUTLOOK_MCP_SERVER_ID="$BEAVER_OUTLOOK_MCP_SERVER_ID" \
  -e DEFAULT_USER_FILES_MAX_UPLOAD_BYTES="$BEAVER_USER_FILES_MAX_UPLOAD_BYTES" \
  -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

DEPLOY_PUBLIC_BASE_DOMAIN 来自 BEAVER_BASE_DOMAIN。本机测试时可以是 localhost;如果要让其他设备访问,必须换成它们能解析到 Beaver 服务器的真实域名。修改后需要重启 beaver-deploy-control,并重新创建实例或手动更新 registry 后重载 router-proxy

当前版本创建实例时会传 --skip-provider-config,也就是先不写 provider、model 或 API key。注册成功后auth-portal 会进入模型配置引导页,再调用 deploy-control /api/instances/configure-provider 写入该实例的 config.json 并重启容器。

DEFAULT_AUTHZ_INTERNAL_TOKEN 会写入新建 app-instance 的后端 runtime env用于 app-instance 后端读取自己的 internal MinIO settings。它不会传给前端。

10. 启动 auth-portal

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

检查关键环境变量:

docker inspect beaver-auth-portal --format '{{range .Config.Env}}{{println .}}{{end}}' \
  | egrep '^(AUTHZ_API_BASE_URL|DEPLOY_API_BASE_URL)='

11. 健康检查

curl http://127.0.0.1:19090/healthz
curl http://127.0.0.1:8090/healthz
curl -I http://127.0.0.1:3081
curl -I http://127.0.0.1:9001
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
docker logs --tail=50 beaver-router-proxy

至少应该看到这些容器:

  • beaver-authz-service
  • beaver-minio
  • beaver-deploy-control
  • beaver-auth-portal
  • beaver-router-proxy

12. 浏览器首次测试

打开:

http://127.0.0.1:3081/register

预期流程:

  1. 注册一个新账号。
  2. Portal 创建不含模型凭证的实例。
  3. 页面进入模型配置引导。
  4. 填 provider、model、API key 后确认,或暂时跳过。
  5. 浏览器跳到你的实例地址。

跳转目标示例:

http://alice.localhost:8088

也可以运行 Playwright 冒烟测试,自动验证注册、跳过模型配置、登录交接、文件页四个根目录、上传文件,并检查 /api/user-files/* 不泄露 bucket、namespace 或凭据字段:

cd "$PROJECT_ROOT"
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/home/worker/.cache/ms-playwright/chromium-1194/chrome-linux/chrome \
  ./scripts/smoke-auth-files.sh

如果要同时验证上传后的对象确实进入 MinIO 的 users/{backend_id}/uploads/ namespace可以加上 MinIO 校验:

BEAVER_SMOKE_VERIFY_MINIO=1 \
BEAVER_SMOKE_MINIO_ENDPOINT=http://beaver-minio:9000 \
BEAVER_SMOKE_MINIO_ACCESS_KEY="$BEAVER_MINIO_ROOT_USER" \
BEAVER_SMOKE_MINIO_SECRET_KEY="$BEAVER_MINIO_ROOT_PASSWORD" \
BEAVER_SMOKE_MINIO_BUCKET="$BEAVER_USER_FILES_BUCKET" \
BEAVER_SMOKE_MINIO_NETWORK="$BEAVER_NET" \
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/home/worker/.cache/ms-playwright/chromium-1194/chrome-linux/chrome \
  ./scripts/smoke-auth-files.sh

如果你的机器没有这个 Chromium 路径,可以去掉 PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH,让 Playwright 使用自己安装的浏览器。

外部正式 MinIO 的重复验证流程:

cd "$PROJECT_ROOT"

BEAVER_SMOKE_VERIFY_MINIO=1 \
BEAVER_SMOKE_MINIO_ENDPOINT=http://10.6.80.98:19000 \
BEAVER_SMOKE_MINIO_ACCESS_KEY="$BEAVER_MINIO_ROOT_USER" \
BEAVER_SMOKE_MINIO_SECRET_KEY="$BEAVER_MINIO_ROOT_PASSWORD" \
BEAVER_SMOKE_MINIO_BUCKET="$BEAVER_USER_FILES_BUCKET" \
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/home/worker/.cache/ms-playwright/chromium-1194/chrome-linux/chrome \
  ./scripts/smoke-auth-files.sh

预期结果:

  • 新注册用户的 AuthZ MinIO settings 指向正式 endpoint 和 users/{backend_id} namespace。
  • 文件页只展示 uploadsoutputssharedtasks 四个根目录。
  • /api/user-files/* 上传的对象出现在正式 bucket 的 users/{backend_id}/uploads/ 下。
  • 覆盖、下载、删除都由 Beaver API 完成,前端响应不包含 bucket、namespace、access key 或 secret key。

也可以运行更完整的用户文件系统验证自动化,覆盖 Files 页面四根目录 UI 删除、Logs/Subagents 等页面回归、MinIO 缺失对象验证和临时用户清理:

cd "$PROJECT_ROOT"

BEAVER_VALIDATE_VERIFY_MINIO=1 \
BEAVER_VALIDATE_MINIO_ENDPOINT=http://10.6.80.98:19000 \
BEAVER_VALIDATE_MINIO_ACCESS_KEY="$BEAVER_MINIO_ROOT_USER" \
BEAVER_VALIDATE_MINIO_SECRET_KEY="$BEAVER_MINIO_ROOT_PASSWORD" \
BEAVER_VALIDATE_MINIO_BUCKET="$BEAVER_USER_FILES_BUCKET" \
BEAVER_VALIDATE_DEPLOY_TOKEN="$BEAVER_DEPLOY_TOKEN" \
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/home/worker/.cache/ms-playwright/chromium-1194/chrome-linux/chrome \
  ./scripts/validate-filesystem-automation.sh

清理测试账号和 MinIO 用户文件

测试注册、Playwright smoke 或反复部署会创建 app-instance、本地实例目录、AuthZ MinIO settings以及 MinIO 里的 scoped user、policy 和 users/{backend_id}/ 对象。不要只手工删除 local path否则 MinIO 里会留下无效测试资源。

优先使用清理脚本。默认是 dry-run只列出将要清理的测试账号

cd "$PROJECT_ROOT"
./scripts/cleanup-test-users.py --username-prefix smoke

确认列表无误后执行删除:

DEPLOY_CONTROL_API_TOKEN="$BEAVER_DEPLOY_TOKEN" \
./scripts/cleanup-test-users.py \
  --username-prefix smoke \
  --purge-data \
  --execute

这会调用 deploy-control 的实例删除接口,并带上:

X-Purge-Data: 1
X-Purge-User-Files: 1

其中 X-Purge-Data 删除本地实例数据,X-Purge-User-Files 让 deploy-control 调 AuthZ 内部接口清理 MinIO 用户文件资源。用户和普通 Files 页面不会看到 bucket、access key、secret key、policy 或 raw prefixMinIO 仍然只是后端实现细节。

如果同一个实例删除请求重复执行deploy-control 会把本地实例已不存在报告为 already_absent 的成功 no-op。带 X-Purge-User-Files: 1 时,它仍会对同名 backend id 调用 AuthZ 的 best-effort 用户文件清理AuthZ user-file cleanup 本身是幂等的,已不存在的 scoped user、policy、settings 或 namespace 会以 absent/no-op 状态返回。

如果只想清理一个明确实例,也可以直接调用:

curl -X DELETE "http://127.0.0.1:8090/api/instances/<instance_id>" \
  -H "Authorization: Bearer $BEAVER_DEPLOY_TOKEN" \
  -H "X-Purge-Data: 1" \
  -H "X-Purge-User-Files: 1"

如果删除过程中只有一部分成功,可以按下面的方式手工恢复。先设置 MinIO alias

docker run --rm --network "$BEAVER_NET" --entrypoint /bin/sh minio/mc:latest -lc "
  mc alias set beaver http://beaver-minio:9000 '$BEAVER_MINIO_ROOT_USER' '$BEAVER_MINIO_ROOT_PASSWORD'
"

然后针对某个 backend id 清理对象、policy 和 user

BACKEND_ID='<backend_id>'
ACCESS_KEY="beaver-$BACKEND_ID"
POLICY_NAME="beaver-user-files-$BACKEND_ID"

docker run --rm --network "$BEAVER_NET" --entrypoint /bin/sh minio/mc:latest -lc "
  mc alias set beaver http://beaver-minio:9000 '$BEAVER_MINIO_ROOT_USER' '$BEAVER_MINIO_ROOT_PASSWORD' >/dev/null &&
  mc rm --recursive --force 'beaver/$BEAVER_USER_FILES_BUCKET/users/$BACKEND_ID/' || true &&
  mc admin policy detach beaver '$POLICY_NAME' --user '$ACCESS_KEY' || true &&
  mc admin user remove beaver '$ACCESS_KEY' || true &&
  mc admin policy remove beaver '$POLICY_NAME' || true
"

最后删除 AuthZ 里的 MinIO settings

curl -X DELETE "http://127.0.0.1:19090/backends/$BACKEND_ID/settings/minio"

13. 确认实例已创建

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

14. 只看 auth-portal 页面

如果只想看 Portal 页面,不跑全链路:

cd /home/ivan/xuan/beaver_project/auth-portal/src
npm install
npm run dev

打开:

http://127.0.0.1:3081

注意:这只能看页面。注册、登录、创建实例仍依赖 authz-servicedeploy-control

15. 常用排错命令

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

实例创建失败时再看:

cd "$PROJECT_ROOT/app-instance"
./list-instances.sh --json
docker ps --format 'table {{.Names}}\t{{.Status}}' | grep app-instance

排查 URL 变量:

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

16. 常见问题

注册页报 URL 缺少协议

现象:

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

错误:

http://127.0.0.1:19090

正确:

http://beaver-authz-service:19090

原因是 app-instance 容器里的 127.0.0.1 指向它自己。

deploy-control 路径挂载写错

错误思路:

宿主机 app-instance -> 容器 /app-instance

正确思路:

$PROJECT_ROOT/app-instance -> $PROJECT_ROOT/app-instance
$PROJECT_ROOT/router-proxy -> $PROJECT_ROOT/router-proxy

因为 deploy-control 会通过宿主机 Docker socket 再创建新容器,传给 Docker 的 bind mount 源路径必须是宿主机真实路径。

本地域名解析失败

检查:

getent hosts alice.localhost

如果浏览器或系统没有把 <slug>.localhost 解析到本机,可以临时在 /etc/hosts 添加当前测试账号的主机名:

127.0.0.1 alice.localhost

旧版文档使用过 127.0.0.1.nip.io,但部分网络会把 nip.io / sslip.io / lvh.me 劫持到非本机地址。例如 127.0.0.1.nip.io 可能被解析成 198.18.1.27这通常是代理、VPN 或 DNS 网关返回的假 IP不是 Beaver 服务地址。遇到这种情况,本机测试优先使用 localhost 子域名。

服务器上不能用 .localhost

localhost 是保留域名,浏览器会把它解析到“当前这台客户端机器”。所以:

  • 在 Beaver 服务器本机浏览器里打开 alice.localhost,访问的是 Beaver 服务器本机。
  • 在另一台电脑浏览器里打开 alice.localhost,访问的是那台电脑自己。

因此远程访问、局域网访问和正式部署都不能使用 *.localhost。请改用真实域名或可被客户端解析到服务器的内部域名,并保证 router-proxy 能收到原始 Host 头。

端口被占用

默认端口:

  • 3081
  • 8088
  • 8090
  • 19090

检查:

ss -ltnp | grep -E '3081|8088|8090|19090'

17. 重新部署基础容器

只重建基础四个容器:

docker rm -f \
  beaver-auth-portal \
  beaver-authz-service \
  beaver-deploy-control \
  beaver-router-proxy 2>/dev/null || true

这不会自动删除实例数据。如果你还需要旧账号、旧实例或模型配置,不要删除 runtime/ 目录。