拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

openEuler Embedded新增软件包:离线与在线安装全流程实战

openEuler Embedded新增软件包:离线与在线安装全流程实战

做嵌入式Linux开发的人,十有八九都被“装个软件”这件事恶心过。通用Linux上一条dnf install就能解决的事,在嵌入式板子上经常变成一场灾难。我最近在 openEuler Embedded 上折腾新增软件包,把在线安装和离线安装的路子都试了一遍,踩了不少坑,也把真正能跑通的流程梳理了出来。这篇文章就专门聊聊 openEuler Embedded 新增软件包和在线安装软件这件事,适合正在用或者准备用 openEuler Embedded 做产品开发的工程师参考,尤其适合那些被“无法定位软件包”“软件包似乎无效”这类报错折磨过的朋友。

先说结论:openEuler Embedded 和普通 Linux 发行版的包管理思路差异很大,你没法把它当成 mini 版 CentOS 来用。镜像默认做了大量裁剪,很多通用发行版自带的工具在这里不存在,软件源也不像服务器版那么全。想“新增软件包”,你得先搞清楚系统当前的架构、版本、包管理工具状态,再决定走“离线 RPM”“本地源”还是“在线 dnf”哪条路。这篇文章会把每条路线的原理、步骤、坑点一次讲透。

1. 先搞清楚问题出在哪:openEuler Embedded 的包管理背景

1.1 它和通用 Linux 最大的区别:裁剪、只读、架构差异

openEuler Embedded 是面向嵌入式场景的发行版,虽然继承了 openEuler 的 RPM 体系,但它和服务器版、桌面版完全是两回事。嵌入式系统为了控制镜像体积、减少攻击面、保证启动速度,rootfs 通常是深度裁剪过的,很多命令、库、工具链都不会预装。你和通用 Linux 的习惯性操作,在这里要么找不到命令,要么能跑但一运行就缺库。

比裁剪更麻烦的是只读文件系统。不少 openEuler Embedded 镜像是把根文件系统做成只读或者 overlayfs 的形态,你辛辛苦苦装好一个软件,重启之后发现“回到了解放前”。这不是软件包本身有问题,而是系统的存储布局设计成“重启还原”,你得先搞清楚/的挂载方式,再决定怎么持久化安装结果。

另外还有架构差异。开发机绝大多数是 x86_64,而 openEuler Embedded 的常见目标平台是 aarch64(ARM64)和 riscv64。你在 x86 服务器上dnf install习惯了,拿到板子上手就装,十有八九会碰上“架构不匹配”的报错。这不是软件不存在,而是你选的包根本不是这个 CPU 能跑的。

1.2 包管理工具链现状:rpm、dnf 可能都不完整

openEuler Embedded 继承了 openEuler 的 RPM 体系,但“继承”不等于“完整”。我实际遇到的情况是:镜像里rpm命令经常是有的,但dnf不一定带,yum基本别指望。这就带来一个很现实的问题——rpm本身不会自动解析依赖,没有dnf就缺少“在线安装”的通道。

登录板子之后,我建议你立刻跑下面三组命令,把家底摸清楚:

which rpm dnf yum ls /etc/yum.repos.d/ 2>/dev/null || ls /etc/dnf/ 2>/dev/null cat /etc/os-release

如果which输出里只有 rpm,没有 dnf,那你别急着想在线安装,先把“离线 RPM”和“构建新包”这两条路走通。如果 dnf 也在,那后面的在线源方案才真正有戏。无论哪条路,都要先确认系统大版本,因为官方源是按openEuler-22.03-LTS、openEuler-23.09这类版本组织目录的。

1.3 新增软件包的四种路径,先看清再动手

我把 openEuler Embedded 上新增软件包的方法总结为四条路径,你可以按实际环境选:

路径适用场景依赖处理复杂度
离线 RPM 安装临时应急、网络隔离、包数量少手动处理或提前拉取依赖低
本地 repo 源(伪在线)内网批量部署、多台板子配置 dnf 自动解析中
在线 repo 源(dnf install)能访问外网、官方源有现成包dnf 自动解析中
SDK/bitbake 构建官方源没有、需要自定义编译构建时自动处理高

这个表格是我实际选型的心得。很多人一上来就想“在线安装”,结果发现源里没有想要的包;也有很多人喜欢直接rpm -ivh硬刚,结果被依赖折磨到怀疑人生。正确的思路是:先看官方源里有没有,有就尽量用 dnf 在线装;没有但有现成 rpm,就离线装;都没有,老老实实去构建系统里现造一个。下面我把每条路径逐一展开。

2. 准备工作:确认环境、锁对软件包来源

2.1 登录板子后第一时间要跑的检查命令

不要一拿到板子就想着装软件,先花两分钟做环境体检。我每次拿到新板子,都会固定执行一套命令,后面能少踩一半的坑。

uname -m # 查看 CPU 架构,找对 rpm 包 cat /etc/os-release # 查看系统大版本,决定用哪个源 df -h / # 确认根分区剩余空间 cat /proc/mounts | grep " / " # 看根文件系统是不是只读/overlay free -m # 确认内存足够,避免大软件装完运行直接 OOM

为什么要看前两个?因为嵌入式镜像的架构和版本决定了你所有后续动作。如果你在 aarch64 的板子上装了一个 x86_64 的 rpm,rpm -ivh可能会直接拒绝,也可能装进去之后一运行就是Exec format error。版本不匹配更隐蔽,装进去大概率碰到.so版本冲突,查起来非常费劲。

至于第三、四条,是我吃过亏之后才养成的习惯。有块板子的根分区只有 400MB,缓存一满,dnf install装到一半就报磁盘空间不足,还把系统搞到半残。遇到这种板子,优先清理dnf clean all,或者先把软件包下到内存挂载点、确认能装再往系统里放。

2.2 openEuler Embedded 的软件仓库长什么样

openEuler Embedded 的软件包并不是随便找个 openEuler 源就能装的。官方把嵌入式场景的软件包单独放在了 repo.openeuler.org 站点下,结构上通常按“大版本 / embedded_apps / 架构 / Packages”这样的路径组织。比如 22.03 LTS 的 aarch64 嵌入式软件源,路径大概长这样:

http://repo.openeuler.org/openEuler-22.03-LTS/embedded_apps/aarch64/

注意版本号一定要和板子cat /etc/os-release里看到的大版本对齐,架构目录也要选对。这里有个特别容易犯的错误:看到 openEuler 官方源里有很多包,就把标准服务器版的 repo 配到嵌入式板子上。这通常不是你想要的,因为服务器版软件的依赖链路和嵌入式裁剪版不一致,装了很容易把系统搞坏。

嵌入式专用仓库里的包是为裁剪环境重新处理过的,依赖相对干净,针对目标架构(尤其是 aarch64 和 riscv64)也有现成的编译产物。所以在开始之前,去 repo.openeuler.org 看一眼有没有和你系统版本、架构完全匹配的 embedded 目录,这决定你后面是“直接在线装”还是“离线下载”。

2.3 下载软件包前必须确认的三个元信息

不管走哪条路,你在下载/安装一个 rpm 之前,都要先看三个东西:架构(Arch)、版本(Version)、依赖(Requires)。

  • 架构:通过uname -m确认,aarch64 对应 ARM64,riscv64 对应 RISC-V,别下载错。
  • 版本:尽量匹配系统大版本,比如系统是 22.03 LTS,就优先找 22.03 系列的包。
  • 依赖:用rpm -qpR查看这个包依赖哪些.so和子包,提前把依赖链拉齐。

如果你手头已经有一个 rpm 文件,在没安装之前就能这样检查:

rpm -qip xxx.rpm # 查看基本信息和架构 rpm -qpR xxx.rpm # 查看依赖列表 rpm -qp --scripts xxx.rpm # 查看安装前/后脚本,这个尤其重要

--scripts是我强烈推荐你养成的习惯。嵌入式系统裁剪很狠,rpm脚本里常用的useradd、install、ldconfig等命令不一定存在,脚本一旦失败,安装就会报“post-installation 脚本 子进程返回错误状态”之类的错误。提前看一眼,能预判很多问题。

3. 实操一:离线 RPM 安装——最直接,但也最容易翻车

3.1 下载 rpm 包的几种姿势

如果你确定系统里暂时没有可用的在线源,只能靠离线 rpm,那第一步是找包。我按优先级列出几种方式:

  • 官方 repo 站点手动下载:打开 repo.openeuler.org 对应版本和架构的 embedded_apps 目录,按名字找包,wget下载。
  • 在板子上用 dnf 拉包:如果板子上有 dnf,哪怕还没配好源,也可以在后文配好源之后执行dnf install --downloadonly --downloaddir=/tmp xxx,把包和依赖一起拉到本地目录。
  • 在开发机上用 repoquery 检查依赖:如果开发机是 openEuler/CentOS 系列,可以安装dnf-utils或createrepo_c相关工具,配合仓库查询依赖。

这里有个典型错误:只下载主包,不下载依赖。rpm -ivh时系统会告诉你缺哪个.so,你再去下一个包,装完又告诉你缺另一个库,一层套一层,最后“补依赖补到怀疑人生”。正确做法是下载之前先用rpm -qpR把依赖列清楚,一次性把需要的 rpm 都找齐。

3.2 rpm -ivh 之前,先给你的包做“体检”

把 rpm 通过scp、U 盘、TFTP 等方式传到板子后,不要着急安装。先执行一遍我在 2.3 节说的检查命令,确认架构、版本、依赖、脚本都正常。一切 OK 之后,再正式安装:

rpm -ivh tree-2.0.0-1.aarch64.rpm

安装完成后,用这三条命令确认状态:

rpm -q tree # 查询是否安装成功 rpm -ql tree # 查看文件都装到了哪里 which tree && tree /tmp # 实际跑一下,验证二进制能否正常工作

很多人在rpm -q显示已安装后就不管了,结果一执行就报error while loading shared libraries。这说明依赖库缺失,rpm安装本身没问题,但运行环境不完整。判断“有没有装上”和“能不能用”是两码事。

3.3 大坑:依赖问题的三种处理原则

依赖问题离线安装中避不开,我总结出三条处理原则:

第一,能一次性把依赖都下载齐,就别裸着用rpm。第二,必须--nodeps时,装完后立刻用ldconfig -v检查动态库情况,缺库赶紧补。第三,永远不要在嵌入式系统里用--force强行覆盖核心包,比如 glibc、openssl、systemd 这一类,一旦覆盖,shell 可能当场崩掉,甚至板子直接起不来。

如果你实在找不到某个依赖,可以用rpm -qpR看一下它具体依赖哪个库文件,再去 openEuler 源里搜索对应库的 rpm。比如报缺少libcrypto.so.3(),那大概率需要补装openssl-libs或对应版本包。这种“按库找包”的思路,在嵌入式环境里比按软件名找包可靠得多。

4. 实操二:配置本地源,实现“伪在线”安装

4.1 在服务器上建本地仓库,一步到位

离线 RPM 装几个包还行,如果板上要装十几个软件、依赖关系错综复杂,手动处理依赖会痛不欲生。这时候我推荐搭建一个本地 repo 源,让 dnf 像在线源一样自动解析依赖。

做法很简单。在一台 Linux 服务器上建目录,把需要的 rpm 全部放进来,然后执行:

mkdir -p /srv/rpm/openeuler-embedded cp *.rpm /srv/rpm/openeuler-embedded/ cd /srv/rpm/openeuler-embedded createrepo .

如果服务器上没有createrepo命令,在 openEuler 系列系统上可以用dnf install createrepo_c安装,本质一样。执行完会生成一个repodata目录,这就是 dnf 识别仓库的依据。

4.2 板子上写 repo 文件,把源指过来

仓库建好之后,把整个目录通过 NFS、HTTP 或直接拷贝的方式让板子能访问到。最简单的是在板子上写一个 repo 文件:

mkdir -p /etc/yum.repos.d cat > /etc/yum.repos.d/openeuler-embedded.repo <<'EOF' [embedded-local] name=openEuler Embedded Local Repo baseurl=file:///srv/rpm/openeuler-embedded enabled=1 gpgcheck=0 EOF

如果板子和服务器在同一内网,也可以用 HTTP 方式,nginx 或python3 -m http.server起一个静态服务,baseurl写成http://服务器IP:8000。关键在于gpgcheck=0,本地源通常没有对应的公钥,先保证能装上,签名校验等生产环境再严格处理。

配好之后在板子上执行:

dnf clean all dnf makecache dnf list available | head

能看到包列表,说明本地源已经生效。之后你就可以像在线安装一样执行dnf install xxx了。

4.3 为什么说本地源在嵌入式环境里最实用

我实际部署过几十块板子,深有体会:很多嵌入式项目根本不允许设备连接公网,所以“在线源”方案在这种环境里就是个摆设。但你又不能靠逐个rpm -ivh装几十个包,依赖问题会写到崩溃。本地源正好卡在两者中间:从有网络的机器上把官方仓库的嵌入式软件目录整个同步下来,或者只同步你要的几十个包,然后在隔离内网里搭一个包服务器,所有板子统一配这个源。维护起来也很顺手,新包只要丢到仓库目录、重新createrepo,板子上dnf update就能看到。

注意:本地源方案的前提是板子上有dnf。如果镜像连 dnf 都没有,优先考虑用 SDK 构建一个带 dnf 的镜像,否则本地源对rpm命令帮助有限。

5. 实操三:配置在线源,真正实现 dnf install

5.1 找对官方 embedded 仓库地址

在线安装是所有方法里体验最好的,但前提是选对源。不要随手拿一个 apt 源或者标准 openEuler 源来配,必须去官方站点确认嵌入式仓库的实际路径。大版本和架构都要和设备完全对应。

以 openEuler 22.03 LTS、aarch64 为例,repo 文件可以写成这样:

cat > /etc/yum.repos.d/openeuler-embedded.repo <<'EOF' [openeuler-embedded] name=openEuler Embedded baseurl=http://repo.openeuler.org/openEuler-22.03-LTS/embedded_apps/aarch64/ enabled=1 gpgcheck=0 EOF

路径里的openEuler-22.03-LTS要和cat /etc/os-release看到的大版本一致,aarch64要和uname -m一致。如果你用的是 riscv64 板子,目录就要切成riscv64。

5.2 板子上配置 repo 并刷新缓存

写完之后,最关键的一步是清缓存、重建元数据:

dnf clean all dnf makecache

然后验证源是否真的通了:

dnf list available | grep -i tree

如果能搜到tree,说明在线源没问题。接下来就可以直接安装:

dnf install -y tree

安装过程如果显示多个依赖包一起被解析、下载、安装,就说明源和仓库都工作正常。注意,dnf install默认会询问是否确认,脚本化部署时一定要加-y。

5.3 在线安装成功后的验证和注意事项

装完别急着收工,按这个顺序验证一遍:

rpm -q tree tree --version rpm -qa | grep tree

如果rpm -q显示已安装,但执行tree报缺库,说明依赖还是没解决干净。这时候回去看dnf list available是否漏掉了某些依赖子包,或者源里是否有版本冲突。

还有一点要注意:在线源里搜不到某个包很正常,不要慌。openEuler Embedded 的软件仓库覆盖面比通用发行版小得多,很多桌面级、服务器级软件根本没有嵌入式构建版本。遇到这种情况,就直接跳到下一章用 SDK 自己构建。

6. 实操四:没有现成包?从 SDK 构建一个 rpm 出来

6.1 进入构建环境,找到正确的构建入口

在线源、本地源、离线包都不好使的时候,说明该自己造轮子了。openEuler Embedded 是基于类似 Yocto 的构建体系来生成镜像和软件包的,官方 SDK 里有一套完整的构建环境,入口一般是oe-init-build-env脚本:

source oe-init-build-env

进入构建环境后,如果你是往镜像里加一个已有 recipe 的包,可以编辑conf/local.conf,在IMAGE_INSTALL里追加包名,然后重新构建镜像。如果是要为某个新软件创建包,那就需要写一个.bb格式的 recipe 文件,定义源码地址、依赖、安装逻辑,这个稍微有点门槛,但思路和写PKGBUILD或spec文件是相通的。

6.2 bitbake 产出 rpm 并传到板子

假设你要构建一个软件包mypkg,在构建环境里执行:

bitbake mypkg

构建完成后,rpm 产物会输出到类似tmp/deploy/rpm/aarch64/的目录下。找到对应的 rpm 文件,传到板子上,按照第三章的方法离线安装,或者放入本地源让 dnf 统一管理。

如果你想跳过“安装到镜像”这一步,只想快速在板子上跑某个工具,也可以直接配置本地源指向同步下来的deploy/rpm目录,这样板子端完全不碰编译,只是消费 rpm,非常干净。

6.3 构建常见坑:网络下载、机器配置、依赖不全

构建系统确实能“造出”官方源里没有的包,但坑也不少。

第一,源码下载。openEuler Embedded 构建时经常要从上游下载源码包,国内外网络状况不稳定的话,很容易卡在Fetch failed。建议设置DL_DIR指向一个共享下载缓存目录,并预先手动下载好常见源码包。

第二,内存和磁盘。bitbake 编译大软件很吃资源,我遇到过几次 OOM,构建进程直接被内核杀掉。建议构建机内存不小于 8GB,磁盘剩余空间至少 20GB,编译临时目录不要用 tmpfs。

第三,MACHINE 配置。构建时必须明确目标机器名(比如 qemuarm64、raspberrypi4-64),不同机器输出的包架构、内核模块都不一样,搞错了生成出来的 rpm 板子根本装不上。

7. 常见问题与排查技巧实录

7.1 “无法定位软件包”的几种真实原因

“无法定位软件包”是我收到最多的问题,但背后原因五花八门。

  • 仓库源没配好:baseurl写错了、目录不存在、网络不通,dnf makecache都过不去。
  • 包名和文件名不一致:你要搜的是tree,但实际 rpm 文件名是tree-2.0.0-1.aarch64.rpm,dnf搜索时用的是包名不是文件名。
  • 架构不匹配:源里只有aarch64的包,板子却是x86_64,dnf当然认为没有可用包。
  • 仓库缓存太旧:源里其实有,但dnf元数据没更新,先执行dnf clean all && dnf makecache。
  • 版本不对:你配的是 23.09 的源,系统是 22.03 LTS,依赖关系乱,搜索不到很正常。

遇到这个问题别急着换源,按上面几条逐项排查,80% 的“无法定位”都能定位到具体原因。

7.2 安装脚本报错、依赖死循环怎么破

post-installation 脚本 子进程返回错误状态是嵌入式环境常见问题,原因大多是 rpm 包里的脚本调用了系统里不存在的命令或路径。排查方式就是我在 2.3 节说的rpm -qp --scripts,提前看脚本内容,缺啥命令就补装啥。

依赖死循环也比较经典,比如 A 依赖 B,B 依赖 A,rpm单个安装会提示依赖缺失。解决办法有两个:

# 一起安装两个包,rpm 可以同时解析 rpm -ivh A.rpm B.rpm # 或者临时跳过依赖检查,装完马上验证可用性 rpm -ivh --nodeps A.rpm

--nodeps是双刃剑,能用但要用在刀刃上。装完立刻ldd验证二进制,不要拖。

7.3 常见错误速查表

错误现象常见原因解决思路
无法定位软件包 / 没有可用软件包源没配、架构不符、包名不对核对 baseurl、uname -m,dnf makecache
软件包似乎无效rpm 损坏、下载不完整重新下载,或用rpm -K校验
post-installation 脚本返回错误状态包脚本依赖命令缺失rpm -qp --scripts预检,补齐依赖工具
依赖不足 / error while loading shared libraries只装了主包,依赖链没拉齐用 dnf 在线装或提前下载全部依赖
磁盘空间不足嵌入式分区太小dnf clean all,必要时扩容根分区
Exec format error架构不匹配,装了错误的 CPU 架构包用uname -m确认,重新下对应架构 rpm

这张表是我在几十次踩坑后浓缩出来的,基本覆盖了嵌入式软件包安装 80% 以上的报错场景。

最后再分享一个经验:在 openEuler Embedded 上装软件,最忌讳的就是“沿用服务器习惯”。先确认架构、版本、包管理工具、磁盘布局这四件事,再选安装路径。官方源有包,就配置在线源用 dnf;没网络,就建本地源;源里没有,就用 SDK 构建。过程不复杂,但顺序错了,每一步都会很难受。希望这篇文章能让你少走点弯路。

返回列表