1. 系统崩过一次之后,我决定把整个环境做成ISO
先说说我为什么会折腾这个。去年有一台装了半年的Ubuntu 22.04工作站,上面配置好了完整的Python环境、Node.js工具链、Docker容器组、剪映和OBS这类多媒体工具,甚至输入法都调到了我最顺手的快捷键方案。结果一次日常更新后,开机直接卡在GRUB rescue界面。折腾了很久没救回来,最后只能重装。重装本身一小时不到,但之后把环境恢复到我习惯的样子,断断续续花了两三天——装软件、改配置、导数据、处理各种依赖冲突。那种感觉就像你住了很久的房子被清空了,每一件家具都要重新买重新摆。
所以从那天起,我认真研究“如何制作自己的Ubuntu系统镜像ISO”。我希望达成两件事:第一,如果系统真坏了,我可以在一小时内回到完全可用的工作环境;第二,如果我想在另一台电脑上部署同样配置的系统,直接装一个ISO就能搞定,不用再手工配一堆东西。
这个标题里“界面化操作系统”其实有两层意思。一层是制作出来的镜像装上以后本身带图形桌面环境,不是精简到只剩字符界面的服务器版;另一层是制作过程尽量用图形化工具来完成,而不是全程跟黑乎乎的终端较劲。我这次两条都覆盖了。文章里我会把完整流程、涉及到的工具选型、底层原理、以及我踩过的坑全部讲清楚。适合三类人看:一是被系统重装折磨过的普通桌面用户,二是需要给实验室或团队统一开发环境的运维同学,三是想学习Linux启动机制和文件系统打包原理的进阶用户。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 制作前必须想清楚:你要的是可安装ISO还是可还原备份
很多人一上来就想找一个“一键打包系统”的工具,但实际动手前,必须先分清需求。你做出来的东西,到底是“可以装到别的电脑上的安装盘”,还是“可以把当前系统原样恢复回来的备份快照”?这两件事虽然都叫系统镜像,但技术路线完全不同。
2.1 三种主流制作路线的适用场景对比
我觉得最稳妥的做法,是把市面上主流的方案先拉出来对比一遍,再决定自己走哪条路。
| 方案 | 原理 | 优点 | 缺点 | 最适合的场景 |
|---|---|---|---|---|
| dd整盘备份 | 把整个硬盘或分区按bit逐字节复制 | 最忠实,能保留引导、分区表、所有数据 | 镜像文件巨大,不能跨硬盘容量恢复,耗时 | 完整克隆到同型号硬盘 |
| tar/rsync文件级打包 | 按文件系统复制所有文件,再配合恢复盘 | 体积可控,可以排除临时文件,易于增量备份 | 需要单独准备恢复引导环境,恢复时需自己处理引导 | 日常备份+硬件更换迁移 |
| 定制官方ISO | 解压Ubuntu官方安装盘的squashfs,改完再重新打包 | 分发方便,可直接安装到新机器,体验和官方安装一致 | 制作流程较长,改起来需要一点Linux基础 | 给多台机器装机、给别人发系统 |
还有一个老牌工具叫Systemback,以前确实很方便,能直接把当前系统打成ISO。但从Ubuntu 18.04之后它基本不维护了,新系统上安装依赖问题一堆,我不建议在这个工具上浪费时间。
2.2 我建议的组合方案
我自己最后采用的是“一长一短”两条腿走路:
- 长方案:通过图形化工具Cubic来定制官方Ubuntu ISO。我把常用软件、中文环境、输入法、开发工具链、系统配置都塞进去,得到一个“开箱即用”的安装盘。以后装机直接装它,就能在第一遍装系统时就得到比较完整的工作环境。
- 短方案:针对当前正在用的系统,用rsync做文件级备份,再配合squashfs把它打包成可以动态加载的根文件系统。这个用于“这台电脑立即恢复原状”,保留我所有的用户数据、已装软件和个性化配置。
这套组合的优势在于:长方案适合新机器和新装系统,干净利落;短方案适合当前这台机器快速逃生,保留最完整的状态。两者不冲突,而且底层的技术原理是互通的——说到底,你都需要理解squashfs、GRUB引导链和文件系统打包这几个核心概念。
3. 一个ISO文件里到底装着什么:squashfs、GRUB与引导链
定制镜像如果不理解ISO的构成,操作起来就是在盲人摸象。我建议先花十分钟搞清楚你即将打开的那个Ubuntu官方ISO里到底有些什么。
3.1 官方Ubuntu ISO的目录结构
拿Ubuntu 22.04.3桌面版ISO来说,用终端挂载或者用归档管理器解开后,你看到的目录结构大概是这样:
text复制├── boot
│ ├── grub
│ │ └── grub.cfg
│ └── ...
├── casper
│ ├── filesystem.squashfs
│ ├── filesystem.size
│ ├── vmlinuz
│ └── initrd
├── dists
├── efi
│ └── boot
├── install
├── isolinux
│ ├── isolinux.cfg
│ └── ...
├── md5sum.txt
└── preseed
关键就藏在casper目录里。vmlinuz是Linux内核,initrd是初始内存磁盘,用来在启动阶段加载驱动和挂载真正的根文件系统。最重要的filesystem.squashfs是一个只读压缩文件系统,里面装的就是Ubuntu完整系统的根目录——所有程序、库、配置、桌面环境都躺在里面。
3.2 squashfs压缩包与Live引导原理
filesystem.squashfs就是整个Live系统的“实体”。计算机启动时,引导程序把内核和initrd加载到内存,initrd再去读取这个squashfs文件,把它挂载为根文件系统(以只读方式)。程序运行时如果需要写入文件,系统会借助overlayfs机制在内存或临时空间中叠一层可写层。这个方案的好处是只读压缩、节省空间、启动稳定,这也是为什么绝大多数Live Linux发行版都采用同样的套路。
理解了这一点,你就会明白:定制一个Ubuntu ISO,本质上不是去修改引导程序那些外壳,而是替换掉casper/filesystem.squashfs这个文件——把你的“系统根目录”重新打成squashfs,再放回去。如果你还需要定制启动时的默认参数(比如加一个nomodeset),才需要动boot/grub/grub.cfg和isolinux/isolinux.cfg。
3.3 GRUB引导与UEFI/BIOS双启动
另一个需要理解的点是双启动兼容。传统BIOS引导会读取isolinux目录下的配置,走ISOLINUX引导链条;UEFI引导则走efi/boot目录里的EFI程序,再跳到GRUB读取boot/grub/grub.cfg。所以一个能“通吃”的ISO,这两个部分都必须存在。这也是为什么我建议使用Cubic这类成熟工具来打包,而不是手动改文件——它会帮你把UEFI和BIOS两套引导都处理好。
4. 第一轮实操:用Cubic把官方Ubuntu镜像改造成“我的系统”
接下来进入正题。我用的是Cubic(Custom Ubuntu ISO Creator),这是一个图形化的ISO定制工具,操作方式和文件管理器有点像,但它会通过chroot进入系统根目录,让你像在真实系统里一样安装软件、修改配置。它会自动处理squashfs解压、重新打包和引导配置生成,对新手非常友好。
4.1 安装Cubic与初始界面
先安装Cubic。在Ubuntu 22.04中,逐行执行下面这些命令:
bash复制sudo apt-add-repository ppa:cubic-wizard/release
sudo apt update
sudo apt install cubic
安装完成后,从应用菜单打开Cubic。首次进入会让你选择一个Ubuntu官方ISO文件,以及一个工作目录。工作目录建议选在一个剩余空间足够大的分区上,因为Cubic会解压整个squashfs,包含源文件至少要预留20GB以上自由空间。我一开始图省事放在了家目录,结果磁盘被挤爆,后面打包时直接报错。所以这一步千万别省。
选择完ISO,Cubic会弹出一个终端窗口,自动完成解压。等它结束,你会发现界面变成了一个类似文件管理器的样子,同时底部有一个“虚拟终端”按钮。这个虚拟终端非常关键,它实际上已经把你切进了解压出来的根文件系统里,你的操作就等同于在一个最小Ubuntu系统里操作。
4.2 在rootfs内做定制:软件、配置、语言、输入法
定制的过程实际上就是在解压出来的根目录里,用chroot方式执行各种命令。我以装中文支持和开发环境为例,梳理一遍标准流程。
进入Cubic虚拟终端后,先更新软件源:
bash复制apt update
然后安装中文语言包和输入法框架。这一步很多人会漏掉语言包,导致装完系统后桌面还是英文、中文输入法怎么都调不出来:
bash复制apt install -y language-pack-zh-hans fonts-wqy-microhei fcitx5 fcitx5-chinese-addons
接下来按需安装自己的常用软件。比如我是做开发和内容创作的,一般会装这些:
bash复制apt install -y python3-pip git curl vim openssh-server docker.io docker-compose-plugin
apt install -y gcc g++ make cmake build-essential
apt install -y obs-studio kdenlive gimp
这里有一个常见的认知误区:你是在chroot环境中,不是在官方软件源外的环境里,所以不需要sudo,因为这个虚拟终端本身就是root。但如果发现某些软件在源里找不到,说明你需要先把对应的PPA或第三方源配置进这个最小系统。配置方式和你正常使用Ubuntu时一模一样,写到/etc/apt/sources.list或者/etc/apt/sources.list.d/下就行。
装完软件后,我习惯顺手做几项系统配置:改主机名、添加DNS、设置时区、创建默认用户并设置密码。这些可以直接修改文件,也可以使用带-的命令,比如:
bash复制echo "my-custom-ubuntu" > /etc/hostname
systemctl enable ssh
timedatectl set-timezone Asia/Shanghai
最后一步很重要:清理apt缓存。因为打包时会把你下载的deb包也一起打进去,白白增大镜像体积。
bash复制apt clean
全部做完,关闭虚拟终端,Cubic会问你是否要重新打包。到这里,定制阶段的“手工活”就完成了。
4.3 chroot环境下的网络与挂载处理
为什么Cubic能让你在虚拟终端里访问网络、安装软件?这里其实有几个关键的底层操作:Cubic自动帮你把宿主机的/etc/resolv.conf复制或者绑定挂载进了rootfs,否则apt update会因为无法解析域名而失败;同时它把/proc、/sys、/dev这些虚拟文件系统挂载了进去,这样你在根目录里运行命令时,才能正常和内核、硬件设备交互。
我之所以特意讲这一点,是因为如果你以后想手动用chroot命令进某个根文件系统做定制,忘了挂载这些目录就会遇到各种诡异问题。哪怕网络能通,像systemctl这类命令也会报“D-Bus connection failed”。所以记住这个套路:chroot之前先挂载/proc、/sys、/dev,还有/dev/pts,做好之后要记得卸载清理。
4.4 导出ISO与首次引导验证
Cubic重启打包后,会让你做几个选择:设置镜像的发行版本名称、版本号,以及默认启动参数。启动参数里我建议保留quiet splash,但如果你要在很多不同硬件上跑,可以加上nomodeset来规避显卡驱动黑屏问题(后面踩坑里会详细说)。
打包是一个比较耗时的过程,尤其是mksquashfs压缩大目录时,10GB的根文件系统可能要跑20多分钟。完成后会生成一个新的ISO文件。建议马上做两个校验动作:一是用sha256sum计算哈希值并记录,二是先在虚拟机里引导一次确认能进入桌面。
bash复制sha256sum custom-ubuntu.iso
如果虚拟机里验证通过,就可以写U盘了。写U盘推荐使用BalenaEtcher或Rufus,选择“DD模式”写入,而不是“ISO模式”。我后面会解释为什么这个细节决定了安装盘在UEFI机器上能不能启动。
5. 第二轮实操:把“当前运行的带桌面环境系统”整体打成可还原镜像
定制官方ISO适合给新机器装系统,但如果你想在原机器上“连数据带配置完全恢复”,那需要的是另一套方法。接下来这部分,就是把当前正在运行的完整Ubuntu系统,做成一个可以恢复的镜像。
5.1 系统瘦身与清理步骤
在打包之前,必须先给系统做一次“大扫除”。因为你会把整棵根目录文件系统都打包进去,系统里任何多余的缓存、日志、旧内核都会被带进去,白白撑大镜像。我会按顺序清理这些内容:
bash复制sudo apt autoremove --purge
sudo apt clean
sudo journalctl --vacuum-size=50M
sudo rm -rf ~/.cache/*
sudo rm -rf /tmp/*
日志目录/var/log里有些东西想保留,可以用--exclude排除,但为了镜像干净,我一般直接清理。旧内核也要检查一下:dpkg --list | grep linux-image看看有没有一堆旧版本,用sudo apt purge把不用的删掉。瘦身做完,建议先用df -h看一眼根目录已用空间,知己知彼,打包时心里有底。
5.2 用rsync + squashfs生成新版根文件系统
打包当前系统的方式,我推荐用rsync把所有文件复制到一个空目录,然后在这个目录基础上生成squashfs。这里不使用tar,是因为squashfs可以直接被Live系统挂载,恢复体验更接近系统原生。
先创建目标目录并挂载排除虚拟文件系统:
bash复制sudo mkdir /mnt/rootfs
sudo rsync -aAXv --delete --exclude=/proc/* --exclude=/sys/* \
--exclude=/dev/* --exclude=/tmp/* --exclude=/run/* \
--exclude=/mnt/* --exclude=/media/* --exclude=/lost+found \
--exclude=/var/cache/apt/archives/* \
/ /mnt/rootfs/
这里的-a表示归档模式,保留权限、时间戳等;-A保留ACL;-X保留扩展属性(xattr)。这三项组合是制作可引导系统镜像的标配,比单纯用cp -a更能完整还原文件元数据。
复制完成后,就可以打包成squashfs:
bash复制sudo mksquashfs /mnt/rootfs /path/to/custom-rootfilesystem.squashfs -comp xz
-comp xz表示用xz算法压缩,压缩率高但打包慢。如果你机器性能一般,也可以用-comp gzip,体积稍大但速度快。
生成的文件,就是你的“系统根目录快照”。如果你只是想做一个可加载的恢复系统,可以把这个squashfs放到一个可引导的Live系统里,通过修改引导参数让它作为根文件系统进行挂载启动。更简单的做法是借助Cubic,把官方ISO里的filesystem.squashfs替换成这个文件,就得到了一张能启动一个和你当前系统完全一致的Live/安装盘。
5.3 用“dd整盘备份”作兜底方案
文件级备份的缺点是有极少数文件(比如数据库的WAL日志、某些特殊设备文件)可能在复制时出现不一致。所以我不排斥偶尔用dd做一次整盘备份,当作“最后保底”。
bash复制sudo dd if=/dev/sda of=/media/backup/system-backup.img bs=64M status=progress
这里把整个/dev/sda设备拖成镜像。缺点也明显:镜像文件跟磁盘大小一样大,而且恢复时只能恢复到不小于原盘的设备。所以我不建议用dd做日常备份,只适合“这台机器马上要交回公司”或者“要整体迁移到相同型号硬盘”这种场景。
5.4 验证与日常增量备份
无论用哪种方式,做完镜像后都必须在虚拟机里验证一次。我在Cubic替换完squashfs后,会直接在虚拟机里挂载,选择“Try Ubuntu”,如果能看到桌面并且自己装的应用都在,说明根文件系统没有问题。
日常使用中,我更依赖rsync的增量备份能力。把上面那条rsync命令固化到一个脚本里,配合--delete和--exclude,就能实现类似“时间机器”的增量同步。每次同步后运行一次mksquashfs,就能得到最新的完整镜像。这个流程虽然名字听着复杂,但跑顺一次后,每次都只要一条命令。
6. 实测中踩过的十个坑与完整排查链路
做镜像这个事,教程看着简单,实操时坑是真的多。我把印象最深的五个讲透,每一个都附上完整的排查思路,而不是直接给结论。
6.1 写盘后开机黑屏、进GRUB rescue,排查链路是什么
现象是做完ISO,写进U盘,插到目标机器上一开机,直接黑屏,或者卡在grub>提示符。
排查思路是从“引导程序是否被找到”开始。U盘启动要看主板的引导方式。如果是UEFI模式主板,引导程序会去EFI系统分区找EFI/BOOT/BOOTX64.EFI;如果是传统BIOS,则去读MBR中的ISOLINUX引导。你在Windows下用老式UltraISO的“写入硬盘映像”方式,默认可能写入成了传统BIOS模式,但目标机器开启了UEFI优先,自然启动失败。
正确做法是用Rufus写U盘时选择“DD镜像模式”或者GPT分区方案,这样U盘内会同时存在EFI引导和传统引导所需的文件。BalenaEtcher也是同样的逻辑,它会智能处理。另外,如果目标机器是UEFI但开了Secure Boot,建议临时关闭Secure Boot再做验证。
6.2 定制时装了中文输入法,装完系统后却找不到
现象是Cubic虚拟终端里明明装了fcitx5,但装完系统进入桌面,输入法怎么调都调不出来。
排查链路是:先确认语言包是否安装。很多人只装了fcitx本身,没装language-pack-zh-hans,系统locale还是英文,输入法框架没有正确生成。
其次要看输入法框架是否设置了环境变量。在chroot环境里,你可能看不到桌面用户的家目录配置。正确做法是进入Cubic虚拟终端后,设置系统默认输入法环境,比如创建/etc/environment中的变量,或者在桌面用户的家目录里预配置:
bash复制apt install -y im-config
im-config -n fcitx5
最后还要留意:如果你在chroot里用root装了软件,但真正登录的桌面用户是另一个普通用户,那么该用户家目录里的输入法配置是空的。我会在rootfs的/etc/skel目录里预置好配置文件,这样新建用户就会自动继承。
6.3 启动到桌面时黑屏,但能看到TTY界面
这个坑在NVIDIA显卡机器上特别常见。现象是Live系统引导过程中出现闪烁的logo,然后屏幕变黑,按Ctrl+Alt+F2能切到字符终端,说明系统其实已经起来了,只是显示服务没有正常接管。
原因通常是开源驱动和闭源驱动在Live环境下的冲突,或者内核模式设置问题。排查时先切到TTY终端,看看Xorg或Wayland的日志,多半能在/var/log/Xorg.0.log里找到驱动加载失败的信息。
解决办法是给启动参数加nomodeset。在Cubic导出ISO前的引导参数设置里,把quiet splash后面加上nomodeset,让内核不去动态切换显卡模式,而是交给Xorg自己处理。如果还是不行,再加acpi=off试试,这个参数能排除ACPI电源管理对显卡的影响。
6.4 mksquashfs打包到约100%时卡住或报“No space left”
现象是打包过程进行到最后,快要完成时卡了很长时间,最后报磁盘空间不足。
根因是mksquashfs在压缩生成最终squashfs文件时,需要临时空间存放排序索引和中间数据。很多人忽略了这个过程需要的临时空间量,预留的磁盘刚好够装源目录,但不够生成压缩文件。
排查时先检查工作目录剩余空间,确保至少有源目录大小的1.5倍以上余量。如果确实空间紧张,可以分两步:先压缩成不带排序的格式,去掉一部分高压缩比选项;或者把不需要的高清视频、大块缓存文件先从源目录中排除,等系统恢复后再从备份中拉取。
另外还有一点:如果使用了-processors参数手动限制CPU线程数,老版本mksquashfs在并发写盘时可能崩溃,也会造成假死。建议在一次打包时不要手动限制处理器数,让它全速跑。
6.5 同一张U盘,这台机器能启动,那台机器不能
这类兼容性问题的排查思路主要看三处:分区表方式(GPT还是MBR)、启动文件是否完整、Secure Boot是否开启。
目前主流做法是U盘同时写入GPT分区和传统MBR引导。用Rufus选“GPT+UEFI”写出来的U盘,在纯BIOS老机器上常常无法启动。反过来用“MBR+BIOS”写出来的,在UEFI新机器上也不行。最保险的做法是使用BalenaEtcher写入镜像,它会自动生成双启动结构;或者在Rufus里尝试“DD镜像模式”强制直写,这种方式生成的U盘兼容性最好。
Secure Boot则是另一道坎。你在定制ISO时如果用了自编译的内核模块,签名没跟上,Secure Boot开启后必然被拦下。这个没有捷径,要么关闭Secure Boot,要么给内核和模块做正规签名。
7. 几个能让系统镜像真正“好用”的细节经验
踩完坑之后,我总结了一些“让镜像不再是玩具”的经验。这些东西不是必需步骤,但加上的效果是完全不同的。
7.1 用preseed自动应答,实现无人值守安装
定制出来的ISO如果还要在安装过程中一路点“下一步”,效率太低。Ubuntu虽然逐渐转向Subiquity和autoinstall,但旧版preseed机制在很多场景下依然可用。
比如在Cubic工作目录的rootfs里新建/preseed/custom.seed,写入:
text复制d-i debian-installer/locale string zh_CN.UTF-8
d-i keyboard-configuration/xkb-keymap select us
d-i passwd/user-fullname string admin
d-i passwd/username string admin
d-i passwd/user-password password admin123
d-i passwd/user-password-again password admin123
d-i clock-setup/utc boolean false
d-i time/zone string Asia/Shanghai
d-i partman-auto/method string regular
d-i partman-auto/choose_recipe select atomic
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
然后在ISO的isolinux/isolinux.cfg或boot/grub/grub.cfg的启动参数末尾加上preseed/file=/cdrom/preseed/custom.seed。这样装系统时全程不用人工干预,非常适合批量部署。
7.2 镜像版本管理与校验,是保持“长期可用”的关键
镜像做出来不是一劳永逸。我通常给镜像命名采用“系统代号-用途-日期-版本”的格式,比如ub2204-dev-20240816-01.iso。每次生成后立即计算SHA256并保存到一个文本文件里,同时记录这次镜像相对上一版改了什么。
bash复制sha256sum ub2204-dev-20240816-01.iso >> checksums.txt
这个习惯一开始看起来没必要,但当你维护了三个版本的镜像后,就会发现没有记录完全分不清哪个是最新的、哪个有那个显卡修复。
7.3 我最终长期保留的三种镜像版型
经过半年多实践,我最终维护了三个版型:
- 纯净安装版:官方Ubuntu加中文支持加基础驱动,约4GB。用于大多数新机和临时环境。
- 开发工作站版:纯净版加Python全家桶、Node.js、Docker、Git、VSCode、JetBrains全家桶。约9GB。用于团队开发和自己的主力机器。
- 快速恢复快照:rsync方式备份的当前机器全量状态。不固定大小,随着数据量增大而增大。用于单机紧急恢复。
每隔一个季度,我会重新生成一次开发工作站版,然后在虚拟机里完整跑一遍安装验证。这三个月一次的验证,反而比天天折腾新功能更关键。
7.4 一个让我少走很多弯路的检查清单
作为最后的干货,我把每次制作镜像前都会过一遍的检查清单放在这里:
- [ ] 工作目录有足够空间(至少源目录大小的2倍)
- [ ] 已执行
apt autoremove --purge和apt clean - [ ] 已清空
/tmp和用户缓存 - [ ] 已正确配置语言包和输入法框架
- [ ] 已在
/etc/skel为新建用户预置配置 - [ ] 已确认启动参数含
nomodeset(针对NVIDIA机器) - [ ] 已预置preseed自动应答文件
- [ ] 打包完成后已计算SHA256
- [ ] 已在虚拟机中至少引导验证过一次
- [ ] 写U盘时使用DD/Etcher直写方式
这些步骤看起来多,但跑过一次之后,整个流程就是一条命令串和一个半小时的等压缩时间。把检查清单做成一个脚本,每次执行前自动检查前四步,能省掉大量因“忘了做某一步”而导致的返工。
我个人最大的体会是:制作系统镜像这件事,最难的从来不是某个命令怎么敲,而是整个流程的连贯性和对底层机制的理解。当你明白了squashfs、GRUB引导链和文件系统元数据这些概念之后,不管是定制官方ISO、迁移旧机器、还是做批量部署,都能顺手拈来。现在我的主力工作机再次出问题时,最坏情况也就是找个空U盘,花半小时恢复到一个和之前几乎一样的环境——这份从容,是值得花一个周末换来的。
