📺 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 不是一个“上传文件的网站”,也不是一个“装了客户端就自动升级”的黑盒,而是一条完整链路。

在这条链路里,至少要分清四个角色:

  1. Build Host(构建机)
  2. Mender Server(服务端)
  3. Device Client(设备端客户端)
  4. Artifact(更新载体)

很多看似复杂的问题,本质上都是因为把这四者混在一起了。


2. Build Host:负责构建,不负责管理设备

在当前场景里,Build Host 就是 Yocto 构建机。它的职责是:

  • 拉取 layer
  • 配置 local.confmachine.conf
  • 执行 bitbake
  • 构建 rootfs
  • 生成刷机镜像
  • 生成 .mender Artifact

也就是说,构建机负责的是:

生产系统镜像,生产 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-auth
  • mender-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 设备来说:

  • localhost
  • 127.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-setupmender-authmender-update 的关系再总结一遍

这个关系你现在已经基本理顺了,但很值得再压缩成更牢固的一条线。

mender-setup

作用:配置客户端
理解:填资料

它告诉客户端:

  • 我是谁
  • 连谁
  • 怎么轮询

mender-auth

作用:认证
理解:去报到,拿 token

它负责:

  • 用设备身份和密钥去访问认证接口
  • 申请 token

mender-update

作用:升级
理解:拿着 token 去干活

它负责:

  • 上报 inventory
  • 检查 deployment
  • 下载 Artifact
  • 安装更新
  • 回报状态

最短记忆方法:

先配置,再认证,再更新。


九、token 到底是什么

token 最简单的理解:

设备认证通过后,Server 发下来的一张临时访问通行证。

它不是设备永久身份,不是设备类型,也不是版本号。
它只是:

  • 认证通过后的访问凭证

所以后面设备要做的:

  • Inventory 上报
  • Deployment 查询
  • 状态回报

都必须依赖 token。

如果拿不到 token,就会看到:

  • Failed to fetch new token
  • Timed-out waiting for a new token

这意味着:

认证没过,后面的事情都别想继续。


十、设备是怎样一步步出现在 Server 上的

这条链今天已经完整走通过了。

第一步:本地配置写好

mender-setup 成功,只说明配置落地成功。

第二步:设备开始发起认证

mender-auth 去访问 Server。

第三步:设备进入 Pending

这表示:

  • Server 已经看到了请求
  • 但还没授权

第四步:管理员点击 Accept

这一步之后,设备才真正进入可管理状态。

此时日志会从:

  • Unauthorized

变成:

  • Successfully received new authorization data

第五步:Inventory 上报成功

Server 上开始显示:

  • device_type
  • artifact_name

第六步:开始轮询 Deployment

如果没新版本,就会显示:

  • No update available

这时其实已经说明整条设备接入链打通了。


十一、为什么 device_typeartifact_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 installed
  • Skipped = 1
  • Total download size = 0 Bytes

这说明:

同版本可以发 Deployment,但不会真正重复安装。


十四、为什么 /etc/os-releaseshow-artifact 会不一致

你后面看到了一个很典型的现象:

  • /etc/os-release 看起来像 1.0.3
  • mender-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 包体积会越来越夸张。


十八、当前你已经真正跑通了哪些内容

回头看,这一轮已经打通了很完整的一条链:

  1. 用 Docker Compose 起本地 Mender Server
  2. 浏览器进入 https://localhost/ui
  3. 设备端执行 mender-setup
  4. 设备端把 docker.mender.io 指向主机真实 IP
  5. demo.crt / mender.crt 加入设备信任链
  6. mender-auth 成功认证
  7. 设备进入 Pending
  8. 在页面中 Accept
  9. 设备拿到 token
  10. mender-update 成功上报 Inventory
  11. Server 能显示 device_typeartifact_name
  12. Deployment 能正常做兼容性与版本判断
  13. 同版本被识别为 Already installed
  14. 当前设备能正常轮询并显示 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_typeartifact_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


Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐