从零理清 Mender OTA:基于 Yocto、Docker 演示服务器与 Jetson 实战的完整总结
📺 B站:博主个人介绍
📘 博主书籍-京东购买链接*:Yocto项目实战教程
📘 加博主微信,进技术交流群: jerrydev
从零理清 Mender OTA:基于 Yocto、Docker 演示服务器与 Jetson 实战的完整总结
前言
这轮围绕 Mender 的学习,表面上看做的事情并不复杂:在主机上用 Docker Compose 跑起一个本地 Mender Server,把 Jetson 设备接进去,上传 Yocto 构建出来的 .mender 文件,然后尝试做 OTA 升级。
但只要真正开始实操,就会发现它绝不是“上传一个更新包,设备就自动升级”这么简单。中间会不断碰到一些很典型、也很容易让人混乱的问题:
为什么浏览器能打开 https://localhost/ui,设备端却不能用 localhost?
为什么设备明明已经联网了,却还是拿不到 token?
为什么日志里有时是 certificate verify failed,有时是 Unauthorized?
为什么设备已经在列表里了,却还是不能真正升级?
为什么同一个版本也能创建 Deployment,但设备端却显示 Already installed?
为什么 /etc/os-release 里已经像是新版本了,可 mender-update show-artifact 还是旧版本?
为什么有些设备端没有 artifact_info 文件,服务端却仍然能识别它当前的 artifact_name?
这些问题如果只是零散地看,很容易越看越糊。真正的问题不在于某一条命令不会敲,而在于没有把 Mender 的整体结构想清楚。
Mender 不是一个单独的软件工具,而是一整套围绕设备身份、TLS/HTTPS 通信、认证、Inventory 上报、Artifact 分发、A/B 更新与状态回报组织起来的 OTA 体系。只有把这条链路拆开,并且把各个概念放在正确的位置上,后面的日志、页面状态和升级结果才会真正变得可解释。
这篇文章就基于这一轮完整的实操过程,把 Mender 的核心结构、关键概念、实际踩坑、经验总结和当前不足系统整理出来。目标不是简单记录命令,而是建立一套之后还能复用的知识框架。

一、先把 Mender 的整体结构想清楚
1. Mender 不是单点工具,而是一条完整链路
先给出一个最核心的结论:
Mender 不是一个“上传文件的网站”,也不是一个“装了客户端就自动升级”的黑盒,而是一条完整链路。
在这条链路里,至少要分清四个角色:
- Build Host(构建机)
- Mender Server(服务端)
- Device Client(设备端客户端)
- Artifact(更新载体)
很多看似复杂的问题,本质上都是因为把这四者混在一起了。
2. Build Host:负责构建,不负责管理设备
在当前场景里,Build Host 就是 Yocto 构建机。它的职责是:
- 拉取 layer
- 配置
local.conf、machine.conf - 执行
bitbake - 构建 rootfs
- 生成刷机镜像
- 生成
.menderArtifact
也就是说,构建机负责的是:
生产系统镜像,生产 OTA 更新包。
它不是服务端,不是设备端,也不是用来管理设备的地方。
这次在构建目录里能看到很多文件,例如:
*.ext4*.dataimg*.tegraflash.tar.gz*.mender*.bootstrap-artifact
这些产物正说明 Mender 已经参与了 Yocto 构建流程。
其中最关键的是两类:
- 首次刷机使用的镜像
- 后续 OTA 使用的
.mender文件
所以当你问“是不是要构建 host 端”时,首先要分清你说的 host 是谁。
如果你说的是 Yocto 构建机,那它本来就在做构建;
如果你说的是 Mender Server,那它不是构建出来的,而是部署出来的。
3. Mender Server:负责管理、分发和状态追踪
Mender Server 就是你在浏览器里看到的那个管理界面,例如:
https://localhost/ui
它的职责包括:
- 接收设备认证请求
- 维护 Pending / Accepted 设备
- 展示设备 Inventory
- 上传 Artifact
- 生成 Release
- 创建 Deployment
- 跟踪部署状态和错误
所以从角色上看,它就是服务端控制中心。
但这里必须加一个边界:
你现在通过 Docker Compose 跑起来的是官方的演示/评估环境,不是生产环境。
这套环境非常适合:
- 学习
- 验证
- 测试端到端链路
- 熟悉 Mender Server 的页面和概念
但不适合直接作为正式量产环境使用。
也就是说,功能逻辑是真的,部署级别是演示级别的。
4. Device Client:真正执行认证与升级的角色
设备端最核心的工具,其实只有两个:
mender-authmender-update
最容易记的理解方式就是:
先认证,再更新。
其中:
mender-auth
负责:
- 读取设备身份
- 读取设备密钥
- 访问认证接口
- 向 Server 申请 token
mender-update
负责:
- 使用 token 检查有没有新任务
- 上报 inventory
- 下载 Artifact
- 执行安装
- 回报状态
而 mender-setup 本身并不负责升级,它只是一个配置工具,用来把:
- 设备类型
- 服务器地址
- 轮询方式
写进客户端配置文件中。
所以这三者关系最简单的一句话就是:
mender-setup 先配置,mender-auth 去认证,mender-update 去升级。
5. Artifact:Mender OTA 的正式交付单元
在 Mender 体系里,真正上传到 Server 的更新文件是:
*.mender
它不是一个普通压缩包,也不是随便复制的 rootfs 文件,而是 Mender 定义的标准 Artifact。
服务端和客户端都通过它来理解:
- 这是哪个版本
- 这个包适合哪些设备
- 里面是什么类型的 payload
- 安装后应该如何处理
所以一句话概括:
Artifact 是 Mender OTA 链路中的正式更新载体。
二、Docker Compose 起起来的,到底是不是“真正的服务器”
这是最容易让人摇摆的问题之一。
答案其实很明确:
是,它就是 Mender Server,只不过它是 Docker Compose 演示版,而不是生产版。
1. 为什么说它是服务器
因为它具备了完整服务端能力:
- 有 Web 登录页面
- 能看到设备状态
- 能接收 Artifact
- 能创建 Deployment
- 能管理设备授权
- 能展示日志与状态
所以从功能上,它完全是服务器。
2. 为什么又说它不是生产服务器
因为这套环境的定位是:
- 快速搭建
- 快速学习
- 快速验证
- 功能评估
而不是:
- 高可用
- 高安全
- 面向量产
- 面向大规模设备管理
所以一定要形成这个正确认识:
这套 Docker 环境不是假的服务器,而是真实逻辑的演示服务器。
这意味着你今天学到的:
- Pending / Accepted
- Artifact 上传
- Deployment 匹配
- 状态追踪
这些概念都是真实有效的,只是部署方式还不是最终生产形态。
三、为什么浏览器里能用 localhost,设备端却不能
这也是今天非常关键的一个认知点。
1. 在主机浏览器里,localhost 指向主机自己
你在跑 Docker Server 的主机上访问:
https://localhost/ui
这是正确的,因为浏览器运行在这台主机上,localhost 指向的就是这台主机。
2. 在设备端,localhost 指向设备自己
对 Jetson 设备来说:
localhost127.0.0.1
都只会指向设备本机。
所以如果把 localhost 填进设备端 Mender 配置,那么设备只会尝试访问自己,而不会访问你的 Mender Server 主机。
这就是为什么设备端必须配置成:
- Server 主机的真实 IP
- 或者能正确解析到该 IP 的域名
这也是后面整个网络排障的起点。
四、为什么一定要改 /etc/hosts
Mender Docker 演示环境里,/etc/hosts 不是可选项,而是 HTTPS 证书和域名匹配的关键。
1. 主机上的 hosts
主机上通常写成:
127.0.0.1 docker.mender.io
127.0.0.1 s3.docker.mender.io
原因很简单:
演示环境的自签名证书就是为这些域名准备的。
所以主机上的浏览器、容器环境都会按这组域名和证书来工作。
2. 设备上的 hosts
设备上则必须写成:
192.168.50.131 docker.mender.io
192.168.50.131 s3.docker.mender.io
也就是把这两个域名解析到运行 Mender Server 的主机真实 IP 上。
如果设备上也写成 127.0.0.1,那它永远只会回到自己。
所以这里一定要记住两句话:
- 主机上,
docker.mender.io -> 127.0.0.1 - 设备上,
docker.mender.io -> Server 主机真实 IP
五、demo.crt / mender.crt 到底是什么
1. 它不是私钥,而是证书
你后来问到一个很关键的问题:
mender.crt是不是私钥?
答案是:不是。
.crt 是证书文件,里面包含的是:
- 服务器身份信息
- 公钥
- 签名信息
真正的私钥通常是另一个 *.key 文件。
所以:
- 设备端需要的是
.crt - 设备端不需要,也绝不能拿 Server 私钥
2. 它的作用是什么
它的作用是:
让设备端信任这台 Mender Server 提供的 HTTPS 服务。
因为设备访问的不是明文 HTTP,而是:
https://docker.mender.io
所以设备必须判断:
- 这张证书是不是可信
- 当前连接的是不是真正的
docker.mender.io
3. 为什么会报 certificate verify failed
当日志出现这个错误时,它的准确含义不是:
- “找不到服务器”
而是:
- 网络已经通了
- 域名也解析对了
- 但是设备不信任这张自签名证书
所以更准确的说法是:
找到了 Server,但不相信它。
后面把证书拷到设备、导入系统信任链后,TLS 这一层才真正打通。
六、服务器为什么要持有私钥
这部分也值得明确一下。
服务器持有私钥,不是为了“发给客户端”,而是为了在 HTTPS/TLS 握手时证明:
“我确实是这张证书对应的那台服务器。”
可以这样理解:
.crt= 公开展示的身份证.key= 只由服务器自己持有的私章- TLS 握手 = 现场验证“你是不是这张身份证的主人”
所以:
- 客户端只需要证书
- 服务器必须保留私钥
- 私钥绝不能拷到设备端
七、Mender 到底用的是什么协议
这是理解整个交互过程的核心。
Mender 设备端和服务端之间,本质上是:
- HTTPS
- REST API
- JSON 请求与响应
所以它不是某种神秘私有协议,也不是网页点击后的本地脚本。
它本质上是一个带认证、带状态机的 HTTPS API 系统。
一旦把这一点想清楚,很多日志就都能分层理解:
1. 网络层问题
比如 IP 不通、网卡没起来。
2. 域名层问题
比如 docker.mender.io 没正确解析到 Server 主机。
3. TLS 层问题
比如证书不信任,出现 certificate verify failed。
4. 认证层问题
比如设备还没被 Accept,出现 Unauthorized。
5. 更新层问题
比如没有新版本,显示 No update available;或者版本已安装,显示 Already installed。
这其实就是今天整轮排障的真实路线图。
八、mender-setup、mender-auth、mender-update 的关系再总结一遍
这个关系你现在已经基本理顺了,但很值得再压缩成更牢固的一条线。
mender-setup
作用:配置客户端
理解:填资料
它告诉客户端:
- 我是谁
- 连谁
- 怎么轮询
mender-auth
作用:认证
理解:去报到,拿 token
它负责:
- 用设备身份和密钥去访问认证接口
- 申请 token
mender-update
作用:升级
理解:拿着 token 去干活
它负责:
- 上报 inventory
- 检查 deployment
- 下载 Artifact
- 安装更新
- 回报状态
最短记忆方法:
先配置,再认证,再更新。
九、token 到底是什么
token 最简单的理解:
设备认证通过后,Server 发下来的一张临时访问通行证。
它不是设备永久身份,不是设备类型,也不是版本号。
它只是:
- 认证通过后的访问凭证
所以后面设备要做的:
- Inventory 上报
- Deployment 查询
- 状态回报
都必须依赖 token。
如果拿不到 token,就会看到:
Failed to fetch new tokenTimed-out waiting for a new token
这意味着:
认证没过,后面的事情都别想继续。
十、设备是怎样一步步出现在 Server 上的
这条链今天已经完整走通过了。
第一步:本地配置写好
mender-setup 成功,只说明配置落地成功。
第二步:设备开始发起认证
由 mender-auth 去访问 Server。
第三步:设备进入 Pending
这表示:
- Server 已经看到了请求
- 但还没授权
第四步:管理员点击 Accept
这一步之后,设备才真正进入可管理状态。
此时日志会从:
Unauthorized
变成:
Successfully received new authorization data
第五步:Inventory 上报成功
Server 上开始显示:
device_typeartifact_name
第六步:开始轮询 Deployment
如果没新版本,就会显示:
No update available
这时其实已经说明整条设备接入链打通了。
十一、为什么 device_type 和 artifact_name 是判断 OTA 的核心
这是 OTA 逻辑里最关键的一组信息。
1. device_type
设备上可以通过:
cat /var/lib/mender/device_type
看到:
device_type=jcl-tablet-p3701-0005
它回答的是:
这台设备属于哪一类设备。
2. artifact_name
设备上可以通过:
mender-update show-artifact
看到:
my-jetson-image-ota_1.0.2
它回答的是:
这台设备当前运行的是什么 Artifact 版本。
3. Server 会同时对比这两个值
因此会出现三种典型结果:
结果一:设备类型不匹配
表现为:
No compatible artifact found
结果二:版本已经相同
表现为:
Already installed
结果三:类型匹配,版本更高
表现为:
- 真正开始 OTA 安装
十二、为什么会出现 No compatible artifact found
这个问题今天前面已经踩到过。
根本原因只有一个:
设备端 device_type 和包里声明的兼容设备类型不一致。
例如:
- 设备:
jcl-tablet-p3701-0004 - 包:
jcl-tablet-p3701-0005
那 Server 就会直接判断:
这包不是给你这台设备的。
所以这是兼容性问题,不是网络问题,也不是证书问题。
十三、为什么会出现 Already installed
这个问题更容易让人误判。
1. 设备会告诉 Server 自己当前版本
通过:
mender-update show-artifact
可以看到设备当前版本,例如:
my-jetson-image-ota_1.0.2
2. 如果新包也叫这个名字
那设备就会认为:
我已经装过这个版本了。
于是 Deployment 虽然能创建,但最终会表现为:
Already installedSkipped = 1Total download size = 0 Bytes
这说明:
同版本可以发 Deployment,但不会真正重复安装。
十四、为什么 /etc/os-release 和 show-artifact 会不一致
你后面看到了一个很典型的现象:
/etc/os-release看起来像1.0.3mender-update show-artifact还是1.0.2
这说明:
系统文本版本和 Mender 真正认定的当前 Artifact 版本,不一定是一回事。
/etc/os-release
只是 rootfs 里的一个系统版本文本文件。
mender-update show-artifact
才是 Mender 用来判断 OTA 版本的值。
所以在 OTA 语义里,更应该信:
mender-update show-artifact
而不是只看 /etc/os-release。
十五、为什么设备没有 artifact_info 文件也正常
你还特地查过:
find / -name artifact_info
没找到。
这并不代表 Mender 有问题。
因为:
artifact_name不一定要通过/etc/mender/artifact_info这种明文文件存在- Mender 也可以从本地状态中知道当前 Artifact 名字
- 只要 Server 上已经看到
artifact_name - 就说明客户端已经成功上报出来了
所以:
没有 artifact_info 文件,不代表没有当前版本信息。
十六、.mender 包和 A/B 分区到底什么关系
1. .mender 不是把 A/B 两个分区都打进去
标准 rootfs-image 类型的 .mender,通常只包含:
一份新的 rootfs payload
2. A/B 是设备本来就有的分区布局
设备侧预先就存在:
- rootfs A
- rootfs B
- data
3. 真正安装过程是:
- 当前系统跑在 A
- 新内容写到 B
- 重启切到 B
- 成功就 commit
- 失败就回滚到 A
所以最值得记住的一句话是:
A/B 是设备分区结构,Artifact 是写进去的新内容。
十七、为什么 .mender 文件会那么大
这不是因为 Mender 固定就要大,而是因为当前你用的是:
整 rootfs 镜像 OTA
只要 rootfs 本身比较大:
.ext4大.mender也会跟着大
这也是为什么:
- 大模型
- 大资源包
- cache
- 日志
- 下载文件
不适合全塞进 rootfs。
否则 OTA 包体积会越来越夸张。
十八、当前你已经真正跑通了哪些内容
回头看,这一轮已经打通了很完整的一条链:
- 用 Docker Compose 起本地 Mender Server
- 浏览器进入
https://localhost/ui - 设备端执行
mender-setup - 设备端把
docker.mender.io指向主机真实 IP - 把
demo.crt / mender.crt加入设备信任链 mender-auth成功认证- 设备进入 Pending
- 在页面中 Accept
- 设备拿到 token
mender-update成功上报 Inventory- Server 能显示
device_type和artifact_name - Deployment 能正常做兼容性与版本判断
- 同版本被识别为
Already installed - 当前设备能正常轮询并显示
No update available
这一条线如果没有真正走过一遍,很难只靠看文档建立完整理解。
而现在,你已经把这条链跑通了。
十九、这轮实践最大的经验总结
1. 别把 Mender 想成“上传升级包的网站”
它本质上是一套:
- 设备认证
- TLS/HTTPS
- Artifact 分发
- A/B 更新
- 状态回报
组成的完整 OTA 系统。
2. 排障一定要分层
别一律说“连不上”。
要先问清:
- 网络通不通
- 域名对不对
- 证书信不信
- 是否已 Accept
- token 有没有拿到
- Artifact 是否兼容
- 当前版本是不是已经装过
3. mender-setup successfully 只说明配置写好了
真正接入成功要看:
- 是否进 Pending
- 是否被 Accept
- 是否拿到 token
- inventory 是否成功上报
4. Server 判断 OTA,核心就是 device_type + artifact_name
这组值是最关键的判断基础。
5. show-artifact 比 /etc/os-release 更值得信
在 OTA 判断语义里,Mender 自己认定的版本,比普通系统文本版本更关键。
二十、你当前的不足与后续要补强的方向
虽然已经把链路跑通了,但也要客观看到还没完全闭环的地方。
1. A/B 完整验证还不够
你已经理解了:
- 当前跑哪个分区
- 新内容写到非活动分区
- 成功 commit,失败 rollback
但真正的完整验证还需要继续做:
- 确认写入的是哪一个 rootfs
- 确认重启后切换是否成功
- 确认 commit
- 人为制造失败看 rollback
2. Jetson 平台 slot / rootfs 切换还需要更深结合
Jetson 本身有自己的 slot 机制,和 Mender 的 rootfs A/B 逻辑相关但不完全等同。
这部分如果后面继续深入 Jetson OTA,还需要更系统地整理。
3. 目前主要是演示环境经验
Docker Compose 环境非常适合学习,但离生产环境还差很多。
后续还要继续理解:
- 正式部署方式
- 更稳健的存储与证书体系
- 多设备管理
- 更完整的安全设计
4. 目前更多还是根文件系统 OTA
如果以后要做更灵活的更新,例如:
- 单文件
- 目录
- 应用
- 容器
- 模型
还要继续理解 Update Module 的边界和设计方式。
结语
如果把这一轮学习最后压缩成一句话,我觉得最准确的表达是:
Mender OTA 不是“上传一个 .mender 文件然后设备自动升级”,而是一条由客户端配置、HTTPS/TLS 认证、token 获取、Inventory 上报、Artifact 匹配、A/B 安装与状态回报组成的完整链路。
而这一轮最大的收获,不是某一条命令,而是你开始真正把这条链的每个环节分清楚了:
- Build Host 做什么
- Server 做什么
- Client 做什么
- 证书为什么重要
- token 为什么重要
- Pending / Accept / Unauthorized 各自意味着什么
device_type和artifact_name如何决定 OTA 行为
当这些点真正连成一张图时,Mender 才不再是“有点复杂的 OTA 工具”,而是一套你已经能看懂、能定位、能解释、还能继续深入的系统。
这就是这轮学习最重要的价值。“”"
out = Path(“/mnt/data/mender_systematic_experience_summary.md”)
out.write_text(content, encoding=“utf-8”)
print(f"Wrote {out}")
Japgollyassistant to=python_user_visible.exec კომენტary code
📺 B站:博主个人介绍
📘 博主书籍-京东购买链接*:Yocto项目实战教程
📘 加博主微信,进技术交流群: jerrydev
更多推荐




所有评论(0)