refactor!: rename the project from Monitor to MoShiro
Namespaces, assemblies, projects and directories, the solution, binaries, images, systemd units, config paths, environment variables, protocol method names, the media type and the raw chunk magic all move to MoShiro/moshiro. Existing data and deployments are not compatible and must be recreated. Also fix the architecture tests after the solution layout change and make the update tests independent of their order.
This commit is contained in:
1 parent
17b35223aa
commit
83e5120377
309 files changed
+1505
-1499
No files matched your search
+2
-2
@@ -135,7 +135,7 @@ chunk 入库 ─┬─> 流式路径:按 1s 桶计算累加器 → 热层环
|
||||
## 9. 项目结构
|
||||
|
||||
```text
|
||||
backend/src/Monitor.Backend.Analysis/ 依赖:Monitor.Protocol、Fleet.Core(纯计算,没有 I/O)
|
||||
backend/src/MoShiro.Backend.Analysis/ 依赖:MoShiro.Protocol、Fleet.Core(纯计算,没有 I/O)
|
||||
├─ Accumulators/ Counter Ratio Sum Min Max Histogram RunLength Series + AccumulatorCodec
|
||||
├─ Ops/ Rtt Loss OwdVar LossRuns Reorder Gap KernelDelta Rate PhaseMs Outcome ChangePoint
|
||||
├─ Metrics/ LinkMetrics TcpMetrics UdpTrainMetrics ConnectMetrics …(只包含声明)
|
||||
@@ -144,4 +144,4 @@ backend/src/Monitor.Backend.Analysis/ 依赖:Monitor.Protocol、Fleet.Core
|
||||
└─ Clock/ ClockDriftFit(估计两端的时钟偏移和漂移)
|
||||
```
|
||||
|
||||
分析库没有 I/O,可以直接用于离线 CLI `tools/monitor-analyze`,对下载的原始块做实验。
|
||||
分析库没有 I/O,可以直接用于离线 CLI `tools/moshiro-analyze`,对下载的原始块做实验。
|
||||
@@ -16,7 +16,7 @@ web:openapi-typescript 生成类型,调用方的类型错误会直接暴露
|
||||
|
||||
- 端点**必须**声明精确的返回类型,例如 `Results<Ok<AgentDto>, NotFound<ProblemDetails>>`,不能返回 `IResult` 或匿名对象,否则 OpenAPI 文档会不准确。
|
||||
- 每个端点都必须设置 `WithName("agents_list")`(即 operationId,snake_case、`资源_动作`)、`WithSummary`、`WithTags`。
|
||||
- 请求和响应 DTO 放在 `Monitor.Backend.Api/Contracts/`,并注册到 Backend 的 JSON 源生成上下文。**不要直接暴露 `Monitor.Protocol` 中的线协议类型**,因为 REST 与线协议的演进节奏不同。
|
||||
- 请求和响应 DTO 放在 `MoShiro.Backend.Api/Contracts/`,并注册到 Backend 的 JSON 源生成上下文。**不要直接暴露 `MoShiro.Protocol` 中的线协议类型**,因为 REST 与线协议的演进节奏不同。
|
||||
- **结构强类型,指标弱类型**:资源结构(Agent、任务、测试等)必须是强类型 DTO;而**指标值和评分**统一使用字典 `metrics: { "<metric_id>": number }`、`scores: { "<model_id>": number }`,键的含义、单位、版本由 `/api/public/catalog/*` 给出。这样新增指标或模型不需要改 OpenAPI,也不需要改前端类型。
|
||||
|
||||
## 2. 版本与兼容性
|
||||
|
||||
+4
-4
@@ -95,7 +95,7 @@
|
||||
`GET /api/public/about`:`{show_build, backend}`。`show_build` 关着时 `backend` 是 `null`;打开后 `backend` 是 `{version, tag, built_at}`(后端自己的版本、tag、构建时间,不含提交号)。
|
||||
|
||||
- 系统和架构是节点自报的:Agent 和 Probe 都在各自连面板时的 `fleet.hello` 里自报。只有粗粒度的名字,没有主机名、内核版本、CPU 型号。
|
||||
- 国家和运营商由 **Backend** 用节点的来源地址向 IP 归属服务查一次(`Monitor__GeoLookupUrl`,缺省 `https://ip.007995.xyz`,留空就不查):Probe 用 Agent 看到的来源 IP,Agent 用它的公开地址。**Probe 和 Agent 不访问任何外部服务。** 只查公网地址(回环、内网、CGNAT、链路本地一律不发出去);同一个地址一周内只查一次,失败 10 分钟后再试;查到的只留国家代码和运营商,地址只存在 Backend 内部的 `node_meta.geo_host`,不对外。
|
||||
- 国家和运营商由 **Backend** 用节点的来源地址向 IP 归属服务查一次(`MoShiro__GeoLookupUrl`,缺省 `https://ip.007995.xyz`,留空就不查):Probe 用 Agent 看到的来源 IP,Agent 用它的公开地址。**Probe 和 Agent 不访问任何外部服务。** 只查公网地址(回环、内网、CGNAT、链路本地一律不发出去);同一个地址一周内只查一次,失败 10 分钟后再试;查到的只留国家代码和运营商,地址只存在 Backend 内部的 `node_meta.geo_host`,不对外。
|
||||
|
||||
### 2.4 测试结果与目录
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
|
||||
### 2.5 跨域访问
|
||||
|
||||
默认前端和 Backend 同源(Cookie 会话)。前端在别的来源时(比如本机跑前端、连线上的 Backend),Backend 设 `Monitor__CorsOrigins`(逗号分隔的来源,或 `*`;默认空 = 不允许)。只对 `/api/*` 生效,响应带 `Access-Control-Allow-Origin`,预检请求(`OPTIONS`)返回 204,允许的头是 `Authorization, Content-Type, X-CSRF-Token`。**不带凭据**,Cookie 不会跨域,所以跨域的调用只能用 `Authorization: Bearer <API 令牌>`:展示端用只读令牌(站点私有时需要),管理端用 admin 范围的令牌(`/api/admin/auth/me` 对令牌同样可用)。SSE 在跨域时前端用 fetch 读流,因为 `EventSource` 带不了请求头。
|
||||
默认前端和 Backend 同源(Cookie 会话)。前端在别的来源时(比如本机跑前端、连线上的 Backend),Backend 设 `MoShiro__CorsOrigins`(逗号分隔的来源,或 `*`;默认空 = 不允许)。只对 `/api/*` 生效,响应带 `Access-Control-Allow-Origin`,预检请求(`OPTIONS`)返回 204,允许的头是 `Authorization, Content-Type, X-CSRF-Token`。**不带凭据**,Cookie 不会跨域,所以跨域的调用只能用 `Authorization: Bearer <API 令牌>`:展示端用只读令牌(站点私有时需要),管理端用 admin 范围的令牌(`/api/admin/auth/me` 对令牌同样可用)。SSE 在跨域时前端用 fetch 读流,因为 `EventSource` 带不了请求头。
|
||||
|
||||
## 3. Admin API
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
| POST | `/api/admin/lines/{line_id}/tests` | `{plan, params?}` → **202** + `Location: /api/public/tests/{id}`;Probe 离线时返回 409 `probe_offline`,节点繁忙时返回 409 `test_busy` |
|
||||
| POST | `/api/admin/tests/{id}/cancel` | |
|
||||
| GET | `/api/admin/raw/chunks?agent_id=&kind=&from=&to=&test_id=` | 原始块元数据 |
|
||||
| GET | `/api/admin/raw/chunks/{chunk_id}` | 下载原始块(`application/vnd.monitor.rawchunk`);过期返回 410 `raw_expired` |
|
||||
| GET | `/api/admin/raw/chunks/{chunk_id}` | 下载原始块(`application/vnd.moshiro.rawchunk`);过期返回 410 `raw_expired` |
|
||||
| GET | `/api/admin/events?agent_id=&kind=&from=&to=` | 事件 |
|
||||
| GET | `/api/admin/analysis/recompute` | 指标重算进度 |
|
||||
| GET/POST/DELETE | `/api/admin/api-tokens` | 作用域为 `read`、`admin` 或 `release`(只能登记发布清单,给 CI 用,什么都读不了);明文只显示一次 |
|
||||
@@ -157,7 +157,7 @@
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| POST | `/api/agent/v1/enroll` | 由 `MapFleetEnrollment` 提供(使用加入令牌认证),见 [fleet/link.md](fleet/link.md) 第 3 节 |
|
||||
| GET | `/api/agent/v1/rpc` | 由 `MapFleetLink` 提供,升级为 WebSocket;Monitor 的方法见 [protocol.md](protocol.md) |
|
||||
| GET | `/api/agent/v1/rpc` | 由 `MapFleetLink` 提供,升级为 WebSocket;MoShiro 的方法见 [protocol.md](protocol.md) |
|
||||
| GET | `/fleet/releases/{app}/{channel}/latest.json`、`/fleet/releases/{app}/{version}/…` | 由 `MapFleetReleases` 提供的公开发布源(清单和制品),供 `fleet-updater` 轮询;也可以改用 GitHub Releases |
|
||||
|
||||
## 5. 运维端点
|
||||
|
||||
@@ -53,13 +53,13 @@
|
||||
|
||||
| 主题 | 要点 | 文档 |
|
||||
| --- | --- | --- |
|
||||
| 节点框架 | 注册、身份、连接、RPC、可靠上报、状态同步、宿主、更新都由与业务无关的 **Fleet** 提供;Monitor 只写业务,其他产品也能复用 | [fleet/overview.md](fleet/overview.md) |
|
||||
| 节点框架 | 注册、身份、连接、RPC、可靠上报、状态同步、宿主、更新都由与业务无关的 **Fleet** 提供;MoShiro 只写业务,其他产品也能复用 | [fleet/overview.md](fleet/overview.md) |
|
||||
| 注册与身份 | 使用 `fleet-enroll` 和加入令牌自动注册,之后用签名认证;Probe 用自己的身份直连面板,Agent 列表由面板下发 | [enrollment.md](enrollment.md) |
|
||||
| 更新 | 只接受发布密钥签名的清单,更新策略由节点本地决定,失败自动回滚。容器节点(Agent、Probe)已实现:CI 对镜像 digest 签名,宿主机上的 `monitor-update` 验签、按 digest 拉取、重启;纯二进制的 `fleet-updater` 还只是设计 | [deployment.md](deployment.md)、[fleet/update.md](fleet/update.md) |
|
||||
| 更新 | 只接受发布密钥签名的清单,更新策略由节点本地决定,失败自动回滚。容器节点(Agent、Probe)已实现:CI 对镜像 digest 签名,宿主机上的 `moshiro-update` 验签、按 digest 拉取、重启;纯二进制的 `fleet-updater` 还只是设计 | [deployment.md](deployment.md)、[fleet/update.md](fleet/update.md) |
|
||||
| 任务模型 | `Task = {kind, target, params, schedule}`;持续采集、按需测试、目标任务统一建模;下发“期望任务集”,由节点自行调和 | [measurement.md](measurement.md) |
|
||||
| 采集扩展 | Kind 由积木组合声明;采集密度靠参数调整;原始块自描述 | [measurement.md](measurement.md) |
|
||||
| 统计扩展 | 指标注册制:少数几种累加器 + 统计算子;一行 `MetricDef` 就是一个新指标 | [analysis.md](analysis.md) |
|
||||
| 协议 | 控制链路使用 Fleet(JSON-RPC 2.0 + 二进制通道);Monitor 只定义 `monitor.*` 方法、`TaskSet` 状态文档和 UDP/TCP 数据面 | [protocol.md](protocol.md) |
|
||||
| 协议 | 控制链路使用 Fleet(JSON-RPC 2.0 + 二进制通道);MoShiro 只定义 `moshiro.*` 方法、`TaskSet` 状态文档和 UDP/TCP 数据面 | [protocol.md](protocol.md) |
|
||||
| 存储 | 原始块追加写入段文件,SQLite 记录偏移量;指标窗口通用存储并降采样 | [storage.md](storage.md) |
|
||||
| 展示查询 | 任意时间段(相对或绝对);自动步长(每条曲线最多约 720 个点);1s 内存热层(1 小时)加上 10s/1m/5m/1h 各层;累加器现场合并,汇总精确;列式响应;SSE 实时跟随 | [query.md](query.md) |
|
||||
| API | `/api/public`(只读,可设为公开)、`/api/admin`(登录)、`/api/agent`(注册与 RPC);实时数据用 SSE | [api.md](api.md) |
|
||||
@@ -70,7 +70,7 @@
|
||||
CI ──签名的发布清单(镜像 digest)──▶ Backend ──原样转发(offer)──▶ Agent / Probe 进程
|
||||
│ 只写进数据目录的 update/
|
||||
▼
|
||||
宿主机 monitor-update(root):用本机公钥验签 → 核对组件、仓库和 seq → podman pull 仓库@digest → 重启 → 不健康就回滚
|
||||
宿主机 moshiro-update(root):用本机公钥验签 → 核对组件、仓库和 seq → podman pull 仓库@digest → 重启 → 不健康就回滚
|
||||
│ 状态写进只读的 update-status/
|
||||
▼
|
||||
节点随心跳上报更新状态 ──▶ Backend ──▶ 管理端(有新版本时点「更新」)
|
||||
@@ -97,13 +97,13 @@ Agent / Probe 能做的 执行编译在二进制中的 Kind;监听和访问
|
||||
|
||||
- **本地硬上限**写在节点本机的配置文件中,Backend 下发的任务如果超出,节点直接拒绝:允许访问的目标网段(默认为空,即不开放目标任务)、总码率和包速率上限、允许监听的端口。
|
||||
- **构建期强制**:常驻节点进程使用 `BannedSymbols.Node.txt`,禁用 `System.Diagnostics.Process`、`Assembly.Load*`、`NativeLibrary.Load`、`HttpClient`,以及 `ConfigStore` 以外的文件写入 API,详见 [project-layout.md](project-layout.md) 第 7 节。
|
||||
- **权限分离**:注册(`fleet-enroll`,一次性运行)、采集(`monitor-agent` / `monitor-probe`)、更新是不同的进程。容器节点里,采集进程在容器内以 UID 1654 运行(只读文件系统、不带额外能力),更新由宿主机上 root 运行的 `monitor-update` 完成:它只读取节点放进 `update/` 的普通文件、复制到自己的临时目录后验签,状态写在节点只读的 `update-status/`;采集进程改写不了自己的二进制,更新器也读不到节点身份私钥。纯二进制安装的 `fleet-updater` 还只是设计。
|
||||
- **权限分离**:注册(`fleet-enroll`,一次性运行)、采集(`moshiro-agent` / `moshiro-probe`)、更新是不同的进程。容器节点里,采集进程在容器内以 UID 1654 运行(只读文件系统、不带额外能力),更新由宿主机上 root 运行的 `moshiro-update` 完成:它只读取节点放进 `update/` 的普通文件、复制到自己的临时目录后验签,状态写在节点只读的 `update-status/`;采集进程改写不了自己的二进制,更新器也读不到节点身份私钥。纯二进制安装的 `fleet-updater` 还只是设计。
|
||||
- **容器运行**:Agent、Probe 的镜像基于 Alpine(musl,NativeAOT),宿主机用 rootful podman 加 systemd 运行:UID/GID 1654、只读文件系统、`--cap-drop all`、`no-new-privileges`、`/tmp` 用 tmpfs;配置和身份放在挂载的数据目录里。
|
||||
- **最小权限运行**:非 root 用户;只在需要时授予 `CAP_NET_RAW`(ICMP);默认端口都高于 1024,不需要 `CAP_NET_BIND_SERVICE`,只有在 Agent 本地显式开启 443 时才授予。
|
||||
- **身份**:节点使用本地生成的 ECDSA P-256 密钥对签名认证(Probe 的密钥由面板生成、只返回一次,同一把用来连面板和连 Agent),Backend 只存公钥,数据库泄露也无法冒充节点。
|
||||
- **防放大**:未认证的包静默丢弃;回复长度与请求相等;每个流按声明速率限速;使用重放窗口。
|
||||
- **容器里的主机指标**:容器里的 Agent 通过只读挂载宿主机的 `/proc`、`/sys`、根目录和 `/etc/os-release`(环境变量 `HOST_PROC`、`HOST_SYS`、`HOST_ROOT`)读取宿主机的资源占用和系统名;读不到的项返回空值,不会退回去读容器自己的数据。Probe 只需要挂载 `/etc/os-release`。
|
||||
- **自身状态**:节点上报进程内的计数(会话数、包速率、队列、丢弃数);Agent 另外每隔几秒用 `monitor.host` 上报一份主机资源占用的汇总数字(可以在 `agent.json` 里用 `reporting.host_stats = false` 整个关掉),最新值实时推给展示页,Backend 每分钟存一个样本(默认保留 7 天,`retention.host_days`)。
|
||||
- **自身状态**:节点上报进程内的计数(会话数、包速率、队列、丢弃数);Agent 另外每隔几秒用 `moshiro.host` 上报一份主机资源占用的汇总数字(可以在 `agent.json` 里用 `reporting.host_stats = false` 整个关掉),最新值实时推给展示页,Backend 每分钟存一个样本(默认保留 7 天,`retention.host_days`)。
|
||||
|
||||
## 6. 可扩展性目标
|
||||
|
||||
@@ -119,7 +119,7 @@ Kind 目录编译在二进制中,节点不接受远程下发代码(安全边
|
||||
|
||||
## 7. 可靠性与资源
|
||||
|
||||
- Probe:每个 Agent 未交付的原始列 ≤ 8 MB;Agent:上传队列 ≤ 64 MB,满时丢弃最旧的块,并通过 `monitor.event(gap)` 上报缺口。
|
||||
- Probe:每个 Agent 未交付的原始列 ≤ 8 MB;Agent:上传队列 ≤ 64 MB,满时丢弃最旧的块,并通过 `moshiro.event(gap)` 上报缺口。
|
||||
- 至少一次投递:Backend 持久化之后才回复 ACK,按 `chunk_id` 去重。
|
||||
- 节点重启会丢失未上传的数据,并体现为数据缺口。
|
||||
- 窗口时间以 Agent 时钟为准;时钟偏差超过 5 秒时记录告警事件。
|
||||
@@ -144,7 +144,7 @@ Kind 目录编译在二进制中,节点不接受远程下发代码(安全边
|
||||
| D13 | Probe/Agent 运行时只依赖 BCL;.NET 10 NativeAOT | 轻量 |
|
||||
| D14 | 节点机制抽成与业务无关的 Fleet 框架,放在同一仓库的 `backend/fleet/` 目录,暂不独立(由用户确认;不设拆分时间表,需要时再评估;不使用 PublicApiAnalyzers) | 可复用;`samples/EchoNode` 作为第二个使用者,防止过度设计 |
|
||||
| D15 | 注册独立为 `fleet-enroll` 工具;常驻进程不包含注册代码 | 常驻进程去掉 HttpClient,边界更纯 |
|
||||
| D16 | 更新与业务进程分离:发布密钥签名、本地策略、健康确认与回滚。容器节点已实现(宿主机 `monitor-update`,清单里是镜像 digest);纯二进制的 `fleet-updater` 待做 | 面板被攻破时也无法推送恶意二进制;可以同时管理多个产品 |
|
||||
| D16 | 更新与业务进程分离:发布密钥签名、本地策略、健康确认与回滚。容器节点已实现(宿主机 `moshiro-update`,清单里是镜像 digest);纯二进制的 `fleet-updater` 待做 | 面板被攻破时也无法推送恶意二进制;可以同时管理多个产品 |
|
||||
| D17 | Probe 支持 OpenWrt:正式支持 `linux-musl-x64`、`linux-musl-arm64`;armv7 视工具链验证结果决定;MIPS 不支持,改为在局域网设备上运行 Probe | 由用户确认;NativeAOT 不支持 MIPS |
|
||||
| D18 | 默认测量端口不使用 443:控制 7443/tcp,UDP 数据 7443/udp,TCP 数据 7444/tcp;443 只在 Agent 本地显式开启时才允许使用 | 由用户确认;避免与 VPS 上的真实服务冲突;结果会记录实际端口 |
|
||||
| D19 | 展示查询最细 1s:最近 1 小时来自内存热层,更早的数据来自 10s/1m/5m/1h 各层,现场合并;时间段内的汇总由累加器精确合并得出 | 由用户确认;5 分钟、15 分钟这类视图足够精细且快 |
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
## 1. 格式与命名(`.editorconfig` 强制)
|
||||
|
||||
- 使用 Allman 大括号(左大括号独占一行),四空格缩进,不使用 Tab;`if`/`for` 等语句必须加大括号。
|
||||
- 使用文件作用域 namespace;namespace 与目录一致(`Monitor.Agent.Sessions` 对应 `Sessions/`)。
|
||||
- 使用文件作用域 namespace;namespace 与目录一致(`MoShiro.Agent.Sessions` 对应 `Sessions/`)。
|
||||
- `using` 放在文件顶部、namespace 之外,`System.*` 排在最前面,其余按字母排序。
|
||||
- 始终显式写出可见性,并且放在修饰符的第一位;`static readonly` 的顺序固定。
|
||||
|
||||
@@ -82,8 +82,8 @@
|
||||
|
||||
## 9.1 Fleet 的通用性
|
||||
|
||||
- `backend/fleet/**` 不能引用任何 `Monitor.*`,也不能出现产品相关的概念、命名或默认值(例如端口 7443、`monitor` 字样),由 `scripts/check-deps` 和代码评审把关。
|
||||
- Fleet 的每个公开 API 都要在 `samples/EchoNode` 或 `EchoServer` 中有用法。新增的公开 API 如果只有 Monitor 使用,应先放在 Monitor 中,等出现第二个使用者后再下沉到 Fleet。
|
||||
- `backend/fleet/**` 不能引用任何 `MoShiro.*`,也不能出现产品相关的概念、命名或默认值(例如端口 7443、`moshiro` 字样),由 `scripts/check-deps` 和代码评审把关。
|
||||
- Fleet 的每个公开 API 都要在 `samples/EchoNode` 或 `EchoServer` 中有用法。新增的公开 API 如果只有 MoShiro 使用,应先放在 MoShiro 中,等出现第二个使用者后再下沉到 Fleet。
|
||||
- Fleet 永远不提供 exec、shell、文件读写或远程下发代码的方法,这条规则不接受例外。
|
||||
- 公开 API 的变更记录在 `PublicAPI.Unshipped.txt` 中;PR 描述需要说明是否属于破坏性变更。
|
||||
|
||||
@@ -107,5 +107,5 @@
|
||||
## 12. 提交与评审
|
||||
|
||||
- 提交信息使用 `<范围>: <摘要>` 格式,范围取值为 `probe`、`agent`、`backend`、`shared`、`web`、`docs`、`scripts`。
|
||||
- 修改 `backend/shared/Monitor.Protocol` 的 PR 必须说明兼容性影响(新增字段、行为变化、需要升级版本)。
|
||||
- 修改 `backend/shared/MoShiro.Protocol` 的 PR 必须说明兼容性影响(新增字段、行为变化、需要升级版本)。
|
||||
- 修改 `backend/openapi/v1.json` 的 PR 必须按照 [api-guidelines.md](api-guidelines.md) 第 2 节判断是否属于破坏性变更。
|
||||
+47
-47
@@ -6,10 +6,10 @@
|
||||
|
||||
```text
|
||||
scripts/publish.sh <agent|probe|backend|fleet-enroll> <rid> → backend/artifacts/publish/<project>/<rid>/
|
||||
scripts/package.sh <agent|probe> <rid> → backend/artifacts/packages/monitor-<role>-<version>-<rid>.tar.gz(附 .sha256)
|
||||
scripts/package.sh <agent|probe> <rid> → backend/artifacts/packages/moshiro-<role>-<version>-<rid>.tar.gz(附 .sha256)
|
||||
```
|
||||
|
||||
构建信息(程序里显示的 tag、提交、构建时间):`publish.sh` 会用 `git describe --tags --always --dirty`、`git rev-parse` 和当前时间自动设置环境变量 `MONITOR_TAG`、`MONITOR_COMMIT`、`MONITOR_BUILT_AT`,直接用 `dotnet publish` 或 CI 构建时自己设置它们(容器镜像的 `*.NativeBuild.Containerfile` 用同名的 `--build-arg`,CI 脚本会传);不设置就是空,本地构建照常可重复。版本号取自 `backend/Directory.Build.props` 的 `Version`。前端的信息由 `web/build-info.mjs` 在构建时写入(版本取自 `web/package.json`)。
|
||||
构建信息(程序里显示的 tag、提交、构建时间):`publish.sh` 会用 `git describe --tags --always --dirty`、`git rev-parse` 和当前时间自动设置环境变量 `MOSHIRO_TAG`、`MOSHIRO_COMMIT`、`MOSHIRO_BUILT_AT`,直接用 `dotnet publish` 或 CI 构建时自己设置它们(容器镜像的 `*.NativeBuild.Containerfile` 用同名的 `--build-arg`,CI 脚本会传);不设置就是空,本地构建照常可重复。版本号取自 `backend/Directory.Build.props` 的 `Version`。前端的信息由 `web/build-info.mjs` 在构建时写入(版本取自 `web/package.json`)。
|
||||
|
||||
| rid | 目标 | 构建方式 |
|
||||
| --- | --- | --- |
|
||||
@@ -24,15 +24,15 @@ macOS 的 Apple Silicon 产物带临时 ad-hoc 签名,Intel 产物未签名。
|
||||
|
||||
## Backend 容器
|
||||
|
||||
Backend 使用 OCI 标准镜像,通过 `registry.oeo.one/monitor/monitor-backend:<version>` 分发,支持 `linux/amd64` 和 `linux/arm64`。容器使用 musl NativeAOT 单文件程序,基底为 `mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine`,以非 root 用户运行,在 `8080/tcp` 提供 HTTP 服务;数据库和原始数据统一写入 `/data`。程序启用 InvariantGlobalization,无需 .NET 运行时或 ICU;时间查询使用 UTC。SQLite 从固定版本的官方源码编译并静态链接,不再随镜像携带 `libe_sqlite3.so`。将 `/data` 挂载为持久卷,并在反向代理处配置 HTTPS。
|
||||
Backend 使用 OCI 标准镜像,通过 `registry.oeo.one/moshiro/moshiro-backend:<version>` 分发,支持 `linux/amd64` 和 `linux/arm64`。容器使用 musl NativeAOT 单文件程序,基底为 `mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine`,以非 root 用户运行,在 `8080/tcp` 提供 HTTP 服务;数据库和原始数据统一写入 `/data`。程序启用 InvariantGlobalization,无需 .NET 运行时或 ICU;时间查询使用 UTC。SQLite 从固定版本的官方源码编译并静态链接,不再随镜像携带 `libe_sqlite3.so`。将 `/data` 挂载为持久卷,并在反向代理处配置 HTTPS。
|
||||
|
||||
在部署目录保存此仓库的 `compose.yaml`,先通过部署平台的环境变量或密钥管理设置 `MONITOR_ADMIN_PASSWORD`(可选设置 `MONITOR_ADMIN_USER`、`MONITOR_VERSION`),然后启动:
|
||||
在部署目录保存此仓库的 `compose.yaml`,先通过部署平台的环境变量或密钥管理设置 `MOSHIRO_ADMIN_PASSWORD`(可选设置 `MOSHIRO_ADMIN_USER`、`MOSHIRO_VERSION`),然后启动:
|
||||
|
||||
```sh
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Compose 将容器端口只绑定到宿主机 `127.0.0.1:8080`,适合由同机 HTTPS 反向代理转发;数据保存在 Docker 命名卷 `monitor-data`。首次启动成功创建管理员后,改密应在管理端完成。
|
||||
Compose 将容器端口只绑定到宿主机 `127.0.0.1:8080`,适合由同机 HTTPS 反向代理转发;数据保存在 Docker 命名卷 `moshiro-data`。首次启动成功创建管理员后,改密应在管理端完成。
|
||||
|
||||
Backend 的 NativeAOT 发布默认启用 `SqliteStaticLink`,支持 Linux glibc/musl、macOS、Windows 的 x64/arm64 RID;跨平台构建使用 `-p:UseAotAnywhere=true`。SQLite 源码版本、下载年份和 SHA-256 统一配置在根目录 `Directory.Packages.props`,构建会校验下载文件并按 RID 缓存原生对象。普通 JIT 开发构建仍使用 NuGet 原生库。发布 Backend 时设置 `-p:OpenApiGenerateDocuments=false`,API 文档在普通开发构建中生成。
|
||||
|
||||
@@ -50,30 +50,30 @@ Pull request 和 `dev` push 会运行 Backend tests;通过后,独立的 `web
|
||||
|
||||
### main 开发镜像
|
||||
|
||||
`main` 上只有提交信息包含 `[CI]` 的 push 才构建镜像。合并 PR 时,请把 `[CI]` 放入 PR 标题或合并提交标题。Backend、Agent、Probe 的 Linux/amd64 镜像会并行构建,并推送到同一个 Zot Registry 下独立的 `monitor-dev/` 仓库:
|
||||
`main` 上只有提交信息包含 `[CI]` 的 push 才构建镜像。合并 PR 时,请把 `[CI]` 放入 PR 标题或合并提交标题。Backend、Agent、Probe 的 Linux/amd64 镜像会并行构建,并推送到同一个 Zot Registry 下独立的 `moshiro-dev/` 仓库:
|
||||
|
||||
| 镜像 | 滚动标签 | 固定提交标签 |
|
||||
| --- | --- | --- |
|
||||
| Backend | `registry.oeo.one/monitor-dev/monitor-backend:latest` | `registry.oeo.one/monitor-dev/monitor-backend:dev-<12位SHA>` |
|
||||
| Agent | `registry.oeo.one/monitor-dev/monitor-agent:latest` | `registry.oeo.one/monitor-dev/monitor-agent:dev-<12位SHA>` |
|
||||
| Probe | `registry.oeo.one/monitor-dev/monitor-probe:latest` | `registry.oeo.one/monitor-dev/monitor-probe:dev-<12位SHA>` |
|
||||
| Backend | `registry.oeo.one/moshiro-dev/moshiro-backend:latest` | `registry.oeo.one/moshiro-dev/moshiro-backend:dev-<12位SHA>` |
|
||||
| Agent | `registry.oeo.one/moshiro-dev/moshiro-agent:latest` | `registry.oeo.one/moshiro-dev/moshiro-agent:dev-<12位SHA>` |
|
||||
| Probe | `registry.oeo.one/moshiro-dev/moshiro-probe:latest` | `registry.oeo.one/moshiro-dev/moshiro-probe:dev-<12位SHA>` |
|
||||
|
||||
Dev 镜像目前仅支持 amd64;Agent/Probe 在 `sdk:10.0-alpine-aot` 中构建 `linux-musl-x64` 产物,运行基底为 `runtime-deps:10.0-alpine`。它们不创建 Gitea Release,也不修改正式版 `monitor/` 仓库中的标签。普通 main push 不会构建镜像。
|
||||
Dev 镜像目前仅支持 amd64;Agent/Probe 在 `sdk:10.0-alpine-aot` 中构建 `linux-musl-x64` 产物,运行基底为 `runtime-deps:10.0-alpine`。它们不创建 Gitea Release,也不修改正式版 `moshiro/` 仓库中的标签。普通 main push 不会构建镜像。
|
||||
|
||||
### 正式版发布
|
||||
|
||||
推送匹配项目版本的 `vMAJOR.MINOR.PATCH` tag 会触发完整跨平台构建、正式 OCI 镜像推送和 Gitea Release。正式镜像使用 `registry.oeo.one/monitor/` 路径;Agent、Probe 提供 amd64 与 arm64 多架构标签,稳定版本更新正式仓库自己的 `latest`。这与 `monitor-dev/` 的 dev `latest` 相互独立。
|
||||
推送匹配项目版本的 `vMAJOR.MINOR.PATCH` tag 会触发完整跨平台构建、正式 OCI 镜像推送和 Gitea Release。正式镜像使用 `registry.oeo.one/moshiro/` 路径;Agent、Probe 提供 amd64 与 arm64 多架构标签,稳定版本更新正式仓库自己的 `latest`。这与 `moshiro-dev/` 的 dev `latest` 相互独立。
|
||||
|
||||
Agent 和 Probe 镜像分别发布为 `registry.oeo.one/monitor/monitor-agent:<version>` 与 `registry.oeo.one/monitor/monitor-probe:<version>`,包含 amd64、arm64 多架构索引;也可指定 `:<version>-amd64` 或 `:<version>-arm64`。稳定版本同时更新 `latest`。镜像以非 root 用户(UID/GID 1654)运行,默认读取 `/etc/monitor/agent.json` 或 `/etc/monitor/probe.json`。运行容器时挂载对应配置目录;Probe 还会在该目录写入 `agents.json`,因此目录需可由 1654 用户写入。Agent、Probe 需使用宿主网络以保持节点端口和测量流量行为,并按需添加 `NET_RAW` 能力以执行 ICMP 测量:
|
||||
Agent 和 Probe 镜像分别发布为 `registry.oeo.one/moshiro/moshiro-agent:<version>` 与 `registry.oeo.one/moshiro/moshiro-probe:<version>`,包含 amd64、arm64 多架构索引;也可指定 `:<version>-amd64` 或 `:<version>-arm64`。稳定版本同时更新 `latest`。镜像以非 root 用户(UID/GID 1654)运行,默认读取 `/etc/moshiro/agent.json` 或 `/etc/moshiro/probe.json`。运行容器时挂载对应配置目录;Probe 还会在该目录写入 `agents.json`,因此目录需可由 1654 用户写入。Agent、Probe 需使用宿主网络以保持节点端口和测量流量行为,并按需添加 `NET_RAW` 能力以执行 ICMP 测量:
|
||||
|
||||
```sh
|
||||
docker run -d --name monitor-agent --restart unless-stopped --network host \
|
||||
--cap-add NET_RAW -v "$PWD/agent-config:/etc/monitor" \
|
||||
registry.oeo.one/monitor/monitor-agent:<version>
|
||||
docker run -d --name moshiro-agent --restart unless-stopped --network host \
|
||||
--cap-add NET_RAW -v "$PWD/agent-config:/etc/moshiro" \
|
||||
registry.oeo.one/moshiro/moshiro-agent:<version>
|
||||
|
||||
docker run -d --name monitor-probe --restart unless-stopped --network host \
|
||||
--cap-add NET_RAW -v "$PWD/probe-config:/etc/monitor" \
|
||||
registry.oeo.one/monitor/monitor-probe:<version>
|
||||
docker run -d --name moshiro-probe --restart unless-stopped --network host \
|
||||
--cap-add NET_RAW -v "$PWD/probe-config:/etc/moshiro" \
|
||||
registry.oeo.one/moshiro/moshiro-probe:<version>
|
||||
```
|
||||
|
||||
宿主机配置目录应允许 UID/GID 1654 读写;Probe 需要写权限来更新 `agents.json`。也可通过 Docker Compose 配置相同的网络、卷和能力。
|
||||
@@ -86,8 +86,8 @@ Agent 和 Probe 的默认发布配置:NativeAOT、完整裁剪、`Optimization
|
||||
|
||||
| 程序 | linux-musl-x64 | linux-x64 | win-x64 |
|
||||
| --- | --- | --- | --- |
|
||||
| monitor-probe | 7.7 MB | 7.7 MB | 5.6 MB |
|
||||
| monitor-agent | 8.4 MB | 8.4 MB | 7.3 MB |
|
||||
| moshiro-probe | 7.7 MB | 7.7 MB | 5.6 MB |
|
||||
| moshiro-agent | 8.4 MB | 8.4 MB | 7.3 MB |
|
||||
| fleet-enroll | 6.4 MB | 6.4 MB | — |
|
||||
|
||||
Linux 上比 Windows 大,是因为 .NET 的加密、压缩、网络本机库被静态链接进来。tar.gz 压缩后 Probe 约 3.6 MB。
|
||||
@@ -108,31 +108,31 @@ Linux 上比 Windows 大,是因为 .NET 的加密、压缩、网络本机库
|
||||
|
||||
| 位置 | 内容 | 所有者 |
|
||||
| --- | --- | --- |
|
||||
| `/usr/lib/monitor/` | 二进制 | root,0755:采集进程不能改写自己的可执行文件 |
|
||||
| `/etc/monitor/` | `agent.json` 或 `probe.json`、身份(`node.json`、`node.key`、`tls.pfx`)、`agents.json` | `monitor` 用户,0750 |
|
||||
| `/usr/lib/moshiro/` | 二进制 | root,0755:采集进程不能改写自己的可执行文件 |
|
||||
| `/etc/moshiro/` | `agent.json` 或 `probe.json`、身份(`node.json`、`node.key`、`tls.pfx`)、`agents.json` | `moshiro` 用户,0750 |
|
||||
|
||||
安装脚本会:检查 OpenSSL、创建 `monitor` 用户(`useradd`、busybox `adduser`,OpenWrt 没有这些时直接写 `/etc/passwd`)、安装二进制和服务文件;缺少配置时只安装、不启动,并打印下一步。
|
||||
安装脚本会:检查 OpenSSL、创建 `moshiro` 用户(`useradd`、busybox `adduser`,OpenWrt 没有这些时直接写 `/etc/passwd`)、安装二进制和服务文件;缺少配置时只安装、不启动,并打印下一步。
|
||||
|
||||
### systemd(`packaging/systemd/*.service`)
|
||||
|
||||
以 `monitor` 用户运行,沙箱:`NoNewPrivileges`、`ProtectSystem=strict`、`ProtectHome`、`PrivateTmp/Devices`、`ProtectKernel*`、`RestrictAddressFamilies=AF_INET AF_INET6`、`RestrictNamespaces`、`MemoryDenyWriteExecute`(NativeAOT 不 JIT)、`SystemCallArchitectures=native`,`MemoryMax`(Agent 128 MB、Probe 64 MB)。
|
||||
以 `moshiro` 用户运行,沙箱:`NoNewPrivileges`、`ProtectSystem=strict`、`ProtectHome`、`PrivateTmp/Devices`、`ProtectKernel*`、`RestrictAddressFamilies=AF_INET AF_INET6`、`RestrictNamespaces`、`MemoryDenyWriteExecute`(NativeAOT 不 JIT)、`SystemCallArchitectures=native`,`MemoryMax`(Agent 128 MB、Probe 64 MB)。
|
||||
|
||||
- **Agent** 只保留 `CAP_NET_RAW`(ICMP 目标任务用),`/etc/monitor` 只读;Probe 没有任何 capability,只对 `agents.json` 所在的目录可写。
|
||||
- **Agent** 只保留 `CAP_NET_RAW`(ICMP 目标任务用),`/etc/moshiro` 只读;Probe 没有任何 capability,只对 `agents.json` 所在的目录可写。
|
||||
- 监听 1024 以下的端口(例如把数据面放到 443)需要在 drop-in 里加 `AmbientCapabilities=CAP_NET_BIND_SERVICE` 和对应的 `CapabilityBoundingSet`。默认端口是 7443/tcp+udp(Probe 控制通道和 UDP 数据面)、7444/tcp。
|
||||
|
||||
### OpenWrt(`packaging/openwrt/`)
|
||||
|
||||
只支持 Probe(Agent 面向服务器)。`install.sh probe` 安装 `/etc/init.d/monitor-probe`(procd,崩溃自动重启,日志进 logd)并写入 `/lib/upgrade/keep.d/monitor`,sysupgrade 时保留 `/etc/monitor/`。
|
||||
只支持 Probe(Agent 面向服务器)。`install.sh probe` 安装 `/etc/init.d/moshiro-probe`(procd,崩溃自动重启,日志进 logd)并写入 `/lib/upgrade/keep.d/moshiro`,sysupgrade 时保留 `/etc/moshiro/`。
|
||||
|
||||
OpenWrt 默认不带 OpenSSL:`opkg install libopenssl3`(较新的快照和 24.10 之后用 `apk add libopenssl3`)。闪存很小时注意:二进制 7.7 MB,配置只在 Agent 列表变化时才写。路由器上建议把 `probe.json` 的 `limits.max_test_mbps` 调低。
|
||||
|
||||
## 3. 验证
|
||||
|
||||
`scripts/smoke-linux.sh` 在 Linux 容器里用发布的二进制跑完整链路(后端 → 注册 → Agent → Probe → 数据落库),`MODE=install` 用打包产物和 `install.sh` 安装、以 `monitor` 用户运行。`scripts/smoke-systemd.sh` 在 systemd 容器里验证服务单元。已经验证过:
|
||||
`scripts/smoke-linux.sh` 在 Linux 容器里用发布的二进制跑完整链路(后端 → 注册 → Agent → Probe → 数据落库),`MODE=install` 用打包产物和 `install.sh` 安装、以 `moshiro` 用户运行。`scripts/smoke-systemd.sh` 在 systemd 容器里验证服务单元。已经验证过:
|
||||
|
||||
- Alpine 3.22(musl)、Debian 12(glibc):直接运行和 `install.sh` 安装都通;
|
||||
- OpenWrt(官方 rootfs,busybox,无 `install`/`useradd`/`su`):安装、建用户、卸载、二进制启动和 OpenSSL 自检;
|
||||
- systemd(Debian 12):两个服务 active,Agent 只有 `CAP_NET_RAW`、Probe 没有 capability,`NoNewPrivs`、seccomp 生效,Agent 无法写 `/etc/monitor`,没有重启和错误日志。
|
||||
- systemd(Debian 12):两个服务 active,Agent 只有 `CAP_NET_RAW`、Probe 没有 capability,`NoNewPrivs`、seccomp 生效,Agent 无法写 `/etc/moshiro`,没有重启和错误日志。
|
||||
|
||||
没有验证的:arm64 构建、真正的 OpenWrt 设备(有 procd 和真实的内存/闪存限制)、跨公网的真实线路。
|
||||
|
||||
@@ -172,20 +172,20 @@ OpenWrt 默认不带 OpenSSL:`opkg install libopenssl3`(较新的快照和 2
|
||||
|
||||
- **Backend 通过隧道暴露**:隧道把一个公网域名转到家宽机器的 `29181`;Backend 监听 `http://0.0.0.0:29181`(只写一个监听地址,同时写 `0.0.0.0` 和 `[::]` 会报地址已被占用),家宽路由器的防火墙不放行这个端口,公网只能经隧道进来。Agent 用 `fleet-enroll --server https://<公网域名>` 注册,之后用 WSS 连回 Backend。
|
||||
- **经隧道注册之后,检查 Agent 的 `public_host`**:Backend 看到的来源地址是隧道的出口,不是 VPS 的地址,要改成 VPS 的真实地址,Probe 才连得上。
|
||||
- **Probe 直连面板**:`probe.json` 里的 `server` 就是面板地址(取自 `Monitor__PublicUrl`,没配就用创建它时的请求地址),Probe 用它连面板,Agent 列表由面板下发;先注册还是先创建 Probe 都可以。
|
||||
- **Probe 直连面板**:`probe.json` 里的 `server` 就是面板地址(取自 `MoShiro__PublicUrl`,没配就用创建它时的请求地址),Probe 用它连面板,Agent 列表由面板下发;先注册还是先创建 Probe 都可以。
|
||||
- **小 VPS 把 `limits.max_total_mbps` 调低**(试跑时用 100);Agent 常驻约 23 MB,VPS 上原有的服务不受影响,结束后删除 `/opt/…`、停掉进程即可,没有留下任何东西。
|
||||
- 家宽的 IPv6 前缀会在路由器重新拨号(比如停电)后变化,SSH 地址要跟着换;面板走隧道的域名,不受影响。
|
||||
- 本地电脑没有直连 VPS 的路径时,用家宽机器当跳板(`SSH_ASKPASS` + `SSH_ASKPASS_REQUIRE=force`,密码放环境变量,不写进文件)。
|
||||
|
||||
**常驻(开机自启)**:
|
||||
|
||||
- 家宽机器(没有 root 时):`~/.config/systemd/user/monitor-backend.service` 和 `monitor-probe.service`,`EnvironmentFile` 指向放配置的 `backend.env`,`Restart=always`,`systemctl --user enable --now …`,再用 `sudo loginctl enable-linger <用户>` 让它们不登录也能起来。Probe 的命令行是 `monitor-probe --config <probe.json 路径>`(默认路径是 `/etc/monitor/probe.json`)。
|
||||
- VPS:`/etc/systemd/system/monitor-agent.service`,`Restart=always`,加 `NoNewPrivileges`、`ProtectSystem=strict`、`ReadWritePaths=/opt/monitor`、`ProtectHome`、`PrivateTmp`。这是不走安装脚本的手动方式;正式安装用上面的 `packaging/systemd`。
|
||||
- 日志进 journal:`journalctl -u monitor-agent`、`journalctl --user -u monitor-probe`。重启 Backend 用 `systemctl` 而不是手动再起一个进程,否则会端口冲突。
|
||||
- 家宽机器(没有 root 时):`~/.config/systemd/user/moshiro-backend.service` 和 `moshiro-probe.service`,`EnvironmentFile` 指向放配置的 `backend.env`,`Restart=always`,`systemctl --user enable --now …`,再用 `sudo loginctl enable-linger <用户>` 让它们不登录也能起来。Probe 的命令行是 `moshiro-probe --config <probe.json 路径>`(默认路径是 `/etc/moshiro/probe.json`)。
|
||||
- VPS:`/etc/systemd/system/moshiro-agent.service`,`Restart=always`,加 `NoNewPrivileges`、`ProtectSystem=strict`、`ReadWritePaths=/opt/moshiro`、`ProtectHome`、`PrivateTmp`。这是不走安装脚本的手动方式;正式安装用上面的 `packaging/systemd`。
|
||||
- 日志进 journal:`journalctl -u moshiro-agent`、`journalctl --user -u moshiro-probe`。重启 Backend 用 `systemctl` 而不是手动再起一个进程,否则会端口冲突。
|
||||
|
||||
**国家和运营商**:Backend 用 Probe、Agent 的来源地址向 IP 归属服务查一次,展示在国旗和运营商图标上(`Monitor__GeoLookupUrl`,缺省 `https://ip.007995.xyz`;留空就不查;这个服务只供公益使用,署名和使用条款由部署的人自己遵守)。Probe、Agent 自己不访问外部服务,所以客户的防火墙审计里不会多出出站连接;Backend 发出去的只有公网 IP(内网地址不发)。
|
||||
**国家和运营商**:Backend 用 Probe、Agent 的来源地址向 IP 归属服务查一次,展示在国旗和运营商图标上(`MoShiro__GeoLookupUrl`,缺省 `https://ip.007995.xyz`;留空就不查;这个服务只供公益使用,署名和使用条款由部署的人自己遵守)。Probe、Agent 自己不访问外部服务,所以客户的防火墙审计里不会多出出站连接;Backend 发出去的只有公网 IP(内网地址不发)。
|
||||
|
||||
**从别处看这个面板**:本机跑前端,在页面里把数据源指向这个域名,用 API 令牌访问,Backend 设 `Monitor__CorsOrigins` 放行前端的来源(见 [web/README.md](../web/README.md))。
|
||||
**从别处看这个面板**:本机跑前端,在页面里把数据源指向这个域名,用 API 令牌访问,Backend 设 `MoShiro__CorsOrigins` 放行前端的来源(见 [web/README.md](../web/README.md))。
|
||||
|
||||
实测(Probe 在广东移动家宽,Agent 在日本 IIJ):
|
||||
|
||||
@@ -203,24 +203,24 @@ OpenWrt 默认不带 OpenSSL:`opkg install libopenssl3`(较新的快照和 2
|
||||
|
||||
## 容器部署(Docker / Podman 共用命名 volume)
|
||||
|
||||
镜像和 Compose 配置共用。普通用户执行 Podman 即为 rootless,使用命名 volume 不需要 `keep-id` 或手动 `chown`。Agent 的配置、原有身份和缓存保存在 `monitor-agent-config`,Probe 保存在 `monitor-probe-config`,更新状态使用独立的 `monitor-<role>-status` volume,在业务容器内只读。不会重新生成已有身份。更新/重建容器不删除 volume;不要使用 `compose down -v`,该参数会删除数据。
|
||||
镜像和 Compose 配置共用。普通用户执行 Podman 即为 rootless,使用命名 volume 不需要 `keep-id` 或手动 `chown`。Agent 的配置、原有身份和缓存保存在 `moshiro-agent-config`,Probe 保存在 `moshiro-probe-config`,更新状态使用独立的 `moshiro-<role>-status` volume,在业务容器内只读。不会重新生成已有身份。更新/重建容器不删除 volume;不要使用 `compose down -v`,该参数会删除数据。
|
||||
|
||||
在 `packaging/compose/agent` 下首次注册,并写配置(下列 `podman` 可替换为 `docker`):
|
||||
|
||||
```sh
|
||||
podman compose run --rm --entrypoint /usr/local/bin/fleet-enroll agent \
|
||||
--server https://<backend> --join <token> --name <name> --identity-dir /etc/monitor/agent-id
|
||||
podman compose run --rm -T --entrypoint /bin/sh agent -c 'cat > /etc/monitor/agent.json' <<'JSON'
|
||||
{"fleet":{"server":"https://<backend>","identity_dir":"/etc/monitor/agent-id"},"listen":{"bind":"0.0.0.0"}}
|
||||
--server https://<backend> --join <token> --name <name> --identity-dir /etc/moshiro/agent-id
|
||||
podman compose run --rm -T --entrypoint /bin/sh agent -c 'cat > /etc/moshiro/agent.json' <<'JSON'
|
||||
{"fleet":{"server":"https://<backend>","identity_dir":"/etc/moshiro/agent-id"},"listen":{"bind":"0.0.0.0"}}
|
||||
JSON
|
||||
podman compose up -d
|
||||
```
|
||||
|
||||
Probe 在 `packaging/compose/probe` 下,将面板下载的身份文件通过标准输入导入 volume:`podman compose run --rm -T --entrypoint /bin/sh probe -c 'umask 077; cat > /etc/monitor/probe.json' < probe.json`,再 `podman compose up -d`。私钥权限为 600。已有部署迁移时,应把原配置与身份原样复制进 volume,不重新注册或轮换密钥。Docker 与 Podman 各自管理 volume,切换引擎不会自动共享数据。
|
||||
Probe 在 `packaging/compose/probe` 下,将面板下载的身份文件通过标准输入导入 volume:`podman compose run --rm -T --entrypoint /bin/sh probe -c 'umask 077; cat > /etc/moshiro/probe.json' < probe.json`,再 `podman compose up -d`。私钥权限为 600。已有部署迁移时,应把原配置与身份原样复制进 volume,不重新注册或轮换密钥。Docker 与 Podman 各自管理 volume,切换引擎不会自动共享数据。
|
||||
|
||||
纯 Compose 使用 `compose pull && compose up -d` 更新镜像;面板自动更新使用下述 systemd 单元和更新器。Rootless Podman 使用 `monitor-<role>-rootless.service`,安装到 `~/.config/systemd/user/`,镜像文件为 `~/.config/monitor/<role>.image`;rootful Podman 使用 `monitor-<role>-container.service`,Docker 使用 `monitor-<role>-docker.service`,安装到 `/etc/systemd/system/`,镜像文件为 `/etc/monitor-update/<role>.image`。所有单元均使用相同的配置和状态 volume 名称。自动启动分别使用 `systemctl --user enable --now <unit>` 或 `sudo systemctl enable --now <unit>`。Rootless 在退出登录后继续运行需管理员执行一次 `loginctl enable-linger <用户名>`。
|
||||
纯 Compose 使用 `compose pull && compose up -d` 更新镜像;面板自动更新使用下述 systemd 单元和更新器。Rootless Podman 使用 `moshiro-<role>-rootless.service`,安装到 `~/.config/systemd/user/`,镜像文件为 `~/.config/moshiro/<role>.image`;rootful Podman 使用 `moshiro-<role>-container.service`,Docker 使用 `moshiro-<role>-docker.service`,安装到 `/etc/systemd/system/`,镜像文件为 `/etc/moshiro-update/<role>.image`。所有单元均使用相同的配置和状态 volume 名称。自动启动分别使用 `systemctl --user enable --now <unit>` 或 `sudo systemctl enable --now <unit>`。Rootless 在退出登录后继续运行需管理员执行一次 `loginctl enable-linger <用户名>`。
|
||||
|
||||
Backend 的 `packaging/compose/backend/compose.yaml` 使用 `monitor-backend-data` 保存数据库和原始数据,设置 `MONITOR_INITIAL_ADMIN_PASSWORD` 后即可启动。首次安装 Podman 与用户 subuid/subgid 映射需要管理员权限,之后普通用户运行无需 sudo。
|
||||
Backend 的 `packaging/compose/backend/compose.yaml` 使用 `moshiro-backend-data` 保存数据库和原始数据,设置 `MOSHIRO_INITIAL_ADMIN_PASSWORD` 后即可启动。首次安装 Podman 与用户 subuid/subgid 映射需要管理员权限,之后普通用户运行无需 sudo。
|
||||
|
||||
### Agent 的系统指标挂载
|
||||
|
||||
@@ -247,16 +247,16 @@ Rootless Podman 普通用户安装示例(先将对应 rootless 服务单元复
|
||||
|
||||
```sh
|
||||
sh packaging/update/install-updater.sh agent --user \
|
||||
--volume monitor-agent-config --status-volume monitor-agent-status \
|
||||
--helper-image registry.oeo.one/monitor/monitor-agent:<version> \
|
||||
--image-repo registry.oeo.one/monitor/monitor-agent \
|
||||
--unit monitor-agent-rootless.service --policy auto
|
||||
--volume moshiro-agent-config --status-volume moshiro-agent-status \
|
||||
--helper-image registry.oeo.one/moshiro/moshiro-agent:<version> \
|
||||
--image-repo registry.oeo.one/moshiro/moshiro-agent \
|
||||
--unit moshiro-agent-rootless.service --policy auto
|
||||
```
|
||||
|
||||
使用 Docker 时以管理员运行相同安装命令,去掉 `--user`,加 `--engine docker`,单元选择 `monitor-agent-docker.service`。rootful Podman 去掉 `--user`,单元选择 `monitor-agent-container.service`。Probe 替换角色名。初始化对应 `<role>.image` 为 `IMAGE=<当前镜像引用>` 后启动容器单元。辅助镜像可用本机已有的 digest 引用,避免标签变化。Docker rootless 更新尚未适配。
|
||||
使用 Docker 时以管理员运行相同安装命令,去掉 `--user`,加 `--engine docker`,单元选择 `moshiro-agent-docker.service`。rootful Podman 去掉 `--user`,单元选择 `moshiro-agent-container.service`。Probe 替换角色名。初始化对应 `<role>.image` 为 `IMAGE=<当前镜像引用>` 后启动容器单元。辅助镜像可用本机已有的 digest 引用,避免标签变化。Docker rootless 更新尚未适配。
|
||||
|
||||
命名 volume 的更新服务由 systemd timer 每 30 秒检查一次。`--policy auto` 验签后自动应用,默认 `manual` 等待面板写入应用请求。清单必须通过 ECDSA P-256 签名校验,组件、镜像仓库和递增 seq 都必须匹配本机配置;镜像按签名清单中的 digest 拉取。更新后观察服务 20 秒,启动失败或崩溃循环会恢复旧镜像引用并重启。无效签名、重放清单、拉取失败不会切换当前镜像。同一组件的更新通过 flock 串行处理。
|
||||
|
||||
更新器依赖 Linux、systemd、OpenSSL、util-linux/flock 和所选引擎。用户部署把配置、公钥和镜像引用放在 `~/.config/monitor/`、版本记录放在 `~/.local/state/monitor-update/`;系统部署使用 `/etc/monitor-update/` 和 `/var/lib/monitor-update/`。私有仓库的 `--authfile` 对 Podman 使用其认证文件,对 Docker 使用标准 `config.json`,不改写全局 Docker 配置。旧 `--data-dir` 安装方式保留兼容,但新部署统一使用 `--volume`。
|
||||
更新器依赖 Linux、systemd、OpenSSL、util-linux/flock 和所选引擎。用户部署把配置、公钥和镜像引用放在 `~/.config/moshiro/`、版本记录放在 `~/.local/state/moshiro-update/`;系统部署使用 `/etc/moshiro-update/` 和 `/var/lib/moshiro-update/`。私有仓库的 `--authfile` 对 Podman 使用其认证文件,对 Docker 使用标准 `config.json`,不改写全局 Docker 配置。旧 `--data-dir` 安装方式保留兼容,但新部署统一使用 `--volume`。
|
||||
|
||||
发布私钥只放在 Woodpecker 的 `release_signing_key` secret;发布公钥位于 `packaging/update/release-key.pub.pem`。CI 的 `release_api_token` 只有发布登记权限,`RELEASE_API_URL` 指向面板。面板保存并转发签名清单,Agent/Probe 将它与更新请求写进配置 volume;更新器验签后才调用容器引擎和 systemd。管理端仍能看到可更新、更新中、已回滚和无效清单状态。纯二进制更新器尚未实现。
|
||||
+13
-13
@@ -1,6 +1,6 @@
|
||||
# Monitor 的注册与身份
|
||||
# MoShiro 的注册与身份
|
||||
|
||||
通用的注册、身份、认证机制由 Fleet 提供,见 [fleet/link.md](fleet/link.md) 第 2~3 节。本文只描述 **Monitor 在 Fleet 之上的约定**:Agent 注册后如何自动接入测量、Probe 的身份以及 Agent 列表同步,以及节点本地的硬上限。
|
||||
通用的注册、身份、认证机制由 Fleet 提供,见 [fleet/link.md](fleet/link.md) 第 2~3 节。本文只描述 **MoShiro 在 Fleet 之上的约定**:Agent 注册后如何自动接入测量、Probe 的身份以及 Agent 列表同步,以及节点本地的硬上限。
|
||||
|
||||
## 1. 身份一览
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
| Probe | Fleet 节点身份(同时用于连面板和连 Agent) | 面板生成 `probe.json`(可以有多个 Probe,每个独立吊销) | 公钥 |
|
||||
| 面板用户 | 账号 + 密码(PBKDF2-SHA256) | 面板 | 密码哈希 |
|
||||
| API 令牌 | 随机高熵令牌,作用域为 `read` 或 `admin` | 面板 | SHA-256 |
|
||||
| 发布签名 | ECDSA P-256 发布密钥 | CI(Woodpecker 密钥 `release_signing_key`)或离线环境 | 只在节点宿主机的更新器里配置公钥(容器节点是 `monitor-update`,见 [deployment.md](deployment.md);纯二进制方案的 `fleet-updater` 还只是设计),**面板不保存私钥,也不验签** |
|
||||
| 发布签名 | ECDSA P-256 发布密钥 | CI(Woodpecker 密钥 `release_signing_key`)或离线环境 | 只在节点宿主机的更新器里配置公钥(容器节点是 `moshiro-update`,见 [deployment.md](deployment.md);纯二进制方案的 `fleet-updater` 还只是设计),**面板不保存私钥,也不验签** |
|
||||
|
||||
## 2. Agent 接入流程
|
||||
|
||||
@@ -20,14 +20,14 @@
|
||||
curl -fsSL https://panel/install.sh | sh -s -- --join jt_xxx --name jp-1
|
||||
|
||||
install.sh(只做安装,不包含任何远程控制功能;下面的更新器部分是纯二进制方案的设计,还没有实现,容器节点的做法见 deployment.md)
|
||||
├─ 下载 monitor-agent、fleet-enroll、fleet-updater,并验证发布签名
|
||||
├─ 创建系统用户:monitor(运行 Agent)、fleet-update(运行更新器)
|
||||
├─ fleet-enroll --server https://panel --join jt_xxx --name jp-1 --identity-dir /etc/monitor
|
||||
│ └─ 注册元数据(Monitor 定义):{ probe_tcp_port, probe_udp_ports, public_host?, kinds[] }
|
||||
├─ 下载 moshiro-agent、fleet-enroll、fleet-updater,并验证发布签名
|
||||
├─ 创建系统用户:moshiro(运行 Agent)、fleet-update(运行更新器)
|
||||
├─ fleet-enroll --server https://panel --join jt_xxx --name jp-1 --identity-dir /etc/moshiro
|
||||
│ └─ 注册元数据(MoShiro 定义):{ probe_tcp_port, probe_udp_ports, public_host?, kinds[] }
|
||||
├─ 写入 agent.json(本地硬上限)和 /etc/fleet/updater.json
|
||||
└─ 启用 monitor-agent.service 和 fleet-updater.service
|
||||
└─ 启用 moshiro-agent.service 和 fleet-updater.service
|
||||
|
||||
面板收到 OnEnrolled(Monitor 挂钩)
|
||||
面板收到 OnEnrolled(MoShiro 挂钩)
|
||||
├─ public_host 未提供时,取注册请求的来源 IP
|
||||
├─ 按“线路任务模板”为该 Agent 计算有效任务集(TaskSet)
|
||||
└─ 面板把新的 Agent 列表作为期望状态推给所有 Probe → Probe 自动连接新 Agent
|
||||
@@ -58,11 +58,11 @@ Probe 启动
|
||||
- **哪些 Probe 连哪些 Agent 由面板决定,始终是 Probe 主动拨出**:设置 `fleet.auto_link`(默认开)打开时,所有未吊销的 Probe 和所有 Agent 两两连;关闭时只连手动选中的。每一对还可以在管理端 Probe 页的「线路」矩阵里手动强制开或关(`PUT /api/admin/links/{probe_id}/{agent_id}`),手动设置优先于自动连接。结果同时用在 Probe 的 Agent 列表、Agent 的 `peers` 和线路目录(关掉的线路从总览里消失,历史数据还在)。
|
||||
- 吊销 Probe:在面板上重新生成身份,旧公钥立即从所有 Agent 的期望状态中移除,旧会话随之断开。
|
||||
|
||||
## 4. Agent 本地配置(`/etc/monitor/agent.json`,面板无法修改)
|
||||
## 4. Agent 本地配置(`/etc/moshiro/agent.json`,面板无法修改)
|
||||
|
||||
```json
|
||||
{
|
||||
"fleet": { "server": "https://panel.example.com", "identity_dir": "/etc/monitor" },
|
||||
"fleet": { "server": "https://panel.example.com", "identity_dir": "/etc/moshiro" },
|
||||
"listen": { "control_port": 7443, "udp_ports": [7443], "tcp_ports": [7444] },
|
||||
"limits": {
|
||||
"max_total_mbps": 200,
|
||||
@@ -70,7 +70,7 @@ Probe 启动
|
||||
"allowed_target_cidrs": [],
|
||||
"max_concurrent_tests": 1
|
||||
},
|
||||
"fleet_policy": { "allow_methods": ["monitor.test.run", "monitor.test.cancel"] }
|
||||
"fleet_policy": { "allow_methods": ["moshiro.test.run", "moshiro.test.cancel"] }
|
||||
}
|
||||
```
|
||||
|
||||
@@ -101,4 +101,4 @@ Probe 启动
|
||||
{ "reporting": { "host_stats": true, "host_interval_s": 2 } }
|
||||
```
|
||||
|
||||
`host_stats` 为 true(默认)时,Agent 每隔 `host_interval_s` 秒用 `monitor.host` 上报一份主机资源占用的汇总数字(CPU、内存、根分区、网卡累计流量与网速、运行时长;没有型号、主机名、IP);设为 false 就一个字也不上报。
|
||||
`host_stats` 为 true(默认)时,Agent 每隔 `host_interval_s` 秒用 `moshiro.host` 上报一份主机资源占用的汇总数字(CPU、内存、根分区、网卡累计流量与网速、运行时长;没有型号、主机名、IP);设为 false 就一个字也不上报。
|
||||
+3
-3
@@ -49,7 +49,7 @@
|
||||
|
||||
## 4. RPC(Fleet.Rpc)
|
||||
|
||||
- 信封采用 JSON-RPC 2.0;方法名为 `领域.动作`。`fleet.*` 保留给框架,产品使用自己的前缀,例如 `monitor.*`。
|
||||
- 信封采用 JSON-RPC 2.0;方法名为 `领域.动作`。`fleet.*` 保留给框架,产品使用自己的前缀,例如 `moshiro.*`。
|
||||
- 二进制帧:`u8 channel | 正文`。channel `0` 到 `15` 保留给框架,产品从 `16` 开始注册自己的二进制通道。
|
||||
- 错误:`error.data.code` 为 snake_case 字符串,框架的错误码以 `fleet_` 开头,产品可以扩展。
|
||||
- **与传输方式无关**:同一套路由既可以跑在 wss 上,也可以跑在 TLS 帧(`u32 长度 | u8 类型 | 正文`)上。
|
||||
@@ -86,7 +86,7 @@ connect(wss) → fleet.challenge → fleet.auth → fleet.hello → 状态同步
|
||||
- 投递语义为**至少一次**:每条消息都带有 `msg_id`(UUIDv7),服务端按此去重。
|
||||
- 在途数据按字节限制(默认 4 MiB),保证服务端慢时不会把内存撑爆。
|
||||
- 丢弃消息时会累计缺口计数,通过 `OnGap` 回调通知产品,由产品决定如何上报。
|
||||
- 默认只保存在内存中,不落盘。产品如果需要持久化,可以自行实现 `IOutboxStore`(Monitor 不使用)。
|
||||
- 默认只保存在内存中,不落盘。产品如果需要持久化,可以自行实现 `IOutboxStore`(MoShiro 不使用)。
|
||||
|
||||
## 7. 期望状态同步(State Sync)
|
||||
|
||||
@@ -134,4 +134,4 @@ public interface IFleetStore
|
||||
}
|
||||
```
|
||||
|
||||
产品可以使用 `Fleet.Server.Sqlite`,也可以把这些表放进自己的数据库中。Monitor 的做法是把它们放进自己的 `monitor.db`,并由同一个单写入者负责写入。
|
||||
产品可以使用 `Fleet.Server.Sqlite`,也可以把这些表放进自己的数据库中。MoShiro 的做法是把它们放进自己的 `moshiro.db`,并由同一个单写入者负责写入。
|
||||
@@ -1,8 +1,8 @@
|
||||
# Fleet:通用节点框架
|
||||
|
||||
> **实现状态**:`Fleet.Update`、`Fleet.Server.Releases`、`fleet-updater`、`fleet-release` 还只是设计;容器节点的更新目前由宿主机上的 `monitor-update` 脚本和 Monitor 的 `ReleaseService` 完成(见 [deployment.md](../deployment.md))。其余组件已实现。
|
||||
> **实现状态**:`Fleet.Update`、`Fleet.Server.Releases`、`fleet-updater`、`fleet-release` 还只是设计;容器节点的更新目前由宿主机上的 `moshiro-update` 脚本和 MoShiro 的 `ReleaseService` 完成(见 [deployment.md](../deployment.md))。其余组件已实现。
|
||||
|
||||
> 名称暂定。Fleet 与业务无关,Monitor 是它的第一个使用者。
|
||||
> 名称暂定。Fleet 与业务无关,MoShiro 是它的第一个使用者。
|
||||
|
||||
## 1. 定位
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
```text
|
||||
┌──────────────────────────── 产品层(各产品自己写) ─────────────────────────────┐
|
||||
│ Monitor: Protocol(Kind/任务) · Measurement(积木) · Agent · Probe · Backend │
|
||||
│ MoShiro: Protocol(Kind/任务) · Measurement(积木) · Agent · Probe · Backend │
|
||||
│ 其他产品: YourApp.Node · YourApp.Server │
|
||||
└──────────────────────────────────┬─────────────────────────────────────────────┘
|
||||
│ 只依赖 Fleet;Fleet 不认识任何产品
|
||||
@@ -29,14 +29,14 @@
|
||||
|
||||
## 2. 设计原则
|
||||
|
||||
1. **与业务无关**:Fleet 中不出现任何产品概念。产品通过扩展点接入:元数据类型、RPC 方法、发件箱消息类型、期望状态文档类型。`scripts/check-deps` 强制规定 `backend/fleet/**` 不能引用 `Monitor.*`。
|
||||
1. **与业务无关**:Fleet 中不出现任何产品概念。产品通过扩展点接入:元数据类型、RPC 方法、发件箱消息类型、期望状态文档类型。`scripts/check-deps` 强制规定 `backend/fleet/**` 不能引用 `MoShiro.*`。
|
||||
2. **安全默认值**:
|
||||
- Fleet **永远不提供** exec、shell、文件读写、远程下发代码这类方法;
|
||||
- 节点只响应本地注册过的方法,本地策略 `allow_methods` 还可以进一步收窄;
|
||||
- 更新必须通过离线密钥签名验证,面板即使被攻破也推不出恶意二进制。
|
||||
3. **权限分离**:注册(`fleet-enroll`,一次性运行)、常驻业务进程、更新(`fleet-updater`)是三个独立的进程,使用不同的系统用户,各自只拥有最小权限。
|
||||
4. **NativeAOT 优先**:节点侧只依赖 BCL;泛型扩展点通过产品传入的 `JsonTypeInfo<T>` 实现序列化,不使用反射。
|
||||
5. **只抽取真正用到的**:每项能力都必须同时被 Monitor 和 `samples/EchoNode` 使用,防止过度设计。
|
||||
5. **只抽取真正用到的**:每项能力都必须同时被 MoShiro 和 `samples/EchoNode` 使用,防止过度设计。
|
||||
|
||||
## 3. 组件
|
||||
|
||||
@@ -67,15 +67,15 @@ fleet/ 以后可以原样拆成独立仓库并发
|
||||
│ ├─ Fleet.Enrollment.Client/
|
||||
│ └─ Fleet.Server.Enrollment/ Fleet.Server.Link/ Fleet.Server.Releases/ Fleet.Server.Sqlite/
|
||||
├─ tools/ fleet-enroll/ fleet-updater/ fleet-release/
|
||||
├─ samples/ EchoNode/ EchoServer/ 最小示例:不依赖 Monitor 的第二个使用者
|
||||
├─ samples/ EchoNode/ EchoServer/ 最小示例:不依赖 MoShiro 的第二个使用者
|
||||
├─ tests/
|
||||
└─ docs → 仓库 docs/fleet/
|
||||
```
|
||||
|
||||
## 5. 演进策略
|
||||
|
||||
- **现阶段**(已确认,暂不独立):放在 monitor 仓库的 `backend/fleet/` 目录中,通过项目引用使用,一处修改两边同时编译。
|
||||
- **拆分条件**:公开 API 连续两个里程碑没有破坏性变更,并且 `samples` 能覆盖全部能力。满足后拆成独立仓库,按 semver 发布 NuGet 包,Monitor 改为包引用。
|
||||
- **现阶段**(已确认,暂不独立):放在 MoShiro 仓库的 `backend/fleet/` 目录中,通过项目引用使用,一处修改两边同时编译。
|
||||
- **拆分条件**:公开 API 连续两个里程碑没有破坏性变更,并且 `samples` 能覆盖全部能力。满足后拆成独立仓库,按 semver 发布 NuGet 包,MoShiro 改为包引用。
|
||||
- **公开 API 审查**:`backend/fleet/src/*` 的 public 类型使用 `PublicAPI.Shipped.txt` / `PublicAPI.Unshipped.txt` 跟踪(使用 `Microsoft.CodeAnalysis.PublicApiAnalyzers`,只在构建期生效),任何变更都会在 PR 中显示出来。
|
||||
|
||||
## 6. 产品接入一览
|
||||
|
||||
+11
-11
@@ -1,6 +1,6 @@
|
||||
# Fleet:通用更新(Fleet.Update / fleet-updater)
|
||||
|
||||
> **实现状态**:容器节点(Agent、Probe)的更新已经实现,见 [deployment.md](../deployment.md) 的「容器节点的更新」:同样的信任模型(离线发布密钥签名、本机配置公钥和仓库、`seq` 防重放、面板只通知和请求),清单里是镜像 digest,更新器是宿主机上的 `monitor-update`。本文下面描述的纯二进制方案(`Fleet.Update`、`fleet-updater`、`fleet-release`、健康信标)还没有实现。
|
||||
> **实现状态**:容器节点(Agent、Probe)的更新已经实现,见 [deployment.md](../deployment.md) 的「容器节点的更新」:同样的信任模型(离线发布密钥签名、本机配置公钥和仓库、`seq` 防重放、面板只通知和请求),清单里是镜像 digest,更新器是宿主机上的 `moshiro-update`。本文下面描述的纯二进制方案(`Fleet.Update`、`fleet-updater`、`fleet-release`、健康信标)还没有实现。
|
||||
|
||||
## 1. 原则
|
||||
|
||||
@@ -30,15 +30,15 @@
|
||||
"check_interval_s": 3600,
|
||||
"apps": [
|
||||
{
|
||||
"id": "monitor-agent",
|
||||
"sources": ["https://panel.example.com/fleet/releases", "https://github.com/org/monitor/releases/download"],
|
||||
"id": "moshiro-agent",
|
||||
"sources": ["https://panel.example.com/fleet/releases", "https://github.com/org/moshiro/releases/download"],
|
||||
"channel": "stable",
|
||||
"policy": "notify",
|
||||
"rid": "linux-x64",
|
||||
"install_dir": "/opt/monitor-agent",
|
||||
"install_dir": "/opt/moshiro-agent",
|
||||
"keep_versions": 2,
|
||||
"service": { "kind": "systemd", "name": "monitor-agent" },
|
||||
"health": { "kind": "beacon", "path": "/run/fleet/monitor-agent.beacon", "timeout_s": 60 }
|
||||
"service": { "kind": "systemd", "name": "moshiro-agent" },
|
||||
"health": { "kind": "beacon", "path": "/run/fleet/moshiro-agent.beacon", "timeout_s": 60 }
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -53,16 +53,16 @@
|
||||
```json
|
||||
{
|
||||
"schema": 1,
|
||||
"app": "monitor-agent",
|
||||
"app": "moshiro-agent",
|
||||
"channel": "stable",
|
||||
"version": "0.4.2",
|
||||
"released_at": "2026-10-01T00:00:00Z",
|
||||
"min_from": "0.3.0",
|
||||
"artifacts": [
|
||||
{ "rid": "linux-x64", "url": "monitor-agent-0.4.2-linux-x64.tar.gz", "sha256": "…", "size": 4120334 },
|
||||
{ "rid": "linux-arm64", "url": "monitor-agent-0.4.2-linux-arm64.tar.gz", "sha256": "…", "size": 3987112 },
|
||||
{ "rid": "linux-musl-arm64", "url": "monitor-agent-0.4.2-linux-musl-arm64.tar.gz", "sha256": "…", "size": 3990021 },
|
||||
{ "rid": "win-x64", "url": "monitor-agent-0.4.2-win-x64.zip", "sha256": "…", "size": 4455010 }
|
||||
{ "rid": "linux-x64", "url": "moshiro-agent-0.4.2-linux-x64.tar.gz", "sha256": "…", "size": 4120334 },
|
||||
{ "rid": "linux-arm64", "url": "moshiro-agent-0.4.2-linux-arm64.tar.gz", "sha256": "…", "size": 3987112 },
|
||||
{ "rid": "linux-musl-arm64", "url": "moshiro-agent-0.4.2-linux-musl-arm64.tar.gz", "sha256": "…", "size": 3990021 },
|
||||
{ "rid": "win-x64", "url": "moshiro-agent-0.4.2-win-x64.zip", "sha256": "…", "size": 4455010 }
|
||||
],
|
||||
"notes_url": "https://…/CHANGELOG#0.4.2",
|
||||
"key_id": "rel-2026a",
|
||||
|
||||
+6
-6
@@ -3,7 +3,7 @@
|
||||
## 1. 原则
|
||||
|
||||
- **只采集原始数据**:逐包、逐消息、逐事件,TCP 另加周期性的内核采样;不计算任何统计,解释全部放在 [analysis.md](analysis.md)。
|
||||
- **一处声明,处处生效**:采集方法(Kind)由积木组合**声明**而成。Probe 和 Agent 共用同一套积木(`backend/shared/Monitor.Measurement`)。
|
||||
- **一处声明,处处生效**:采集方法(Kind)由积木组合**声明**而成。Probe 和 Agent 共用同一套积木(`backend/shared/MoShiro.Measurement`)。
|
||||
- **密度即参数**:频率、包大小、采样间隔全部是任务参数,由 API 下发,不需要改代码。
|
||||
|
||||
## 2. 任务模型
|
||||
@@ -26,7 +26,7 @@ schedule continuous 持续运行,按时间切块
|
||||
|
||||
## 3. Kind 目录:积木组合
|
||||
|
||||
### 3.1 积木(`Monitor.Measurement`,只有新增积木才需要写代码)
|
||||
### 3.1 积木(`MoShiro.Measurement`,只有新增积木才需要写代码)
|
||||
|
||||
| 类别 | 积木 | 参数示例 |
|
||||
| --- | --- | --- |
|
||||
@@ -39,7 +39,7 @@ schedule continuous 持续运行,按时间切块
|
||||
|
||||
**两种角色互相独立**:连接角色(Probe 永远是拨号方,适配 NAT)与数据角色(发送端、接收端、回显端,由 `direction` 决定)无关。每种积木只实现一次,Probe 和 Agent 共用。
|
||||
|
||||
### 3.2 Kind 声明(`Monitor.Protocol/Catalog/Kinds`)
|
||||
### 3.2 Kind 声明(`MoShiro.Protocol/Catalog/Kinds`)
|
||||
|
||||
```csharp
|
||||
public static readonly KindSpec TcpEcho = new(
|
||||
@@ -109,7 +109,7 @@ RawChunk
|
||||
- **列定义写在块头中**:新增列或新增 Kind 时,**不需要升级 `container_version`**。只有容器格式本身变化时才升级。Backend 可以通用地解码任何块,不认识的列会被保留,但会被分析层忽略。
|
||||
- **切块规则**:持续任务每 **10 秒**切一块,或者记录数达到上限(默认 50 000)时提前切块。这样采集密度提高后,单块大小仍然有界,实时显示和持久化也共用同一条链路。
|
||||
- 按需测试按阶段切块,阶段内同样受记录数上限约束。
|
||||
- 编解码器位于 `Monitor.Protocol/Raw`,Probe、Agent、Backend、离线工具使用同一份代码。
|
||||
- 编解码器位于 `MoShiro.Protocol/Raw`,Probe、Agent、Backend、离线工具使用同一份代码。
|
||||
|
||||
## 5. 列的归属与合并
|
||||
|
||||
@@ -131,12 +131,12 @@ RawChunk
|
||||
|
||||
新增一种记录流只需要写声明,不需要写合并代码。Probe 的列在规定时间内没有送达时,目前 Agent 带着已有的数据继续(不上传只有 Agent 一侧的 `partial` 块,见已知缺口)。
|
||||
|
||||
## 6. 代码结构(`backend/shared/Monitor.Measurement`)
|
||||
## 6. 代码结构(`backend/shared/MoShiro.Measurement`)
|
||||
|
||||
同一种采集方法的两端是**一对角色**写在同一个文件里:拨号方(Probe 永远是拨号方,适配 NAT)和监听方(Agent)。两端共用包的构造和校验、时钟、行表和合并;Probe 和 Agent 只负责组装自己那一侧。
|
||||
|
||||
```text
|
||||
Monitor.Measurement/ 依赖:Monitor.Protocol(仅 BCL,零 NuGet)
|
||||
MoShiro.Measurement/ 依赖:MoShiro.Protocol(仅 BCL,零 NuGet)
|
||||
├─ Core/
|
||||
│ ├─ Contracts.cs IBlock、BlockSpec、BlockEnv、DataSession、IAdmission、IFragmentSink……
|
||||
│ ├─ RowStore.cs 本侧记录的行:只含本侧拥有的列,按键索引的环形缓冲,热路径不分配
|
||||
|
||||
+53
-53
@@ -3,8 +3,8 @@
|
||||
## 1. 目标
|
||||
|
||||
1. **三个独立项目加一个前端**:Probe、Agent、Backend 各自拥有解决方案,可以单独构建、测试和发布;Web 是独立的 Vite 项目(`web/`)。
|
||||
2. **通用能力只写一次**:TCP/UDP/TLS/ICMP 采集积木(类似 iperf3 的能力,但只记录原始数据)放在 `Monitor.Measurement` 中,Probe 和 Agent 共用;采集方法(Kind)由积木组合声明而成。
|
||||
3. **节点机制通用化**:注册、身份、连接、RPC、可靠上报、状态同步、宿主、更新,由与业务无关的 **Fleet** 框架提供(见 [fleet/overview.md](fleet/overview.md))。Monitor 只写业务,其他产品也可以复用 Fleet。
|
||||
2. **通用能力只写一次**:TCP/UDP/TLS/ICMP 采集积木(类似 iperf3 的能力,但只记录原始数据)放在 `MoShiro.Measurement` 中,Probe 和 Agent 共用;采集方法(Kind)由积木组合声明而成。
|
||||
3. **节点机制通用化**:注册、身份、连接、RPC、可靠上报、状态同步、宿主、更新,由与业务无关的 **Fleet** 框架提供(见 [fleet/overview.md](fleet/overview.md))。MoShiro 只写业务,其他产品也可以复用 Fleet。
|
||||
4. **改一处,整条链路受益**:协议、原始数据格式、测试方案、指标、错误码、版本号、编译设置、代码风格都只有**一个真源**。改动之后,下游要么自动更新,要么在编译期或 CI 中直接报错。
|
||||
5. **模块化**:Probe/Agent 由可插拔的模块组成;Backend 按层分成多个项目,层之间的依赖在编译期强制。
|
||||
6. **Probe/Agent 轻量**:运行时只依赖 BCL 和仓库内的库,不引用任何 NuGet 包。
|
||||
@@ -12,45 +12,45 @@
|
||||
## 2. 仓库布局
|
||||
|
||||
```text
|
||||
monitor/
|
||||
moshiro/
|
||||
├─ README.md .gitignore
|
||||
├─ docs/ docs/fleet/
|
||||
├─ scripts/ # publish / package / smoke / stability 等脚本(check-deps 等检查也放这里)
|
||||
├─ packaging/ # install.sh、systemd 单元、OpenWrt 文件
|
||||
├─ web/ # 前端:Vite + React + TypeScript(自写),只依赖 backend/openapi/v1.json
|
||||
│
|
||||
├─ global.json Directory.Packages.props Monitor.slnx # SDK、NuGet 版本和全部 .NET 项目入口
|
||||
├─ global.json Directory.Packages.props MoShiro.slnx # SDK、NuGet 版本和全部 .NET 项目入口
|
||||
├─ .editorconfig BannedSymbols.txt # 风格与禁用 API(构建期强制)
|
||||
├─ BannedSymbols.Node.txt # 仅用于常驻节点进程:纯采集边界
|
||||
│
|
||||
├─ ci/Monitor.Tar/ # CI 归档工具,也加入根解决方案
|
||||
├─ ci/MoShiro.Tar/ # CI 归档工具,也加入根解决方案
|
||||
└─ backend/ # 服务和节点 C# 代码(构建产物在 backend/artifacts/)
|
||||
├─ Directory.Build.props Monitor.slnx # 后端编译设置和后端目录解决方案
|
||||
├─ fleet/ # 通用节点框架(不认识 Monitor,以后可以拆成独立仓库)
|
||||
├─ Directory.Build.props MoShiro.slnx # 后端编译设置和后端目录解决方案
|
||||
├─ fleet/ # 通用节点框架(不认识 MoShiro,以后可以拆成独立仓库)
|
||||
│ ├─ src/ Fleet.Core Fleet.Identity Fleet.Rpc Fleet.Link Fleet.Hosting Fleet.Update
|
||||
│ │ Fleet.Enrollment.Client Fleet.Server.{Enrollment,Link,Releases,Sqlite}
|
||||
│ ├─ tools/ fleet-enroll fleet-updater fleet-release
|
||||
│ ├─ samples/ EchoNode EchoServer
|
||||
│ └─ tests/
|
||||
├─ shared/ # Monitor 自己的共享库
|
||||
│ ├─ Monitor.Protocol/ # 契约真源:Kind/Plan 目录、任务集、monitor.* 方法、UDP 包、RawChunk、错误码
|
||||
│ ├─ Monitor.Measurement/ # 采集引擎:积木、Recorder、任务调和(见 measurement.md)
|
||||
├─ shared/ # MoShiro 自己的共享库
|
||||
│ ├─ MoShiro.Protocol/ # 契约真源:Kind/Plan 目录、任务集、moshiro.* 方法、UDP 包、RawChunk、错误码
|
||||
│ ├─ MoShiro.Measurement/ # 采集引擎:积木、Recorder、任务调和(见 measurement.md)
|
||||
│ └─ tests/
|
||||
├─ probe/ src/Monitor.Probe/ tests/ # 家宽端采集器
|
||||
├─ agent/ src/Monitor.Agent/ tests/ # VPS 端采集器
|
||||
├─ probe/ src/MoShiro.Probe/ tests/ # 家宽端采集器
|
||||
├─ agent/ src/MoShiro.Agent/ tests/ # VPS 端采集器
|
||||
├─ src/ # Backend(面板)
|
||||
│ ├─ Monitor.Backend/ # 宿主与组合根(Program.cs)
|
||||
│ ├─ Monitor.Backend.Api/ # API 层:HTTP 端点、DTO、错误映射
|
||||
│ ├─ Monitor.Backend.Gateway/ # 在 Fleet.Server.Link 上注册 Monitor 的方法、状态与收件箱
|
||||
│ ├─ Monitor.Backend.Application/ # 用例层 + 端口接口
|
||||
│ ├─ Monitor.Backend.Analysis/ # 分析(纯计算,见 analysis.md)
|
||||
│ └─ Monitor.Backend.Storage.Sqlite/ # 端口接口的实现,同时实现 IFleetStore
|
||||
│ ├─ MoShiro.Backend/ # 宿主与组合根(Program.cs)
|
||||
│ ├─ MoShiro.Backend.Api/ # API 层:HTTP 端点、DTO、错误映射
|
||||
│ ├─ MoShiro.Backend.Gateway/ # 在 Fleet.Server.Link 上注册 MoShiro 的方法、状态与收件箱
|
||||
│ ├─ MoShiro.Backend.Application/ # 用例层 + 端口接口
|
||||
│ ├─ MoShiro.Backend.Analysis/ # 分析(纯计算,见 analysis.md)
|
||||
│ └─ MoShiro.Backend.Storage.Sqlite/ # 端口接口的实现,同时实现 IFleetStore
|
||||
├─ tests/
|
||||
├─ openapi/v1.json # 构建生成并提交
|
||||
└─ Monitor.Backend.slnx
|
||||
└─ MoShiro.Backend.slnx
|
||||
```
|
||||
|
||||
节点机器上的发布物是三个独立进程,使用不同的系统用户:`monitor-agent`(或 `monitor-probe`)、`fleet-enroll`(一次性运行)、`fleet-updater`(常驻或定时运行)。
|
||||
节点机器上的发布物是三个独立进程,使用不同的系统用户:`moshiro-agent`(或 `moshiro-probe`)、`fleet-enroll`(一次性运行)、`fleet-updater`(常驻或定时运行)。
|
||||
|
||||
## 3. 依赖关系
|
||||
|
||||
@@ -65,21 +65,21 @@ monitor/
|
||||
│ └─────┬──────┘ │ │
|
||||
▼ ▼ ▼ ▼
|
||||
┌──────────────────────────────────┐ ┌─────────────────────────┐
|
||||
│ Monitor.Protocol │ │ Fleet.Server.* │
|
||||
│ MoShiro.Protocol │ │ Fleet.Server.* │
|
||||
└────────────────┬─────────────────┘ └───────────┬─────────────┘
|
||||
▼ ▼
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ Fleet(节点侧):Core · Identity · Rpc · Link · Hosting │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
反向依赖一律禁止:fleet/** ✗→ Monitor.*
|
||||
反向依赖一律禁止:fleet/** ✗→ MoShiro.*
|
||||
```
|
||||
|
||||
简要来说:
|
||||
|
||||
```text
|
||||
Probe, Agent ──> Measurement ──> Monitor.Protocol ──> Fleet.Core / Fleet.Rpc
|
||||
Probe, Agent ──> Measurement ──> MoShiro.Protocol ──> Fleet.Core / Fleet.Rpc
|
||||
Probe, Agent ──> Fleet.Link / Fleet.Hosting / Fleet.Identity
|
||||
Backend.* ──> Monitor.Protocol + Fleet.Server.* (不引用 Measurement)
|
||||
Backend.* ──> MoShiro.Protocol + Fleet.Server.* (不引用 Measurement)
|
||||
fleet-enroll ──> Fleet.Enrollment.Client ──> Fleet.Identity / Core
|
||||
fleet-updater ──> Fleet.Update ──> Fleet.Core (不引用 Link,也不读取节点身份)
|
||||
Web ──> openapi/v1.json
|
||||
@@ -87,12 +87,12 @@ Web ──> openapi/v1.json
|
||||
|
||||
| 项目 | 允许依赖 | 禁止 |
|
||||
| --- | --- | --- |
|
||||
| `Fleet.*`(节点侧) | BCL、其他节点侧 Fleet 库 | 任何 NuGet 运行时包;任何 `Monitor.*` |
|
||||
| `Fleet.Server.*` | ASP.NET Core、`Microsoft.Data.Sqlite`(仅 Sqlite 项目)、节点侧 Fleet 库 | 任何 `Monitor.*` |
|
||||
| `Monitor.Protocol` | Fleet.Core、Fleet.Rpc | 任何 I/O、套接字、日志 |
|
||||
| `Monitor.Measurement` | Monitor.Protocol、Fleet.Core | NuGet;日志(只通过计数接收器上报);读取配置;统计或解释逻辑 |
|
||||
| `Fleet.*`(节点侧) | BCL、其他节点侧 Fleet 库 | 任何 NuGet 运行时包;任何 `MoShiro.*` |
|
||||
| `Fleet.Server.*` | ASP.NET Core、`Microsoft.Data.Sqlite`(仅 Sqlite 项目)、节点侧 Fleet 库 | 任何 `MoShiro.*` |
|
||||
| `MoShiro.Protocol` | Fleet.Core、Fleet.Rpc | 任何 I/O、套接字、日志 |
|
||||
| `MoShiro.Measurement` | MoShiro.Protocol、Fleet.Core | NuGet;日志(只通过计数接收器上报);读取配置;统计或解释逻辑 |
|
||||
| Probe / Agent | 上述库,以及 Fleet.Link / Hosting / Identity | NuGet 运行时包;彼此互相引用;Fleet.Update;Fleet.Enrollment.Client |
|
||||
| Backend.* | Monitor.Protocol、Fleet.Server.*,以及第 5.2 节列出的包 | Measurement、EF Core 等依赖反射的库 |
|
||||
| Backend.* | MoShiro.Protocol、Fleet.Server.*,以及第 5.2 节列出的包 | Measurement、EF Core 等依赖反射的库 |
|
||||
|
||||
分析器类包(`PrivateAssets="all"`)不受限制。`scripts/check-deps` 在 CI 中检查上述规则。
|
||||
|
||||
@@ -102,28 +102,28 @@ Web ──> openapi/v1.json
|
||||
|
||||
组件、扩展点与接入方式见 [fleet/overview.md](fleet/overview.md),连接细节见 [fleet/link.md](fleet/link.md),更新见 [fleet/update.md](fleet/update.md)。
|
||||
|
||||
> **实现状态**:`Fleet.Core`、`Fleet.Identity`、`Fleet.Rpc`、`Fleet.Link`、`Fleet.Hosting`、`Fleet.Enrollment.Client`、`Fleet.Server.*`(Abstractions、Enrollment、Link)和 `fleet-enroll` 已实现。`Fleet.Update`、`Fleet.Server.Releases`、`fleet-updater`、`fleet-release` 还只是设计(纯二进制方式的更新);容器节点的更新是宿主机上的 `monitor-update` 脚本和面板的 `ReleaseService`,见 [deployment.md](deployment.md)。原来的 `Monitor.Common` 和 `Monitor.NodeHost` 已经并入 Fleet:
|
||||
> **实现状态**:`Fleet.Core`、`Fleet.Identity`、`Fleet.Rpc`、`Fleet.Link`、`Fleet.Hosting`、`Fleet.Enrollment.Client`、`Fleet.Server.*`(Abstractions、Enrollment、Link)和 `fleet-enroll` 已实现。`Fleet.Update`、`Fleet.Server.Releases`、`fleet-updater`、`fleet-release` 还只是设计(纯二进制方式的更新);容器节点的更新是宿主机上的 `moshiro-update` 脚本和面板的 `ReleaseService`,见 [deployment.md](deployment.md)。原来的 `MoShiro.Common` 和 `MoShiro.NodeHost` 已经并入 Fleet:
|
||||
|
||||
| 原位置 | 现位置 |
|
||||
| --- | --- |
|
||||
| `Monitor.Common`(Result、缓冲区、VarInt、有界队列、重放窗口、退避、ID、令牌哈希) | `Fleet.Core` |
|
||||
| `Monitor.NodeHost`(模块宿主、日志、配置、ConfigStore、服务外壳) | `Fleet.Hosting` |
|
||||
| `MoShiro.Common`(Result、缓冲区、VarInt、有界队列、重放窗口、退避、ID、令牌哈希) | `Fleet.Core` |
|
||||
| `MoShiro.NodeHost`(模块宿主、日志、配置、ConfigStore、服务外壳) | `Fleet.Hosting` |
|
||||
| 节点密钥与签名 | `Fleet.Identity` |
|
||||
| JSON-RPC 信封与路由 | `Fleet.Rpc` |
|
||||
| Agent 注册 | `Fleet.Enrollment.Client` + `fleet-enroll` |
|
||||
|
||||
### 4.2 Monitor.Protocol(Monitor 的契约真源)
|
||||
### 4.2 MoShiro.Protocol(MoShiro 的契约真源)
|
||||
|
||||
```text
|
||||
Monitor.Protocol/
|
||||
├─ ProtocolVersion ProtocolLimits MonitorErrorCode(在 Fleet 错误码基础上扩展)
|
||||
MoShiro.Protocol/
|
||||
├─ ProtocolVersion ProtocolLimits MoShiroErrorCode(在 Fleet 错误码基础上扩展)
|
||||
├─ Ids/ AgentId(= Fleet NodeId)、ProbeId、TaskId、TestId、ChunkId、StreamId
|
||||
├─ Catalog/
|
||||
│ ├─ Kinds/ KindSpec 声明(积木组合 + ParamSpec + 记录列 + 预算)
|
||||
│ ├─ Plans/ 多阶段测试方案
|
||||
│ └─ ParamSpec 参数定义(类型、默认值、上下限),节点与 Backend 共用同一套校验
|
||||
├─ Tasks/ TaskSet(Fleet 期望状态的文档类型)、调和结果
|
||||
├─ Rpc/ monitor.* 方法名、params/result、二进制通道号(从 16 开始)
|
||||
├─ Rpc/ moshiro.* 方法名、params/result、二进制通道号(从 16 开始)
|
||||
├─ Wire/ UDP 包、数据连接首帧
|
||||
├─ Raw/ 自描述 RawChunk:头部、列类型、列编解码、Brotli、crc32c
|
||||
└─ Json/ AgentRpcContext、ProbeRpcContext(按信道拆分,便于裁剪)
|
||||
@@ -131,7 +131,7 @@ Monitor.Protocol/
|
||||
|
||||
累加器、直方图等统计类型**不在 Protocol 中**,只属于 `Backend.Analysis`。
|
||||
|
||||
### 4.3 Monitor.Measurement(采集引擎)
|
||||
### 4.3 MoShiro.Measurement(采集引擎)
|
||||
|
||||
包含:
|
||||
|
||||
@@ -147,14 +147,14 @@ Monitor.Protocol/
|
||||
|
||||
### 5.1 Agent 与 Probe:组装
|
||||
|
||||
节点的组装点是 `AgentApp` 和 `ProbeApp`:在构造函数里**显式组装**,不使用 DI 容器,也不做反射扫描;没有全局状态,测试可以在同一进程里启动多个。连接、认证、重连、发件箱、状态同步都由 Fleet 提供,Monitor 只写业务:
|
||||
节点的组装点是 `AgentApp` 和 `ProbeApp`:在构造函数里**显式组装**,不使用 DI 容器,也不做反射扫描;没有全局状态,测试可以在同一进程里启动多个。连接、认证、重连、发件箱、状态同步都由 Fleet 提供,MoShiro 只写业务:
|
||||
|
||||
```text
|
||||
AgentApp Probe(ProbeApp)
|
||||
FleetLink(到面板) FleetLink(到面板)
|
||||
状态 TaskSet、方法 monitor.test.* 状态 probe_agents(Agent 列表)、hello 带系统和构建信息
|
||||
发件箱 RawChunk(通道 16) 心跳带更新器的状态;monitor.update.* 通知
|
||||
心跳带更新器的状态;monitor.update.* AgentDirectory → 每个 Agent 一个 AgentConnection(点对点会话)
|
||||
状态 TaskSet、方法 moshiro.test.* 状态 probe_agents(Agent 列表)、hello 带系统和构建信息
|
||||
发件箱 RawChunk(通道 16) 心跳带更新器的状态;moshiro.update.* 通知
|
||||
心跳带更新器的状态;moshiro.update.* AgentDirectory → 每个 Agent 一个 AgentConnection(点对点会话)
|
||||
PeerListener / PeerHub(Probe 会话) 会话内:线路任务集 line_task_set 的调和、角色、列块发件箱(通道 17)
|
||||
UdpMux / TcpAcceptor(数据端口)
|
||||
TaskManager / TestRunner / ChunkAssembler
|
||||
@@ -163,7 +163,7 @@ AgentApp Probe(ProbeApp)
|
||||
|
||||
模块之间通过构造函数传入的接口或 `Channel<T>` 通信,禁止通过静态全局状态共享数据。
|
||||
|
||||
**新增 RPC 方法**:在 `Monitor.Protocol/Rpc` 中定义 params/result,然后在组装处加一行 `.On(Methods.X, HandleX)`。
|
||||
**新增 RPC 方法**:在 `MoShiro.Protocol/Rpc` 中定义 params/result,然后在组装处加一行 `.On(Methods.X, HandleX)`。
|
||||
|
||||
### 5.2 Backend:分层与 API 层
|
||||
|
||||
@@ -172,7 +172,7 @@ AgentApp Probe(ProbeApp)
|
||||
│ │ ▲
|
||||
┌──────────────▼───────────────┐ ┌───────────▼──────────┐ │
|
||||
│ Backend.Api │ │ Backend.Gateway │ │
|
||||
│ Public/ Admin/ │ │ 注册 monitor.* 方法 │ │
|
||||
│ Public/ Admin/ │ │ 注册 moshiro.* 方法 │ │
|
||||
│ 认证(Cookie/CSRF/令牌) │ │ TaskSet 状态提供者 │ │
|
||||
│ 错误映射 · OpenAPI · SSE 端点 │ │ RawChunk 收件箱 │ │
|
||||
└──────────────┬───────────────┘ └───────────┬──────────┘ │
|
||||
@@ -187,7 +187,7 @@ AgentApp Probe(ProbeApp)
|
||||
│ 实现端口
|
||||
┌───────────────────────────┴─────────────────────────────┐
|
||||
│ Backend.Storage.Sqlite:段文件 + SQLite(只做读写) │
|
||||
│ 同时实现 IFleetStore(Fleet 的节点和令牌表放在 monitor.db 中)│
|
||||
│ 同时实现 IFleetStore(Fleet 的节点和令牌表放在 moshiro.db 中)│
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
|
||||
端口:IAgentStore、ITaskStore、IRawStore、IMetricStore、ITestStore、IAuthStore
|
||||
@@ -198,16 +198,16 @@ AgentApp Probe(ProbeApp)
|
||||
- **Api 不能引用 Storage**(编译期保证);Api 和 Gateway 只调用 Application。
|
||||
- Application 返回 `Result<T>`。**错误码到 HTTP 状态的映射只在 Api 层做一次**(`ErrorCodeHttpMap`);到 JSON-RPC 错误的映射由 Fleet.Rpc 统一完成。
|
||||
- Api 层按调用方分目录:`Public/`、`Admin/`,公共部分放在 `Common/`(分页、ETag、认证过滤器、ProblemFactory、`EndpointConventions`)。每类资源一个端点文件,新增资源时在 `ApiModule.Map` 中加一行。
|
||||
- 注册端点、签名认证、连接注册表、状态推送、收件箱 ACK、发布通道都来自 Fleet(`MapFleetEnrollment`、`MapFleetLink`、`MapFleetReleases`)。Monitor 只提供:
|
||||
- 注册端点、签名认证、连接注册表、状态推送、收件箱 ACK、发布通道都来自 Fleet(`MapFleetEnrollment`、`MapFleetLink`、`MapFleetReleases`)。MoShiro 只提供:
|
||||
- `IFleetStore` 的实现;
|
||||
- `TaskSet` 的状态提供者;
|
||||
- `RawChunk` 的收件箱;
|
||||
- `monitor.*` 方法。
|
||||
- `moshiro.*` 方法。
|
||||
- public 层的 DTO 单独定义,只暴露可以公开的字段,并统一经过“可见性过滤器”。
|
||||
- 请求校验:简单字段约束使用 .NET 10 内置的 Minimal API 校验;任务参数统一用 `ParamSpec` 校验(与节点共用);跨字段规则放在 Application 层。
|
||||
- Backend 使用 ASP.NET Core 自带的 DI 容器,只在组合根显式注册。
|
||||
|
||||
Backend.Analysis 只依赖 Monitor.Protocol 和 Fleet.Core,没有 I/O,可以单独做成离线 CLI 使用。
|
||||
Backend.Analysis 只依赖 MoShiro.Protocol 和 Fleet.Core,没有 I/O,可以单独做成离线 CLI 使用。
|
||||
|
||||
Backend 允许的包:ASP.NET Core 共享框架、`Microsoft.Data.Sqlite`、`Microsoft.AspNetCore.OpenApi`、`Microsoft.Extensions.ApiDescription.Server`(仅构建期使用)。
|
||||
|
||||
@@ -221,11 +221,11 @@ Backend 允许的包:ASP.NET Core 共享框架、`Microsoft.Data.Sqlite`、`Mi
|
||||
| 原始数据格式 | `Protocol/Raw`(自描述) | 新增列不需要升级版本;四处使用同一份编解码器 |
|
||||
| 新增统计指标 | `Analysis/Metrics` 中加 1 行 `MetricDef` | 存储、降采样、API、SSE、目录全部自动处理;回填 30 天历史 |
|
||||
| 服务评分 | `Analysis/Models` | 查询时计算,立即对全部历史生效 |
|
||||
| 新增 RPC 能力 | `Monitor.Protocol/Rpc` + 组装处加 1 行 | 通过 Fleet 的 capabilities 协商,旧节点不受影响 |
|
||||
| 注册、认证、重连、发件箱、状态同步 | `backend/fleet/` | Monitor 和所有其他产品同时受益 |
|
||||
| 新增 RPC 能力 | `MoShiro.Protocol/Rpc` + 组装处加 1 行 | 通过 Fleet 的 capabilities 协商,旧节点不受影响 |
|
||||
| 注册、认证、重连、发件箱、状态同步 | `backend/fleet/` | MoShiro 和所有其他产品同时受益 |
|
||||
| 更新机制 | `Fleet.Update` + `fleet-updater` | 所有产品共用;产品只提供发布清单 |
|
||||
| REST DTO 与端点 | `Backend.Api` | 生成 OpenAPI,CI 检查 diff,前端的 TS 类型随之更新 |
|
||||
| 错误码 | Fleet 错误码 + `MonitorErrorCode` | REST、JSON-RPC、OpenAPI、TS 全部一致 |
|
||||
| 错误码 | Fleet 错误码 + `MoShiroErrorCode` | REST、JSON-RPC、OpenAPI、TS 全部一致 |
|
||||
| 版本、编译设置、风格、禁用 API | `backend/` 下的 props、`.editorconfig`、`BannedSymbols*.txt` | 所有项目 |
|
||||
|
||||
## 7. 编译设置
|
||||
@@ -334,10 +334,10 @@ M0 阶段先测出基线,之后作为 CI 的回归门槛(超出 10% 即失
|
||||
| C 库 | musl:使用 `linux-musl-*` 目标发布 |
|
||||
| TLS | .NET 在 Linux 上依赖 OpenSSL,而 OpenWrt 默认使用 mbedTLS/wolfSSL。需要安装 `libopenssl3`(安装脚本自动检查并提示)。启动时自检,缺少时明确报错,不会静默降级 |
|
||||
| 全球化 | 已启用 `InvariantGlobalization`,不依赖 ICU |
|
||||
| 服务管理 | procd:提供 `/etc/init.d/monitor-probe`(`procd_set_param respawn`,前台运行,日志输出到 logd);`Fleet.Hosting.ServiceShell` 在 procd 下不需要 `sd_notify` |
|
||||
| 存储 | 闪存很小,而且需要避免频繁写入:二进制放在 `/usr/lib/monitor/`,配置和身份放在 `/etc/monitor/`(sysupgrade 时通过 `/lib/upgrade/keep.d/` 保留);测量数据本来就不落盘;`ConfigStore` 只在 Agent 列表变化时写入 |
|
||||
| 服务管理 | procd:提供 `/etc/init.d/moshiro-probe`(`procd_set_param respawn`,前台运行,日志输出到 logd);`Fleet.Hosting.ServiceShell` 在 procd 下不需要 `sd_notify` |
|
||||
| 存储 | 闪存很小,而且需要避免频繁写入:二进制放在 `/usr/lib/moshiro/`,配置和身份放在 `/etc/moshiro/`(sysupgrade 时通过 `/lib/upgrade/keep.d/` 保留);测量数据本来就不落盘;`ConfigStore` 只在 Agent 列表变化时写入 |
|
||||
| 内存 | 预算 ≤ 32 MB,适合 ≥ 256 MB 内存的路由器;在本地配置中限制重负载测试的码率(`limits.max_test_mbps`),防止路由器 CPU 成为瓶颈,反过来影响测量结果 |
|
||||
| 测量偏差 | Probe 运行在网关上时,自己没有 NAT;一旦路由器 CPU 饱和,结果就会失真。因此 Probe 在 `monitor.event` 中上报自身的 CPU 繁忙标记(仅进程级别),分析层据此标注置信说明 |
|
||||
| 测量偏差 | Probe 运行在网关上时,自己没有 NAT;一旦路由器 CPU 饱和,结果就会失真。因此 Probe 在 `moshiro.event` 中上报自身的 CPU 繁忙标记(仅进程级别),分析层据此标注置信说明 |
|
||||
| 打包 | 提供 `tar.gz` 和 procd 脚本;包格式(ipk 或 apk)按目标 OpenWrt 版本提供 |
|
||||
| 更新 | `fleet-updater` 使用 procd 适配器;`keep_versions = 1`,下载前检查剩余空间,避免写满闪存 |
|
||||
|
||||
|
||||
+17
-17
@@ -1,35 +1,35 @@
|
||||
# 线协议
|
||||
|
||||
Monitor 的两条控制链路都建立在 Fleet 之上:
|
||||
MoShiro 的两条控制链路都建立在 Fleet 之上:
|
||||
|
||||
- **Agent ↔ Backend**:Fleet 连接服务端(wss),见 [fleet/link.md](fleet/link.md);
|
||||
- **Probe ↔ Agent**:Fleet 节点间点对点会话(TLS),见 [fleet/link.md](fleet/link.md) 第 8 节。
|
||||
|
||||
认证、心跳、重连、能力协商、可靠发件箱、期望状态同步、JSON-RPC 信封都由 Fleet 负责。本文只定义 **Monitor 自己的内容**:`monitor.*` 方法、状态文档、二进制通道,以及 Fleet 之外的数据面(UDP 包和数据连接)。所有类型都放在 `Monitor.Protocol` 中。
|
||||
认证、心跳、重连、能力协商、可靠发件箱、期望状态同步、JSON-RPC 信封都由 Fleet 负责。本文只定义 **MoShiro 自己的内容**:`moshiro.*` 方法、状态文档、二进制通道,以及 Fleet 之外的数据面(UDP 包和数据连接)。所有类型都放在 `MoShiro.Protocol` 中。
|
||||
|
||||
## 1. Fleet 能力的映射
|
||||
|
||||
| Monitor 需要的能力 | 使用的 Fleet 机制 | Monitor 提供的内容 |
|
||||
| MoShiro 需要的能力 | 使用的 Fleet 机制 | MoShiro 提供的内容 |
|
||||
| --- | --- | --- |
|
||||
| 下发任务 | 期望状态 `fleet.state.sync` | 文档类型 `TaskSet`,包括 `tasks[]`、`peers`(允许连这个 Agent 的 Probe 公钥,已按线路开关过滤) |
|
||||
| 下发 Agent 列表给 Probe | 期望状态 `fleet.state.sync`(Probe 直连面板) | 文档类型 `probe_agents`:`version`、`agents[]`(`agent_id`、`host`、`tcp_port`、`udp_ports`、`spki`),已按线路开关过滤;Agent 集合或线路开关变化时重新推送 |
|
||||
| 更新 | 通知 `monitor.update.offer` / `monitor.update.apply`(B→A、B→P),心跳附加字段 `update` | `offer` 带 CI 签名的发布清单原文和签名,节点只写进数据目录;`apply` 请求更新;`update` 是宿主机更新器的状态(`state`、`current`、`available`、`error`、`at`),见 [deployment.md](deployment.md) |
|
||||
| 更新 | 通知 `moshiro.update.offer` / `moshiro.update.apply`(B→A、B→P),心跳附加字段 `update` | `offer` 带 CI 签名的发布清单原文和签名,节点只写进数据目录;`apply` 请求更新;`update` 是宿主机更新器的状态(`state`、`current`、`available`、`error`、`at`),见 [deployment.md](deployment.md) |
|
||||
| 上报原始块 | 发件箱,二进制通道 **16** | `RawChunk`(自描述,见 [measurement.md](measurement.md) 第 4 节),`msg_id = chunk_id` |
|
||||
| Probe 交付列块 | 点对点会话的发件箱,二进制通道 **17** | 列块:`u32 stream_id \| u32 first_seq \| u32 count \| 列数据(Brotli)` |
|
||||
| 节点自身状态 | `fleet.heartbeat` 的附加字段 | Agent:`sessions`、`probes[]`、`pps_in/out`、`dropped_invalid`、`dropped_rate_limited`、`queue_bytes`、`update`;Probe:只有 `update` |
|
||||
| 版本与能力 | `fleet.hello` | capabilities 包括 `monitor.kinds:<列表>`、`monitor.blocks:<列表>`;Agent 附加 `platform`(系统、架构、版本、tag、提交、构建时间)、`tls_spki`、`probe_ports`、`local_limits` 摘要;Probe 附加 `platform` |
|
||||
| 版本与能力 | `fleet.hello` | capabilities 包括 `moshiro.kinds:<列表>`、`moshiro.blocks:<列表>`;Agent 附加 `platform`(系统、架构、版本、tag、提交、构建时间)、`tls_spki`、`probe_ports`、`local_limits` 摘要;Probe 附加 `platform` |
|
||||
|
||||
## 2. `monitor.*` 方法
|
||||
## 2. `moshiro.*` 方法
|
||||
|
||||
### Agent ↔ Backend
|
||||
|
||||
| 方法 | 方向 | 类型 | 参数与结果 |
|
||||
| --- | --- | --- | --- |
|
||||
| `monitor.test.run` | B→A | 请求 | `test_id`、`probe_id`(在哪个 Probe 上跑)、`plan`、`phases[]` → `accepted`,或错误 `test_busy`、`probe_offline` |
|
||||
| `monitor.probe.disconnect` | B→A | 请求 | `probe_id` → `accepted`(Agent 是否真的有这个会话);Agent 关闭该 Probe 的会话 |
|
||||
| `monitor.test.cancel` | B→A | 请求 | `test_id` |
|
||||
| `monitor.test.status` | A→B | 通知 | `test_id`、`status`(`running`、`phase_done`、`succeeded`、`failed`、`aborted`)、`phase?`、`error_code?` |
|
||||
| `monitor.event` | A→B | 通知 | `kind`(`probe_connected`(`detail` 是 `probe_id`,`probe` 里带版本、远端地址和连接时间)、`probe_disconnected`、`endpoint_changed`、`session_rejected`、`clock_step`、`gap`、`cpu_saturated`(节点进程 CPU 饱和,可能影响测量,由 Probe 或 Agent 上报))、`at_utc`、`detail` |
|
||||
| `moshiro.test.run` | B→A | 请求 | `test_id`、`probe_id`(在哪个 Probe 上跑)、`plan`、`phases[]` → `accepted`,或错误 `test_busy`、`probe_offline` |
|
||||
| `moshiro.probe.disconnect` | B→A | 请求 | `probe_id` → `accepted`(Agent 是否真的有这个会话);Agent 关闭该 Probe 的会话 |
|
||||
| `moshiro.test.cancel` | B→A | 请求 | `test_id` |
|
||||
| `moshiro.test.status` | A→B | 通知 | `test_id`、`status`(`running`、`phase_done`、`succeeded`、`failed`、`aborted`)、`phase?`、`error_code?` |
|
||||
| `moshiro.event` | A→B | 通知 | `kind`(`probe_connected`(`detail` 是 `probe_id`,`probe` 里带版本、远端地址和连接时间)、`probe_disconnected`、`endpoint_changed`、`session_rejected`、`clock_step`、`gap`、`cpu_saturated`(节点进程 CPU 饱和,可能影响测量,由 Probe 或 Agent 上报))、`at_utc`、`detail` |
|
||||
|
||||
心跳的附加字段里有 `probes[]`:Agent 当前连着的每个 Probe(`id`、`version`、`remote`、`since_utc_ms`),每次都是完整快照,Backend 靠它纠正错过的连接事件。点对点握手的认证消息里可以带 Probe 的 `version`(只用于展示)。块头里的 `probe_id` 以 Agent 认证过的会话为准,Probe 自报的值会被覆盖。
|
||||
|
||||
@@ -38,10 +38,10 @@ Monitor 的两条控制链路都建立在 Fleet 之上:
|
||||
| 方法 | 方向 | 类型 | 参数与结果 |
|
||||
| --- | --- | --- | --- |
|
||||
| `fleet.state.sync`(文档类型 `line_task_set`) | A→P | 请求 | 这条线路的任务(`id`、`kind`、`params`、`stream_id`、`port`)和 Agent 的数据面端口 |
|
||||
| `monitor.clock` | P→A | 请求 | `probe_mono_us` → `agent_mono_us`;Probe 按往返中点估计两端单调时钟的偏移(取最小往返的一次),记入块头 `probe_offset_us` |
|
||||
| `monitor.test.phase` | A→P | 请求 | 测试的一个阶段:`test_id`、`phase`、`kind`、`params`、`stream_id`、`duration_s`、`port` → `accepted`;Probe 在回复之前准备好自己一侧的角色(单向数据流:先发几个探活包让 Agent 学到源地址) |
|
||||
| `monitor.test.phase_end` | A→P | 请求 | `sent_count`(Agent 发出的包数)→ `fragments`:Probe 收尾,把列块交给 Agent,回复交出的个数;Agent 等全部列块合并上传之后才进入下一阶段 |
|
||||
| `monitor.test.abort` | A→P | 通知 | `test_id` |
|
||||
| `moshiro.clock` | P→A | 请求 | `probe_mono_us` → `agent_mono_us`;Probe 按往返中点估计两端单调时钟的偏移(取最小往返的一次),记入块头 `probe_offset_us` |
|
||||
| `moshiro.test.phase` | A→P | 请求 | 测试的一个阶段:`test_id`、`phase`、`kind`、`params`、`stream_id`、`duration_s`、`port` → `accepted`;Probe 在回复之前准备好自己一侧的角色(单向数据流:先发几个探活包让 Agent 学到源地址) |
|
||||
| `moshiro.test.phase_end` | A→P | 请求 | `sent_count`(Agent 发出的包数)→ `fragments`:Probe 收尾,把列块交给 Agent,回复交出的个数;Agent 等全部列块合并上传之后才进入下一阶段 |
|
||||
| `moshiro.test.abort` | A→P | 通知 | `test_id` |
|
||||
| `fleet.outbox.ack` | 双向 | 通知 | Probe 的列块(通道 17)是至少一次投递,Agent 每收到一个就确认,重复到达的只处理一次 |
|
||||
|
||||
会话密钥不需要单独的方法:点对点握手(TLS 固定证书 → `peer.challenge` → Probe 用身份私钥签 `(peer_id, nonce, client_pub, server_pub)` → 两端各自 ECDH + HKDF)直接得到 `session_id` 和 `session_key`,`peer.auth` 的应答只回显 `session_id` 用来互相确认。
|
||||
@@ -58,7 +58,7 @@ Probe 只记录自己一侧的列,Agent 在数据面记录另一侧,由 Agen
|
||||
|
||||
单块最多 `MaxRecordsPerChunk`(50 000)条记录:Probe 必须把大的阶段按序号范围切成多个列块(例如 100 Mbit/s、1000 B 的 10 秒阶段是 125 000 个包 → 4 个列块),`phase_end` 的应答报告总块数。块头里 Probe 必须带上 `agent_id`(它知道自己在和哪个 Agent 通信)。
|
||||
|
||||
新增方法只需要在 `Monitor.Protocol/Rpc` 中定义,并在节点组装处加一行 `.On(...)`。旧节点会通过 capabilities 协商自动避开不支持的方法。
|
||||
新增方法只需要在 `MoShiro.Protocol/Rpc` 中定义,并在节点组装处加一行 `.On(...)`。旧节点会通过 capabilities 协商自动避开不支持的方法。
|
||||
|
||||
## 3. 数据面(不属于 Fleet)
|
||||
|
||||
@@ -95,7 +95,7 @@ Probe 只记录自己一侧的列,Agent 在数据面记录另一侧,由 Agen
|
||||
## 4. 版本演进
|
||||
|
||||
- Fleet 层的兼容规则见 [fleet/link.md](fleet/link.md)。
|
||||
- `monitor.*` 方法:新增方法或可选字段不需要升级版本,接收方忽略未知字段。
|
||||
- `moshiro.*` 方法:新增方法或可选字段不需要升级版本,接收方忽略未知字段。
|
||||
- RawChunk:块头自描述,新增列或新增 Kind 不需要升级 `container_version`;只有容器格式变化时才升级。
|
||||
- UDP 包和数据连接首帧的布局变化时,必须升级 `protocol_version`,并更新黄金字节测试。
|
||||
- Agent 同时支持当前版本和上一个版本的 Probe;Backend 能解码保留期内所有的 `container_version`。
|
||||
+3
-3
@@ -1,12 +1,12 @@
|
||||
# Backend 存储
|
||||
|
||||
实现位于 `Monitor.Backend.Storage.Sqlite`,负责实现 `Backend.Application` 中定义的端口接口,**只做读写,不含业务逻辑**。降采样和分析由 Application 调用 Analysis 完成。
|
||||
实现位于 `MoShiro.Backend.Storage.Sqlite`,负责实现 `Backend.Application` 中定义的端口接口,**只做读写,不含业务逻辑**。降采样和分析由 Application 调用 Analysis 完成。
|
||||
|
||||
## 1. 目录布局
|
||||
|
||||
```text
|
||||
data/
|
||||
├─ monitor.db # SQLite:配置、身份、原始块索引、指标、测试结果
|
||||
├─ moshiro.db # SQLite:配置、身份、原始块索引、指标、测试结果
|
||||
└─ raw/
|
||||
└─ 2026-09-27/ # 按 UTC 日期分目录,保留期满整目录删除
|
||||
├─ agt_01J…/ # 每条线路或每个 Agent 一个目录
|
||||
@@ -28,7 +28,7 @@ data/
|
||||
|
||||
## 3. 表结构
|
||||
|
||||
`agents`、`join_tokens` 以及公钥和状态版本字段,同时作为 Fleet 的 `IFleetStore` 存储(由 `Monitor.Backend.Storage.Sqlite` 实现),和 Monitor 的其他数据放在同一个 `monitor.db` 中,由同一个单写入者写入。发布与灰度相关的表(`fleet_releases`、`fleet_rollouts`)由 `Fleet.Server.Releases` 自带的迁移脚本创建(职责见 [fleet/update.md](fleet/update.md) 第 6 节)。
|
||||
`agents`、`join_tokens` 以及公钥和状态版本字段,同时作为 Fleet 的 `IFleetStore` 存储(由 `MoShiro.Backend.Storage.Sqlite` 实现),和 MoShiro 的其他数据放在同一个 `moshiro.db` 中,由同一个单写入者写入。发布与灰度相关的表(`fleet_releases`、`fleet_rollouts`)由 `Fleet.Server.Releases` 自带的迁移脚本创建(职责见 [fleet/update.md](fleet/update.md) 第 6 节)。
|
||||
|
||||
```sql
|
||||
-- 身份与注册 -------------------------------------------------------------
|
||||
|
||||
Reference in new issue
Block a user