当我最近读到Alan Formy-Duval的文章时 用Cockpit管理你的Raspberry Pi的文章,我认为有一个已经预装了Cockpit的镜像是个好主意。幸运的是,至少有两种方法可以完成这项任务。
- 适应树莓派操作系统图像构建工具链pi-gen的来源,它使你能够从头开始构建一个树莓派图像。
- 将你正在运行的、经过修改的Raspberry Pi操作系统转换为别人可以使用的图像。
这篇文章涵盖了这两种方法。我将强调每种技术的优点和缺点。
Pi-gen
让我们从pi-gen开始。在我们开始之前,你需要考虑一些先决条件。
前提条件
为了成功运行构建过程,建议使用32位版本的Debian Buster或Ubuntu Xenial。它也可能在其他系统上运行,但为了避免不必要的麻烦,我建议用推荐的系统之一设置一个虚拟机。如果你对虚拟机不熟悉,可以看看我的文章《用VirtualBox在任何操作系统上尝试Linux》。当你把一切都准备好并运行时,还要安装软件库描述中提到的依赖项。还要考虑到你需要在虚拟机中接入互联网和足够的可用磁盘空间。我为我的虚拟机设置了一个40GB的硬盘,这似乎已经足够了。
为了遵循本文的说明,请克隆pi-gen仓库,如果你想开始开发你自己的镜像,请分叉它。
仓库概述
整个构建过程被分成几个阶段。每个阶段都是一个普通的文件夹,代表了一个完整的Raspberry Pi操作系统镜像的逻辑中间环节。
- 阶段0:Bootstrap-创建一个可用的文件系统
- 阶段1:最小的系统--创建一个绝对最小的系统
- 阶段2:精简系统-对应于Raspberry Pi OS Lite。
- 阶段3:桌面系统-安装X11、LXDE、网络浏览器等。
- 阶段4: 对应于普通的树莓派操作系统
- 阶段5。对应于Raspberry Pi OS Full
这些阶段是相互建立的。如果不建立低级阶段,就不可能建立高级阶段。你也不能遗漏中间的一个阶段。例如,要建立一个Raspberry Pi OS Lite,你必须建立0、1和2阶段。要建立一个带桌面的Raspberry Pi操作系统,你必须建立0、1、2、3、4和5阶段。
构建过程
构建过程是由build.sh
,它可以在根仓库中找到。如果你已经知道如何读和写bash脚本,那么理解那里定义的过程就不会有什么障碍。如果没有,阅读build.sh
,并试图理解正在发生的事情,确实是一个很好的做法。但是,即使没有bash脚本技能,你也能用预装的Cockpit创建自己的镜像。
一般来说,构建过程由几个嵌套的for-loop组成。
- **阶段性循环。**按升序循环处理所有阶段性目录
- 如果发现一个名为_SKIP_的文件,则跳过进一步处理
- 运行脚本
prerun.sh
- **子循环的变量。**按升序循环每个子目录,如果有以下文件,就处理这些文件。
-
00-run-sh
:预先运行的任意指令00-run-chroot.sh
: 在镜像的chroot目录下运行该脚本00-debconfs
:变量为[debconf-set-selection](https://wiki.debian.org/PackageManagement/Preseed)
00-packages
: 一个要安装的软件包的列表00-packages-nr
: 类似于_00-packages_,除了这将导致安装时使用 --no-install-recommends -y 参数给_apt-get_
00-patches
: 一个包含要应用的补丁文件的目录,使用quilt- 回到阶段性循环中,如果发现一个名为
EXPORT_IMAGE
的文件,为这个阶段生成一个镜像 - 如果发现一个名为
SKIP_IMAGE
的文件,跳过创建镜像。
-
build.sh
还需要一个名为config
的文件,其中包含一些启动时读取的规范。
实践
首先,我们将创建一个基本的Raspberry Pi OS Lite镜像。这个Raspberry Pi OS Lite镜像将作为我们自定义镜像的基础。创建一个名为_config_的空文件并添加以下两行。
IMG_NAME='Cockpit'
ENABLE_SSH=1
在目录stage3
,stage4
, 和stage5
中创建一个名为SKIP
的空文件。Stages 4
和5
默认会发出一个图像,因此在stage4
和stage5
中添加一个名为SKIP_IMAGE
的空文件。
现在打开一个终端,通过输入su
,切换到根用户。导航到版本库的根目录,通过键入./build.sh
启动构建脚本。
构建过程将需要一些时间。
构建过程结束后,你会发现在版本库的根目录下还有两个目录:work
和deploy
。work
文件夹包含一些中间的输出。在deploy
文件夹中,你应该找到压缩后的镜像文件,准备进行部署。
如果整个构建过程是成功的,我们现在可以修改这个过程,使其额外地安装Cockpit。
扩展构建过程
Raspberry Pi OS Lite镜像是我们安装Cockpit的基础。由于Raspberry Pi OS Lite镜像中包含了stage2
,我们将创建我们自己的stage3
,它将处理Cockpit的安装。
我们完全删除原来的stage3
,并创建一个新的、空的stage3
。
rm -rf stage3 && mkdir stage3
在stage3
,我们创建一个子阶段来安装cockpit。
mkdir stage3/00-cockpit
要在图像上安装cockpit,我们只需要将其添加到软件包列表中。
echo "cockpit" >> stage3/00-cockpit/00-packages
我们还想配置我们的新stage3
,以输出一个图像,因此我们只需在stage3
目录中添加这个文件。
touch stage3/EXPORT_IMAGE
由于之前的构建过程中已经有了中间图像,我们可以通过在相关目录中添加skip-files
,来防止这些阶段再次被构建。
跳过stage0
和stage1
的构建过程。
touch stage0/SKIP && touch stage1/SKIP
跳过stage2
的构建过程,也跳过图像的创建。
touch stage2/SKIP && touch stage2/SKIP_IMAGE
现在再次运行构建脚本。
./build.sh
在deployment
文件夹中,你现在应该找到一个压缩的镜像-Cockpit-lite.zip
,它已经准备好进行部署。
疑难解答
如果你试图应用更复杂的修改,在用pi-gen构建你自己的Raspberry Pi镜像时,会有很多的尝试和错误。你肯定会遇到构建过程会因为某些原因而停止的情况。由于在构建过程中没有异常处理,我们确实有一些手动的清理工作,以防过程停止。
很可能在进程停止后,chroot
文件系统仍然被挂载。如果不解除挂载,你将无法启动一个新的构建进程。在它仍然被挂载的情况下,通过打字手动解除挂载。
umount work//tmpimage/
我确定的另一个问题是,当chroot
文件系统即将被卸载时,脚本停止了。在文件scripts/qcow2_handling
中,你可以看到,在试图卸载sync
之前,直接调用了Sync
,迫使系统刷新了写缓冲区。作为一个虚拟机运行构建系统,当unmount
被调用时,写入过程还没有准备好,所以脚本在这里停止了。
为了解决这个问题,我只是在sync
和unmount
之间插入了一个sleep
,这就解决了问题。
(我知道30秒的时间太长了,但由于整个构建过程需要20分钟以上,30秒只是沧海一粟而已)
修改现有图像
与使用pi-gen
构建镜像相比,你也可以直接在运行中的Raspberry Pi操作系统上应用修改。在我们的方案中,只需登录并使用以下命令安装Cockpit。
sudo apt install cockpit
现在关闭你的Raspberry Pi,取出SD卡,并将其连接到你的电脑上。通过输入lsblk -p
,检查你的系统是否已经自动安装了SD卡上的分区。
在上面的截图中,SD卡的设备是/dev/sdc
,boot
- 和rootfs
- 分区被自动挂载在上述挂载点。在你继续进行之前,用.NET Framework卸载它们。
umount /dev/sdc1 && umount /dev/sdc2
现在我们把SD卡的内容复制到我们的文件系统。确保你有足够的磁盘空间,因为镜像的大小将与SD卡相同。用下面的命令开始复制过程。
dd if=/dev/sdc of=~/MyImage.img bs=32M
一旦复制过程完成,我们就可以用PiShrink缩小镜像了。按照软件库中提到的安装说明进行安装。
wget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh
chmod +x pishrink.sh
sudo mv pishrink.sh /usr/local/bin
现在通过输入以下命令来调用脚本。
sudo pishrink.sh ~/MyImage.img
PiShrink将镜像的大小减少了近10倍。从以前的30GB到3.5GB。你仍然可以在上传或分享之前通过压缩来优化大小。
就这样,你现在可以分享和闪现这张图片了。
闪存图像
如果你想用Linux把你自己定制的Raspberry Pi图像闪回SD卡,请按照下面的步骤进行。
将SD卡放入你的电脑。如果之前已经安装了SD卡,你的系统可能会自动加载SD卡上的文件系统。你可以通过打开一个命令行并输入lsblk -p
来检查。
正如你在上面的截图中看到的,我的系统自动挂载了两个文件系统,boot
和rootfs
,因为这张SD卡已经包含了Raspberry Pi操作系统。在我们开始闪烁SD卡之前,我们必须先通过输入来卸载文件系统。
umount /dev/sdc1 && umount /dev/sdc2
lsblk -p
的输出应该是这样的,这样才能继续。
现在你可以将镜像闪存到SD卡上了。打开一个命令行并输入。
dd if=/path/to/image.img of=/dev/sdc bs=32M, conv=fsync
用bs=32M
,你指定SD卡是以32兆字节的块写入的,conv=fsync
强制进程物理地写入每个块。
如果成功,你应该看到这个输出。
完成了!现在你可以把SD卡放回Raspberry Pi,并启动它。
总结
本文介绍的两种技术都有其优点和缺点。虽然使用pi-gen
来创建你自己的Raspberry Pi图像比简单地修改一个现有的图像更容易出错,但如果你打算建立一个CICD管道,它是首选的方法。我个人最喜欢的显然是修改现有的图像,因为你可以直接确保你应用的变化是有效的。