☰
Ubuntu自定义镜像制作:从rootfs构建到SD卡烧录全流程
2026/10/1 14:23:24 网站建设 项目流程

1. 先把"制作镜像"这件事想清楚:你要做的到底是什么

很多刚接触 Ubuntu 的开发者,一说"制作镜像"就以为是把 ISO 文件拷进 U 盘,或者用软碟通把官方镜像写入存储卡。这其实只是"烧录官方镜像",不是真正意义上的"制作镜像"。

我在实际项目里被问到最多的场景是这几类:手里有一块 ARM 开发板(比如 RK3588、树莓派、全志系列),官方提供的 Ubuntu 镜像要么版本太老,要么预装了一堆用不上的东西;公司内部要批量部署几十台同一型号的 x86 工控机,希望每台的系统分区、预装软件、SSH 配置完全一致;还有一种是离线环境,内网机器无法访问 Ubuntu 官方源,需要提前把带有所需软件包的系统做成一个可直接写入的镜像文件。

如果你也属于上面任何一种情况,这篇文章就是写给你的。我会完整走一遍从零构建 Ubuntu 根文件系统、打包成磁盘镜像、再烧录到 SD 卡或硬盘的流程,所有命令都是我在树莓派、RK3399 和 x86 工控机上实际验证过的。

先区分两个概念,这对后面理解每一步做什么很重要:

  • 官方 ISO 镜像:这是一个"安装媒介",它的作用是启动电脑、引导安装程序,然后把系统解压安装到目标磁盘上。ISO 不能被直接当成系统盘运行(Live 模式例外,但那是另一套机制)。
  • 可烧录的系统镜像(磁盘镜像):这是一个"目标磁盘的完整快照",包含分区表、文件系统、引导程序、根文件系统。用 dd 或 Etcher 把它写入 SD 卡后,卡插进设备就能直接启动。

本文做的是第二种。制作它的核心链路是:构建根文件系统(rootfs)→ 设计分区布局 → 把内容写入镜像文件 → 烧录到物理介质。

这个流程初看复杂,但拆解开就是几个独立步骤。我建议你按顺序执行,不要跳步,尤其是 chroot 配置阶段,少一步后面启动时就要花几倍时间排查。

2. 动手之前的硬件确认与环境准备

2.1 明确目标架构:arm64 和 amd64 不可以混用

制作镜像最容易犯的第一个错误,就是忽略了目标设备的 CPU 架构。x86 的 Ubuntu 镜像拷到 ARM 开发板上是起不来的,反过来也一样。

确认架构的方法很简单:如果设备能进系统,执行uname -m。常见的输出有三种:

  • x86_64:Intel/AMD 桌面机、服务器、工控机
  • aarch64:ARMv8 及以上的 64 位芯片,RK3399、RK3588、树莓派 4B/5 都是这一代
  • armv7l:32 位 ARM 芯片,树莓派 2 时代或更早的板子

选定架构之后,所有软件包的下载、工具链的选择都要跟着走。例如在 x86 的 PC 上为树莓派构建系统,就必须借助 QEMU 模拟 ARM 环境,具体方法下面会说。

2.2 推荐使用 Ubuntu 22.04/24.04 LTS 作为构建宿主

构建镜像的宿主机(就是你现在用的电脑)不必和目标架构一致,但系统版本我建议使用 20.04 以上的 Ubuntu LTS,或者 Debian 11/12。原因有两个:

第一,构建 rootfs 的核心工具debootstrap在较新的系统里版本更新,对 Ubuntu 24.04(noble)等新版本的支持更完整,老版本 debootstrap 可能不认新 Ubuntu 的源结构。

第二,qemu-user-static 在较新内核上配合 binfmt_misc 更稳定,交叉 chroot 时不容易出现莫名其妙的段错误。

如果你的宿主系统是 Windows 或 macOS,可以先装一台 VMware/VirtualBox 虚拟机跑 Ubuntu,再在虚拟机里做。我早期就是在 VMware 里完成的整套制作流程,没有什么障碍。

2.3 安装构建工具:debootstrap 与 QEMU

以 Ubuntu 宿主为例,先把依赖装上:

sudo apt update sudo apt install -y debootstrap qemu-user-static binfmt-support \ rsync wget parted dosfstools xxd

这里解释一下几个工具的角色:

  • debootstrap:制作最小 rootfs 的主工具,它会从 Ubuntu 源下载指定版本的软件包,解压到目标目录,完成系统骨架的搭建。
  • qemu-user-static:让 x86 机器可以运行 ARM 程序。chroot 进 ARM rootfs 执行apt、dpkg时,其实就是靠它在背后翻译系统调用。
  • binfmt-support+ 注册脚本:让内核自动识别 ELF 程序的架构,并将其交给对应的 QEMU 解释器。安装/usr/bin/qemu-aarch64-static后,通常还要执行一次update-binfmts --enable qemu-aarch64(Debian 系默认已启用,但最好确认一下)。

注意一个细节:qemu-user-static 的特殊之处在于,它提供的是静态编译版本,即使 chroot 环境的动态库不全也能运行。这也意味着,如果你要把 ARM rootfs 打包成镜像给真实硬件用,应该把qemu-aarch64-static从 rootfs 里删掉,因为它在真实 ARM 硬件上没有任何作用,反而会误导排查。

还有一个容易被忽略的点:如果用debootstrap的第二阶段在 chroot 里修系统,宿主的binfmt_misc内核模块必须是加载状态。一般 Ubuntu 桌面系统默认识别 ARM 可执行文件,但如果你的宿主比较精简,可能需要手动加载:

sudo modprobe binfmt_misc ls /proc/sys/fs/binfmt_misc/

看到目录不为空,就可以继续了。

3. 用 debootstrap 构建最小 Ubuntu 根文件系统

3.1 选择好挂载目录并下载核心软件包

整个流程的第一个大动作,是创建 rootfs 的存放目录并运行 debootstrap。我习惯把构建目录集中放在~/image-build下,rootfs 直接用rootfs命名,方便后续引用。

mkdir -p ~/image-build/rootfs sudo debootstrap --arch=arm64 --foreign --variant=minbase \ noble ~/image-build/rootfs \ http://ports.ubuntu.com/ubuntu-ports

如果你构建的是 x86_64 系统,把--arch换成amd64,源换成http://archive.ubuntu.com/ubuntu即可。

这条命令里的参数按顺序拆开看:

  • --arch=arm64:目标架构
  • --foreign:关键参数,表示"只下载并解压软件包,不进行 chroot 配置"。因为在 x86 机器上直接对 ARM rootfs 执行 chroot 里的 post-install 脚本会失败(你还未注册 QEMU),所以第一步只能做解压,第二步再借助 QEMU 完成配置。
  • --variant=minbase:最小化安装,不装文档、不装标准工具包,只保留 dpkg/apt 核心。后面需要的软件包我们用apt install按需补充。
  • noble:Ubuntu 24.04 的代号。Ubuntu 22.04 是jammy,20.04 是focal,选 LTS 就好。
  • ports.ubuntu.com是 ARM、RISC-V 等非 x86 架构使用 Ubuntu 软件源的官方入口。

下载时间取决于网络,通常 3~10 分钟。命令结束时,rootfs 里应该已经有一套/bin、/usr、/etc等目录骨架。

3.2 注册 QEMU 并执行第二阶段配置

接下来把静态 QEMU 复制进 rootfs,再 chroot 进去执行配置:

sudo cp /usr/bin/qemu-aarch64-static ~/image-build/rootfs/usr/bin/ sudo chroot ~/image-build/rootfs /debootstrap/debootstrap --second-stage

第二阶段会执行所有软件包的配置脚本,需要几分钟。如果这一步报错,九成原因出在 binfmt_misc 没生效。可以用一个简单的命令验证:

sudo chroot ~/image-build/rootfs /bin/true

如果返回正常,说明 QEMU 调用成功;如果报cannot execute binary file,就是 binfmt 没注册成功。

3.3 在 chroot 环境里定制系统

第二阶段完成后,rootfs 已经是一个可用的最小系统了。接下来的所有定制,都可以通过 chroot 进行。我建议先把宿主机的 DNS 和网络信息带进去,否则 chroot 里apt update可能解析不了域名:

sudo mount --bind /dev ~/image-build/rootfs/dev sudo mount --bind /dev/pts ~/image-build/rootfs/dev/pts sudo mount --bind /proc ~/image-build/rootfs/proc sudo mount --bind /sys ~/image-build/rootfs/sys sudo cp /etc/resolv.conf ~/image-build/rootfs/etc/resolv.conf

然后一次性进入 chroot,执行下面的配置。有一个使用技巧:我把下面所有配置写成一个bootstrap.sh脚本放在 rootfs 根目录,再在 chroot 里执行,能避免多次进出 chroot 造成的遗漏。

sudo chroot ~/image-build/rootfs

进入之后,切换软件源。/etc/apt/sources.list里的默认源是构建时写入的,不同地区的下载速度差异很大。可以换成国内源(比如清华 TUNA、阿里云),这一步对于后续安装大量软件包节省的时间非常可观。以 24.04 的 ports 源为例:

cat > /etc/apt/sources.list <<EOF deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ noble main restricted universe multiverse deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ noble-updates main restricted universe multiverse deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ noble-security main restricted universe multiverse EOF

x86 机器记得把ubuntu-ports改成ubuntu,路径完全对应。

接着:

apt update apt install -y systemd systemd-sysv udev kmod net-tools iproute2 \ openssh-server network-manager locales sudo vim curl wget \ wireless-tools wpasupplicant

这组软件包决定了系统能不能正常启动和登录,不要精简掉。systemd-sysv尤其关键,它提供/sbin/init程序,没有它系统启动时会报"Failed to execute /sbin/init"。

然后创建用户、设置密码、启用 SSH:

useradd -m -s /bin/bash ubuntu echo "ubuntu:ubuntu" | chpasswd echo "root:root" | chpasswd usermod -aG sudo ubuntu # 允许 root 使用 SSH(按需开启,日常建议保持禁用) echo "PermitRootLogin yes" >> /etc/ssh/sshd_config systemctl enable ssh

设置时区和语言环境:

ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime echo "Asia/Shanghai" > /etc/timezone sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen locale-gen update-locale LANG=en_US.UTF-8

配置 fstab。这一步非常关键,而且容易踩坑。fstab 决定了内核挂载根文件系统时,哪些分区要自动挂载:

cat > /etc/fstab <<EOF # <file system> <mount point> <type> <options> <dump> <pass> PARTUUID=XXXX-01 /boot vfat defaults 0 2 PARTUUID=XXXX-02 / ext4 defaults 0 1 EOF

那个PARTUUID=XXXX-01现在先占位,等你确定了分区布局和磁盘的 PARTUUID 之后,再替换成真实值。这一步千万别偷懒直接写/dev/mmcblk0p1这种设备名,因为设备名在不同的启动阶段、不同的 USB 插拔顺序下会变化(/dev/sda 变成 /dev/sdb 是常有的事),但 PARTUUID 在同一个磁盘内是固定不变的。

配置网络。如果目标是树莓派这种需要 WiFi 的场景,NetworkManager 更适合。如果是工控机接网线,networkd 也够用。我个人习惯全用 NetworkManager,因为它提供了nmcli,后续排查网络非常直观:

systemctl enable NetworkManager

清理 APT 缓存和临时文件:

apt clean rm -rf /var/lib/apt/lists/* rm -rf /tmp/*

然后退出 chroot:

exit

到这里,rootfs 的构建已经完成。你可以用du -sh ~/image-build/rootfs看一眼体积,minbase 加上述软件包通常会在 700MB~1GB 之间。如果你的目标存储空间有限,可以用apt-get autoremove --purge再清一轮,但不要手动删库文件,否则后面安装任何包都会报依赖错误。

4. 设计分区布局并制作镜像文件

4.1 计算镜像容量:留足余量是基本原则

在创建空白镜像文件之前,先算一算需要多大空间。一个基础 Ubuntu rootfs 约 1GB,但我们还需要 boot 分区、交换分区(可省)、以及系统运行时的日志和临时文件空间。

我推荐这么算:

总大小 = rootfs 当前体积 * 1.5 + boot 分区大小(建议 256MB~512MB) + 200MB 冗余

比如 rootfs 是 1GB,那镜像文件建议做到 2GB 左右。如果你要预装 Docker、开发工具链,rootfs 体积会膨胀到 3~4GB,镜像就按同样的比例扩大。

注意镜像文件不是越大越好。写入时间、SD 卡兼容性、后续压缩传输的耗时都跟大小正相关。够用就行,不要拍脑袋建一个 16GB 的镜像,除非你的需求里确实有那么多数据要放。

用truncate创建稀疏文件是比较推荐的做法,它不会立即占用真实的磁盘空间,只在写入数据时按需分配:

truncate -s 2G ~/image-build/ubuntu.img

4.2 用 fdisk 划分 boot 分区与 root 分区

接下来给这个空白镜像写入分区表。我采用经典的两分区布局:第一个是 FAT32 格式的 boot 分区,存放内核、设备树、引导文件;第二个是 ext4 格式的 root 分区,存放整个 rootfs。

为什么不把 /boot 直接放在 ext4 根分区里?因为某些 ARM 平台的引导程序(比如树莓派的 VideoCore GPU 固件、Rockchip 的 miniloader)并不会解析 ext4 文件系统,它们只认 FAT32。这个约束不是 Ubuntu 设计的,而是底层 SoC 的 BootROM 决定的。理解了这个逻辑,你就知道为什么大多数 ARM 板卡的镜像顶部分区都是 vfat。

# 挂载镜像为 loop 设备,得到 /dev/loop0 sudo losetup -fP --show ~/image-build/ubuntu.img sudo fdisk /dev/loop0

在 fdisk 交互界面中依次输入以下命令:

  • o:新建一个空的 DOS 分区表(MBR)
  • n→p→1:创建主分区 1,起始扇区默认(2048,即 1MB 对齐),大小输入+512M
  • t→ 输入c:把分区 1 的类型改为 W95 FAT32 (LBA)
  • n→p→2:创建主分区 2,起始扇区默认,大小直接回车(用完剩余空间)
  • w:写入分区表并退出

执行完w后,运行lsblk /dev/loop0,能看到loop0p1和loop0p2两个分区节点。如果你像我一样用了新版 util-linux,fdisk 可能会默认创建一个 GPT 分区表,那也没问题,ARM 引导程序大多兼容 GPT,只是个别老旧的 SoC BootROM 不认,建议还是用 MBR 最稳。

给两个分区创建文件系统:

sudo mkfs.vfat -F 32 -n bootfs /dev/loop0p1 sudo mkfs.ext4 -L rootfs /dev/loop0p2

-L是给文件系统写卷标。卷标对后续定位分区有一定帮助,也方便你忘了哪个分区是干嘛的时候用blkid一眼认出来。

4.3 用 rsync 把 rootfs 灌入镜像

文件系统建好后,把镜像的分区挂载到临时目录,把构建好的 rootfs 同步过去。这一步我强烈推荐用rsync而不是cp -a,因为 rsync 对大目录的同步效率更高,而且如果中途失败,重跑时能够断点续传。

mkdir -p /mnt/rootfs_mnt sudo mount /dev/loop0p2 /mnt/rootfs_mnt sudo rsync -aHAX ~/image-build/rootfs/ /mnt/rootfs_mnt/ sudo mkdir -p /mnt/rootfs_mnt/boot sudo mount /dev/loop0p1 /mnt/rootfs_mnt/boot

注意-a保留权限和符号链接,-H保留硬链接(Ubuntu 某些库文件之间是硬链接关系,丢了会导致系统异常),-X保留扩展属性(SELinux 标签、ACL 等,Ubuntu 默认不强制 SELinux,但保留没有坏处)。

同步完成之后,把 qemu 静态文件从 rootfs 中删除(已经是真实目标平台了,不需要模拟器):

sudo rm -f /mnt/rootfs_mnt/usr/bin/qemu-aarch64-static

然后把 fstab 里的 PARTUUID 占位符换成真实值:

sudo blkid /dev/loop0p1 sudo blkid /dev/loop0p2

拿到两个分区的 PARTUUID 后,编辑/mnt/rootfs_mnt/etc/fstab替换占位字符串。

如果你的 target 平台需要特殊的内核与设备树文件,比如树莓派的kernel8.img、bcm2711-rpi-4b.dtb,或者 RK3588 的boot.img、dtb/rockchip/*.dtb,把它们拷贝到/mnt/rootfs_mnt/boot/对应位置。这部分内容因硬件而异,我在第六章里会单独细说。

确认无误后卸载所有挂载点:

sudo umount /mnt/rootfs_mnt/boot sudo umount /mnt/rootfs_mnt sudo losetup -d /dev/loop0

到这一步,ubuntu.img已经是一块具备完整 Ubuntu 系统的"虚拟硬盘"了。可以随时用 dd 写入 SD 卡或硬盘。

4.4 验证镜像完整性后再烧录

烧录前先做一次全力检查。重新把镜像挂到 loop 设备上,跑一遍文件系统检查:

sudo losetup -fP --show ~/image-build/ubuntu.img sudo fsck -f /dev/loop0p1 sudo fsck -f /dev/loop0p2

如果没有输出错误,说明文件系统是健康的。接着挂载检查关键文件是否存在:

sudo mount /dev/loop0p2 /mnt/rootfs_mnt ls /mnt/rootfs_mnt/bin/init 2>/dev/null || ls /mnt/rootfs_mnt/sbin/init

只要init存在(通常是指向 systemd 的符号链接),系统启动的第一关就过了。全部验证完后记得卸载写保护:

sudo umount /mnt/rootfs_mnt sudo losetup -d /dev/loop0

5. 烧录到 SD 卡:工具、命令与防呆操作

5.1 最稳妥的方式还是 dd,但它要求你足够小心

Linux 下最直接、也最容易被骂的方式就是 dd。它在烧录时不做任何校验,成败全靠镜像本身质量和设备的正确性。正因如此,烧录前必须确认目标设备名,这一步做错,后果是灾难性的。

我的防呆流程是这样的:

# 1. 先看当前有哪些块设备 lsblk -o NAME,SIZE,MODEL,TRAN # 2. 插入 SD 卡后,再次执行 lsblk,找到新增的那一项 lsblk -o NAME,SIZE,MODEL,TRAN

TRAN列能看到设备传输类型,usb、sata、mmc一目了然。新增的那个设备名就是我们需要的,比如/dev/sdb。注意是 /dev/sdb 而不是 /dev/sdb1,dd 必须整盘写入,写分区会丢失分区表。

另外可以用一个更直观的方式确认:插卡前后各执行一次lsblk,用 diff 对比输出,新增的设备就是目标。这一步多花十秒钟,能避免手滑覆盖整块硬盘。

确认目标无误后执行:

sudo dd if=~/image-build/ubuntu.img of=/dev/sdb bs=4M status=progress oflag=sync conv=fsync

参数说明:

  • bs=4M:每次读写 4MB,是性能和稳定性的主流平衡点
  • status=progress:显示实时进度
  • oflag=sync conv=fsync:确保数据真正写入物理介质后才返回,避免 write-back 缓存导致的假完成

dd 结束后,执行sync再等待几秒钟,然后查看分区是否被识别:

sudo partprobe /dev/sdb lsblk /dev/sdb

如果看到两个分区,并按预期大小出现,烧录成功。

5.2 Windows/macOS 下的替代烧录工具

如果你的开发环境是 Windows,最省心的工具是 balenaEtcher。它提供可视化界面,选中镜像、选中目标设备、烧录,不容易出错。它默认也会在写入后做一遍校验。

Rufus 也可以,但对"整盘镜像写入"的支持不如 Etcher 直观,Rufus 更适合做安装 U 盘。至于 Win32DiskImager,在读写速度上明显偏慢,如果不是老项目沿用,不建议再用了。

macOS 下同样推荐 Etcher,或者用命令行dd,命令格式和 Linux 类似,只是目标设备名是/dev/rdisk2这样带r前缀的原始磁盘节点。对 macOS 的 dd 一定要多加小心,macOS 的设备命名方式和 Linux 完全不同,/dev/disk0 是系统盘,烧错几乎等于重装电脑。

建议无论用什么工具,烧录完成后都执行一次fsck或重新挂载检查文件,这是很多教程不会强调的步骤。

5.3 烧录后的第一次启动验证清单

插卡上电之前,先把验证清单理清,避免启动失败后手忙脚乱:

  • 串口或 HDMI 输出是否能看到 U-Boot 日志(ARM 平台通常是串口,树莓派可以直接 HDMI)
  • 内核是否解压并挂载 rootfs(看到VFS: Mounted root (ext4 filesystem)就说明分区没问题)
  • systemd 是否启动到 multi-user.target(能看到登录提示符即可)
  • 网络是否正常,能否 ping 通网关

如果卡在某个位置,优先查看 U-Boot 传递的内核参数。ARM 平台的/boot里通常会有一个cmdline.txt或者是 extlinux.conf 文件,里面的root=参数必须指向我们配置的 root 分区。比如:

console=ttyS0,115200 root=PARTUUID=XXXX-02 rw rootwait

rootwait参数很关键。它告诉内核等待存储设备就绪后再挂载根文件系统,没有它,SD 卡这类慢速设备经常在启动早期还不可见,导致内核 panic。

6. 我在实际烧录中踩过的坑与解决记录

6.1 PARTUUID 与设备名的混用问题

我最早做树莓派镜像时,直接在 fstab 里写了/dev/mmcblk0p1。在树莓派上相安无事,因为它的 SD 卡接口固定是 mmcblk0。但同一套镜像拿到 USB SD 读卡器启动的 x86 工控机上,内核把 SD 卡识别成了/dev/sda,fstab 里写的/dev/mmcblk0p1根本不存在,启动直接进入 initramfs 紧急 shell。

后来我统一改成 PARTUUID,无论设备名怎么变,只要分区属于同一块磁盘,PARTUUID 就不变。改完后再也没出现过这类问题。

具体操作是:先在宿主机上拿到两个分区的 PARTUUID,烧录后在目标机上用blkid核对一致,再启动。这样跟设备名彻底解耦。

6.2 烧录目标选错,覆盖了数据盘

这个教训比较惨痛。当时我同时插着 USB 移动硬盘和 SD 卡读卡器,lsblk看了一眼,看到/dev/sdb大小接近就下了 dd 命令,结果把移动硬盘的数据全写了。从那以后我形成了一套自己的强制规范:

  • 烧录前先弹出所有不相关的 USB 存储设备
  • 用lsblk -o NAME,SIZE,MODEL,TRAN确认型号,而不仅仅是大小
  • 执行 dd 前再跑一次fdisk -l /dev/sdX看分区表,跟记忆中的目标做交叉验证

这套流程看起来啰嗦,但在批量烧录几十张卡的场景下,多花的一点时间完全可以接受。毕竟任何一张卡烧错,返工成本都远高于这十几秒。

6.3 镜像文件越来越大:稀疏文件与压缩的取舍

用truncate创建的稀疏文件,虽然在文件系统层面不占空间,但一旦被 dd 写到 SD 卡,整张卡都会被填满(未写入的区域是零)。如果只给它分配 2GB 的空间,但 rootfs 只用了 1.2GB,那剩余 800MB 全是零,既浪费存储也不优雅。

更好的做法是:在镜像制作阶段就只建"够用"的大小,多出的空间等烧录后用growpart扩展到 SD 卡的真实容量。ARM 平台一般需要 initramfs 里带 growpart 工具,或者在首次启动时通过 systemd 服务自动扩展。如果你需要这个能力,在 chroot 阶段就安装cloud-guest-utils和growpart,然后在/etc/systemd/system/下写一个 one-shot 的扩展服务。

分发镜像时,用压缩能够显著减小传输体积。对全零区域,xz的压缩比非常高:

xz -9 -k ubuntu.img

压缩后通常能减少一半以上体积。接收方解压后直接 dd 即可。发布镜像的同时附上sha256sum,让使用者校验完整性,能避免很多"镜像损坏"类的假故障。

6.4 ARM 平台引导程序的差异:不是有 rootfs 就能启动

这是专门提醒想给 ARM 开发板做镜像的读者。不同厂商的引导方式差别很大:

  • 树莓派:引导流程是 BootROM 读取 SD 卡第一个 FAT 分区里的start4.elf和fixup4.dat,再由这些固件加载内核。所以树莓派的 /boot 里必须有这两个文件,缺一个都起不来。
  • Rockchip(RK3399/RK3588):通常需要 idbloader.img、u-boot.itb 等引导文件,放在分区起始的特定偏移位置,用 dd 单独写入,而不是简单地拷贝。官方 SDK 里一般有update.img的制作脚本,会一并处理。
  • Allwinner(全志):需要boot.scr和uEnv.txt之类的 U-Boot 脚本,指定内核加载地址和设备树文件。

如果你只是想把 Ubuntu 装到树莓派上,直接用树莓派官方 Ubuntu 镜像套壳修改是最省力的路径。如果一定要从零做,需要先去厂商的 SDK 文档里确认引导文件布局,这部分内容没法在本文里统一覆盖,因为每一家的 BootROM 行为都不同。

7. 把镜像做成"可升级"的分发物

做好的镜像通常是给多台设备用的。直接发一个 2GB 的 ubuntu.img 当然可以,但在实际工程里,我更推荐把"镜像"和"权限还原"分开管理。

具体做法是:保留一份最小化的"基础镜像"(包含最基本的系统、网络、SSH),需要部署新机器时,把基础镜像烧录进去,然后通过 Ansible 或 Shell 脚本注入每台机器的独立配置(hostname、IP、SSH key、业务软件)。这样团队里的其他人拿到镜像后,不需要再关心系统内部结构,只需要跑一遍配置脚本即可。这个模式在公司内批量部署工控机时尤其好用。

另外,镜像制作流程本身值得用脚本固化下来。我自己把前文的步骤整理成了一个build.sh,每次要出新版本 Ubuntu 的系统时,只需改一下版本代号和目标架构,跑一遍就能得到新镜像。这样既能追 Ubuntu 的版本更新,也能保证流程的可重复性。脚本化还有另一个好处:如果有人问你"这个镜像里做了哪些定制",把脚本给他看,比对着系统一个个翻命令要清晰得多。

如果你打算长期维护多个镜像(比如同时维护 arm64 和 amd64),建议给每个镜像都配一个version文件,记录创建时间、基础版本、已装软件包列表。这个文件在排查问题时能省下大量时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询