云计算百科
云计算领域专业知识百科平台

在Linux环境下构建ISO实战(lorax + genisoimage)

作者: 鲨鱼辣钊
版本: 1.0
日期: 2026年8月6日
标签: ISO构建、lorax、genisoimage、createrepo、Linux发行版、UOS
适合人群: Linux发行版维护者、系统集成工程师、DevOps


摘要

本文说了什么:基于UOS系统,完整拆解一个 ISO 集成脚本 make_iso.sh 的实现——解析 yum 源地址、lorax 生成引导骨架、yumdownloader 按清单批量下载 RPM、createrepo 生成仓库元数据、genisoimage 打包 BIOS/UEFI 双启动 ISO,最后用 isohybrid / implantisomd5 完成 U 盘可写与介质校验。

实现了什么:一个一键脚本,输入一个可用的 yum 源,输出一个可直接安装系统的 ISO 镜像(含 md5 校验文件)。

目的是什么:沉淀发行版镜像集成的完整做法与关键参数含义,便于维护与移植;与《Pungi 添加双内核集成技术总结》互为补充——Pungi 适合 koji 驱动的全自动 compose,本脚本适合轻量、可控的手动集成场景。


一、背景:为什么要自己写 ISO 集成脚本

做 Linux 发行版维护,绕不开一件事:把一堆 RPM 包变成一张可安装的系统盘。

重量级方案是 Pungi / lorax 全家桶,配置驱动、阶段完整,但依赖 koji 构建体系和一整套 compose 配置,学习和维护成本都不低(相关问题可参考《Pungi 添加双内核集成技术总结》)。

而日常集成场景往往是:源已经有了(koji/Jenkins 构建产物),只差"收包 + 打包"这两步。这时用现成工具串一条轻量流水线更直接:

阶段工具作用
引导骨架 lorax 生成安装程序(anaconda)、isolinux / efiboot 等引导文件
收包 yumdownloader 按 pkglist 清单批量下载 RPM 到 BaseOS
仓库元数据 createrepo 生成 repodata,-g 挂上 comps 分组
ISO 打包 genisoimage 把目录打成 BIOS/UEFI 双启动 ISO
U盘可写 isohybrid 让 ISO 可直接 dd 到 U 盘启动
校验 implantisomd5 / md5sum 安装介质自检 + 交付校验文件

make_iso.sh 就是把这张表从头到尾串起来。


二、整体流程

先看 ISO 集成在整条交付流水线里的位置:

在这里插入图片描述

图上可以看到:bug 修复、koji 构建、Jenkins 自动化校验都通过之后,才进入"集成 ISO"这一步——也就是本脚本的职责。构建失败则检查报错修正后重来,不进入集成。

脚本内部的执行顺序:

#mermaid-svg-k4GKrDKbdQJd8bf0{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-k4GKrDKbdQJd8bf0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-k4GKrDKbdQJd8bf0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-k4GKrDKbdQJd8bf0 .error-icon{fill:#552222;}#mermaid-svg-k4GKrDKbdQJd8bf0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-k4GKrDKbdQJd8bf0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-k4GKrDKbdQJd8bf0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-k4GKrDKbdQJd8bf0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-k4GKrDKbdQJd8bf0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-k4GKrDKbdQJd8bf0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-k4GKrDKbdQJd8bf0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-k4GKrDKbdQJd8bf0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-k4GKrDKbdQJd8bf0 .marker.cross{stroke:#333333;}#mermaid-svg-k4GKrDKbdQJd8bf0 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-k4GKrDKbdQJd8bf0 p{margin:0;}#mermaid-svg-k4GKrDKbdQJd8bf0 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-k4GKrDKbdQJd8bf0 .cluster-label text{fill:#333;}#mermaid-svg-k4GKrDKbdQJd8bf0 .cluster-label span{color:#333;}#mermaid-svg-k4GKrDKbdQJd8bf0 .cluster-label span p{background-color:transparent;}#mermaid-svg-k4GKrDKbdQJd8bf0 .label text,#mermaid-svg-k4GKrDKbdQJd8bf0 span{fill:#333;color:#333;}#mermaid-svg-k4GKrDKbdQJd8bf0 .node rect,#mermaid-svg-k4GKrDKbdQJd8bf0 .node circle,#mermaid-svg-k4GKrDKbdQJd8bf0 .node ellipse,#mermaid-svg-k4GKrDKbdQJd8bf0 .node polygon,#mermaid-svg-k4GKrDKbdQJd8bf0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-k4GKrDKbdQJd8bf0 .rough-node .label text,#mermaid-svg-k4GKrDKbdQJd8bf0 .node .label text,#mermaid-svg-k4GKrDKbdQJd8bf0 .image-shape .label,#mermaid-svg-k4GKrDKbdQJd8bf0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-k4GKrDKbdQJd8bf0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-k4GKrDKbdQJd8bf0 .rough-node .label,#mermaid-svg-k4GKrDKbdQJd8bf0 .node .label,#mermaid-svg-k4GKrDKbdQJd8bf0 .image-shape .label,#mermaid-svg-k4GKrDKbdQJd8bf0 .icon-shape .label{text-align:center;}#mermaid-svg-k4GKrDKbdQJd8bf0 .node.clickable{cursor:pointer;}#mermaid-svg-k4GKrDKbdQJd8bf0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-k4GKrDKbdQJd8bf0 .arrowheadPath{fill:#333333;}#mermaid-svg-k4GKrDKbdQJd8bf0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-k4GKrDKbdQJd8bf0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-k4GKrDKbdQJd8bf0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-k4GKrDKbdQJd8bf0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-k4GKrDKbdQJd8bf0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-k4GKrDKbdQJd8bf0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-k4GKrDKbdQJd8bf0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-k4GKrDKbdQJd8bf0 .cluster text{fill:#333;}#mermaid-svg-k4GKrDKbdQJd8bf0 .cluster span{color:#333;}#mermaid-svg-k4GKrDKbdQJd8bf0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-k4GKrDKbdQJd8bf0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-k4GKrDKbdQJd8bf0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-k4GKrDKbdQJd8bf0 .icon-shape,#mermaid-svg-k4GKrDKbdQJd8bf0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-k4GKrDKbdQJd8bf0 .icon-shape p,#mermaid-svg-k4GKrDKbdQJd8bf0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-k4GKrDKbdQJd8bf0 .icon-shape .label rect,#mermaid-svg-k4GKrDKbdQJd8bf0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-k4GKrDKbdQJd8bf0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-k4GKrDKbdQJd8bf0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-k4GKrDKbdQJd8bf0 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

是, 防覆盖

解析 comm.repo 取出 baseurl

lorax 生成引导骨架 outdir

outdir 是否已存在?

直接退出

yumdownloader 按 pkglist 批量下载 RPM

createrepo -g 生成 BaseOS 仓库元数据

复制 .treeinfo / AppStream 占位 / kernel-4.18

genisoimage 打包双启动 ISO

isohybrid –uefi 支持U盘启动

implantisomd5 植入校验 + 导出 md5 文件

一句话概括:lorax 搭骨架、yum 收血肉、genisoimage 装壳、isohybrid 开门。


三、目录结构与前置文件

脚本所在目录需要以下文件:

文件作用备注
make_iso.sh 主脚本 在目录内执行(依赖 pwd)
pkglist BaseOS 包清单 上千个包名,空格分隔一行
comm.repo yum 源配置 baseurl= 指向构建源
comps-BaseOS.x86_64.xml BaseOS 分组定义 createrepo -g 使用
comps-AppStream.x86_64.xml AppStream 分组定义 同上
.treeinfo 镜像元信息 覆盖 lorax 生成值,声明变体
kernel-4.18/ 额外集成的内核 双内核场景预留
AppStream/ AppStream 占位仓库 源未就绪时的临时方案

运行环境要求:

  • root 权限(写入 /opt)
  • 工具链:lorax、yumdownloader、createrepo、genisoimage、isohybrid、implantisomd5
  • 一个网络可达的 yum 源(comm.repo 中配置)

comm.repo 用占位符管理源地址,脚本切环境只改这一个文件:

[comm]
name=comm
baseurl=${repo_url}
gpgcheck=0


四、脚本逐段解析

4.1 变量与源地址解析

workdir=`pwd`
date=$(date +%Y%m%d%H%M)
repodir=`cat $workdir/comm.repo |grep baseurl|awk -F "baseurl=" '{print $2}'`
outdir=/opt/$date-JC
basedir=$outdir/BaseOS/Packages
basecomps=comps-BaseOS.x86_64.xml
appcomps=comps-AppStream.x86_64.xml
AppStream_dir=$workdir/AppStream

isodir=/opt

arch=`uname -r |awk -F "." '{print $NF}'`
family=UnionTechOS
version=20
short=UnionTechOS
LABLE=UnionTechOS-20-x86_64

几个关键设计:

  • outdir=/opt/$date-JC:用分钟级时间戳做目录名,每次构建产物隔离,天然保留历史版本
  • repodir 从 comm.repo 里 awk 出来:源地址单一配置点,脚本本身不含任何硬编码源地址
  • arch 从内核_release 末段取(如 x86_64),用于日志展示
  • LABLE 是卷标:后面 lorax –volid 和 genisoimage -volid 共用,保证挂载名一致

4.2 lorax 生成引导骨架

echo lorax started to execute, arch:$arch
if [ ! -d "$outdir" ]; then
lorax –noupgrade -p "$family" -v "$version" –release=20 -r "$short" \\
–nomacboot –isfinal –volid="$LABLE" –rootfs-size=3 \\
-s "$repodir" "$outdir"
if [ $? != 0 ]; then
exit
fi
else
echo $outdir already exists
exit
fi

参数速查:

参数含义
-p / -v / –release / -r 产品名、版本等发布信息,最终写入 os-release、.treeinfo 等镜像元数据
–nomacboot 不生成 mac 引导,服务器场景用不上
–isfinal 去掉"测试版"水印标记
–volid ISO 卷标
–rootfs-size=3 安装环境 rootfs 大小(GB),够用即可,小了装不下、大了浪费
-s 源地址,lorax 从中拉取构建安装程序所需的包

注意两层保护:

  • outdir 已存在直接退出——绝不覆盖已有构建产物,配合时间戳目录名,误操作也不会毁掉历史镜像
  • lorax 返回非 0 直接退出——骨架失败就不往下走,避免打出一张残废 ISO
  • 这一步跑完,outdir 里就是安装程序的完整骨架:isolinux/、images/efiboot.img、.treeinfo 等,还差 RPM 包。

    4.3 BaseOS:批量收包 + 生成 repodata

    download_baseos(){
    pkglist=`cat pkglist`
    mkdir -p $basedir

    yumdownloader -c $workdir/comm.repo –destdir $basedir $pkglist
    cp $basecomps $outdir/BaseOS/
    createrepo -g $basecomps $outdir/BaseOS/ || { echo "Createrepo failed"; exit 1; }
    rm -f $outdir/BaseOS/$basecomps
    }

    三步:

  • yumdownloader 按 pkglist 清单批量下载到 BaseOS/Packages/。pkglist 是整张镜像的"包菜单",上千个包一行写完,增删包只改这个文件
  • createrepo -g 生成仓库元数据,-g 把 comps 分组挂进 repodata——安装界面的"最小化安装 / 带 GUI 的服务器"等选项就来自 comps
  • 用完即删 comps:rm -f 把 xml 从产物里清掉,保持 ISO 内目录干净(元数据已进 repodata)
  • createrepo 失败立即终止(|| { …; exit 1; }),理由同 lorax:元数据坏了的仓库打出来的 ISO 安装时必炸。

    4.4 AppStream:占位策略(当前启用)

    download_appstream 是与 4.3 对称的 AppStream 收包函数(用 yum –installroot –downloadonly 收包再搬出来),但当前被注释停用:

    #AppStream is not ready, so use an empty warehouse temporarily.
    # When ready, enable download_appstream and close empty_repo
    empty_repo(){
    \\cp -r $workdir/.treeinfo $AppStream_dir $workdir/kernel-4.18 $outdir/
    }

    download_baseos
    #download_appstream
    empty_repo

    AppStream 源未就绪时,用 empty_repo 做三件事:

    • 用自定义 .treeinfo 覆盖 lorax 生成的,声明镜像变体布局
    • 拷入 AppStream/ 空仓库占位,保证 ISO 目录结构与正式版一致,安装程序不会因缺目录报错
    • 拷入 kernel-4.18/——双内核集成的预留位,与《Pungi 添加双内核集成技术总结》解决的问题同源

    两个细节:

    • \\cp 前加反斜杠:绕过某些环境里 cp -i 的 alias,避免交互确认卡住脚本
    • 开关式演进:AppStream 就绪后,启用 download_appstream、注释 empty_repo 即可,脚本结构不用动

    4.5 genisoimage 打包双启动 ISO

    if ! TEMPLATE="$($(head -n1 $(which lorax) | cut -c3-) -c 'import pylorax; print(pylorax.find_templates())')"; then
    TEMPLATE=/usr/share/lorax
    fi
    /usr/bin/genisoimage -untranslated-filenames -volid $LABLE -J -joliet-long \\
    -rational-rock -translation-table -input-charset utf-8 -x ./lost+found \\
    -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table \\
    -eltorito-alt-boot -e images/efiboot.img -no-emul-boot \\
    -o $workdir/UnionTechOS-20-x86_64-dvd1-$date.iso $outdir

    开头的 TEMPLATE 探测是一段防御性代码:动态询问 pylorax 模板目录,失败回退 /usr/share/lorax(为将来引用模板留的口子,当前打包命令未直接使用)。

    genisoimage 这行是全文信息量最大的一条命令,拆开看:

    通用参数:

    参数含义
    -volid $LABLE 卷标,与 lorax 保持一致
    -J / -joliet-long Joliet 格式(Windows 下可读),允许长文件名
    -rational-rock / -translation-table Rock Ridge 优化 + 文件名转换表(Linux 下保留大小写与权限)
    -untranslated-filenames 保留原始文件名
    -input-charset utf-8 输入文件名按 UTF-8 解析
    -x ./lost+found 排除 lost+found 目录

    BIOS 引导(第一个 El Torito 启动项):

    参数含义
    -b isolinux/isolinux.bin 引导镜像文件
    -c isolinux/boot.cat 启动目录文件
    -no-emul-boot 不模拟软盘/硬盘,直接引导
    -boot-load-size 4 加载 4 个扇区,isolinux 的固定要求
    -boot-info-table 给引导镜像打补丁表,isolinux 需要

    UEFI 引导(第二个启动项):

    参数含义
    -eltorito-alt-boot 开启备用启动目录,后面挂第二启动项
    -e images/efiboot.img EFI 引导镜像(lorax 已生成)
    -no-emul-boot 同样不模拟

    一套 ISO 同时塞进 BIOS 和 UEFI 两个启动入口,老机器新机器都能装——这就是"双启动 ISO"的全部秘密。

    4.6 isohybrid 与校验

    /usr/bin/isohybrid –uefi $workdir/UnionTechOS-20-x86_64-dvd1-$date.iso
    /usr/bin/implantisomd5 –supported-iso $workdir/UnionTechOS-20-x86_64-dvd1-$date.iso

    md5sum UnionTechOS-20-x86_64-dvd1-$date.iso > UnionTechOS-20-x86_64-dvd1-$date.iso.md5

    • isohybrid –uefi:在 ISO 的 MBR/GPT 上做手脚,让它既能刻盘、也能直接 dd 进 U 盘启动(含 UEFI 分区表)
    • implantisomd5:把 ISO 自身 md5 植进镜像,安装程序的 media check(介质自检)读的就是它
    • md5sum > .md5:交付用的外部校验文件,下载方先验 md5 再刻盘

    五、注意事项与常见坑

  • 必须在脚本目录内执行:workdir=``pwd```,pkglist、comm.repo`、comps 文件都按相对路径找,换目录跑必然报错
  • 源地址不要硬编码进脚本:统一走 comm.repo 的 baseurl,换源/脱敏都只改一个文件;对外分享脚本时确认没有内网地址残留
  • outdir 防覆盖是"退出"不是"清理":同分钟内重复跑会因目录已存在直接退出,属预期行为,需要重跑就等一分钟或手动删目录
  • –rootfs-size 要给够:包清单增大后若 lorax 报空间不足,先调这个值
  • comps 与 pkglist 要同步维护:comps 里引用了 pkglist 没有的包,安装界面选了该分组会报缺包
  • 占位 AppStream 别带上生产:empty_repo 是临时方案,发布正式镜像前确认已切换到 download_appstream

  • 六、运行方式与环境验证

    # 构建机上以 root 执行
    cd /path/to/mkiso
    ./make_iso.sh
    # 产物:当前目录 UnionTechOS-20-x86_64-dvd1-<时间戳>.iso 及同名 .md5

    本机(普通开发机)做的验证:

    $ bash -n make_iso.sh && echo "SYNTAX OK"
    SYNTAX OK

    $ genisoimage –version
    genisoimage 1.1.11 (Linux)

    即:脚本语法校验通过,打包工具 genisoimage 可用;lorax、yumdownloader、createrepo、isohybrid、implantisomd5 属构建机工具链,完整构建输出(各阶段日志、最终 ISO 大小与耗时)需在构建机以 root 实跑后回填,本文不虚构运行结果。


    七、总结

    • 流水线思维:lorax 骨架 → yumdownloader 收包 → createrepo 元数据 → genisoimage 双启动打包 → isohybrid/implantisomd5 收尾,每步单一职责、失败即停
    • 配置与代码分离:源地址在 comm.repo、包清单在 pkglist、分组在 comps,脚本本身不含业务数据,移植只换配置文件
    • 时间戳产物 + 防覆盖:构建历史天然留存,误跑不毁产物
    • 占位演进:AppStream 未就绪用空仓库占位保结构一致,就绪后一个开关切换
    • 双内核预留:kernel-4.18/ 的拷贝位与《Pungi 添加双内核集成技术总结》方案呼应

    轻量集成用本脚本,全自动 compose 用 Pungi,两条路线按场景取舍即可。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 在Linux环境下构建ISO实战(lorax + genisoimage)
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!