OneUptime IoT 设备遥测接入指南:通过 OTLP 与 MQTT 采集传感器、网关与边缘设备指标
【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime
本指南面向需要在 OneUptime 中纳管 IoT 设备(传感器、网关、控制器、边缘盒子)的开发者与运维人员,完整讲解设备遥测数据的接入链路:既可通过设备端 OpenTelemetry SDK 或网关上的 OpenTelemetry Collector 以 OTLP/HTTP 上报,也可直接连接 OneUptime 内置的 MQTT 端点发布 JSON 读数。读完本文,你将掌握iot.fleet.name/device.id资源属性建模、iot_*指标命名约定、MQTT Topic 契约与 Last Will 离线检测,并能在自托管实例上完成端到端接入与故障排查。
一、概览:两种接入路径,同一套数据模型
OneUptime 通过采集一组精简的iot_*指标来监控整支 IoT 设备机队(Fleet),每一条读数都携带其所属的机队与设备 ID。OneUptime 将这些指标聚合为机队、构建实时设备清单(Device Inventory),并逐设备跟踪电池、连接性、温度、CPU、内存与可用性。
设备侧无需安装任何专有 Agent,接入方式有两种,且二者汇入完全相同的机队清单、看板与监控器管线:
- OpenTelemetry(OTLP)——设备上运行 OTel SDK,或在网关上部署 OpenTelemetry Collector 向多台设备扇出采集;
- MQTT——设备直接连接 OneUptime 内置 MQTT 端点(WebSocket 为
wss://<your-host>/mqtt,自托管部署还可走裸 MQTT TCP),发布 JSON 读数即可,无需 Collector,且 Last Will 机制支持即时离线检测。
本页定位是接入(Ingestion)指南;基于已推送数据配置 IoT 监控器与告警,见 IoT Device Monitor。
二、接入前提条件
- 一台能够向 OneUptime 发送 OTLP/HTTP 的设备、网关或 Collector;
- 设备/网关到 OneUptime 实例的网络可达性;
- 一个OneUptime Telemetry Ingestion Token——在项目设置 → Telemetry & APM → Ingestion Keys中创建,并复制
x-oneuptime-token的值。
三、OneUptime 如何为 IoT 建模
OneUptime 借助 OpenTelemetry 资源属性(Resource Attributes)把设备映射为两个概念:
- 机队(Fleet)——设备的逻辑分组(例如
building-a-sensors或field-gateways)。机队由iot.fleet.name资源属性推导而来,在 OneUptime 中表现为遥测服务iot/<fleet>。应设置service.name=iot/<fleet>,使日志与指标归入同一服务。 - 设备(Device)——机队内的单个设备,由
device.id属性标识。OneUptime 按device.id为每个机队建立并维护设备清单。
可选属性用于细化设备的分类与监控器作用域:
| 属性 | 是否必需 | 说明 |
|---|---|---|
iot.fleet.name | 是 | 设备所属机队,将成为 OneUptime 服务iot/<fleet> |
device.id | 是 | 设备在机队内的稳定、唯一 ID |
iot.device.kind | 否 | 设备类别——例如Device、Sensor或Gateway,默认值为Device |
iot.device.type | 否 | 更细的设备类型/型号,用于监控器过滤(例如temp-sensor) |
iot.device.firmware | 否 | 设备上报的固件版本 |
值得强调的是,iot.fleet.name必须作为资源属性(resource attribute)而非数据点标签(datapoint label)携带;从源码实现看,MQTT 路径在 MqttTelemetryMapper.ts 的buildOtlpMetricsBody中会把机队写入resourceMetrics.resource.attributes,同时补上service.name=iot/<fleet>,以保证与 OTel SDK 直连路径的服务归属一致(否则指标会被归入 "Unknown Service")。
四、通过 OpenTelemetry SDK 发送指标
若设备直接运行 OpenTelemetry SDK,将其指向 OneUptime,并通过标准OTEL_*环境变量打上 IoT 资源属性即可。请将 Token、端点、机队名与设备 ID 替换为你环境中的实际值:
export OTEL_EXPORTER_OTLP_ENDPOINT=https://oneuptime.com/otlp export OTEL_EXPORTER_OTLP_HEADERS=x-oneuptime-token=YOUR_TELEMETRY_INGESTION_TOKEN export OTEL_RESOURCE_ATTRIBUTES=iot.fleet.name=building-a-sensors,device.id=sensor-001,service.name=iot/building-a-sensors| 环境变量 | 是否必需 | 说明 |
|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT | 是 | OneUptime OTLP 端点(https://oneuptime.com/otlp,自托管为http(s)://YOUR-ONEUPTIME-HOST/otlp) |
OTEL_EXPORTER_OTLP_HEADERS | 是 | x-oneuptime-token=YOUR_TELEMETRY_INGESTION_TOKEN |
OTEL_RESOURCE_ATTRIBUTES | 是 | 逗号分隔的资源属性,必须包含iot.fleet.name、device.id与service.name=iot/<fleet> |
随后按下方指标约定中的iot_*名称以指标形式上报读数。约一分钟后,设备就会出现在 OneUptime 仪表盘的IoT区段下。
五、通过 OpenTelemetry Collector 发送指标
当大量设备经网关上报时,可在网关上运行 OpenTelemetry Collector 并向 OneUptime 导出。由resource处理器为数据打上机队属性;接收来自设备的各种读数(OTLP、MQTT 桥接、文件日志等)并转发:
receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 processors: batch: send_batch_size: 512 timeout: 5s resource: attributes: - key: iot.fleet.name value: field-gateways action: upsert - key: service.name value: iot/field-gateways action: upsert exporters: otlphttp: endpoint: "https://oneuptime.com/otlp" # OneUptime 要求使用 JSON 编码器替代默认的 Proto(buf) encoding: json headers: "Content-Type": "application/json" "x-oneuptime-token": "YOUR_TELEMETRY_INGESTION_TOKEN" service: pipelines: metrics: receivers: [otlp] processors: [resource, batch] exporters: [otlphttp]resource为每条记录打上机队属性。请按网关分别设置iot.fleet.name(及配套的service.name=iot/<fleet>),确保各网关下设备落入正确机队。- 保留每个数据点上的
device.id(以及可选的iot.device.kind/iot.device.type/iot.device.firmware),OneUptime 才能解析机队内的具体设备。 otlphttp通过 HTTPS 携带摄取 Token 发送到 OneUptime。注意本方案要求encoding: json并配合Content-Type: application/json请求头。
batch处理器将数据按 512 条一批、5 秒超时聚合,能显著降低设备高频上报时的连接与写放大,适合大规模机队场景。
六、通过 MQTT 发送指标
OneUptime 自带内置 MQTT 端点,已在使用 MQTT 的设备可以直接推送读数——无需 OpenTelemetry SDK、Collector 或桥接程序。所有经 MQTT 发布的数据落入与 OTLP 完全相同的管线:机队自动创建、设备清单更新,每个 IoT 监控器与告警模板均无需改动即可工作。
6.1 端点
| 传输方式 | 地址 | 说明 |
|---|---|---|
| MQTT over WebSocket | wss://<your-host>/mqtt | 适用于所有部署形态——经由 OneUptime 入口(Ingress)走常规 HTTPS 端口 |
| MQTT over TCP | <app-host>:1883(MQTT_INGEST_PORT) | 自托管:默认仅在集群/Compose 网络内部,如需对外暴露请自行开放 |
从源码看,WebSocket 监听器复用主 HTTP 服务器(Express.getHttpServer()),以noServer模式挂载在MQTT_WEBSOCKET_PATH(默认/mqtt)上,避免干扰同服务器上 socket.io 的 WebSocket 流量(见 MqttServer.ts 的startWebSocketListener)。TCP 监听器直接绑定MQTT_INGEST_PORT(默认1883)。
6.2 认证:两种方式
- 项目级(Project-wide):将Telemetry Ingestion Token作为 MQTT 密码发送(用户名被忽略;若客户端只暴露用户名字段,可将 Token 填在用户名处)。适用于代表多台设备发布的网关。
- 设备级(Per-device,推荐直连设备使用):在仪表盘机队的Device Registry标签页注册设备。注册会为该设备签发独立凭证——凭证 ID 即 MQTT用户名,Secret 即密码。使用设备凭证的客户端只能发布到自己名下的
oneuptime/<fleet>/<device>/…Topic;单个设备被攻破时,可在仪表盘上直接吊销其凭证而不影响机队其余设备(吊销约在一分钟内生效,对已连接会话同样生效);且已注册设备获得静默死亡离线检测:停止上报时它们会在清单中保持 Offline 状态而非消失,即使没有设置 Last Will,也会触发Device Offline告警模板。
无效凭证会在 CONNECT 阶段即以返回码 4(用户名或密码错误)被拒绝,因此配置错误的设备会"响亮地失败"而非静默丢数据。源码中对应常量CONNACK_BAD_USERNAME_OR_PASSWORD = 4,且认证逻辑(resolveAuthContext)先按 UUID 用户名匹配设备凭证,匹配不到再回退到项目级 Token 路径;为保证安全,设备凭证的 Secret 比对使用常量时间比较timingSafeEqual,并在认证成功后将 aedes 的全局 clientId 加上projectId前缀命名空间,防止不同项目间因同名 clientId 相互抢占会话、误触发对方 Last Will(见 MqttServer.ts)。
6.3 Topic 契约与 Payload
所有 Topic 均以固定前缀oneuptime/开头。机队与设备段不得包含/、+或#,且长度限制为 100 字符(源码中isValidIdentitySegment同时拒绝$开头):
| Topic | Payload |
|---|---|
oneuptime/<fleet>/<device>/telemetry | 读数 JSON 对象——{ "metrics": { "iot_temperature_celsius": 21.5 } },或数值字段即为指标的扁平对象 |
oneuptime/<fleet>/<device>/metrics/<metricName> | 单个值——裸数字(23.4)或{ "value": 23.4 } |
oneuptime/<fleet>/<device>/status | "online"或"offline"(也接受1/0、true/false、up/down)——映射为iot_device_up |
遥测 Payload 还可携带"attributes"(字符串映射,会打到每个数据点上——可用于iot.device.kind、iot.device.type、iot.device.firmware或自定义标签)与"timestamp"(ISO-8601 或 Unix 秒/毫秒)。二者均可选;缺省timestamp时使用摄取时间。
源码中的解析器(MqttTelemetryMapper.ts)补充了几个值得注意的细节:
status的合法取值集合实际更宽,还包含connected/disconnected;布尔值会自动折叠为1/0。timestamp按数量级自动判别秒/毫秒:小于1e11视为秒(乘以 1000),否则视为毫秒;无效值回退为摄取时间。attributes每发布最多 20 个、单键最长 256 字符,且device.id是保留键——它始终由 Topic 解析而来,Payload 无法把数据点"挪"到别的设备上。- MQTT 数据最终由
buildOtlpMetricsBody映射为标准 OTLP-JSONresourceMetrics信封(机队进 resource 属性、设备进 datapoint 标签、全部指标为 gauge),与 OTel SDK POST 到/otlp/v1/metrics的报文形状一致,因此后续的机队自动发现、IoT 快照扫描、IoT Device 监控器与仪表盘完全与传输方式无关。
6.4 通过 Last Will 实现离线检测
在oneuptime/<fleet>/<device>/status上注册 MQTT Last Will,Payload 为offline。当设备宕机或脱离网络时,会话一结束 Broker 便会代它发布iot_device_up = 0——立即触发内置的Device Offline告警模板,并将清单中设备置为 Down,无需轮询、无需等待错过的抓取。连接建立后向同一 Topic 发布online,设备即恢复为 Up。
实现层面,Last Will 的发布同样经过authorizePublish钩子并被摄取(见 MqttServer.ts 的注释说明),这正是iot_device_up能驱动离线告警模板与清单isUp字段的关键。
6.5 客户端示例
mosquitto_pub(裸 TCP,自托管):
mosquitto_pub -h YOUR-ONEUPTIME-APP-HOST -p 1883 \ -u oneuptime -P "YOUR_TELEMETRY_INGESTION_TOKEN" \ -t "oneuptime/building-a-sensors/sensor-001/telemetry" \ -m '{"metrics":{"iot_device_up":1,"iot_battery_percent":87,"iot_temperature_celsius":21.5},"attributes":{"iot.device.type":"temp-sensor","iot.device.firmware":"1.4.2"}}'Node.jsmqtt库(WebSocket,适用于 oneuptime.com 及任意自托管实例):
const mqtt = require("mqtt"); const client = mqtt.connect("wss://oneuptime.com/mqtt", { username: "oneuptime", // 被忽略——实际用下面的 Token 认证 password: "YOUR_TELEMETRY_INGESTION_TOKEN", will: { topic: "oneuptime/building-a-sensors/sensor-001/status", payload: "offline", }, }); client.on("connect", () => { client.publish("oneuptime/building-a-sensors/sensor-001/status", "online"); setInterval(() => { client.publish( "oneuptime/building-a-sensors/sensor-001/telemetry", JSON.stringify({ metrics: { iot_device_up: 1, iot_battery_percent: readBattery(), iot_temperature_celsius: readTemperature(), }, }), ); }, 60 * 1000); });Pythonpaho-mqtt(WebSocket):
import json import paho.mqtt.client as mqtt client = mqtt.Client(transport="websockets") client.username_pw_set("oneuptime", "YOUR_TELEMETRY_INGESTION_TOKEN") client.tls_set() client.will_set("oneuptime/building-a-sensors/sensor-001/status", "offline") client.ws_set_options(path="/mqtt") client.connect("oneuptime.com", 443) client.publish("oneuptime/building-a-sensors/sensor-001/status", "online") client.publish( "oneuptime/building-a-sensors/sensor-001/telemetry", json.dumps({"metrics": {"iot_device_up": 1, "iot_temperature_celsius": 21.5}}), )6.6 MQTT 接入注意事项
- 该端点仅用于摄取:订阅会被拒绝(SUBACK 失败)。若需 Broker 确认接收请使用 QoS 1。摄取语义为至少一次——确认丢失后的 QoS 1/2 重传可能产生重复数据点。
- 超出 Topic 契约或 Payload 格式错误的发布会被接受后丢弃(MQTT 3.1.1 没有逐消息错误应答)——服务器会记录带原因告警日志,若数据未到达请检查 OneUptime 应用日志。源码中
parseMqttPublish返回的错误(如 "Unsupported topic"、"Payload must be a JSON object")会以logger.warn输出,便于定位。 - WebSocket 端点上请将 MQTT keepalive 保持在5 分钟以内——OneUptime 入口会在 300 秒后关闭空闲 WebSocket 连接,从而误触发你的 Last Will 与虚假的 Device Offline 告警。客户端库默认值(
mqtt与paho-mqtt均为 60 秒)没有问题。裸 TCP 端点无此上限。 - Payload 上限为128 KB 与每条发布 100 个指标;超大包会导致连接断开。这两项分别对应源码常量
MQTT_MAX_PAYLOAD_BYTES = 128 * 1024与MQTT_MAX_METRICS_PER_PUBLISH = 100,且原始字节流在入队前还会经过MqttPacketSizeGuard的包大小护栏,防止恶意长度头耗尽内存。
七、指标约定(iot_*名称表)
OneUptime 识别以下iot_*指标名。每个数据点都应携带device.id标签,读数才能归属到正确设备。你只需发送对设备有意义的指标——缺失的指标不会绘制图表。
| 指标名 | 含义 |
|---|---|
iot_device_up | 设备可用性。1= 在线/可达,0= 离线。驱动 IoT Device 监控器 |
iot_device_info | 纯身份信号。携带device.id/ kind / type / firmware,使设备在尚未上报读数前即出现在清单中 |
iot_battery_percent | 电池电量,0–100(%) |
iot_signal_strength_dbm | 无线信号强度,单位 dBm(例如 Wi-Fi / LoRa / 蜂窝 RSSI) |
iot_temperature_celsius | 设备或传感器温度,单位 °C |
iot_cpu_usage_ratio | CPU 利用率,比值0–1(OneUptime 以百分比存储与展示) |
iot_memory_usage_bytes | 当前已用内存,单位字节 |
iot_memory_size_bytes | 设备可用总内存,单位字节 |
iot_uptime_seconds | 设备上次启动以来的秒数 |
八、验证安装
- 确认设备或网关导出无错误(检查 SDK/Collector 日志中的导出失败与 HTTP
401/403响应); - 在 OneUptime 仪表盘打开IoT区段——约一分钟内机队应以
iot/<fleet>形式出现; - 打开机队的Devices标签页——每个发送过的
device.id都应列出,并带有最新的电池、信号、温度、CPU、内存与在线/离线状态; - 打开机队下的Metriken(指标),即可绘制上述任意
iot_*序列。
九、故障排查
9.1 机队未出现
- 确认
iot.fleet.name是作为资源属性设置(而非数据点标签),且service.name为iot/<fleet>; - 确认导出端点确为
https://oneuptime.com/otlp(或自托管的…/otlp),且x-oneuptime-token请求头携带有效 Token; - 使用 Collector 时,确认
otlphttp导出器已设置encoding: json与Content-Type: application/json。
9.2 设备未出现在清单中
- 确保每个数据点都携带
device.id标签——设备以其为索引键; - 对尚未上报读数的设备发送
iot_device_info(纯身份),使其仍能出现在清单中; - 检查
device.id在多次上报间保持稳定;ID 变化会产生重复设备行。
9.3 导出器返回 HTTP 401 / 403
摄取 Token 无效、已吊销或缺失。在项目设置 → Telemetry & APM → Ingestion Keys重新生成一个,并更新x-oneuptime-token请求头。
9.4 指标未绘制图表
- 确认使用的是指标约定表中精确的
iot_*指标名——无法识别的名称会被作为通用指标存储,不会填充 IoT 图表; - 注意
iot_cpu_usage_ratio是0–1的比值;发送原始比值,OneUptime 会以百分比渲染; - 设备开始上报后,留出最多一分钟让首批数据点呈现。
十、自托管 OneUptime
自托管时,将端点指向你自己的实例:
export OTEL_EXPORTER_OTLP_ENDPOINT=https://your-oneuptime-host.example.com/otlp或在 Collector 中:
exporters: otlphttp: endpoint: https://your-oneuptime-host.example.com/otlp encoding: json headers: "Content-Type": "application/json" "x-oneuptime-token": "YOUR_TELEMETRY_INGESTION_TOKEN"MQTT 则连接wss://your-oneuptime-host.example.com/mqtt;若设备无法使用 WebSocket,可对外暴露应用服务的裸 MQTT TCP 端口(MQTT_INGEST_PORT,默认1883)。如需彻底关闭 MQTT 监听器,可在应用服务上设置MQTT_INGEST_ENABLED=false——源码 Config.ts 中该开关默认开启(仅在显式设为"false"时关闭),startMqttServer会据此跳过 TCP 与 WebSocket 两个监听器的启动。
若实例仅支持 HTTP,请将协议改为http://(MQTT 对应ws://)并使用相应端口。
十一、下一步
- 配置IoT Device Monitor,针对设备离线、低电量、弱信号、高温度与高 CPU 等条件告警——见 IoT Device Monitor;
- 非容器化主机(Linux / macOS / Windows 虚拟机与裸金属)可选用 Host OpenTelemetry Collector 方案;
- 深入理解底层 OTLP 集成原理,可阅读仓库内
App/FeatureSet/Docs/Content/en/telemetry/下的 OpenTelemetry 集成文档。
补充阅读:本文内容对应的英文原版文档位于 App/FeatureSet/Docs/Content/en/telemetry/iot-devices.md(德语版即本文关联文档);MQTT 摄取管线的核心实现集中在 App/FeatureSet/Telemetry/MqttServer.ts 与 App/FeatureSet/Telemetry/Utils/MqttTelemetryMapper.ts,配置开关与默认值见 App/FeatureSet/Telemetry/Config.ts。
【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考