从USB存储启动树莓派

默认情况下树莓派Raspberry Pi是从SD卡启动系统的,但是SD卡存储容量有限,如果能够转换成磁盘启动(SSD/HDD)则可以实现一个海量的存储系统。

树莓派没有内建的SATA接口,如果要接磁盘设备,采用的是USB接口连接的磁盘设备。

最初我以为树莓派可以通过扩展卡来支持SATA磁盘,后来参考Raspberry Pi: Adding an SSD drive to the Pi-Desktop kit,发现类似的解决复方案是通过一种USB连接卡转换成 mSATA 或者 SATA接口,这样就类似于常用的USB接口移动硬盘连接,但使用转接卡可以获得紧凑的转接。

mSATA是SATA技术整合在小尺寸装置的接口控制器规范。目前笔记本电脑开始使用这种接口用于固态硬盘。

当前SSD固态硬盘价格还远高于HDD磁盘,在组建大容量存储用于存储离线数据或非高性能访问要求数据的,可以考虑采用低廉的HDD组建。

个人组建存储集群,建议采用的HDD,结合开源存储技术如Ceph,可以构建海量存储容量的NAS以及TimeMachine(用于Apple系列产品数据备份),甚至可以组建个人的云计算存储。

硬件

理论上直接将USB移动硬盘连接到树莓派也可以作为硬盘使用,但是为了方便和紧凑,我使用了硬件SupTronics X820扩展卡。这个HDD扩展卡可以从淘宝上购买。

SupTronics X820扩展卡 Top view

安装过程请参考SupTronics X820扩展卡安装手册,主要是安装顺序(固定树莓派的4个螺钉是在硬盘下方,所以要先插上螺钉后才能安装硬盘)。另外需要注意电源线连接,红色火线需要插在Pin4(5V电压)黑色地线插在Pin6 - GPIO接口可以参考微软的Windows10 IoT Raspberry Pi 2 & 3 Pin Mappings

Raspberry Pi 2 & 3 Pin Mappings

设置USB启动模式

在设置树莓派3能够从磁盘启动之前,它首先需要从SD卡启动并配置激活USB启动模式。这个过程是通过在树莓派SoC的OTP(One Time Programmable)内存设置一个激活从USB存储设备启动的。

一旦这个位设置,就不再需要SD卡。但是要注意:任何修改OTP都是永久的,不能撤销。

  • 通过更新准备/boot目录:

如果已经使用了Raspbian / Raspbian Lite 的 2017-04-10 发布版,则上述更新系统不是必须的。

  • 使用以下命令激活USB启动模式

以上命令在/boot/config.txt中添加了program_usb_boot_mode=1

使用sudo reboot命令重启树莓派,然后检查OTP是否已经设置成可编程:

确保输出是17:3020000a。如果不是这个输出值,责表示OTP还没有成功设置为可编程。

然后就可以从config.txt最后删除掉program_usb_boot_mode,这样你把这个SD卡插到其他树莓派上不会导致其进入USB启动模式。

注意,确保config.txt配置文件最后没有空白行。

准备USB存储设备

2017-04-10版本开始,可以通过复制方式将操作系统镜像直接安装到USB存储器。这种方式也可以针对一个SD卡。

由于我们已经在树莓派快速起步过程中在SD卡上安装了Raspbian,现在我们可以从已经安装了系统的SD卡中直接复制到磁盘中,就可以保留原先所有做过的更改。

这个过程和从Raspbian镜像复制到SD卡相似,只是需要注意磁盘的命令,一定要确保是从SD卡复制到磁盘。

当前在SD卡中的分区如下:

  • 划分USB磁盘分区

使用fdisk对磁盘分区,则msdos分区表就不会建立PARTUUID。但是我测试直接使用UUID替代PARTUUID来指引设置启动分区没有成功。所以改为parted来划分分区,看看能否增加PARTUUID

4k对齐参考How to align partitions for best performance using parted,其中参数查看/dev/sda,所以第一个分区起始扇区选择2048s:

注意:这里划分/dev/sda2只分配30G给操作系统使用,因为我准备把剩余的空间作为存储空间,将在后续使用卷管理来维护,并构建Ceph存储。

此时退出parted程序,可以看到parted工具在划分磁盘分区时候,确实创建了PARTUUID(神奇):

但是,此时看不到UUIDUUID则在mkfs.ext4 /dev/sda2之后就会标记上。

我感觉使用tar方法也应该能够从SD卡中复制出完整的磁盘分区内容到USB磁盘。和普通的PC不同,树莓派会默认尝试搜索可以启动的分区(默认会从SD卡启动,15秒之后将尝试从USB存储启动,即前面修改的配置)。

注意:一定要有一个fat分区用于存放/boot分区内容,因为UEFI启动默认会寻找vfat分区内容来启动。

  • 如果使用dd命令复制磁盘分区,所以要确保/dev/sda2磁盘分区大于源SD卡分区/dev/mmcblk0p2

  • 如果使用tar方式复制磁盘文件系统,则目标分区只要能够容纳源/dev/mmcblk0p2文件就可以

通过dd复制磁盘(我没有采用这个方法)

如果使用dd复制磁盘,责执行操作系统复制命令如下(不需要区分磁盘分区):

注意/dev/mmcblk0p1vfat文件系统,挂载成/boot目录。UEFI启动需要使用一个单独的FAT分区。

dd复制命令参考了在Linux中制作镜像到SD卡的命令 INSTALLING OPERATING SYSTEM IMAGES ON LINUX

通过tar复制磁盘(实践成功)

如果使用tar方式复制磁盘文件

注意:上述备份的/backup.tar.gz没有包含/boot分区内容。需要先挂载/mnt/boot分区之后,再从源分区复制(这个分区是启动分区,必须是vfat文件系统)

注意:在执行了mkfs.ext4 /dev/sda2之后,再使用blkid /dev/sda2就能够看到UUID,这个UUID是文件系统UUID:

注意:要避免包含目录,使用--exclude参数。参考Exclude Multiple Directories When Creating A tar Archive。但是我使用如下命令依然包含了不需要的目录(失败),最后还是参考了Ubuntu的使用tar方式备份和恢复系统来实现tar方式复制系统成功。

配置修改

注意:除非使用dd来复制SD卡到HDD才能保持原有的PARTUUID,否则使用parted划分分区以及使用mkfs创建文件系统,都会使得目标磁盘的UUIDPARTUUID变化。则需要修改启动配置文件反映分区标识的变化。

  • 检查当前SD卡的分区UUID,例如如下:

有点疑惑:/dev/mmcblk0使用parted检查显示是msdos分区表,但是使用blkid检查可以看到具有PARTUUID。参考Persistent block device naming,原文介绍GPT分区表支持PARTUUID。不过,我实践发现树莓派默认安装的系统使用的是msdos分区表,但是也具有PARTUUID。测试验证发现,通过使用parted工耦划分磁盘分区就会有PARTUUID

以下是/dev/mmcblk0partedprint输出

上述可以看到

| 分区 | PARTUUID | | /dev/mmcblk0p1 | 5e878358-01 | | /dev/mmcblk0p2 | 5e878358-02 |

我测试发现,如果使用dd命令来复制磁盘分区,则HDD磁盘的/dev/sda1/dev/sda2PARTUUID会和原先的TF卡完全相同,即依然保持5e878358-015e878358-02。这样就不用修改HDD文件系统中的配置。

但是通过磁盘partedmkfs.ext4创建的HDD文件系统,然后再通过tar恢复操作系统。此时磁盘PARTUUIDUUID不同,则要修改对应配置/boot/cmdline.txt/etc/fstab。例如:

  • 检查/boot/cmdline.txt配置文件,可以看到原先配置内容如下

这里可以看到root=PARTUUID=5e878358-02就是SD卡的分区/dev/mmcblk0p2对应的PARTUUID="5e878358-02"

  • 根据前述检查USB磁盘的分区UUID,即e3f5b3fb-297c-44fe-b763-566b51b87524,注意,我们要将启动指向分区/dev/sda2,因为这个分区就是从/dev/mmcblk0p2通过tar方式复制出来的。修改/mnt/boot/cmdline.txt(该文件位于/dev/sda2这个HDD分区文件系统中)

这里修改了2个地方:

  • root=PARTUUID=e3f5b3fb-297c-44fe-b763-566b51b87524 指向HDD磁盘分区/dev/sda2表示从USB外接的硬盘启动

  • evevator=cfq 是修改原先针对SSD/SDCARD/TFCARD这类固态硬盘优化参数deadline,现在修改成针对HDD硬盘优化参数cfq

  • 修改/mnt/etc/fstab配置文件,修改/行中PARTUUID内容

  • 关机,然后取出TF卡,再次加电,此时树莓派将从外接USB的HDD磁盘启动

测试下来,如果再次使用TF卡,依然能够优先从TF卡启动树莓派。只有TF卡不可用时候,才会从USB HDD启动。

参考

Last updated

Was this helpful?