✨ feat(agent): 添加 token 鉴权和流程文档
* 默认监听调整为 0.0.0.0:8080,并要求查询参数 token * 支持首次 run 自动生成含随机 token 的配置文件 * 补充 HTTP 鉴权、配置校验和相关测试 * 完善代码注释,覆盖缓存、上报、服务和平台采集路径 * 新增架构与开发流程文档,补充一键构建脚本
This commit is contained in:
172
docs/ARCHITECTURE.md
Normal file
172
docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# MFD Monitor Agent 架构与工作流程
|
||||
|
||||
本文档汇总当前代码中的实际运行流程,供开发、排障和评审时快速理解 agent 如何从配置加载到采集、缓存、查询、上报和服务化运行。
|
||||
|
||||
## 进程职责
|
||||
|
||||
MFD Monitor Agent 采用单进程一体化架构。一个 agent 进程同时负责:
|
||||
|
||||
- 周期采集主机状态
|
||||
- 保存最近一次 `HostStatus` 内存缓存
|
||||
- 提供本地 HTTP API
|
||||
- 可选地向中心端主动 POST 上报
|
||||
- 执行 Linux/Windows 服务安装、启动、停止和卸载命令
|
||||
|
||||
HTTP 查询与主动上报读取同一份缓存,不在请求路径上触发实时系统扫描。
|
||||
|
||||
## 包职责
|
||||
|
||||
| 包 | 职责 |
|
||||
|----|------|
|
||||
| `cmd/agent` | CLI 分发、配置加载、进程生命周期、HTTP server 启停 |
|
||||
| `internal/config` | 默认配置、JSON 读取、首次配置生成、环境变量覆盖、配置校验 |
|
||||
| `internal/status` | `/status` 与主动上报共用的 `HostStatus` JSON 数据模型 |
|
||||
| `internal/collector` | 平台相关主机、网络、资源、进程、TCP 监听采集 |
|
||||
| `internal/snapshot` | 子采集器聚合、partial 错误记录、并发安全缓存 |
|
||||
| `internal/httpapi` | `GET /health` 与 `GET /status`,查询参数 token 鉴权 |
|
||||
| `internal/reporter` | 周期性 POST 缓存快照,失败重试与日志记录 |
|
||||
| `internal/service` | Linux systemd 与 Windows SCM 服务生命周期封装 |
|
||||
|
||||
## 启动与生命周期
|
||||
|
||||
`agent` 无参数时等价于 `agent run`。
|
||||
|
||||
`agent run [-config config.json]` 流程:
|
||||
|
||||
1. 解析 `-config`,未指定时使用当前工作目录 `config.json`。
|
||||
2. 调用 `config.LoadOrCreate`。配置不存在时创建默认 JSON 并生成随机 `http.token`。
|
||||
3. 创建 `snapshot.Cache`,启动后台采集循环。
|
||||
4. 若 `report.enabled=true`,启动主动上报循环。
|
||||
5. 启动 HTTP server,默认监听 `0.0.0.0:8080`。
|
||||
6. 收到 `SIGINT` 或 `SIGTERM` 后优雅关闭 HTTP server。
|
||||
|
||||
服务命令 `install`、`uninstall`、`start`、`stop` 使用 `config.Load` 读取已有配置,不会自动创建配置文件。
|
||||
|
||||
## 配置加载流程
|
||||
|
||||
配置来源按以下顺序生效:
|
||||
|
||||
1. `config.Default()` 提供默认值。
|
||||
2. JSON 文件覆盖默认值。
|
||||
3. `MFD_AGENT_*` 环境变量覆盖 JSON。
|
||||
4. `validate` 校验安全和必填项。
|
||||
|
||||
关键规则:
|
||||
|
||||
- `http.token` 必填;`agent run` 首次生成配置时会自动写入随机 token。
|
||||
- `report.enabled=true` 时 `report.endpoint` 必填。
|
||||
- 自动生成的配置文件权限为 `0600`,因为其中包含 API token。
|
||||
- 非法布尔或时长环境变量会被忽略,保留文件中的原值。
|
||||
|
||||
## 采集与缓存流程
|
||||
|
||||
`snapshot.Cache.Run` 启动后立即采集一次,随后按 `collect.interval` 周期刷新。
|
||||
|
||||
每轮采集流程:
|
||||
|
||||
1. 用 `collect.timeout` 创建整体超时 context。
|
||||
2. 构造新的 `HostStatus`,默认 `agent.snapshot_status=ok`。
|
||||
3. 依次执行 `system`、`network`、`resource`、`process`、`socket` 采集器。
|
||||
4. 子采集器失败时记录到 `errors[]`,整份快照降级为 `partial`,其他字段继续保留。
|
||||
5. 用锁一次性发布最新快照,供 HTTP 与 reporter 并发读取。
|
||||
|
||||
`processes.status` 与 `tcp_listeners.status` 支持 `ok`、`partial`、`error`。例如 Linux 读取 `/proc/<pid>/fd` 权限不足时,TCP 端口仍会返回,但进程映射可能不完整并标记为 `partial`。
|
||||
|
||||
## HTTP 查询流程
|
||||
|
||||
所有 API 都要求查询参数 token:
|
||||
|
||||
```text
|
||||
GET /health?token=<http.token>
|
||||
GET /status?token=<http.token>
|
||||
```
|
||||
|
||||
处理流程:
|
||||
|
||||
1. `withQueryTokenAuth` 使用恒定时间比较校验 token。
|
||||
2. token 缺失、错误或配置 token 为空时返回 `401`。
|
||||
3. 非 `GET` 方法返回 `405`。
|
||||
4. `/health` 读取缓存元数据。首次快照未就绪时返回 `503` 和 `{"status":"starting"}`。
|
||||
5. `/status` 返回完整缓存 `HostStatus`。首次快照未就绪时返回 `503`。
|
||||
|
||||
HTTP 请求不会触发采集,避免高并发查询放大系统扫描成本。
|
||||
|
||||
## 主动上报流程
|
||||
|
||||
启用条件:
|
||||
|
||||
```json
|
||||
{
|
||||
"report": {
|
||||
"enabled": true,
|
||||
"endpoint": "http://center.example.internal/agent/status"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
运行流程:
|
||||
|
||||
1. `reporter.Run` 按 `report.interval` 定时执行。
|
||||
2. 从 `snapshot.Cache` 读取最新 `HostStatus`。
|
||||
3. 首次快照未就绪时跳过本轮。
|
||||
4. 以 `application/json` POST 到 `report.endpoint`。
|
||||
5. 非 2xx、网络错误或超时视为失败。
|
||||
6. 失败后等待短暂退避并重试一次。
|
||||
7. 最终失败只记录日志,不影响采集循环或本地 HTTP 服务。
|
||||
|
||||
上报载荷与 `/status` 响应结构完全一致。
|
||||
|
||||
## 服务生命周期流程
|
||||
|
||||
### Linux systemd
|
||||
|
||||
`agent install -config /path/config.json -exe /path/agent`:
|
||||
|
||||
1. 检查当前用户必须为 root。
|
||||
2. 生成 `/etc/systemd/system/<service.name>.service`。
|
||||
3. `ExecStart` 写入 `<exe> run -config <path>`。
|
||||
4. 执行 `systemctl daemon-reload`。
|
||||
5. 执行 `systemctl enable <service.name>`。
|
||||
|
||||
`start`、`stop` 调用 `systemctl start/stop`。`uninstall` 尽力 `disable --now`,删除 unit 文件后再次 `daemon-reload`。
|
||||
|
||||
### Windows SCM
|
||||
|
||||
Windows 通过 `sc.exe` 管理服务,不依赖 NSSM。
|
||||
|
||||
`install` 使用 `sc.exe create` 注册自动启动服务,`binPath` 为:
|
||||
|
||||
```text
|
||||
"<exe>" run -config "<config>"
|
||||
```
|
||||
|
||||
`start`、`stop`、`uninstall` 分别调用 `sc.exe start`、`sc.exe stop`、`sc.exe delete`。安装、删除和启停通常需要管理员权限。
|
||||
|
||||
## HostStatus 数据模型
|
||||
|
||||
`HostStatus` 是唯一对外状态模型,同时用于:
|
||||
|
||||
- `GET /status` 响应体
|
||||
- 主动上报 POST body
|
||||
- 测试中的快照断言
|
||||
|
||||
顶层字段包括:
|
||||
|
||||
- `agent`:版本、启动时间、快照时间、快照状态
|
||||
- `host`:机器标识、主机名、平台、架构、运行时长
|
||||
- `network`:网卡明细、IPv4/IPv6、MAC
|
||||
- `cpu`:逻辑核心、物理核心、使用率、负载、型号
|
||||
- `memory`:内存容量与使用率
|
||||
- `disks`:文件系统容量与使用率
|
||||
- `processes`:采集状态与完整进程列表
|
||||
- `tcp_listeners`:采集状态与 TCP 监听端口到进程映射
|
||||
- `errors`:子采集器失败原因
|
||||
|
||||
## 平台差异
|
||||
|
||||
Linux 采集主要读取 `/proc`、`/etc/os-release`、`net.Interfaces` 和 `statfs`。进程与 socket 映射受 `/proc/<pid>` 权限和进程生命周期竞争影响。
|
||||
|
||||
Windows 采集使用 Win32 API、Toolhelp、注册表、PSAPI、iphlpapi 和一次尽力 WMIC 命令。部分字段需要高权限读取进程句柄或令牌,失败时降级为空字段或 `partial`。
|
||||
|
||||
非 Linux/Windows 平台保留基础主机和网络采集,进程与 TCP 监听采集明确返回不支持并标记 `partial`。
|
||||
|
||||
@@ -7,12 +7,28 @@
|
||||
MFD Monitor Agent 是单二进制主机状态采集程序,具备以下能力:
|
||||
|
||||
- 后台周期采集主机状态并写入内存缓存
|
||||
- 本地 HTTP API:`GET /health`、`GET /status`(默认仅监听 `127.0.0.1:8080`)
|
||||
- 本地 HTTP API:`GET /health?token=...`、`GET /status?token=...`(默认监听 `0.0.0.0:8080`,需配置 `http.token`)
|
||||
- 可选周期性向中心端 POST 与 `/status` 相同结构的 `HostStatus` JSON
|
||||
- CLI 管理服务生命周期:`run`、`install`、`uninstall`、`start`、`stop`、`version`
|
||||
|
||||
HTTP 接口与上报均读取同一份缓存,**不会在每次请求时实时扫描系统**。
|
||||
|
||||
更多内部流程见 [架构与工作流程](ARCHITECTURE.md),构建测试流程见 [开发流程](DEVELOPMENT.md)。
|
||||
|
||||
## 运行流程
|
||||
|
||||
`agent run` 的运行链路如下:
|
||||
|
||||
1. 解析 `-config`;未指定时使用当前目录 `config.json`。
|
||||
2. 配置不存在时自动生成默认配置与随机 `http.token`,文件权限为 `0600`。
|
||||
3. 加载 JSON,并应用 `MFD_AGENT_*` 环境变量覆盖。
|
||||
4. 启动 `snapshot.Cache.Run`,立即采集一次并按 `collect.interval` 周期刷新缓存。
|
||||
5. 如果 `report.enabled=true`,启动主动上报循环。
|
||||
6. 启动 HTTP API,默认监听 `0.0.0.0:8080`。
|
||||
7. 收到退出信号后优雅关闭 HTTP server。
|
||||
|
||||
单轮采集使用 `collect.timeout` 作为整体超时。子采集器失败不会导致整份 `/status` 失败,而是记录到 `errors[]` 并将 `agent.snapshot_status` 标记为 `partial`。
|
||||
|
||||
## 环境要求
|
||||
|
||||
| 项目 | 要求 |
|
||||
@@ -92,13 +108,16 @@ C:\ProgramData\MFD\config.json
|
||||
|
||||
### JSON 配置文件
|
||||
|
||||
所有字段均有安全默认值;未提供配置文件时也可仅依赖环境变量启动。
|
||||
`agent run` 在未指定 `-config` 时使用当前工作目录下的 `config.json`;若文件不存在,会自动生成含随机 `http.token` 的默认配置(权限 `0600`)。
|
||||
|
||||
`install` / `start` 等命令不会自动创建配置文件,需事先准备好 JSON。
|
||||
|
||||
```json
|
||||
{
|
||||
"host_id": "host-a",
|
||||
"http": {
|
||||
"listen_address": "127.0.0.1:8080"
|
||||
"listen_address": "0.0.0.0:8080",
|
||||
"token": "请替换为强随机值"
|
||||
},
|
||||
"collect": {
|
||||
"interval": "30s",
|
||||
@@ -121,7 +140,8 @@ C:\ProgramData\MFD\config.json
|
||||
| 字段 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| `host_id` | 机器标识;为空时采集器可回退为主机名 | `""` |
|
||||
| `http.listen_address` | 本地 HTTP 监听地址 | `127.0.0.1:8080` |
|
||||
| `http.listen_address` | HTTP 监听地址 | `0.0.0.0:8080` |
|
||||
| `http.token` | API 查询参数 `?token=` 鉴权密钥(必填) | 无(`run` 自动生成) |
|
||||
| `collect.interval` | 采集周期 | `30s` |
|
||||
| `collect.timeout` | 单次采集总超时 | `10s` |
|
||||
| `report.enabled` | 是否启用主动上报 | `false` |
|
||||
@@ -132,18 +152,22 @@ C:\ProgramData\MFD\config.json
|
||||
| `service.display_name` | Windows 显示名称 | `MFD Monitor Agent` |
|
||||
| `service.description` | systemd unit 描述 | 见默认配置 |
|
||||
|
||||
**校验规则:** `report.enabled=true` 时必须设置 `report.endpoint`,否则 `config.Load` 失败。
|
||||
**校验规则:**
|
||||
|
||||
- `http.token` 不能为空,否则 `config.Load` 失败。
|
||||
- `report.enabled=true` 时必须设置 `report.endpoint`,否则 `config.Load` 失败。
|
||||
|
||||
时长字段使用 Go duration 字符串,例如 `"30s"`、`"1m"`。
|
||||
|
||||
### 环境变量覆盖
|
||||
|
||||
在 JSON 加载后,以下环境变量可覆盖对应配置(`Load("")` 时同样生效):
|
||||
在 JSON 加载后,以下环境变量可覆盖对应配置:
|
||||
|
||||
| 环境变量 | 配置字段 |
|
||||
|----------|----------|
|
||||
| `MFD_AGENT_HOST_ID` | `host_id` |
|
||||
| `MFD_AGENT_LISTEN` | `http.listen_address` |
|
||||
| `MFD_AGENT_TOKEN` | `http.token` |
|
||||
| `MFD_AGENT_COLLECT_INTERVAL` | `collect.interval` |
|
||||
| `MFD_AGENT_COLLECT_TIMEOUT` | `collect.timeout` |
|
||||
| `MFD_AGENT_REPORT_ENABLED` | `report.enabled` |
|
||||
@@ -260,8 +284,10 @@ sc.exe query mfd-monitor-agent
|
||||
|
||||
### 健康检查
|
||||
|
||||
将 `YOUR_TOKEN` 替换为配置文件中的 `http.token`:
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:8080/health | jq .
|
||||
curl -s 'http://127.0.0.1:8080/health?token=YOUR_TOKEN' | jq .
|
||||
```
|
||||
|
||||
首次采集完成前可能返回 `503` 且 `{"status":"starting"}`。就绪后示例:
|
||||
@@ -280,9 +306,11 @@ curl -s http://127.0.0.1:8080/health | jq .
|
||||
### 完整状态
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:8080/status | jq '.agent, .host, .processes.status, .tcp_listeners.status'
|
||||
curl -s 'http://127.0.0.1:8080/status?token=YOUR_TOKEN' | jq '.agent, .host, .processes.status, .tcp_listeners.status'
|
||||
```
|
||||
|
||||
浏览器调试示例:`http://<host>:8080/status?token=YOUR_TOKEN`
|
||||
|
||||
### 主动上报验收
|
||||
|
||||
1. 在配置中设置 `report.enabled=true` 与可达的 `report.endpoint`
|
||||
@@ -311,8 +339,9 @@ sc.exe start mfd-monitor-agent
|
||||
|
||||
## 安全注意事项
|
||||
|
||||
- **默认仅本机监听**(`127.0.0.1:8080`),避免将进程列表、监听端口等敏感信息默认暴露到网络。
|
||||
- 若需远程拉取,应通过反向代理、mTLS 或防火墙限制来源,而非直接绑定 `0.0.0.0`(除非明确接受风险)。
|
||||
- **默认监听所有网卡**(`0.0.0.0:8080`),`/health` 与 `/status` 均须通过查询参数 `?token=` 鉴权。
|
||||
- Token 会出现在 URL 中,代理与访问日志可能记录完整 URL;生产环境建议使用 HTTPS 反向代理、防火墙限制来源,并定期轮换 token。
|
||||
- 自动生成的 `config.json` 权限为 `0600`,请妥善保管其中的 `http.token`。
|
||||
- 服务账户权限越高,采集越完整;生产环境应在「采集完整性」与「最小权限」之间权衡。
|
||||
- `/status` 响应体可能较大(进程多、监听多时),注意中心端与本地内存占用。
|
||||
|
||||
@@ -320,7 +349,9 @@ sc.exe start mfd-monitor-agent
|
||||
|
||||
| 现象 | 可能原因 | 处理建议 |
|
||||
|------|----------|----------|
|
||||
| 启动报错 `http.token is required` | 配置中 token 为空 | 在 JSON 中设置 `http.token`,或使用 `agent run` 自动生成 |
|
||||
| 启动报错 `report endpoint is required` | 开启上报但未配置 endpoint | 设置 `report.endpoint` 或关闭 `report.enabled` |
|
||||
| HTTP `401 unauthorized` | 缺少或错误的 `?token=` | 使用配置文件中的 `http.token` |
|
||||
| `install requires root` | Linux 非 root 执行 install | 使用 `sudo` |
|
||||
| `service install is not implemented` | 非 Linux/Windows | 仅使用 `run` 前台模式 |
|
||||
| `/health` 长期 `starting` | 首次采集未完成或采集卡住 | 检查 `collect.timeout`、系统负载与权限 |
|
||||
@@ -332,5 +363,7 @@ sc.exe start mfd-monitor-agent
|
||||
## 相关文档
|
||||
|
||||
- 项目简介:[README.md](../README.md)
|
||||
- 架构与流程:[docs/ARCHITECTURE.md](ARCHITECTURE.md)
|
||||
- 开发与构建:[docs/DEVELOPMENT.md](DEVELOPMENT.md)
|
||||
- 架构与规范:[AGENTS.md](../AGENTS.md)
|
||||
- 示例配置:[configs/example.json](../configs/example.json)
|
||||
|
||||
127
docs/DEVELOPMENT.md
Normal file
127
docs/DEVELOPMENT.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# MFD Monitor Agent 开发流程
|
||||
|
||||
本文档记录当前仓库的构建、测试和跨平台验证流程。
|
||||
|
||||
## 本地环境
|
||||
|
||||
- Go 版本见 [go.mod](../go.mod)
|
||||
- Linux 服务化验证需要 systemd
|
||||
- Windows 服务化验证需要管理员 PowerShell
|
||||
- 构建产物默认写入 `dist/`,该目录不提交到 Git
|
||||
|
||||
## 测试命令
|
||||
|
||||
运行全部单元测试:
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
||||
按包运行:
|
||||
|
||||
```bash
|
||||
go test ./internal/collector
|
||||
go test ./internal/httpapi
|
||||
go test ./internal/reporter
|
||||
go test ./internal/service
|
||||
go test ./internal/snapshot
|
||||
```
|
||||
|
||||
Windows 编译与测试验证:
|
||||
|
||||
```bash
|
||||
GOOS=windows GOARCH=amd64 go test ./...
|
||||
```
|
||||
|
||||
## Linux 构建
|
||||
|
||||
```bash
|
||||
./scripts/build-linux.sh
|
||||
```
|
||||
|
||||
默认产物:
|
||||
|
||||
```text
|
||||
dist/agent-linux-amd64
|
||||
```
|
||||
|
||||
常用变量:
|
||||
|
||||
| 变量 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| `OUT_DIR` | 输出目录 | `dist` |
|
||||
| `VERSION` | 注入到 `cmd/agent.version` 的版本号 | `0.1.0` |
|
||||
| `GOOS_TARGET` | 目标系统 | `linux` |
|
||||
| `GOARCH_TARGET` | 目标架构 | `amd64` |
|
||||
| `OUTPUT_NAME` | 输出文件名 | `agent-<goos>-<goarch>` |
|
||||
| `CGO_ENABLED` | CGO 开关 | `0` |
|
||||
|
||||
版本号注入示例:
|
||||
|
||||
```bash
|
||||
VERSION=1.0.0 ./scripts/build-linux.sh
|
||||
./dist/agent-linux-amd64 version
|
||||
```
|
||||
|
||||
## Windows 构建
|
||||
|
||||
在 Windows PowerShell:
|
||||
|
||||
```powershell
|
||||
.\scripts\build-windows.ps1
|
||||
```
|
||||
|
||||
默认产物:
|
||||
|
||||
```text
|
||||
dist\agent-windows-amd64.exe
|
||||
```
|
||||
|
||||
从 Linux 交叉编译 Windows:
|
||||
|
||||
```bash
|
||||
GOOS_TARGET=windows GOARCH_TARGET=amd64 OUTPUT_NAME=agent-windows-amd64.exe ./scripts/build-linux.sh
|
||||
```
|
||||
|
||||
## 全平台构建
|
||||
|
||||
仓库包含 `scripts/build-all.sh`,用于一次构建 Linux amd64 与 Windows amd64 产物:
|
||||
|
||||
```bash
|
||||
./scripts/build-all.sh
|
||||
```
|
||||
|
||||
建议发布前执行:
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
GOOS=windows GOARCH=amd64 go test ./...
|
||||
./scripts/build-all.sh
|
||||
```
|
||||
|
||||
## 开发验证重点
|
||||
|
||||
变更采集逻辑时,重点验证:
|
||||
|
||||
- 单个子采集器失败后 `agent.snapshot_status=partial`
|
||||
- `errors[]` 包含失败采集器名称和错误摘要
|
||||
- `processes.status` 与 `tcp_listeners.status` 正确反映 `ok`、`partial`、`error`
|
||||
- HTTP 请求只读取缓存,不触发实时采集
|
||||
- 上报载荷与 `/status` 的 `HostStatus` 结构一致
|
||||
- 上报失败不会影响本地 HTTP 服务
|
||||
|
||||
变更服务逻辑时,重点验证:
|
||||
|
||||
- Linux `install` 非 root 返回错误
|
||||
- systemd unit 的 `ExecStart` 保留 `run -config <path>`
|
||||
- Windows `sc.exe create` 的 `binPath` 包含 `run -config <path>`
|
||||
- `uninstall` 对已停止或不存在的服务尽力清理
|
||||
|
||||
## 文档关系
|
||||
|
||||
- [README.md](../README.md):项目入口与快速命令
|
||||
- [DEPLOYMENT.md](DEPLOYMENT.md):部署、验收、安全与排障
|
||||
- [ARCHITECTURE.md](ARCHITECTURE.md):运行时架构和工作流程
|
||||
- [AGENTS.md](../AGENTS.md):仓库规则与功能边界
|
||||
- [plan.md](../plan.md):历史设计草案,当前行为以代码和 docs 文档为准
|
||||
|
||||
Reference in New Issue
Block a user