在ODYSSEY-X86上集成Mender实现工业边缘设备OTA更新与双分区管理

📅 2026/8/1 17:50:51 👁️ 阅读次数 📝 编程学习
在ODYSSEY-X86上集成Mender实现工业边缘设备OTA更新与双分区管理

1. 项目概述与核心价值

最近在折腾一个工业边缘计算的项目,硬件选型落在了ODYSSEY - X86这块板子上。这板子性能不错,x86架构兼容性好,但部署到几十个甚至上百个分散的现场节点后,一个头疼的问题就来了:怎么高效、可靠地进行远程固件更新和系统管理?总不能每次都派人跑到现场插U盘吧。这时候,一个专业的OTA(空中下载技术)解决方案就成了刚需。在众多方案里,Mender以其开源、对嵌入式Linux的深度支持和对生产环境的严谨设计脱颖而出。它不仅仅是一个简单的文件传输工具,更是一套完整的设备管理框架,支持原子化的A/B分区更新、回滚机制和状态上报,这对于保障工业设备的稳定运行至关重要。

简单来说,这个项目就是在ODYSSEY - X86这个硬件平台上,部署Mender客户端,将其纳入Mender服务器的管理体系。这样一来,我们就能在中心服务器上,对分布在全国各地机柜里的ODYSSEY设备进行批量、安全的系统镜像更新、应用部署和状态监控。对于从事物联网、边缘计算、工业自动化的开发者或运维工程师来说,掌握这套流程,意味着能极大地提升设备生命周期的管理效率,降低运维成本,是项目从原型走向规模化部署的关键一步。下面,我就把自己从环境准备到客户端集成的完整过程,以及踩过的坑和总结的经验,详细拆解一遍。

2. 整体方案设计与环境准备

2.1 方案选型与核心组件解析

为什么选择Mender?市面上OTA方案不少,有的简单粗暴用scp+脚本,有的用容器化方案。但对于需要高可靠性的嵌入式或边缘设备,更新过程的“原子性”和“可回滚”是生命线。Mender的核心设计基于双分区(A/B分区)更新:设备同时存在两个完整的系统分区(例如rootfsArootfsB)。更新时,新镜像被写入非活动分区,验证成功后,仅需重启并切换引导指针(如U-Boot的bootcount或GRUB的默认项)即可激活新系统。如果新系统启动失败,设备会自动回滚到旧分区,业务中断时间极短,风险可控。

整个Mender体系包含两个核心部分:

  1. Mender服务器:提供设备认证、部署管理、更新包分发和日志收集的云端或本地服务。对于生产环境,通常需要部署Mender的服务器端(开源版或企业版)。
  2. Mender客户端:运行在设备上的守护进程(mender-client)。它负责与服务器通信,下载更新,执行更新脚本,并管理本地的A/B分区切换。

我们这个项目聚焦在客户端的安装与集成。前提是你已经有一个可用的Mender服务器(可以是Mender提供的云端服务,也可以是自建的On-Premise版本)。客户端的安装不是简单地apt-get install,它需要与你的系统镜像深度集成,特别是在分区布局和引导加载器配置上。

2.2 ODYSSEY - X86硬件与基础系统确认

ODYSSEY - X86是一款基于Intel Celeron J4125的迷你主机板,兼容标准的x86_64架构。这带来了便利(软件生态丰富),也带来了挑战(分区表、引导方式多样)。在开始前,必须明确以下几点:

  • 系统镜像:你正在运行或准备烧录的系统是什么?是Ubuntu Server 22.04,还是Debian 11,或是基于Yocto Project构建的自定义发行版?不同的发行版,集成Mender客户端的步骤有差异。本文将以Ubuntu Server 22.04 LTS为例,因为它常见且文档丰富。
  • 磁盘分区布局:这是集成Mender最关键的一步。你必须确认你的磁盘(通常是/dev/sda/dev/nvme0n1)是否已经配置为A/B双分区布局。标准的Ubuntu Server安装通常是单分区。我们需要将其改造为双分区。
  • 引导加载器:ODYSSEY - X86通常使用GRUB 2作为引导加载器。Mender需要与GRUB配合来管理启动项和实现回滚。

注意:在生产环境中,建议直接从构建系统镜像的阶段就集成Mender,例如使用Yocto Project的meta-mender层。但本文描述的“在已运行系统上安装”,更适合原型验证、存量设备改造或基于标准发行版快速搭建测试环境的场景。

2.3 工具与依赖准备

在开始操作前,确保你的ODYSSEY - X86已经联网,并且拥有sudo权限。我们需要安装一些必要的工具。

# 更新包列表并安装关键工具 sudo apt update sudo apt install -y curl wget u-boot-tools grub-efi-amd64-bin parted gdisk
  • partedgdisk:用于操作磁盘分区表。
  • u-boot-tools:虽然我们是GRUB,但某些工具(如fw_printenv)可能有用,且安装无害。
  • grub-efi-amd64-bin:确保GRUB EFI工具完整。

3. 磁盘分区重构为A/B布局

这是整个流程中最需要谨慎操作的一步。操作失误会导致数据丢失。强烈建议在操作前对重要数据进行完整备份,并在非生产设备上先行测试。

假设我们的系统盘是/dev/sda,当前是单根分区布局。目标是将其改为类似下面的结构:

分区大小文件系统挂载点用途
/dev/sda1512 MBFAT32/boot/efiEFI系统分区
/dev/sda21 GBext4/boot内核与GRUB配置
/dev/sda3剩余空间一半ext4/(临时)系统分区A (rootfsA)
/dev/sda4剩余空间一半ext4(不挂载)系统分区B (rootfsB)

3.1 分析现有分区

首先,使用lsblksudo fdisk -l /dev/sda查看当前分区情况。

sudo fdisk -l /dev/sda

假设输出显示已有/dev/sda1(EFI分区)、/dev/sda2(根分区)。我们需要缩小现有的根分区,为rootfsB腾出空间。

3.2 使用parted调整分区

这是一个高风险操作。我们计划:

  1. 删除原有根分区(/dev/sda2)。
  2. 在相同起始位置,创建两个新的、更小的ext4分区(sda2sda3),分别作为rootfsArootfsB
  3. 确保/dev/sda1(EFI分区)保持不变。

操作前,请再次确认设备符和分区号!以下命令以/dev/sda为例。

# 进入parted交互模式 sudo parted /dev/sda # 在parted中,打印当前分区表,记下根分区(例如/dev/sda2)的起始扇区(Start) print # 删除根分区(假设是2号分区) rm 2 # 创建第一个新根分区 (rootfsA)。假设原sda2起始于1024MB,我们将其结束于磁盘50%的位置。 # 计算大小:如果磁盘总大小是64GB,EFI分区占512MB,那么两个根分区各占约31.75GB。 # 使用单位MB进行计算更直观。 # 命令格式:mkpart [分区类型] [文件系统类型] 起始点 结束点 mkpart primary ext4 1024MB 50% # 创建第二个新根分区 (rootfsB),占用剩余50%空间 mkpart primary ext4 50% 100% # 设置分区标签(可选,但有助于识别) name 2 rootfsA name 3 rootfsB # 打印确认新分区表 print # 退出parted quit

退出parted后,需要让内核重新读取分区表。

sudo partprobe /dev/sda

3.3 创建文件系统并迁移数据

现在,我们在新的/dev/sda2(rootfsA)上创建文件系统,并将当前运行系统的数据复制过去。

# 在/dev/sda2上创建ext4文件系统 sudo mkfs.ext4 /dev/sda2 # 临时挂载新的rootfsA分区 sudo mkdir -p /mnt/rootfsA sudo mount /dev/sda2 /mnt/rootfsA # 使用rsync同步当前根文件系统到新分区,排除一些特殊目录 sudo rsync -aAXv --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found","/boot/*"} / /mnt/rootfsA/ # 创建必要的目录(因为被排除了) sudo mkdir -p /mnt/rootfsA/{boot,dev,proc,sys,tmp,run,mnt,media} # 卸载分区 sudo umount /mnt/rootfsA

接下来,格式化/dev/sda3作为rootfsB备用。

sudo mkfs.ext4 /dev/sda3

3.4 更新GRUB配置与fstab

当前系统仍然从旧的根分区启动。我们需要修改GRUB配置,使其指向新的/dev/sda2,并更新/etc/fstab

首先,检查新分区/dev/sda2的UUID。

sudo blkid /dev/sda2

输出类似:/dev/sda2: UUID="a1b2c3d4-5678-..." TYPE="ext4"

记下这个UUID。然后,编辑当前系统的/etc/fstab文件。

sudo nano /etc/fstab

找到原来根分区(可能是/dev/sda2旧UUID)的挂载行,将其替换为新的UUID。例如:

# 将原来的行替换为 UUID=a1b2c3d4-5678-... / ext4 defaults,errors=remount-ro 0 1

同时,确保/boot/efi/boot的分区UUID正确。

接下来,更新GRUB配置并重新安装到磁盘。

# 更新grub配置,使其识别新的根分区 sudo update-grub # 将GRUB引导程序安装到磁盘(/dev/sda) sudo grub-install /dev/sda # 再次生成最终的grub.cfg sudo update-grub

操作完成后,重启系统。重启后,系统应该从新的/dev/sda2(rootfsA)启动。你可以通过df -hlsblk -f命令确认根分区已经切换到新的/dev/sda2

4. 安装与配置Mender客户端

系统成功从新的A/B分区启动后,我们就可以正式安装Mender客户端了。

4.1 添加Mender仓库并安装

Mender为Debian/Ubuntu提供了官方APT仓库。

# 下载并添加Mender的GPG密钥 curl -fLsS https://downloads.mender.io/repos/debian/gpg | sudo gpg --dearmor | sudo tee /usr/share/keyrings/mender-archive-keyring.gpg > /dev/null # 添加APT仓库(对应Ubuntu 22.04 Jammy) echo "deb [signed-by=/usr/share/keyrings/mender-archive-keyring.gpg] https://downloads.mender.io/repos/debian stable jammy main" | sudo tee /etc/apt/sources.list.d/mender.list # 更新包列表并安装Mender客户端 sudo apt update sudo apt install -y mender-client

安装过程会自动创建一个mender用户和mender组,并启动mender-client服务。

4.2 关键配置解析

Mender客户端的核心配置文件是/etc/mender/mender.conf。安装后,我们需要根据实际情况修改它。另一个重要文件是/var/lib/mender/device_type,它定义了设备的类型。

1. 配置设备类型 (device_type)设备类型是服务器识别和管理设备分组的依据。

# 通常,我们可以设置为硬件型号加用途的组合 echo "odyssey-x86-ubuntu" | sudo tee /var/lib/mender/device_type

2. 编辑主配置文件 (mender.conf)

sudo nano /etc/mender/mender.conf

一个最基础的、指向Mender官方演示服务器的配置示例如下(用于测试)。对于生产环境,你需要将其中的ServerURLTenantToken替换为你自己的服务器信息。

{ "InventoryPollIntervalSeconds": 300, "RetryPollIntervalSeconds": 30, "RootfsPartA": "/dev/sda2", "RootfsPartB": "/dev/sda3", "ServerCertificate": "/etc/mender/server.crt", "ServerURL": "https://hosted.mender.io", "TenantToken": "你的设备令牌", "UpdatePollIntervalSeconds": 1800, "ClientProtocol": "https" }
  • RootfsPartARootfsPartB:这就是我们之前创建的两个分区。务必确认分区设备符正确。
  • ServerURL:你的Mender服务器地址。如果是自建,类似https://your-mender-server.com
  • TenantToken:这是设备接入服务器的“通行证”。在Mender服务器UI中,当你创建设备组时,会提供这个Token。这是必填项,否则客户端无法认证。
  • ServerCertificate:如果使用自签名证书的服务器,需要将服务器的CA证书放到这个路径。对于hosted.mender.io,可以留空或使用其提供的证书。

3. 配置引导加载器集成 (GRUB)Mender需要知道当前从哪个分区启动,以及如何切换分区。这通过一个引导环境变量来实现。在U-Boot中常用bootcount,在GRUB中,我们通常使用一个文本文件来模拟此功能。

创建Mender的GRUB环境配置文件:

sudo nano /etc/default/mender-grubenv.cfg

内容如下:

# 指定存储引导环境变量的文件路径 GRUB_ENV_FILE=/boot/efi/mender_grubenv # 指定标识分区的变量名 PARTITION_A_ROOTFS=rootfsA PARTITION_B_ROOTFS=rootfsB # 当前启动的分区变量名 BOOT_PARTITION=mender_boot_part

然后,运行Mender提供的脚本来安装GRUB集成:

# 这个脚本会修改GRUB配置,添加必要的内核参数和环境变量处理逻辑 sudo /usr/share/mender/install-grub-integration

脚本执行后,它会提示你更新GRUB配置。

sudo update-grub

4.3 客户端启动与服务器对接

完成配置后,重启Mender客户端服务,并检查其状态和日志。

sudo systemctl restart mender-client sudo systemctl status mender-client sudo journalctl -u mender-client -f

查看日志,关注是否有连接服务器、认证成功的消息。如果出现证书错误或连接失败,需要检查网络、ServerURLTenantToken

在Mender服务器的Web UI中,你应该很快能看到一个名为odyssey-x86-ubuntu(或你设置的device_type)的新设备上线,状态为pending。你需要在服务器端将其Accept(接受),设备状态变为accepted后,才能向其部署更新。

5. 制作与部署Mender更新包

客户端就绪后,下一步是制作一个能通过Mender部署的更新包(.mender文件)。这通常需要一个构建环境。

5.1 更新包构成解析

一个Mender更新包本质是一个包含以下内容的tar归档文件:

  • header.tar.gz: 包含更新包的元数据(header-info),如格式版本、压缩算法等。
  • data.tar.gz: 包含实际的根文件系统镜像(如rootfs.img)或单个文件/目录。
  • manifest: 包含header.tar.gzdata.tar.gz的SHA256校验和。
  • version: (可选)版本信息。

对于完整的系统更新,data.tar.gz里就是一个完整的ext4格式的磁盘镜像文件。

5.2 使用mender-artifact工具

Mender提供了命令行工具mender-artifact来创建和操作更新包。我们需要在另一台开发机(而非ODYSSEY设备本身)上安装它。

# 在Ubuntu开发机上安装mender-artifact curl -fLsS https://downloads.mender.io/repos/debian/gpg | sudo gpg --dearmor | sudo tee /usr/share/keyrings/mender-archive-keyring.gpg > /dev/null echo "deb [signed-by=/usr/share/keyrings/mender-archive-keyring.gpg] https://downloads.mender.io/repos/debian stable jammy main" | sudo tee /etc/apt/sources.list.d/mender.list sudo apt update sudo apt install -y mender-artifact

5.3 创建简单文件更新包(示例)

假设我们只想更新设备上的一个配置文件/etc/myapp/config.yaml

首先,准备更新的文件内容,并创建一个work目录。

mkdir mender-update && cd mender-update mkdir -p rootfs/etc/myapp # 将你的新config.yaml文件放入 rootfs/etc/myapp/ echo "new_config: value" > rootfs/etc/myapp/config.yaml

然后,使用mender-artifact创建更新包。你需要指定设备类型(必须与客户端device_type匹配)、更新名称和版本。

mender-artifact write module-image \ -t odyssey-x86-ubuntu \ # 设备类型 -n update-config-v2 \ # 更新名称 -v 2 \ # 版本号 -s \ # 使用提供的软件密钥对签名(需提前生成) -k private.key \ # 私钥路径 -o config-update.mender \ # 输出文件名 -f rootfs # 包含更新文件的目录

这个命令会生成一个签名的config-update.mender文件。签名是可选的,但生产环境强烈建议使用,以确保更新包来源可信。

5.4 部署更新与监控

将生成的.mender文件上传到你的Mender服务器。在服务器UI中:

  1. 进入RELEASES页面,上传该文件。
  2. 进入DEPLOYMENTS页面,创建新的部署(Deployment)。
  3. 选择刚上传的Release,并选择目标设备(或设备组)。
  4. 开始部署。

回到ODYSSEY设备,查看Mender客户端日志。

sudo journalctl -u mender-client -f

你会看到客户端轮询到新部署、下载更新包、进行校验、执行更新脚本(如果有)、然后等待重启的过程。Mender客户端不会自动重启,它会在日志中提示“Update applied successfully. Needs reboot to activate.”。

此时,你可以手动重启设备。

sudo reboot

重启后,观察系统是否从另一个分区(rootfsB)启动(可以通过查看/etc/mender/下的文件或mender -show-artifact判断),并检查/etc/myapp/config.yaml文件是否已更新。在Mender服务器UI上,该部署的状态会变为“成功”。

6. 常见问题与深度排查指南

在实际操作中,你几乎一定会遇到各种问题。这里记录了几个最典型的坑和解决思路。

6.1 客户端无法连接服务器

  • 症状journalctl日志中持续出现连接超时、认证失败或TLS错误。
  • 排查步骤
    1. 网络连通性:在设备上curl -v https://your-mender-server.com,看是否能通。
    2. 配置检查:仔细核对/etc/mender/mender.conf中的ServerURLTenantToken,确保没有多余空格或换行。Token通常很长,复制粘贴容易出错。
    3. 证书问题:如果服务器使用自签名证书,必须将CA证书放到ServerCertificate指定的路径(如/etc/mender/server.crt),并确保文件权限正确(644)。日志中明确的TLS错误信息是关键线索。
    4. 服务器防火墙:确保服务器的443端口对设备开放。

6.2 更新后设备“变砖”或循环重启

  • 症状:部署更新后重启,设备无法进入系统,或不断在A/B分区间切换。
  • 根本原因:通常是引导加载器配置分区标识不正确。
  • 排查与修复
    1. GRUB环境变量:检查/boot/efi/mender_grubenv文件是否存在且内容正确。它应该包含mender_boot_part变量,值为rootfsArootfsB。可以在GRUB引导菜单编辑模式中,临时修改linux行,添加root=/dev/sdaX来指定从特定分区启动,以进入系统。
    2. 分区UUID:确保/etc/fstab中根分区的UUID与当前启动分区的实际UUID一致。如果更新包里的fstab写错了UUID,就会挂载失败。
    3. 内核参数:Mender的GRUB集成脚本会在/etc/default/grub中添加GRUB_CMDLINE_LINUX变量,包含rootwaitrootfstype=ext4等。确保这些参数正确,并且update-grub已成功执行。
    4. 回滚机制:Mender客户端在更新失败后,理论上应自动回滚。如果连回滚都失败,可能需要通过串口或恢复模式,手动使用grub-editenv或修改U-Boot环境变量来强制切换回之前的分区。

6.3 更新包制作失败或部署状态异常

  • 症状mender-artifact命令报错,或服务器显示更新包“损坏”、“格式错误”。
  • 排查
    1. 设备类型不匹配:更新包指定的-t参数必须与设备上的device_type完全一致,包括大小写。
    2. 签名错误:如果使用了签名,确保服务器端配置了对应的公钥,且私钥在制作更新包时使用正确。
    3. 压缩格式:确保header.tar.gzdata.tar.gz使用兼容的压缩算法。旧版客户端可能不支持某些新算法。
    4. Artifact格式版本:使用mender-artifact version查看工具版本,确保与服务器和客户端版本兼容。使用mender-artifact write时,可以指定-f格式版本(如-f 3)。

6.4 磁盘空间不足

  • 症状:更新下载或解压失败,日志提示“No space left on device”。
  • 分析:A/B分区方案意味着你需要至少两倍于根文件系统的磁盘空间。如果原始分区规划时rootfsArootfsB空间分配过紧,系统日志、Docker镜像等运行时数据增长可能导致活跃分区空间不足。
  • 解决方案:在规划阶段就为根分区预留充足余量(例如,只使用磁盘总容量的40%作为每个rootfs分区的大小)。对于已部署的设备,可以通过制作一个精简化的新系统镜像进行更新,或者考虑使用Mender的“增量更新”功能(如果支持)。

6.5 自定义更新脚本的执行问题

Mender支持在更新前后执行自定义脚本(Artifact中的scripts目录)。常见问题包括脚本没有执行权限、脚本中使用了绝对路径错误、脚本执行超时或返回非零退出码导致更新失败。

  • 调试技巧:在测试更新包时,可以在脚本中加入大量的echo语句输出到文件(如/tmp/mender-script.log),以便在更新失败后查看执行到了哪一步。确保脚本开头有#!/bin/sh,并且是Unix格式(LF换行符)。

7. 生产环境进阶考量与优化建议

将Mender用于原型测试和用于成百上千台的生产设备,关注点完全不同。以下是一些进阶建议:

1. 分阶段部署(Canary Release):永远不要一次性对所有设备进行更新。先在少量(如5%)设备上部署,监控其运行状态(通过Mender的设备数据或你自己的监控系统)24-48小时,确认无误后再逐步扩大范围。Mender服务器支持创建设备组并分批部署。

2. 完备的监控与告警:将Mender服务器的部署状态集成到你的运维监控系统(如Prometheus+Grafana,或直接使用Mender的企业版监控功能)。关注部署失败率、设备离线率等关键指标。设置告警,当部署失败超过阈值时及时通知。

3. 更新包的安全与验证: *强制签名:生产环境必须使用密钥对更新包进行签名,并在服务器端验证。 *完整性校验:依赖Mender内置的SHA256校验。 *版本控制:建立清晰的Artifact版本命名规范(如odyssey-x86-ubuntu-v2.1.5)。

4. 网络与带宽优化: *边缘网关:如果设备数量庞大且分布广,考虑在区域中心部署Mender的边缘网关(Mender Gateway),让本地设备从网关拉取更新,减轻中心服务器压力和跨地域带宽消耗。 *差分更新:对于频繁的小更新,研究使用Mender的模块化更新或第三方工具生成二进制差分包,大幅减少传输数据量。

5. 设备生命周期管理:Mender不仅是更新工具。利用其库存功能(inventory),定期收集设备硬件信息、软件版本、网络状态等。编写自定义库存脚本,上报业务相关的指标,实现更精细化的设备管理。

在ODYSSEY - X86上成功集成Mender客户端,只是构建可靠物联网更新管道的起点。这套组合的真正威力,在于它为你提供了一个符合工业标准的、自动化的、安全的设备管理基石。后续你可以在此基础上,构建更复杂的更新策略、更丰富的设备监控以及更深度的业务集成。整个过程虽然涉及不少底层操作,但每一步的清晰理解和谨慎实践,都将直接转化为生产系统稳定性的提升。