起因:核实「管理端在 Cloudflare 上是否有效」时发现一个更要紧的问题 —— Worker 完全没有 UptimeRobot 同步能力(runChecks 的探测目标只有 config.monitors)。 而实际配置是 dataFilter=uptimerobot,也就是说直接部署到 Worker 面板会是空的: 它探测 8 个本地条目,而真正在监控的 6 个 UR 目标它根本不认识。 ① Worker 支持 UptimeRobot(Step 1) - edge/cloudflare/uptimerobot.js(新增):Workers 版 UR 只读数据源, 与 prober/node/lib/uptimerobot.js 同一套语义(状态映射/ratios 解析/故障原因) - runChecks 的探测集合扩展为「自建目标 + UR 镜像目标」;UR 目标只拉取不探测, 采用其状态并补齐本地 daily 分桶 - probeScope 与 dataFilter 解耦(与本地探针同语义):config 范围不拉 UR, uptimerobot 范围不探测本地 - 同步失败时标记 sources.uptimerobot.ok=false 并保留上次数据, 绝不把目标判为离线或从面板消失;UR 侧删除的监控自动清理 - 快照新增 probeScope;UR 目标带 meta(面板区分自建/外部)与 uptimeRatios ② 管理端远程访问(Step 2) - settings.admin.allowRemote:探针默认只监听 127.0.0.1,显式打开才监听全网卡, 启动日志就此告警。这是 Cloudflare Tunnel 的安全前提(已实测两种行为) - edge/cloudflare/tunnel.yml(新增):命名隧道配置,只放行 ^/(admin|api/admin/.*|assets/.*)$,其余 404 —— 不把公开面板一起挂到公网 ③ 前后端分离(Step 3) - Worker 侧 /admin 与 /api/admin/* 显式返回 403 + 指向文档的提示, 而非默默落到静态资源变成 404,确保边缘侧没有任何可写面 测试:新增 scripts/verify-worker.cjs(36 项)—— 把 Worker 以 data: URL 动态载入以 绕开 cloudflare:sockets,注入假 KV/静态资源/UptimeRobot 服务器, 不需要 wrangler 也不需要联网。全项目 8 套 310 项全部通过。 过程中抓到两个真 bug: 1. UR 同步失败的降级路径引用了不含 meta 的旧对象,导致 name/url 为 undefined、 UR 目标从面板上消失 2. stats.total 按 config.monitors.length 计数,在 probeScope=uptimerobot 时 与实际参与的目标数不符 另有一条测试夹具教训:内存版 KV 必须实现 get(key, 'json') 的类型参数 (真实 Workers KV 会自动反序列化),否则 state.monitors 恒为 undefined。
193 lines
10 KiB
Markdown
193 lines
10 KiB
Markdown
# 部署方案详解
|
||
|
||
四种方案按「成本从低到要求、数据从准到轻」排列。所有方案共用同一份 `public/` 目录与 `public/monitors.json` 配置。
|
||
|
||
---
|
||
|
||
## 方案一:本地运行(Node)
|
||
|
||
最适合内网自用与开发调试,数据最实时。
|
||
|
||
```bash
|
||
node prober/node/index.js # 默认端口 8787
|
||
PORT=9000 node prober/node/index.js # 自定义端口
|
||
```
|
||
|
||
- 面板:`http://localhost:8787/`
|
||
- 状态 API:`http://localhost:8787/api/status`
|
||
- 改 `public/monitors.json` 后自动热重载,无需重启
|
||
- 历史心率存于 `public/status.json`,重启后自动恢复
|
||
- 90 天可用率按天累积在 `public/status.json`,重启不丢(每次探测先恢复分桶再追加)
|
||
- 面板刷新即重新检测(`/api/refresh` 等待本轮完成再返回);多标签页同时刷新只触发一轮探测
|
||
- 后台常驻可用 pm2 / systemd / Windows 服务,按需自行封装
|
||
|
||
**适用**:监控内网服务(面板浏览器与目标同内网时,浏览器直连模式也能用)。
|
||
|
||
---
|
||
|
||
## 方案二:GitHub Pages + Actions(零成本公网)
|
||
|
||
架构:仓库里的 `public/` 整体作为 Pages 站点;Actions 每 5 分钟跑一轮 Node 探针,把 `public/status.json` 提交回仓库,面板以 `static` 模式读取。
|
||
|
||
步骤:
|
||
|
||
1. 推送代码到 GitHub 仓库(`main` 分支)
|
||
2. 仓库 **Settings → Pages → Build and deployment → Source** 选 **GitHub Actions**
|
||
3. 首次提交时建议带一份占位数据,避免页面空白:
|
||
```bash
|
||
node prober/node/index.js --once --demo # 生成演示 status.json
|
||
git add public/status.json && git commit -m "chore: demo status" && git push
|
||
```
|
||
4. 之后 `pages.yml` 负责每次推送自动部署;`monitor.yml` 每 5 分钟更新一次 `status.json`(提交信息为 `chore: update status snapshot`)
|
||
5. 访问 `https://<用户名>.github.io/<仓库名>/`
|
||
|
||
注意事项:
|
||
|
||
- Actions 计划任务最小粒度 5 分钟,数据最多 5 分钟延迟;心跳图按「检测次数」记录,5 分钟粒度下 48 格 ≈ 4 小时
|
||
- 90 天可用率同样按「检测次数」统计,5 分钟粒度约 288 次/天;每轮 Actions 会把当日分桶续写到 `status.json` 并提交,因此可用率数据跨运行累积(若某天 Actions 未运行,该日色条会显示「无数据」而不是 0%)
|
||
- **静态快照模式没有服务端,面板无法触发重新检测**:此方案下刷新只是重新读取仓库里的 `status.json`,数据新鲜度完全取决于 Actions 的运行频率(面板页脚会显示「数据源:静态快照」以作区分)
|
||
- 若仓库开启了分支保护,需允许 `github-actions[bot]` 推送(workflow 已声明 `contents: write` 权限)
|
||
- 免费额度对公开仓库无限量,私有仓库每月 2000 分钟,足够
|
||
|
||
---
|
||
|
||
## 方案三:Cloudflare Workers(边缘实时)
|
||
|
||
架构:Worker 每分钟由 cron 触发,用 `fetch` 探测 HTTP、用 `cloudflare:sockets` 探测 TCP,状态写入 KV;同 Worker 伺服 `public/` 静态资源与 `/api/*`。面板以 `api` 模式实时读取。
|
||
|
||
```bash
|
||
# 1. 安装 wrangler(任选其一,不影响本项目运行时)
|
||
npm install -g wrangler
|
||
|
||
# 2. 登录并创建 KV 命名空间
|
||
wrangler login
|
||
wrangler kv namespace create MONITOR_KV
|
||
# 把输出的 id 填到 edge/cloudflare/wrangler.toml 的 [[kv_namespaces]].id
|
||
|
||
# 3. 部署(wrangler.toml 已指向 ../../public 作为静态资产,monitors.json 随之部署,无需手动导入 KV)
|
||
cd edge/cloudflare
|
||
wrangler deploy
|
||
```
|
||
|
||
- 面板与 API:`https://<worker 域名>/` 与 `/api/status`
|
||
- 手动触发一轮:`curl https://<worker 域名>/api/refresh`(等待本轮完成后返回最新快照,与本地探针同语义)
|
||
- cron 表达式在 `wrangler.toml` 的 `[triggers].crons` 中调整(`* * * * *` = 每分钟)
|
||
|
||
### 接入 UptimeRobot 数据源
|
||
|
||
Worker 与本地探针共用同一套 UptimeRobot 语义(只读,映射逻辑见 `edge/cloudflare/uptimerobot.js`)。API Key 用 secret 传入,**不要写进 `monitors.json`**(它会随静态资源公开):
|
||
|
||
```bash
|
||
wrangler secret put UPTIMEROBOT_API_KEY # 填 ur... 开头的只读 Key
|
||
```
|
||
|
||
同时在 `public/monitors.json` 的 `settings` 里开启(`apiKey` 留空,由 secret 提供):
|
||
|
||
```jsonc
|
||
{
|
||
"settings": {
|
||
"dataFilter": "uptimerobot", // 只展示 UptimeRobot 目标
|
||
"uptimeRobot": { "enabled": true, "apiKey": "" }
|
||
}
|
||
}
|
||
```
|
||
|
||
行为与本地探针一致:
|
||
|
||
- `probeScope` 决定真正发请求的范围(`all` / `config` / `uptimerobot`),未显式设置时跟随 `dataFilter`
|
||
- UptimeRobot 目标只拉取不探测,采用其状态并补齐本地 `daily` 分桶
|
||
- 同步失败时**保留上次数据**并把 `sources.uptimerobot.ok` 置为 `false`,绝不因此把目标判为离线
|
||
- UR 侧删除的监控会从面板移除,避免残留僵尸条目
|
||
|
||
注意事项:
|
||
|
||
- **TCP 出站连接(`cloudflare:sockets`)需要 Workers 付费计划($5/月)**;免费计划下 `http` 类型探测与面板完全正常,`tcp` 类型会报错并记为离线
|
||
- **banner 类型边缘端不支持**(需原始 socket 读取),会记为离线并给出说明文案;这类目标请用本地探针
|
||
- KV 为最终一致存储,刚写入后立即读取可能有秒级延迟,对分钟级巡检无影响
|
||
- 90 天可用率分桶随 state 一起存于 KV,按 **UTC 日期** 分桶(边缘环境无本地时区);每分钟 cron 约 1440 次/天
|
||
- 免费版 Worker 每日 10 万次请求,面板静态资源走 Assets 不计入 Worker 请求数
|
||
- 中国大陆访问 Worker 默认域名不稳定,可绑定自有域名(需 Cloudflare 账号管理 DNS)
|
||
|
||
---
|
||
|
||
## 方案四:EdgeOne Pages(国内加速)
|
||
|
||
EdgeOne Pages 当前以静态托管为主,采用与方案二相同的「静态 + 定时数据」路径,国内访问体验更好。
|
||
|
||
步骤:
|
||
|
||
1. 登录腾讯云 EdgeOne 控制台,进入 **Pages → 新建项目 → 导入 Git 仓库**(或直接上传 `public/` 目录)
|
||
2. 构建配置:无构建步骤,输出目录填 `public`
|
||
3. 数据更新二选一:
|
||
- **A. GitHub Actions 驱动**:保持方案二的 `monitor.yml` 不变,EdgeOne 侧开启「构建时拉取最新提交」或配置 Webhook 触发重新部署
|
||
- **B. CI 推送触发**:在 `monitor.yml` 末尾追加一步,调用 EdgeOne 的部署 API / Webhook(按控制台提示填写)
|
||
4. 绑定自定义域名(需要备案的域名走国内加速线路)
|
||
|
||
说明:EdgeOne Pages 若上线边缘函数(类 Pages Functions),可把 `edge/cloudflare/worker.js` 的 `/api/*` 部分平移过去,实现分钟级实时数据;当前初稿以静态路径交付,接口契约完全一致,迁移时面板无需改动。
|
||
|
||
---
|
||
|
||
## 90 天可用率在各方案下的表现
|
||
|
||
可用率按「探测次数」加权统计,因此**探测频率直接决定精度**,也决定色条填充速度:
|
||
|
||
| 方案 | 每次探测间隔 | 每天样本数 | 90 天色条填满所需时间 |
|
||
| --- | --- | --- | --- |
|
||
| 本地 Node | 按 `monitors.json` 的 `interval`(示例 60s) | ≈1440 | 90 天 |
|
||
| Cloudflare Workers | cron 每分钟 | ≈1440(UTC 日期分桶) | 90 天 |
|
||
| GitHub Pages + Actions | ≥5 分钟(Actions 下限) | ≈288 | 90 天 |
|
||
| EdgeOne Pages | 同 GitHub Pages | ≈288 | 90 天 |
|
||
|
||
注意:分桶数据随 `status.json`(或 KV state)持久化并滚动保留 90 天,**必须由探针持续写入才会累积**——静态快照方案下依赖 Actions 按计划运行;Actions 计划任务在仓库长期无活动时可能被 GitHub 自动暂停(后台重新启用即可)。
|
||
|
||
需要临时提升精度时,可把 `interval` 调到 30s 或更低(本地/Worker 均支持,注意目标站点的压力与自身限流)。
|
||
|
||
---
|
||
|
||
## 方案对比与选型建议
|
||
|
||
| 维度 | 本地 Node | GH Pages | CF Workers | EdgeOne Pages |
|
||
| --- | --- | --- | --- | --- |
|
||
| 数据延迟 | 秒级(interval) | ≤5 分钟 | ≤1 分钟 | ≤5 分钟 |
|
||
| 90 天可用率精度 | 高(≈1440 次/天) | 中(≈288 次/天) | 高(≈1440 次/天) | 中(≈288 次/天) |
|
||
| TCP 探测 | ✅ | ✅(Actions 内) | 付费版 ✅ / 免费版 ❌ | 同 GH Pages |
|
||
| 真实状态码 | ✅ | ✅ | ✅ | ✅ |
|
||
| 内网目标 | ✅(浏览器同网即可) | ❌ | ❌ | ❌ |
|
||
| UptimeRobot 数据源 | ✅ | ✅ | ✅ | ❌(需改用自建探测) |
|
||
| 通知 / 维护窗口 / 事件 | ✅ | ❌ | ❌ | ❌ |
|
||
| **管理端(2FA)** | ✅ | ❌ | ❌ | ❌ |
|
||
| 国内访问速度 | 本机最快 | 一般 | 一般 | 快 |
|
||
| 运维成本 | 需保活进程 | 零 | 零 | 零 |
|
||
|
||
建议:个人/演示用 **GitHub Pages**;生产公网监控用 **Cloudflare Workers**;服务对象在国内且以 HTTP 监控为主用 **EdgeOne Pages**;内网资产用 **本地 Node**。
|
||
|
||
> ⚠️ **管理端只存在于「本地 Node」部署形态。** 其余形态的部署环境只提供只读面板与探测,没有任何写接口 —— Cloudflare Worker 上访问 `/admin` 或 `/api/admin/*` 会得到 403(而非 404,因为那里确实不提供管理能力)。需要管理操作(改配置、维护窗口、通知自检、立即重探)时,在本地 Node 那一侧完成。
|
||
>
|
||
> 原因不是「没做」,而是这些能力依赖本地状态:2FA 档案与会话存在 `data/` 目录与进程内存里,而 Workers 无文件系统、会话状态也无法跨 isolate 共享(KV 又是最终一致,无法承载「恢复码一次性」这类语义)。把这个结论说清楚,比在边缘环境里做一个半可靠的管理端更有价值。
|
||
|
||
---
|
||
|
||
## 远程访问管理端(Cloudflare Tunnel)
|
||
|
||
探针在内网/家用主机上时,若要在外网打开 `/admin`,用 Cloudflare Tunnel 让内网**主动出网**建隧道(不需要公网 IP、不需要开放入站端口)。
|
||
|
||
前提与步骤:
|
||
|
||
1. **确认管理端已绑定 2FA**。Tunnel 只负责把端口暴露出去,真正拦住未授权访问的是管理端的 TOTP + 会话 + 失败限流。
|
||
2. 打开探针的远程管理开关(**默认关闭**,此时探针只监听 `127.0.0.1`):
|
||
|
||
```jsonc
|
||
// local.monitors.json
|
||
{ "settings": { "admin": { "allowRemote": true } } }
|
||
```
|
||
|
||
3. 建命名隧道并只回源管理路径,参考 [`edge/cloudflare/tunnel.yml`](../edge/cloudflare/tunnel.yml):
|
||
|
||
```bash
|
||
cloudflared tunnel --no-autoupdate run --token <你的 token>
|
||
```
|
||
|
||
该配置只放行 `^/(admin|api/admin/.*|assets/.*)$`,其余路径一律 404 —— 不要把公开面板与 `/api/status` 一起挂到公网。
|
||
|
||
开启 `allowRemote` 后启动日志会明确警告探针正在监听 `0.0.0.0`,提醒你该端口同时伺服公开面板,前置代理必须启用 HTTPS。
|