用 ESP32 替代虚拟机做网络拨测 —— esp32-blackbox 项目实战
起因
我在市区不同地方有几个局域网,相互之间大概隔了 10 公里左右。为了让这几个网络能互通,我用 NetBird、ZeroTier、Cloudflare Tunnel 这类工具搭了一套跨地域的虚拟局域网。
网络搭好了,但稳定性怎么保障?毕竟这些隧道要穿过公网,中间的链路质量参差不齐。最直接的办法就是用 Prometheus 的 blackbox_exporter 做拨测——定期 HTTP 请求、Ping、DNS 查询,把结果丢进时序数据库,配上告警规则,出问题第一时间知道。
但问题来了:blackbox_exporter 得跑在一台机器上。为了一个拨测服务专门开个虚拟机,电费和硬件成本都不划算。家里那台跑 Proxmox 的服务器已经够费电了,再加一台实在没必要。
正好手上有几块 ESP32 开发板。ESP32 本身就是为网络连接设计的芯片,跑个 HTTP 请求、发个 ICMP 包完全不在话下。功耗还低,USB 供电就能跑,一个月电费几毛钱。于是就有了这个项目:esp32-blackbox。
整体架构
先看下这个系统在整体网络监控里的位置:
graph TB
subgraph SiteA["站点 A(家)"]
A1[ESP32 Blackbox]
A2[路由器/交换机]
A1 --- A2
end
subgraph SiteB["站点 B(办公室)"]
B1[ESP32 Blackbox]
B2[路由器/交换机]
B1 --- B2
end
subgraph SiteC["站点 C(其他)"]
C1[ESP32 Blackbox]
C2[路由器/交换机]
C1 --- C2
end
subgraph Overlay["虚拟组网层"]
N1[NetBird]
N2[ZeroTier]
N3[Cloudflare Tunnel]
end
A2 <--> N1 <--> B2
B2 <--> N2 <--> C2
A2 <--> N3 <--> C2
subgraph Monitor["监控中心"]
P[Prometheus]
G[Grafana]
end
A1 -->|":9090/metrics"| P
B1 -->|":9090/metrics"| P
C1 -->|":9090/metrics"| P
P --> G每个站点放一块 ESP32,通过各自的出口网络去做拨测。Prometheus 从各节点的 9090 端口拉取指标,Grafana 负责展示和告警。
跨地域组网方案
简单介绍下用的几个组网工具:
- NetBird:基于 WireGuard 的 mesh VPN,P2P 打洞成功后延迟很低,管理界面也方便
- ZeroTier:软件定义的二层虚拟网络,稳定性不错,适合做备用链路
- Cloudflare Tunnel:反代隧道,不需要公网端口就能暴露内网服务,适合那些不支持 P2P 的场景
三层组网的好处是互为冗余。NetBird 挂了还有 ZeroTier,都不行了 Cloudflare Tunnel 还能兜底。但冗余越多,需要监控的链路也越多,这也正是 ESP32 拨测发挥作用的地方。
ESP32 Blackbox 项目介绍
项目地址:github.com/Mi-Bee-Studio/esp32-blackbox
硬件选型
目前支持两款芯片:
| 芯片 | 推荐开发板 | 特点 |
|---|---|---|
| ESP32-C3 | SuperMini | 便宜,淘宝十来块钱 |
| ESP32-C6 | XIAO ESP32C6 | 支持 WiFi 6,性能更好 |
我手上用的是 ESP32-C3 SuperMini,跑这个项目绰绰有余。
支持的探测类型
| 协议 | 说明 |
|---|---|
| HTTP/HTTPS | GET/POST 请求,状态码校验 |
| TCP | TCP 连接测试 |
| TCP+TLS | TLS 握手计时 |
| DNS | DNS 解析测试 |
| ICMP Ping | 原生 socket 实现,RTT 测量 |
| WebSocket/WSS | WS 连接测试 |
基本上 blackbox_exporter 能做的它都能做。
首次启动零配置
这个设计我比较满意。第一次上电,ESP32 自动进入 AP 模式,手机连上 ESP32_Blackbox 这个热点(密码 12345678),浏览器打开 192.168.4.1 就能配 WiFi。配完重启就自动连上了,不需要串口操作。
flowchart TD
A[上电启动] --> B{NVS 有 WiFi 凭据?}
B -->|否| C[进入 AP 模式]
C --> D[手机连接 ESP32_Blackbox 热点]
D --> E["浏览器打开 192.168.4.1"]
E --> F[选择 WiFi 并输入密码]
F --> G[保存到 NVS]
G --> H[重启]
B -->|是| I[STA 模式连接 WiFi]
H --> I
I --> J[启动探测任务]
I --> K[启动 Web 管理界面 :80]
I --> L[启动 Metrics 服务 :9090]Web 管理界面
STA 模式下,浏览器打开 ESP32 的 IP 就能看到管理界面:

界面上可以直接编辑 JSON 配置,改完点保存就行,不需要重新编译固件。还支持热加载,改了配置 POST 一下 /api/reload 就生效了。
配置文件格式
探测目标通过 JSON 配置,存在 SPIFFS 文件系统里:
| |
modules 定义探测行为(用什么协议、超时多久、校验规则),targets 定义探测目标,目标通过 module 字段引用模块配置。想加个新探测目标?编辑 JSON 就行,不用碰代码。
Prometheus 集成
ESP32 Blackbox 完全兼容 Prometheus 的抓取方式。/metrics 端点输出标准 Prometheus 文本格式:
| |
在 Prometheus 里配上 scrape job:
| |
第二种写法用了 /probe 端点,跟原版 blackbox_exporter 的用法一模一样。你甚至可以把 Prometheus 里原来指向 blackbox_exporter 的配置直接改个 IP 指向 ESP32,其他不用动。
实际部署
我把几块 ESP32 分别放在不同的站点,每个站点配置不同的探测目标:
- 站点 A 的 ESP32 去拨测站点 B、C 的服务
- 站点 B 的 ESP32 去拨测站点 A、C 的服务
- 站点 C 同理
这样任意两个站点之间的链路质量都有数据。Grafana 里拉个 Dashboard,延迟、丢包率、HTTP 成功率一目了然。
graph LR
subgraph SiteA["站点 A"]
EA["ESP32 #1"]
end
subgraph SiteB["站点 B"]
EB["ESP32 #2"]
end
subgraph SiteC["站点 C"]
EC["ESP32 #3"]
end
subgraph Monitor["监控"]
P[Prometheus]
G[Grafana]
end
EA -->|"拨测 B/C"| EB
EA -->|"拨测 B/C"| EC
EB -->|"拨测 A/C"| EA
EB -->|"拨测 A/C"| EC
EC -->|"拨测 A/B"| EA
EC -->|"拨测 A/B"| EB
EA -->|:9090/metrics| P
EB -->|:9090/metrics| P
EC -->|:9090/metrics| P
P --> G构建 & 烧录
项目基于 ESP-IDF v6.0,提供了几种构建方式:
| |
如果用的是 ESP32-C6,把 esp32c3 换成 esp32c6 就行。
总结
说白了就一句话:不想为跑个 blackbox_exporter 多开一台服务器。ESP32 几块钱一块,功耗不到 1W,USB 充电器供电就行,放哪都不心疼。
项目开源在 GitHub 上,有兴趣可以试试:Mi-Bee-Studio/esp32-blackbox