架构

ServerBee 的系统架构、组件设计、通信协议和安全模型。

本文介绍 ServerBee 的内部架构,帮助希望深入了解系统工作原理的开发者和运维人员。ServerBee 采用 hub-and-spoke(中心辐射)设计:中央 Server 通过 WebSocket 接收来自分布式 Agent 的指标,存入 SQLite,并对外提供 React SPA 仪表盘。

系统概览

+--------------------------------------------------+
|              ServerBee Dashboard                  |
|                                                   |
|  +---------------------------------------------+ |
|  | 前端 (React SPA, rust-embed 嵌入)            | |
|  | React 19, TanStack Router/Query             | |
|  | shadcn/ui, Tailwind CSS v4, Bklit charts        | |
|  +---------------------------------------------+ |
|  +---------------------------------------------+ |
|  | 服务端 (Rust)                                | |
|  | Axum Router                                  | |
|  |  +-- REST API handlers                      | |
|  |  +-- WebSocket: Agent + Browser + Terminal   | |
|  |  +-- 静态文件 (rust-embed)                   | |
|  | Service Layer                                | |
|  |  +-- AgentManager (连接/状态)                | |
|  |  +-- AlertService (告警评估)                 | |
|  |  +-- RecordService (指标/聚合)               | |
|  |  +-- NotificationService (通知分发)          | |
|  | Entity Layer (sea-orm)                       | |
|  |  +-- SQLite                                  | |
|  +---------------------------------------------+ |
+--------------------------+-----------------------+
                           | WebSocket (JSON)
+--------------------------v-----------------------+
|              ServerBee Agent (Rust)               |
|  common crate (共享类型/协议)                     |
|  +-- Collector (系统指标采集)                     |
|  +-- Reporter (WebSocket + 重连)                  |
|  +-- Pinger (ICMP/TCP/HTTP 探测)                  |
|  +-- TerminalManager (PTY 终端会话)               |
+---------------------------------------------------+

组件职责

Server(服务端,crates/server)

整个系统的中枢,承担以下职责:

  • 提供 Web 仪表盘(React SPA 通过 rust-embed 嵌入)
  • 暴露 REST API 进行 CRUD 操作
  • 管理来自 Agent 和浏览器的 WebSocket 连接
  • 将全部数据存入 SQLite
  • 运行后台任务(指标写入、聚合、清理、告警、离线检测)
  • 分发通知

其主要服务模块:

模块职责
Axum RouterHTTP/WebSocket 请求路由和处理
AgentManager管理 Agent WebSocket 连接、维护实时指标缓存、广播更新
AuthService用户登录验证(argon2)、Session 管理、API Key 校验
ServerService服务器 CRUD、分组、标签、排序管理
RecordService指标写入、历史查询、小时聚合、过期清理
AlertService告警规则 CRUD、周期性规则评估、触发通知
NotificationService通知渠道管理、消息分发(Webhook/Telegram/Email/Bark)
PingServicePing 探测任务 CRUD、任务下发、结果存储
TaskService远程命令下发、执行结果查询
ConfigService系统配置读写(K-V 存储)
GeoIpServiceIP 地理位置查询(MaxMind MMDB)
UserService用户 CRUD、角色管理

Agent(采集端,crates/agent)

部署在被监控服务器上的轻量级守护进程,负责:

  • 使用 sysinfo crate 采集系统指标
  • 每 N 秒通过 WebSocket 向 Server 上报指标
  • 执行 Server 下发的 Ping 探测任务
  • 提供 PTY 终端会话以实现远程 Shell 访问
  • 执行 Server 下发的远程命令
  • 支持按指令自升级

其主要模块:

模块职责
Collector使用 sysinfo 库周期性采集 CPU、内存、磁盘、网络等系统指标
Reporter管理 WebSocket 连接、上报指标、处理 Server 下发的指令、断线重连
PingManager执行 Server 下发的定时 ICMP/TCP/HTTP Ping 任务
NetworkProber执行定时网络质量探测并上报聚合结果
Terminal管理 PTY 终端会话,转发终端输入输出

Common(共享 crate,crates/common)

定义 Agent 和 Server 之间共享的类型和协议:

  • 协议定义 —— AgentMessage、ServerMessage、BrowserMessage 枚举
  • 数据类型 —— SystemInfo、SystemReport、GpuReport、PingResult、ServerStatus 等
  • 常量定义 —— 协议版本号、默认端口、超时、保留周期、告警参数

Frontend(前端,apps/web)

基于 React 19 的单页应用,构建后通过 rust-embed 嵌入到 Server 二进制中:

  • 路由 —— TanStack Router(文件式路由)
  • 数据获取 —— TanStack Query 处理 REST,原生 WebSocket 处理实时更新(共享同一 cache)
  • UI 组件 —— shadcn/ui(base-nova 主题)
  • 样式 —— Tailwind CSS v4
  • 图表 —— Bklit 图表原语(visx),统一封装在 components/charts/
  • 终端 —— xterm.js Web 终端模拟器
  • 构建 —— Vite 7

构建后的前端在编译期嵌入 Server 二进制,因此无需单独部署。

通信协议

Agent 的指标上报和控制流量使用 WebSocket。浏览器通过 REST 获取初始数据、历史记录并执行管理操作,同时通过 WebSocket 接收实时更新。WebSocket 应用消息均为 JSON 文本帧;终端 I/O 数据放在消息的 data 字段中,以 base64 编码。当前 Agent 协议版本为 6,连接时通过 Welcome 消息下发。

Agent 连接到 ws://<server>/api/agent/ws,并使用 Authorization: Bearer <agent_token> 认证。Server 暂时兼容旧版 Agent 使用的 ?token= 形式,以便其重新连接并完成升级。

Agent -> Server(AgentMessage)

消息类型说明需要 ACK
SystemInfo静态系统信息,连接/重连后上报一次是
Report周期性指标上报(每 N 秒)否
PingResultPing 探测结果否
TaskResult远程命令执行结果是
TerminalOutputPTY 输出数据(base64 编码)否
TerminalStartedPTY 会话创建成功确认否
TerminalError终端会话错误否
Pong协议层心跳响应否
DockerInfoDocker 系统信息和功能上报否
DockerContainers当前容器列表及状态否
DockerStats容器资源使用统计否
DockerLog容器日志条目(批量)否
DockerEvent容器生命周期事件否

Server -> Agent(ServerMessage)

消息类型说明
Welcome连接确认,包含 server_id、protocol_version、report_interval
Ack确认收到的消息(按 msg_id)
PingTasksSync下发全部分配的探测任务
Exec在 Agent 上执行 Shell 命令
TerminalOpen请求打开新的 PTY 会话
TerminalInput向 PTY 会话转发键盘输入
TerminalResize调整 PTY 会话尺寸
TerminalClose关闭 PTY 会话
Ping协议层心跳(每 30 秒)
Upgrade指示 Agent 自升级
DockerLogsStart开始为容器流式传输日志
DockerLogsStop停止流式传输日志
DockerAction执行容器操作(start/stop/restart/remove)

Server -> Browser(BrowserMessage)

浏览器通过两种方式获取数据:REST API 用于初始加载和历史查询,WebSocket(地址 ws://<server>/api/ws/servers)用于实时推送。该 WebSocket 为单向推送(Server -> 浏览器)。REST 结果和 WebSocket 消息会写入同一份实时服务器目录,使各页面保持同步。

消息类型说明
FullSync所有服务器的完整状态(浏览器连接时下发)
Update变更服务器最新指标的增量更新
ServerOnline服务器上线通知
ServerOffline服务器离线通知
DockerUpdateDocker 容器和统计更新
DockerEventDocker 容器生命周期事件
DockerAvailabilityChangedDocker daemon 可用性变化

握手流程

Agent                              Server
  |                                  |
  |--- WebSocket 连接 + token ------>|
  |                                  | 验证 token,查找 server
  |<-- Welcome { server_id,         |
  |     protocol_version: 6,        |
  |     report_interval: 3 } -------|
  |                                  |
  |--- SystemInfo { cpu_name,       |  连接/重连后上报静态信息
  |     os, mem_total, ... } ------>|
  |<-- Ack { msg_id } -------------|
  |                                  |
  |--- Report (每 3 秒) ----------->|  周期性指标上报

消息大小限制

限制项最大大小
WebSocket 消息大小1 MB
命令输出512 KB
命令长度8 KB

终端数据传输

终端流量与系统其余部分使用同一套 JSON 文本协议,经 Server 中转:

浏览器 <--JSON WebSocket--> Server <--JSON WebSocket--> Agent (PTY)

PTY 原始字节流以 base64 编码放入 JSON 消息的 data 字段,因为终端输出并非 UTF-8 安全。

浏览器 <-> Server 消息(均为 JSON 文本):

  • 浏览器 -> Server:{ "type": "input", "data": <base64> }、{ "type": "resize", "rows": <n>, "cols": <n> }
  • Server -> 浏览器:{ "type": "session", "session_id": <id> }、{ "type": "started" }、{ "type": "output", "data": <base64> }、{ "type": "error", "error": <message> }

Server <-> Agent 消息复用 Agent 协议:

  • Server -> Agent:ServerMessage::TerminalOpen、TerminalInput(data base64)、TerminalResize、TerminalClose
  • Agent -> Server:AgentMessage::TerminalStarted、TerminalOutput(data base64)、TerminalError

数据库设计

ServerBee 使用 SQLite 数据库(WAL 模式),通过 sea-orm 管理。下面按类别列出核心数据表。

用户与认证

表名说明
users用户账户(id、用户名、argon2 密码哈希、角色、TOTP 密钥)
sessions登录会话(Token、IP、User-Agent、过期时间)
api_keysAPI 密钥(argon2 哈希、前缀、最后使用时间)
oauth_accounts已关联的 OAuth 提供商账户

服务器管理

表名说明
servers已注册服务器(Token 哈希、静态信息、元数据、计费、分组)
server_groups用于组织服务器的逻辑分组
server_tags附加到服务器的标签(多对多)
configs键值配置存储(运行时设置等)

指标记录

表名说明
records分钟级原始指标记录,每服务器每分钟一行(复合索引 server_id + time)
records_hourly小时级聚合记录(结构同 records,值为平均值)
gpu_recordsGPU 每卡详细指标(设备索引、名称、显存、利用率、温度)
ping_recordsPing 探测结果(延迟、成功、错误;复合索引 task_id + server_id + time)
ping_tasksPing 任务定义(探测类型、目标、间隔、分配的服务器)
traffic_hourly每服务器每小时流量字节数(入站/出站)
traffic_daily每服务器每日流量字节数(入站/出站)
traffic_state最新累计流量计数器(用于增量计算)
docker_eventsDocker 容器生命周期事件(start/stop/die/create)

告警与通知

表名说明
alert_rules告警规则定义(条件、覆盖类型、触发模式)
alert_states各规则/服务器对的当前告警状态(复合唯一索引 rule_id + server_id)
notifications通知渠道配置(webhook、telegram、bark、email)
notification_groups通知组
tasks远程命令任务(命令、目标服务器、创建者)
task_results命令执行结果(输出、退出码)
audit_logs用户操作审计日志(操作、详情、IP)

所有表均使用 UTC 时间戳。多数实体的 ID 为 UUID(字符串类型),高写入量记录使用自增整型。

SQLite 配置

  • WAL 模式 —— 启动时执行 PRAGMA journal_mode=WAL,提高并发读性能。
  • Busy Timeout —— 5000ms,写冲突时自动等待。
  • 同步模式 —— PRAGMA synchronous=NORMAL。
  • 连接池 —— 最大 10 个连接,主要用于并发读。

后台任务

Server 启动时通过 tokio::spawn 创建多个长期运行的后台任务:

任务频率职责
指标写入 (RecordWriter)每 60 秒从内存缓存把最新的 Agent 上报刷入 records 表
告警评估 (AlertEvaluator)每 60 秒(指标写入后)评估所有启用的告警规则并分发通知
离线检测 (OfflineChecker)每 10 秒扫描 Agent 连接状态,对 30 秒无心跳的 Agent 触发离线事件/告警
小时聚合 (Aggregator)每 1 小时将 records 原始数据聚合为 records_hourly,用于长期存储
数据清理 (Cleanup)每 1 小时(聚合后)删除超过保留周期的 records、GPU 数据、ping 记录和审计日志
Ping 任务同步 (PingTasksSync)配置变更时向相关 Agent 推送 PingTasksSync
Session 清理 (SessionCleaner)每 1 小时删除过期的用户登录 Session

执行顺序保证:

  • 每小时任务:小时聚合 -> 数据清理(先聚合再清理,避免丢失长期数据)。
  • 每分钟任务:指标写入 -> 告警评估(确保评估读到刚写入的数据)。

安全模型

认证路径

面向用户的 REST 和 WebSocket 请求可通过 Session Cookie、API Key 或 Bearer Session Token 解析出用户,随后按该用户的当前角色进行授权。Agent WebSocket 连接使用独立的 Agent run token。

请求进入
  +-- Cookie: session_token=xxx         --> Session 认证(浏览器)
  +-- Header: X-API-Key: serverbee_xxx  --> API Key 认证(自动化)
  +-- Header: Authorization: Bearer xxx  --> Session 认证(移动端)

Agent WebSocket 连接
  +-- Header: Authorization: Bearer xxx  --> Agent run token 认证
  +-- Query: ?token=xxx                  --> 仅用于兼容旧版 Agent

Session 认证(浏览器)

  • 登录时通过 argon2 验证用户名/密码。
  • 生成 32 字节随机 token(base64url 编码)。
  • 存入 sessions 表,设置 HttpOnly + SameSite=Strict Cookie。
  • 滑动过期:每次有效请求自动延长 expires_at(默认 TTL 24 小时)。

API Key 认证(自动化)

  • Admin 可创建具名 API 密钥用于程序化访问。
  • Key 格式:serverbee_ + 32 字节随机 base64url。
  • 以 argon2 哈希存储,并保存 8 位明文前缀用于标识。
  • 校验时先用前缀缩小查询范围,再用 argon2 验证。
  • API Key 会以所属用户的身份认证,并继承该用户当前的 admin 或 member 角色。API Key 是凭据类型,不是第三种角色。

Agent Token 认证(Agent)

  • Enrollment offer 绑定到一个 Server,单次使用且短时有效,其生命周期由 Agent Authority 模块统一管理。
  • Agent 在 claim offer 前生成并持久化 run token,然后将其作为 proposed_run_token 提交。Server 不生成也不返回该 token。
  • Run token 以 argon2 哈希存储在 servers 表,只有 Agent 保留明文。
  • Agent 使用 Authorization: Bearer <agent_token> Header 认证。当前 Agent 不再把原始 token 放入 URI;query token 仅作为旧版本兼容路径保留。
  • WebSocket admission 分两阶段:HTTP upgrade 前做 preflight,再持有 Server lifecycle lock 做最终校验。Authority 转换与消息分发共享该锁,被吊销或替换的连接无法在 fencing 后抢跑。
  • 每次 authority/offer 转换都会追加一个不含密钥的事件。事件保存 Server 身份快照,并在 Server 删除后继续保留。

基于角色的访问控制(RBAC)

用户只有两种角色:admin(完全访问)和 member(只读仪表盘访问)。Session Cookie、Bearer Session Token 和 API Key 都是这些用户的凭据,不会形成额外角色。

面向浏览器的 WebSocket 会在连接期间重新检查持久化 Session 或 API Key 以及用户角色。登出、删除 API Key、密码重置导致的 Session 吊销、删除账号或变更角色后,现有连接会在两秒内关闭,不会继续保留握手时的权限。

资源Admin 用户Member 用户Agent
服务器列表/详情允许允许(受限)-
服务器增删改允许禁止-
告警规则管理允许禁止-
通知配置允许禁止-
用户管理允许禁止-
系统设置允许禁止-
实时服务器更新允许允许-
文件管理器允许禁止-
Web 终端允许禁止-
远程命令允许禁止-
指标上报--允许

OAuth

支持 GitHub、Google 和通用 OIDC 提供商的 OAuth 登录。allow_registration 开关控制首次 OAuth 登录时是否自动创建新用户。

速率限制

基于 tower middleware 实现,内存中维护 IP 到计数器的映射。登录和 Agent 注册接口按 IP 限流,以防止暴力破解。

接口限制
POST /api/auth/login每 IP 15 分钟内 5 次
POST /api/agent/register每 IP 15 分钟内 10 次

超限返回 429 Too Many Requests。管理员可在「设置 → 速率限制」中清除活跃窗口。

TOTP

用户可启用基于 TOTP 的两步验证以增强安全性。

Workspace 结构

Rust crate 采用 Cargo workspace 管理;前端是 apps/web/ 下独立的 Bun/Node 项目。

ServerBee/
  Cargo.toml                    # workspace 根
  crates/
    common/                     # 共享:协议、数据类型、常量
      src/
        lib.rs
        protocol.rs             # Agent <-> Server 消息类型
        types.rs                # SystemReport、GpuInfo 等
        constants.rs            # 协议版本、默认值
    server/                     # 服务端二进制(Axum、sea-orm、后台任务)
      src/
        main.rs
        config.rs
        state.rs                # AppState
        router/                 # REST API + WebSocket handlers
        service/                # 业务逻辑层
        entity/                 # sea-orm Entity(每表一个模块)
        migration/              # 数据库迁移
        middleware/             # 认证、日志中间件
    agent/                      # Agent 二进制(采集、上报、控制)
      src/
        main.rs
        config.rs
        collector/              # 各指标采集器
        reporter/               # WebSocket 上报、重连、指令和文件操作
        pinger.rs                # ICMP/TCP/HTTP Ping 任务
        probe_utils.rs           # 共用探测校验和网络辅助逻辑
        network_prober.rs        # 定时网络探测
        traceroute.rs            # Traceroute 执行
        terminal.rs             # PTY 终端
        file_manager.rs          # 受限远程文件操作
        docker/                  # Docker 检查、操作和日志流
        security/                # 主机安全事件检测
        firewall/                # 防火墙拦截管理
        ip_quality/              # IP 质量检测
        upgrade.rs               # 自升级协调逻辑
        upgrade/                 # 事务式自升级支持
  apps/
    web/                        # React SPA 前端(Vite、TanStack、shadcn/ui)
    docs/                       # 文档站点(TanStack Start + Fumadocs)
  docs/                         # 设计文档与计划