前言
在之前的在Mac上安装Gentoo Linux这篇文章里面我成功的给现在手中的Mac安装上了Gentoo Linux,但是最近在看一些文件系统的资料看到了ZFS其中的特性很是让我心动,虽然没有在虚拟机上面做一些尝试但是呢其中的快照、写时复制、数据压缩的特性很让我心动。
如果你有了解过一定的文件系统的知识你可能会想为啥不选择Btrfs呢?而且btrfs的话内核是可以直接支持的(需要比较新的内核),这个问题也是一个好问题。因为在我之后的规划里面,我打算自建一个机房。出于成本的考虑采取的方案是存算分离,存储的部分就是打算用ZFS。对于存储来说,连续性检查自动修复,RAID就显得尤为重要,这里也是为了这个计划做一个铺垫。也许你还会说这些btrfs都有啊!为啥还要用ZFS呢?由于我现在的水品还是十分有限,现阶段更多的是调研和测试,后续真正决定要用哪个文件系统或者是组合还是要看最终的测试结果。
因为当前用的这个Mac已经有了系统并且正常使用了一段时间,为了保持数据不丢失我先备份了一下系统。关于如何备份系统可以参考我这篇文章备份你的Gentoo系统。
同时也趁着机会对之前的分区大小做一些调整:
分区 | 文件系统 | 大小 |
---|
/dev/nvme0n1p1 | fat32 | 128M |
/dev/nvme0n1p2 | zfs | All |
准备
这次安装需要如下物品:
- USB Livecd * 1 (Ubuntu 20.10 Desktop Livecd 选择这个就是因为支持ZFS Tools,我们需要用这个来初始化我们的pool)
- 另外一个电脑 (方便记录和寻找帮助)
- 备份硬盘(原来的系统备份)
更多准备阶段的内容可以参照这里在Mac上安装Gentoo Linux:环境准备
分区
首先我需要把之前的分区全部干掉,然后创建如下表的分区:
分区 | 文件系统 | 大小 |
---|
/dev/nvme0n1p1 | fat32 | 128M |
/dev/nvme0n1p2 | zfs | All |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| fdisk /dev/nvme0n1
Welcome to fdisk (util-linux 2.36).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help): p
Disk /dev/nvme0n1: 1.86 TiB, 2048408248320 bytes, 4000797360 sectors
Disk model: KXG50PNV2T04 KIOXIA
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: AFD9539F-0A10-4184-9E73-20652BA3D677
Device Start End Sectors Size Type
/dev/nvme0n1p1 2048 133119 131072 64M Linux filesystem
/dev/nvme0n1p2 133120 4000797326 4000664207 1.9T Linux filesystem
Command (m for help): d # 删除分区2
Partition number (1,2, default 2):
Partition 2 has been deleted.
Command (m for help): d # 删除分区1
Selected partition 1
Partition 1 has been deleted.
Command (m for help):
Command (m for help): w # 写入硬盘
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.
|
这里是把原来的分区全部都干掉了,接下来我们要按照上面的表格重新创建分区
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| fdisk /dev/nvme0n1
Welcome to fdisk (util-linux 2.36).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help): n # 创建新分区
Partition number (1-128, default 1):
First sector (34-4000797326, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-4000797326, default 4000797326): +128M # 大小为128M
Created a new partition 1 of type 'Linux filesystem' and of size 128 MiB.
Partition #1 contains a vfat signature.
Do you want to remove the signature? [Y]es/[N]o: Y # 因为之前有过分区这里要移除原来的签名
The signature will be removed by a write command.
Command (m for help): n
Partition number (2-128, default 2):
First sector (264192-4000797326, default 264192):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (264192-4000797326, default 4000797326): # 留空全部给这个分区
Created a new partition 2 of type 'Linux filesystem' and of size 1.9 TiB.
Command (m for help): w # 写入
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.
|
初始化分区
创建完成分区之后我们还需要对分区进行初始化
/dev/nvme0n1p1
1
| mkfs.vfat -F32 /dev/nvme0n1p1
|
这里swap的话我们没有设置成单独的一个分区,打算是测试一下在zfs里面使用swapfile会不会吧内存打满,如果有问题的话我再调整一下加一个swap分区。
创建ZFS池
这里需要注意一点是像是我们之前分区的/dev/nvme0n
这种设备或者是/dev/sda
这样的,如果插拔U盘之类的可能盘序会发生变化,但是ZFS池是不会意识到这个变化的,这样就会产生zfs池不可用的问题,我们需要一些方法来获取到磁盘的id并以此来创建我们的zfs池。
1
2
3
4
5
6
7
8
9
10
11
12
13
| ls -l /dev/disk/by-id
lrwxrwxrwx 1 root root 13 Feb 13 12:29 nvme-KXG50PNV2T04_KIOXIA_Y9IS103FTHDM -> ../../nvme0n1
lrwxrwxrwx 1 root root 15 Feb 13 12:32 nvme-KXG50PNV2T04_KIOXIA_Y9IS103FTHDM-part1 -> ../../nvme0n1p1
lrwxrwxrwx 1 root root 15 Feb 13 12:29 nvme-KXG50PNV2T04_KIOXIA_Y9IS103FTHDM-part2 -> ../../nvme0n1p2
lrwxrwxrwx 1 root root 13 Feb 13 12:29 nvme-eui.000000000000001000080d0200600f01 -> ../../nvme0n1
lrwxrwxrwx 1 root root 15 Feb 13 12:32 nvme-eui.000000000000001000080d0200600f01-part1 -> ../../nvme0n1p1
lrwxrwxrwx 1 root root 15 Feb 13 12:29 nvme-eui.000000000000001000080d0200600f01-part2 -> ../../nvme0n1p2
lrwxrwxrwx 1 root root 9 Feb 13 11:41 usb-ACASIS_MAC3E_000000000001-0:0 -> ../../sda
lrwxrwxrwx 1 root root 10 Feb 13 11:41 usb-ACASIS_MAC3E_000000000001-0:0-part1 -> ../../sda1
lrwxrwxrwx 1 root root 10 Feb 13 11:41 usb-ACASIS_MAC3E_000000000001-0:0-part2 -> ../../sda2
lrwxrwxrwx 1 root root 10 Feb 13 11:41 usb-ACASIS_MAC3E_000000000001-0:0-part3 -> ../../sda3
lrwxrwxrwx 1 root root 10 Feb 13 11:41 usb-ACASIS_MAC3E_000000000001-0:0-part4 -> ../../sda4
lrwxrwxrwx 1 root root 9 Feb 13 11:41 usb-APPLE_SD_Card_Reader_000000000820-0:0 -> ../../sdb
|
这里的KXG50PNV2T04_KIOXIA_Y9IS103FTHDM
就是我们的硬盘nvme-KXG50PNV2T04_KIOXIA_Y9IS103FTHDM-part1
就是我们创建作为未来的/boot
分区
当我们创建zfs池的时候尽可能选择用这种id的方式去创建比如说
1
| /dev/disk/by-id/nvme-eui.000000000000001000080d0200600f01-part2
|
现在我们创建一个加密的zpool:
1
| zpool create -f -o ashift=12 -o cachefile=/etc/zfs/zpool.cache -O compression=lz4 -O xattr=sa -O relatime=on -O acltype=posixacl -O dedup=off -O encryption=on -O keyformat=passphrase -m none -R /mnt/gentoo rock /dev/disk/by-id/nvme-eui.000000000000001000080d0200600f01-part2
|
根据提示输入密码(注意密码不会有回显,并不是键盘坏了)。
这条命令创建了一个名为 rock
的zfs池
使用的压缩算法为lz4
如果说你想创建一个不带加密的zpool可以去掉-O encryption=on -O keyformat=passphrase
这两个选项。
创建 rootfs zfs datasets
接下来我们要创建自己的rootfs,并且在上面打开加密功能
创建os和/
1
2
| zfs create -o mountpoint=none -o canmount=off rock/os
zfs create -o mountpoint=/ rock/os/gentoo
|
安装系统
从硬盘恢复
这次安装系统的话,是打算直接从硬盘恢复的。
首先插上备份好的硬盘。
1
2
3
4
5
6
7
8
9
10
11
12
| fdisk -l
........
Disk /dev/sdc: 232.88 GiB, 250055122432 bytes, 488388911 sectors
Disk model: 00AAJS-00B4A0
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 4096 bytes / 33553920 bytes
Disklabel type: dos
Disk identifier: 0x80f01a9c
Device Boot Start End Sectors Size Id Type
/dev/sdc1 2048 488388910 488386863 232.9G 83 Linux
|
这里看到我们的备份硬盘
我们要创建一个目录用来挂载这个分区:
打开LUKS分区
1
2
| cryptsetup luksOpen /dev/sdc1 backup
Enter passphrase for /dev/sdc1:
|
输入之前设置的密码
挂载分区
1
| mount -v /dev/mapper/backup /backup
|
还有一个分区我们要注意一下就是/boot
分区
创建boot分区的挂载点
1
| mkdir -pv /mnt/gentoo/boot
|
挂载分区
1
| mount -v /dev/nvme0n1p1 /mnt/gentoo/boot
|
将所有文件同步到zfs的dataset里面
1
| rsync -aAXv --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found","/backup/*","/swapfile"} /backup/ /mnt/gentoo
|
这个复制需要一定的时间,耐心等待完成,期间不要断开硬盘或者是拔掉电源。
chroot
等到复制原来的系统完成之后我们就需要进入chroot环境,然后完成其他的必要配置了。
拷贝ZFS池缓存文件
1
2
| mkdir -pv /mnt/gentoo/etc/zfs
cp -v /etc/zfs/zpool.cache /mnt/gentoo/etc/zfs
|
拷贝网络的DNS
1
| cp --dereference /etc/resolv.conf /mnt/gentoo/etc/
|
挂载必要的文件系统
1
2
3
4
5
| mount --types proc /proc /mnt/gentoo/proc
mount --rbind /sys /mnt/gentoo/sys
mount --make-rslave /mnt/gentoo/sys
mount --rbind /dev /mnt/gentoo/dev
mount --make-rslave /mnt/gentoo/dev
|
我们不是在官方的Livecd下面还需要执行:
1
2
3
| test -L /dev/shm && rm /dev/shm && mkdir /dev/shm
mount --types tmpfs --options nosuid,nodev,noexec shm /dev/shm
chmod 1777 /dev/shm
|
进入chroot
1
2
3
| chroot /mnt/gentoo /bin/bash
source /etc/profile
export PS1="(chroot) $PS1"
|
安装ZFS和配置必要项目
安装ZFS
首先是要给原来的系统安装上ZFS的支持
首先确保内核打开了Zlib的支持
1
2
3
4
5
6
7
| General Architecture Dependent Options --->
GCC plug ins --->
[ ] Randomize layout of sensitive kernel structures
Cryptographic API --->
<*> Deflate compression algorithm
Security options --->
[ ] Harden common str/mem functions against buffer overflows
|
我们还需要调整portage让ZFS包接收测试分支的包
1
2
| echo "sys-fs/zfs-kmod ~amd64" >> /etc/portage/package.accept_keywords/zfs-kmod
echo "sys-fs/zfs ~amd64" >> /etc/portage/package.accept_keywords/zfs
|
如果你想要使用实时的包可以:
1
2
| echo "=sys-fs/zfs-kmod-9999 **" >> /etc/portage/package.accept_keywords/zfs-kmod
echo "=sys-fs/zfs-9999 **" >> /etc/portage/package.accept_keywords/zfs
|
但是不推荐用最新的,要到处找patch。当然如果你是老手当我没说.jpg
安装ZFS
有一个很重要的点,每次更新内核或者是编译内核之后最好是重新构建一下模块,否则有可能遇到zpool无法正常初始化的问题
1
| emerge -va @module-rebuild
|
将zfs加入到开机启动项和对应的启动级别
1
2
3
4
| rc-update add zfs-import boot
rc-update add zfs-mount boot
rc-update add zfs-share default
rc-update add zfs-zed default
|
生成和验证zfs hostid
文件
这个文件是用于genkernel
生成initramfs
和zfs导入池的时候验证完整性时候需要的
1
2
| zgenhostid
file /etc/hostid
|
Bootload fstab and Initramfs
GRUB
修改grub的配置文件,内容如下
1
| GRUB_CMDLINE_LINUX="dozfs root=ZFS=rock/os/gentoo"
|
安装bootload
1
| grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=Gentoo
|
生成配置文件
1
| grub-mkconfig -o /boot/grub/grub.cfg
|
fstab
挂载的工作这次交给zfs来去完成,我们这里还需要修改一下fstab
首先查看分区的id
1
2
3
| blkid
/dev/nvme0n1p1: UUID="129F-3405" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="537bd932-cc1f-5643-a297-feebaeb6a5ea"
/dev/nvme0n1p2: LABEL="rock" UUID="7999529021869478878" UUID_SUB="11607083434113154920" BLOCK_SIZE="4096" TYPE="zfs_member" PARTUUID="d68d6b86-a407-4141-a1a7-1379cbe1e049"
|
可以看到我们的/boot
分分区UUID是129F-3405
,现在可以修改/etc/fstab
内容如下
1
2
| # /dev/nvme0n1p1
/dev/nvme0n1p1 /boot vfat rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro 0 2
|
initramfs
重新生成initramfs
之前的initramfs
没有zfs的支持这次要加上,为了保证工作正常还需要将genkrenel
切换到testing分支
1
| echo "=sys-kernel/genkernel-9999 **" > /etc/portage/package.accept_keywords/genkernel
|
更新genkernel
重新生成initramfs
文件
1
| genkernel initramfs --zfs --compress-initramfs
|
重启
在重启验证之前我们需要先做一些清理工作
首先退出chroot环境
卸载备份分区
关闭backup
卷
1
| cryptsetup luksClose backup
|
然后就可以重启啦
故障排除
进入到livecd之后,打开zpool
设置rootfs
的挂载点
1
| zfs set mountpoint=/mnt/gentoo rock/os/gentoo
|
打开rock/os/gentoo
dataset
挂载/boot
分区
1
| mount /dev/nvme0n1p1 /mnt/gentoo/boot/
|
进入chroot
挂载必要的文件系统
1
2
3
4
5
| mount --types proc /proc /mnt/gentoo/proc
mount --rbind /sys /mnt/gentoo/sys
mount --make-rslave /mnt/gentoo/sys
mount --rbind /dev /mnt/gentoo/dev
mount --make-rslave /mnt/gentoo/dev
|
我们不是在官方的Livecd下面还需要执行:
1
2
3
| test -L /dev/shm && rm /dev/shm && mkdir /dev/shm
mount --types tmpfs --options nosuid,nodev,noexec shm /dev/shm
chmod 1777 /dev/shm
|
进入chroot
1
2
3
| chroot /mnt/gentoo /bin/bash
source /etc/profile
export PS1="(chroot) $PS1"
|
结束维护之后
退出chroot
卸除挂载
将rootfs
的挂载点改回来
1
| zfs set mountpoint=/ rock/os/gentoo
|
重启
问题
在重启后你会发现在initramfs
阶段我们的rootfs
没有被解锁,不像是之前使用的LUKS加密那样子在initramfs
阶段提示输入密码,必须要进入initramfs
的shell
中
根据提示进入到shell中。
首先我们需要导入我们的rock
zpool(需要输入密码,同样的不会有回显)
1
| zpool import -f -l rock
|
最后退出 initramfs shell
就可以进入到正常的引导中了。
完成这次操作之后,之后在重启或者是开机就像是之前使用LUKS加密那样子直接在initramfs
阶段提示输入密码,然后就可以进入系统了!
远程解锁zpool
在遇到更新内核的时候需要重启机器,但是由于zpool是加密之后的需要输入密码方式去解锁zpool才能够正常的引导进入系统。为了方便能够远程解锁这里借助genkernel来帮我们构建一个支持ssh同时还带有zfs工具的initramfs。
首先创建文件夹(用于存放公钥):
1
| mkdir -pv /etc/dropbear/
|
创建并编辑/etc/dropbear/authorized_keys
文件将自己的公钥填写进去:
1
| vi /etc/dropbear/authorized_keys
|
使用genkernel重新生成initramfs:
1
| genkernel --ssh --zfs initramfs
|
修改/etc/default/grub
文件(这里是给内核参数让其在initramfs就直接配置好一个临时的ip用于远程使用)的GRUB_CMDLINE_LINUX
,添加内容如下:
1
| GRUB_CMDLINE_LINUX="dozfs root=ZFS=rock/os/gentoo ip=172.22.0.3/24 dosshd gk.sshd.port=22 root_trim=yes rhgb alpha_support=1 loglevel=7 iommu=pt intel_iommu=on"
|
这里的ip就是后续要远程的ip,我这里是通过其他的设备跳转过来的出于安全考虑我并没有配置网关。
具体的配置可以参考: Initramfs kernel command-line parameters
重新生成grub的配置:
1
| grub-mkconfig -o /boot/grub/grub.cfg
|
验证
重启之后使用ssh远程到配置好的ip上:
这里就会到一个远程的交互shell,在这个shell中包含了基本的zfs工具,我们可以使用unlock-zfs
来去解锁zpool:
在解锁zpool的时候会提示输入密码,输入密码同样是没有回显的。
在解锁完成之后我们还需要输入以下这条命令让其进入到正常的引导流程中:
参考链接
结束语
在这次的迁移当中学到了很多关于文件系统的概念,单单从数据上来说首先体验到的一个好处就是文件压缩的功能,在之前的文件系统上是有200G左右的数据,迁移到ZFS之后在140G左右非常的明显。
ZFS感觉是功能很强大,有很多的功能还是没有用上,后期研究研究如何将一些特性应用到实际的场景当中。
玩的开心~