news 2026/10/21 7:21:54

从零搭建Mender OTA升级链路:Yocto集成与Jetson适配实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零搭建Mender OTA升级链路:Yocto集成与Jetson适配实践

去年我们手上有一批嵌入式设备,分散在好几个现场,跑的是裁剪过的Linux系统。最让人头疼的还不是功能开发,而是固件升级:每次更新要么派人带着SD卡去现场重刷,要么让现场人员用一套很不友好的烧录工具手工操作,中间还容易刷错版本。后来我下决心把OTA方案理清楚,调研了一圈,最后选了Mender。这套方案的特点在于它不单单做应用层热更新,而是直接管理整个rootfs镜像,配合U-Boot做双槽位切换,能从底层保证升级失败自动回滚。我这次并没有用托管版,而是从零开始自己搭了一套完整链路:服务器端用官方维护的Docker演示服务器在本地跑起来,设备端操作系统基于Yocto定制,硬件平台是Jetson系列。这篇博文就是把整个落地过程做一个完整总结,内容覆盖前期设计、Yocto集成、Docker服务器搭建、Jetson适配、端到端升级链路和问题排查,给准备入坑Mender的人一条可以直接参考的路线。

1. 项目从零开始的前因与整体设计

1.1 原始痛点与方案选型思路

先交代一下背景。我们当时的设备数量不算多,大约几十台,分布在不同城市的现场,每台设备都跑Linux,需要定期更新:有时候是内核升级,有时候是自定义应用程序,偶尔还要换根文件系统里的库文件。由于设备没有外网固定IP,之前一直靠现场人工维护。这种模式的问题很明显:人力成本高、更新周期长、现场人员误操作风险大,而且无法做到版本一致性管理——两个现场很可能跑着不同的固件版本,出问题不好排查。

所以要解决的其实不是“怎么把文件传上去”,而是三个核心问题:一是升级过程可回滚,不能把设备刷成砖;二是版本可管理,能随时查看每台设备当前跑了什么版本;三是更新过程自动化,至少能做到推送后设备自行下载、校验、切换。市面上的OTA方案我也对比过一些,包括纯应用层的容器替换、基于包管理器的远程安装,以及平台级的全量镜像OTA。最终选Mender,看中的是它对rootfs层级的全量管理能力和A/B分区切换机制。它不像包管理器那样做增量修补,也不像容器方案那样隔离在单层应用,而是把整个镜像当成一个可部署的artifact来管理,底层启动加载器控制当前启动哪一个槽位,这套模型对嵌入式Linux最稳。

1.2 Mender的三大组件与一次升级的基础流程

Mender整个体系可以分成三块:Mender服务器、Mender客户端、Mender artifact。

服务器负责设备管理、用户认证、artifact仓库和部署任务下发;客户端跑在设备上,负责定时与服务器通信,报告设备状态、拉取可用部署、执行部署并上报结果;artifact是升级包的统一封装格式,可以把rootfs镜像、内核、设备树、自定义脚本等内容打包进去,本身又带有校验信息和元数据。三者各有闭环节点,服务器下发目标artifact名称,客户端检查本机当前artifact名称,不一致就进入升级流程。

一次完整的OTA升级,在Mender体系里大概是这样的路径:先在服务器上传包含新镜像的artifact,再创建一个面向某类设备的部署,客户端下次轮询时发现有待处理的部署,开始下载artifact文件;下载完成后对分区写入新镜像,写入成功后切换U-Boot环境变量的激活槽位,然后重启;设备从新槽位启动后,客户端向服务器上报启动成功,服务器把部署标记为成功,如果因为某种原因新槽位启动失败,U-Boot会自动回退到上一个槽位,仍然上报失败,服务器可以收到这个回滚事件。

这套机制意味着更新并不是简简单单的“拷文件覆盖”,而是在分区层面做的一次事务性切换。我们后来在实测中发现,这个“事务性”的价值非常大,后面第五部分会专门拆解。

1.3 技术栈组合:Yocto、Docker、Jetson各自的定位

为什么要用Yocto而不直接用厂家提供的刷机包?因为Jetson平台原厂方案通常给的是完整的Linux镜像,版本和打包方式都是定死的,虽然可以直接刷写,但你要在里面嵌入Mender客户端、加入特定系统服务、裁剪不必要的组件,或者做OTA远程升级,就很难下手。Yocto最大的价值是可以从源码层面拼装出一个完全可控的Linux系统:内核版本、启动加载器、systemd单元、分区表、根文件系统内容,每一项都可以通过layer和recipe定制。Meta-mender这个官方layer就是专门为Yocto集成提供支持,它会处理大部分麻烦事,比如分区变化、U-Boot环境变量注入、客户端安装与配置。

Docker演示服务器则是另一个层面:Mender的完整服务器端涉及微服务比较多,如果手动部署会很痛苦,官方提供了一个容器化的demo环境,用docker-compose就能把用户管理、artifact存储、部署编排等关键服务跑起来。它在功能完整度上和托管版基本对齐,别小看这个demo环境,它完全可以支撑几十台到几百台设备的实测验证。

Jetson平台则是设备端硬件的代表。Mender本身不挑硬件,只要Bootloader支持U-Boot环境变量并且分区可调整,就可以适配。但Jetson的启动链路比较特殊,涉及Boot ROM、ATF、U-Boot、内核多级加载,真正做完适配后,这套经验基本可以移植到其他ARM平台。

2. Yocto环境搭建与meta-mender集成

2.1 准备构建环境与项目源码

Yocto构建我建议准备一台配置高一点的Linux机器,至少8核心、32GB内存、300GB磁盘,别拿低配笔记本硬搞,构建过程中并发任务很多,磁盘爆掉和内存不够是我见过最常见的问题。宿主机用Ubuntu 20.04或22.04 LTS都比较顺,依赖包按Yocto文档装好即可。

源码方面,我建议用repo工具管理多个仓库。核心仓库包括OpenEmbedded核心层(通常对应dunfell或kirkstone分支)、板级BSP层、meta-mender主层和meta-mender集成层。我这次用的是kirkstone分支,Mender版本的兼容性在发布说明里写得很清楚,选分支之前一定要去确认对应关系,否则集成层和主层版本不一致,会出现莫名其妙的bitbake解析错误。

目录布局上,把meta-mender放到sources目录下,并在构建目录外的bblayers.conf里加入对应路径。这里特别提醒一个细节:meta-mender对machine的命名有要求,它的兼容层会检查设备类型,所以最好把你自己的板级层的machine名称定义好之后再接入meta-mender,避免layer依赖解析顺序出错。

2.2 在meta层中接入Mender支持

Mender在Yocto中的集成核心是meta-mender-core。在bblayers.conf中加入meta-mender/meta-mender-core之后,还需要在local.conf里打开Mender相关功能。最重要的一条是让镜像构建走Mender的镜像类型:

IMAGE_FSTYPES = "mender"

这一条会让bitbake在生成标准rootfs镜像之外,额外生成带Mender分区布局的镜像文件。注意这里的分区不是普通的两三个分区,而是按照Mender要求划分出启动分区、两个根文件系统槽位(A/B)、数据分区和用于存储U-Boot环境变量的分区。如果只是把IMAGE_FSTYPES改成mender而没有正确配置分区表,构建出来的镜像启动时会因为找不到根文件系统而失败。

还有一条很关键的配置是设置设备类型,Mender用设备类型来匹配artifact和设备,设备类型不匹配时,部署任务会被客户端拒绝执行。代码里通常这样写:

MENDER_DEVICE_TYPE = "jetson-ta-01"

自定义设备类型完全没问题,只要服务器端创建部署时选择同样的类型即可。注意这里设备类型决定了它能接收哪些artifact,如果改了设备类型,旧的artifact上传时的目标类型就也要对应调整。

2.3 关键BitBake变量解释与本地配置

下面这组配置,是我在实际构建里确认必须设置的,每一个都对应Mender运行的关键环节:

MENDER_ARTIFACT_NAME = "release-1.0" MENDER_SERVER_URL = "https://mender.example.com" MENDER_TENANT_TOKEN = "eyJhbGciOi..." MENDER_ARTIFACT_ROOTFS_CONFIGURATION = "1"

MENDER_ARTIFACT_NAME代表当前构建的镜像版本名,必须先明确,因为客户端启动后会把这个值上报给服务器,服务器通过比对artifact名称判断设备是否已经是最新版本。如果构建时没设,Mender客户端会启动报错。MENDER_SERVER_URL是客户端访问服务器的地址,因为我们的演示服务器是本地部署,没有配置域名,我直接用了HTTPS加IP的方式。MENDER_TENANT_TOKEN是企业版的租户token,演示服务器默认也用这个做认证,后面第三部分会讲到在哪里找。

还有一个容易忽略的点是根文件系统的可写与否。Mender默认会把根文件系统挂载为只读,配合一个独立的数据分区存放运行时数据。我一开始图省事把根文件系统设成了可写,结果每次OTA更新后,之前的运行时改动就和新系统产生冲突。后来按照Mender推荐的方式,把所有动态数据都重定向到/data分区,问题才彻底解决。

2.4 首次构建与产生的关键文件

首次构建耗时很长,我的机器上全量构建花了大约一个半小时。构建完以后,在镜像输出目录会看到这样几个关键文件:*.mender结尾的Mender artifact文件,这是上传到服务器用的升级包;.sdcard或.img结尾的原始镜像文件,这是SD卡烧录用的基础镜像,它本身就带着A/B分区结构。

这里有一个操作上的坑:如果是第一次刷机,千万别直接拿mender artifact去烧录,因为mender artifact是供应商格式,需要先刷入基础镜像,之后才能用OTA方式部署artifact。我们要做的是把带mender分区的完整镜像通过dd或balenaEtcher烧录到SD卡或NVMe存储。首次烧录完成后设备启动,就是已经带Mender客户端的系统,它还没有注册到服务器,这一步要靠后面的客户端连接配置来完成。

3. Docker演示服务器的本地化部署

3.1 演示服务器的组件构成与作用

Mender的演示服务器其实是一组微服务的容器化集合,关键服务包括用户管理服务、设备认证服务、部署编排服务、Artifact存储服务、工作流服务、Redis和MongoDB存储,以及一个用于反向代理的Nginx。我第一次看到docker-compose文件时有点发怵,觉得服务太多了,但跑通之后理解就清晰了:这些服务其实只做了三类事——第一类是管理设备与用户登录,第二类是存储和分发OTA artifact,第三类是执行部署任务的状态机流转。

演示服务器本地部署时默认监听HTTP端口,不过Mender客户端要求走HTTPS,所以我们部署时要在Nginx容器里加上TLS证书配置。我是在本地环境用自签名证书做完整个验证的,设备端的证书信任问题通过把自签名CA导入到客户端的受信任仓库解决。如果你手头有正式的域名和证书,那更好,少踩一个证书信任的坑。

3.2 拉取镜像并启动整套服务

官方仓库里有一份docker-compose文件,正常情况下直接执行:

docker compose up -d

所有服务就会启动。但这里有个容易翻车的地方:仓库默认使用的是mendersoftware下面的多个镜像,这些镜像之间有着严格的版本匹配关系。如果直接用了最新tag而没看README中的版本兼容矩阵,可能会出现用户服务能登录、但设备注册接口报404的情况。我建议按仓库分支自带的tag列表来拉取,而不是无脑latest。

启动完成后,通过web界面可以打开管理后台,首次访问会引导创建管理员账号,这一步会得到两个关键内容:一个是组织名,另一个是用于设备认证的租户token。我现在回头再看,这两样东西在整个链路里就是设备接入的“钥匙”,服务器端发给客户端后,客户端才能通过认证通道上报状态。

3.3 创建设备用户与上传第一个artifact

服务器起来后,先把第二章节里构建生成的.mender文件通过管理界面上传到Artifact仓库。上传时要填设备类型,也就是和我们构建时设置的MENDER_DEVICE_TYPE完全一致,同时填一个版本名,对应MENDER_ARTIFACT_NAME里的字符串。

上传成功后,可以在设备列表里看到尚未注册的设备记录。这时候设备端只要配置好服务器地址和租户token,客户端就会自动完成注册。这一步我在测试时遇到过一个细节:Mender客户端默认配置在/etc/mender/mender.conf里,里面要填的正确密钥是ServerURL和TenantToken,注意是这两个字段名,别写错。官方提供的示例配置里有时候只有ServerURL,把TenantToken漏了,设备注册的时候就会一直卡在待认证状态。

到这里,服务器端和客户端都已经连通,接下来才是更关键的硬件适配部分。

4. Jetson平台适配与Bootloader改造

4.1 Jetson启动流程与Yocto BSP的关系

Jetson平台的启动链路比普通ARM开发板复杂,从加电到进入系统大约经历四个阶段:Boot ROM从存储介质加载Bootloader,Bootloader引导ATF(Arm Trusted Firmware),ATF再引导U-Boot,之后U-Boot读取内核与设备树启动Linux。Mender要实现A/B分区切换,关键切入点就在U-Boot层,因为它负责决定从哪个根文件系统槽位启动。

这就带来一个明显的问题:常规Jetson的刷机包默认启动流程里没有预留双槽位切换机制,U-Boot环境变量也没有被单独放到一个可以安全读写的分区里。原厂的启动流程为了保证启动安全,有比较强的校验逻辑,要做Mender适配就得找到合适的修改位置。实际做下来,我的建议是先抛开原厂刷机引导工具,完全走Yocto定制链路:在Yocto里引入Jetson BSP层提供的U-Boot配置,再叠加meta-mender-core提供的U-Boot集成补丁,后者会自动把Mender的bootloader代码注入到U-Boot构建中。

这里要特别提醒:如果你选择的Yocto分支和Jetson BSP层的分支不匹配,U-Boot的整合位置会完全不一样,Mender的补丁可能打不上。我在某个分支组合下遇到的编译错误,最终就是靠整体切换到另一套版本组合解决的,排查这个问题花了整整半天,这是我的第一个大坑。

4.2 U-Boot的环境变量与双启动槽位

Mender在U-Boot里维护的环境变量包括:mender_boot_part记录当前激活的根文件系统分区,mender_boot_kernel记录对应的内核镜像名,upgrade_available标记当前是否有待确认的升级。启动时U-Boot脚本读取这些变量,决定加载/dev/mmcblk0p2还是/dev/mmcblk0p3这样的分区。两个rootfs分区交替写入,用于保证“当前在A槽位,把B槽位写入新镜像;下次切换时,在B槽位启动,再将A槽位写新镜”。

这个机制用起来很顺手,但要注意Jetson平台存储介质是NVMe/SD/eMMC的差异。如果用的是SD卡,设备节点通常是mmcblk0,但如果从NVMe启动,节点会变成nvme0n1,Mender的分区设备映射必须和实际设备对应。我在meta-mender配置里通过设备树和内核启动参数搞清楚了这一点,否则系统启动后会因为找不到根文件系统直接崩掉。

再补一个细节:U-Boot环境变量分区的大小。默认的环境变量在Mender里一般占1MB,分区表里必须预留足够空间,因为U-Boot环境变量本身很小,但擦写对齐、冗余备份等都会占用空间。我们第一次分区时给的环境变量分区偏小,U-Boot启动时反复报警环境变量写入失败,后来把分区扩展了才稳定。

4.3 设备端配置文件的要点

Jetson设备启动后,Mender客户端的配置文件位于/etc/mender/mender.conf。这个配置文件在meta-mender中可以通过变量预生成为:

{ "DeviceTypeFile": "/etc/mender/device_type", "ServerURL": "https://mender.example.com", "TenantToken": "eyJhbGciOi...", "RootfsPartA": "/dev/mmcblk0p2", "RootfsPartB": "/dev/mmcblk0p3" }

设备类型文件/etc/mender/device_type里写一行device_type=jetson-ta-01,这样客户端上报身份时服务器才知道该用哪类artifact去匹配。另外,如果你用的时systemd作为init系统,Mender客户端会以mender-client.service的形式启动,默认生产环境是自动启动并持续轮询服务器。在测试阶段,我建议先不让它开机自启,手动运行systemctl start mender-client和journalctl -u mender-client -f观察日志,调试通过后再打开自启。

5. 端到端OTA升级链路与问题排查

5.1 一次完整升级的状态流转

从服务器下发部署开始,Mender客户端会经历以下状态:

  • downloading:客户端下载artifact到未激活的根文件系统分区中;
  • installing:写入完成后,客户端更新U-Boot环境变量中的激活槽位;
  • rebooting:设备重启,U-Boot按新环境变量引导新分区;
  • committing:新系统起来后,客户端运行完整性检查并向服务器确认升级成功;
  • success:服务器端显示部署完成。

这里最核心的机制是:服务器端虽然发了一个部署,但客户端实际是在下载和安装完成后才切换激活槽位的。下载或写入过程中断电,不会影响当前运行槽位,内核启动失败也不会导致系统永久不可用,因为U-Boot会检测到启动失败并尝试从另一个槽位启动。这种“先写备胎,再换备胎”的逻辑,比那种原地覆盖升级要安全得多。

本地验证时,我直接用命令查看客户端当前状态:

mender --version mender commit

注意,如果新系统启动后没有自动commit,Mender客户端会认为此次升级未被确认。在生产使用中,建议依赖客户端默认的自动commit配置,不要手动干预,否则同一槽位在下一次升级时会处于未知状态,导致后续升级失败。

5.2 回滚机制与验证

回滚逻辑取决于两部分:U-Boot层的启动计数与客户端层的commit确认。U-Boot在每一次尝试启动新槽位时会声明“upgrade_available”,设备正常启动后客户端报告服务器并执行commit,一旦commit完成,U-Boot里的升级标记被清除,回滚状态就不复存在。如果新系统起不来,U-Boot超时后自动加载另一个槽位,再通过启动日志就可以看到回滚信息。

我在测试时故意做了一套坏升级:把新镜像的内核模块版本改成不兼容,重启后内核崩溃,U-Boot自动回滚到旧系统。整个过程耗时大约三分钟,设备最终回到旧版本正常运行。对于嵌入式设备,这种容错能力非常关键,因为我们没法在每一台设备旁边放一个刷机工具。

验证回滚时最好的手段是查看U-Boot环境变量和客户端日志:

fw_printenv journalctl -u mender-client -f

如果看到Rollback相关日志,说明Mender已经判定新槽位的启动失败并完成了自动回退。

5.3 常见问题与排查命令速查表

我在整个落地过程中遇到了不少问题,整理成下表,基本覆盖了从环境搭建到设备接入的常见故障:

现象可能原因排查命令与解决方向
bitbake解析meta-mender时报错layer版本与Yocto分支不匹配查看meta-mender的kirkstone分支对应版本,切换仓库分支后重新解析
构建出的镜像启动后根文件系统找不到分区表没有按Mender要求生成检查IMAGE_FSTYPES和MENDER_*相关变量,确认烧录的是mender镜像而不是普通镜像
客户端无法连接服务器ServerURL或TenantToken配置错误查看/var/log/mender-*.log,用curl手动访问服务器API测试连通性
设备反复注册失败设备类型和artifact类型不匹配检查device_type文件与artifact上传时的设备类型是否一致
升级过程提示artifact校验失败artifact文件损坏或下载不完整在服务器端重新上传artifact,对比服务器和客户端的SHA256校验值
U-Boot启动时环境变量写入失败环境变量分区太小扩展分区,重新制作镜像并烧录
新系统启动后客户端没有上报状态自动commit被关闭或客户端服务未自启查看mender-client状态,手动执行mender commit确认

排查这些问题我最大的心得是:先用小范围设备把链路跑通,再批量部署。我们当时先在一台上反复做升级和回滚测试,确认了各种异常路径后,才逐步扩大到其他设备。直接上批量很容易在同一个问题上浪费十几台设备的验证时间。

6. 最后补充一些实操体会

这次从零搭建Mender OTA链路,前后花了两周时间,真正跑通那一刻,我最直观的感受是:OTA本身并不可怕,可怕的是把OTA当成一个“安装包”功能来设计。Mender给我的最大启发在于它把“升级”做成了“系统切换”,从分区、Bootloader到客户端回滚,整个链路都围绕可靠性展开。对那些每天要面对几十台甚至几百台嵌入式Linux设备的团队来说,这套方案值得投入时间去掌握。

我特别想把两个经验再强调一下:第一,Yocto的版本对齐非常关键,不要想着用最新分支省事,最好直接参考meta-mender官方文档里的版本对应表;第二,演示服务器虽然是“演示”环境的定位,但其功能和接口完全是生产级的,先在这上面做完整的验证,再决定是否切换到托管版或自建生产集群,这个迭代路径不会白走。

如果你正在做类似的设备远程升级方案,建议先按这条路线走一遍:Yocto集成meta-mender、本地部署Docker演示服务器、Jetson上完成首刷与客户端注册,再做一次模拟坏升试验证回滚。整套流程走完后,你对OTA的认知会完全不同。希望这篇总结对你的项目有参考价值,后续有新的陷坑和心得,我再继续补充。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 7:21:34

转行大模型没头绪?算法岗与应用岗的选型与落地路径解析

1. 先放下"大模型很火所以我能转"这个念头1.1 被问过太多次"要不要转"之后,我发现大家问的不是一个问题过去大半年,我被问过至少二十次类似的问题:"大模型现在这么热,我还在做传统后端/数据分析/产品经理…

作者头像 李华
网站建设 2026/10/21 7:21:31

Linux --- journal

journal 是 Linux 中由 systemd-journald 服务收集和保存的系统日志。 可以把它理解为一个统一的“系统事件记录库”。journal : 日记、日志它记录的内容通常包括:内核事件:USB 设备连接、断开、驱动报错等。系统服务:服务启动、停止和运行错…

作者头像 李华
网站建设 2026/10/21 7:20:12

大模型技术链路全拆解:从数据到评测,附高效收藏法

聊到“大模型技术链路”,很多刚接触这个方向的朋友第一反应是:这不就是调库、调接口、再套一层问答逻辑吗?实际在企业里跑通一个真正能上线的大模型产品,是一条贯穿数据加工、模型训练、推理部署、应用工程与效果评测的长链路。大…

作者头像 李华
网站建设 2026/10/21 7:19:47

Qwen2指令微调实战:数据准备、LoRA配置与避坑指南

如果你最近在折腾开源大模型,应该已经注意到,Qwen2 这个名字的出场率越来越高。它不是一个单一模型,而是一整个系列:既有能跑在消费级显卡上的小尺寸版本,也有能力接近头部商用模型的大参数版本。而“指令微调”这四个…

作者头像 李华
网站建设 2026/10/21 7:19:37

AutoDataBench:LLM数据智能评估基准与工程实践指南

1. 从“喂数据”到“造数据”:为什么数据智能需要专属基准做过大模型训练的人都有一个共同感受:模型架构和训练框架的迭代速度飞快,但真正卡住效果的,往往不是模型本身,而是数据。同样一个7B模型,换一批清洗…

作者头像 李华
网站建设 2026/10/21 7:19:09

2018年10月自考《计算机网络原理》04741真题拆解:结构与考点全解析

2018年10月全国自考《计算机网络原理》(04741)这套真题,我前前后后给三届备考的同学拆解过。很多人一开始都是同一个反应:这门课是不是把教材从头到尾背下来就行了?等他们做完一遍卷子才发现,纯靠背很难拿高…

作者头像 李华