在 Linux 上编译和安装 ZFS 文件系统

因为版权的问题,使用 GPL 协议的 Linux 内核无法直接使用 CDDL 协议的 ZFS 文件系统,有一个办法可以绕过这个问题,就是先把 ZFS 代码编译成版权为 CDDL 的内核模块,然后 Linux 内核加载一个 CDDL 协议的内核模块,很高兴已经有人(ZFS on Linux)这么做了,所以我们终于有了原生的 Linux ZFS 文件系统。在 Linux 上和 ZFS 相似的还有一个 Btrfs 文件系统,被称为 Linux 下一代文件系统,起初是由 Oracle 开发的,有意思的是 Oracle 收购 SUN 后又得到了 ZFS,不过因为 Btrfs 使用的是 GPL 协议,所以不管 Oracle 的态度如何,对 Btrfs 的影响有限。

安装必备软件包:

# yum install kernel-devel rpm-build

下载、编译和安装 SPL 包:

# wget http://github.com/downloads/behlendorf/spl/spl-0.5.1.tar.gz
# tar zxvf spl-0.5.1.tar.gz
# cd spl-0.5.1
# ./configure
# make rpm

# rpm -Uvh *.x86_64.rpm

加载 splat 模块到内核,并测试:

# /sbin/modprobe splat

# splat -t kmem:all
------------------------------ Running SPLAT Tests ------------------------------
                kmem:kmem_alloc           Pass  
                kmem:kmem_zalloc          Pass  
                kmem:vmem_alloc           Pass  
                kmem:vmem_zalloc          Pass  
                kmem:slab_small           Pass  
                kmem:slab_large           Pass  
                kmem:slab_align           Pass  
                kmem:slab_reap            Pass  
                kmem:slab_age             Pass  
                kmem:slab_lock            Pass  
                kmem:slab_overcommit      Pass  
                kmem:vmem_size            Pass

下载、编译和安装 ZFS 包:

# wget http://github.com/downloads/behlendorf/zfs/zfs-0.5.1.tar.gz
# tar zxvf zfs-0.5.1.tar.gz
# cd zfs-0.5.1
# ./configure
# make pkg

# rpm -Uvh *.x86_64.rpm

加载 zfs 模块到内核,并测试:

# /sbin/modprobe zfs

# /usr/libexec/zfs/zpios-sanity.sh 
status    name        id	wr-data	wr-ch	wr-bw	rd-data	rd-ch	rd-bw
-------------------------------------------------------------------------------
PASS:     file-raid0   0	64m	64	112.68m	64m	64	4.81g
PASS:     file-raid10  0	64m	64	10.46m	64m	64	876.71m
PASS:     file-raidz   0	64m	64	12.14m	64m	64	780.49m
PASS:     file-raidz2  0	64m	64	17.68m	64m	64	14.66m
PASS:     lo-raid0     0	64m	64	1.30g	64m	64	876.71m
PASS:     lo-raid10    0	64m	64	666.67m	64m	64	14.65m
PASS:     lo-raidz     0	64m	64	1.10g	64m	64	876.71m
PASS:     lo-raidz2    0	64m	64	492.31m	64m	64	876.71m

如果上述加载内核模块有问题,建议重启系统后重新加载试一下,并检查是否模块加载成功:

# reboot

# /sbin/modprobe splat
# /sbin/modprobe zfs

# lsmod
Module                  Size  Used by
zfs                   866368  0 
zcommon                75504  1 zfs
znvpair                82432  2 zfs,zcommon
zavl                   40576  1 zfs
zlib_deflate           52825  1 zfs
zunicode              353664  1 zfs
splat                 160848  0
...

如果没有多余的硬盘玩 ZFS 的话,可以按照 “ZFS 的简单使用” 介绍的方法创建几个文件充当硬盘,简单玩一下 Linux 下的原生 ZFS 支持。

ZFS Pool 里增加硬盘做镜像

ZFS 第一次出现在 OpenSolaris 上是在2005年11月发布的 build 27 版本,因为稳定性、可靠性等原因直到最近 VPSee 才有考虑把它用在生产环境里,现在 VPSee 所有的 Solaris 服务器还是在用很古老的 UFS,UFS 仍是 Solaris 10 的默认文件系统。昨天简单玩了一下 ZFS,用6个文件模拟6个硬盘在 ZFS 上进行了一系列添加、删除、镜像的操作,立刻体会到了 ZFS 的强大,但这与实际操作物理硬盘还是有点差别。VPSee 当时安装 OpenSolaris 的时候只用了一块 250GB 的硬盘,现在可以增加一块 250GB 的硬盘做镜像,试验一下 ZFS 强大的 mirror,相当于做 RAID1.

检查 pool

要在服务器上增加一块实际硬盘,先要看看系统原来的 pool 有什么:

# zpool status
  pool: rpool
 state: ONLINE
 scrub: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        rpool       ONLINE       0     0     0
          c8t0d0s0  ONLINE       0     0     0

errors: No known data errors

上面显示 c8t0d0s0 是里面唯一的一块硬盘。现在插上一块新硬盘后(必要时需要重启)运行 format 工具:

# format
Searching for disks...

The device does not support mode page 3 or page 4,
or the reported geometry info is invalid.
WARNING: Disk geometry is based on capacity data.

The current rpm value 0 is invalid, adjusting it to 3600
done

c8t1d0: configured with capacity of 232.87GB


AVAILABLE DISK SELECTIONS:
       0. c8t0d0 
          /pci@0,0/pci108e,534c@5/disk@0,0
       1. c8t1d0 
          /pci@0,0/pci108e,534c@5/disk@1,0
Specify disk (enter its number):

按 “Ctrl + C” 退出。c8t0d0 是原来的硬盘,c8t1d0 是后来加上去的新硬盘。

分区和 label

现在用 -e 参数再次运行 format,然后选择 partition,创建一个 Solaris 分区后打印出分区表看看,只有 s0, s2, s8 有内容。完成分区后退到 format 菜单,接着选择 label,因为大多数的 PC BIOS 都不支持 EFI labels,所以要选择 SMI:

# format -e c8t1d0

format> partition
SELECT ONE OF THE FOLLOWING:
   1. Create a partition
...

partition> print
Current partition table (original):
Total disk cylinders available: 30398 + 2 (reserved cylinders)

Part      Tag    Flag     Cylinders         Size            Blocks
  0       root    wm       1 - 30396      232.85GB    (30396/0/0) 488311740
  1 unassigned    wu       0                0         (0/0/0)             0
  2     backup    wu       0 - 30396      232.85GB    (30397/0/0) 488327805
  3 unassigned    wu       0                0         (0/0/0)             0
  4 unassigned    wu       0                0         (0/0/0)             0
  5 unassigned    wu       0                0         (0/0/0)             0
  6 unassigned    wu       0                0         (0/0/0)             0
  7 unassigned    wu       0                0         (0/0/0)             0
  8       boot    wu       0 -     0        7.84MB    (1/0/0)         16065
  9 unassigned    wu       0                0         (0/0/0)             0
partition>quit

format> label
[0] SMI Label
[1] EFI Label
Specify Label type[0]: 0
Ready to label disk, continue? y

做镜像

退出 format,在 rpool 里增加新硬盘作为第1一块硬盘 mirror,会出现以下报错:

# zpool attach -f rpool c8t0d0 c8t1d0
cannot label 'c8t1d0': EFI labeled devices are not supported on root pools.

我们必须把 VTOC 从第1块硬盘拷贝到第2块硬盘,注意这里 s2 是 slice(分区),c8t0d0s2 是 c8t0d0 的 s2 分区,注意上面打印的分区显示 s2 代表整个硬盘,看 Blocks 发现 s2 = s0 + s8,s0 是 root 分区,s8 是 boot 分区:

# prtvtoc /dev/rdsk/c8t0d0s2 | fmthard -s - /dev/rdsk/c8t1d0s2 
fmthard: Partition 2 specifies the full disk and is not equal
full size of disk.  The full disk capacity is 488343870 sectors.
fmthard:  New volume table of contents now in place.

新硬盘的 c8t1d0s0 分区做成 c8t0d0s0 的镜像,注意上面打印的分区显示 s0 是 root 分区,有 232.85GB 空间,也是我们想 mirror 的分区:

# zpool attach -f rpool c8t0d0s0 c8t1d0s0
Please be sure to invoke installgrub(1M) to make 'c8t1d0s0' bootable.

安装 grub

这一步非常重要,需要把 grub 安装在第二块硬盘上:

# installgrub -m /boot/grub/stage1 /boot/grub/stage2 /dev/rdsk/c8t1d0s0 
Updating master boot sector destroys existing boot managers (if any).
continue (y/n)?y

校验 pool

校验 pool 后查看校验后状态:

# zpool scrub rpool

# zpool status
  pool: rpool
 state: ONLINE
 scrub: scrub in progress for 0h0m, 0.95% done, 0h26m to go
config:

        NAME        STATE     READ WRITE CKSUM
        rpool       ONLINE       0     0     0
          c8t0d0s0  ONLINE       0     0     0
          c8t1d0s0  ONLINE       0     0     0

errors: No known data errors

ZFS 的简单使用

ZFS 被 Sun 称为史上最后一个文件系统,说得一点都不夸张,在 VPSee 看来 ZFS 至少要比现有的其他文件系统领先五年。开发一个全新的文件系统非常耗时,ext4 从2006开始到现在都三年了而且还是在前人的基础上(ext3)开发的,最近 RedHat 发布的 RHEL 5.4 还只是把 ext4 列入 technical preview 的状态,默认的安装文件系统还是 ext3,可见要开发一个可靠的文件系统多么困难,ext4 仍然不够稳定,在系统崩溃时某些条件下可能会造成数据丢失现象。虽然 ZFS 是开放源代码的,由于版权是 CDDL 与 Linux kernel 的 GPL2 冲突,导致 ZFS 不能移植到 Linux 上,FreeBSD 上的 ZFS 移植不完整而且不是很稳定,如果想用 ZFS 的话,建议安装和使用 OpenSolaris,以下操作在 OpenSolaris 2009.06 上执行。

准备工作

先创建6个普通文件充当6个硬盘,来看看 ZFS 是如何方便的添加、删除、镜像硬盘的:

# cd /var/tmp
# mkfile 100m c0d0 c0d1 c0d2 c1d0 c1d1 c1d2

创建 pool

使用第1块硬盘(c0d0)创建一个 pool:

# zpool create mypool /var/tmp/c0d0 
# zpool status
  pool: mypool
 state: ONLINE
 scrub: none requested
config:

        NAME             STATE     READ WRITE CKSUM
        mypool           ONLINE       0     0     0
          /var/tmp/c0d0  ONLINE       0     0     0

errors: No known data errors

创建镜像

在 pool 里添加第2块硬盘(c0d1),并且做成第1块硬盘(c0d0)的镜像:

# zpool attach mypool /var/tmp/c0d0 /var/tmp/c0d1
# zpool status
  pool: mypool
 state: ONLINE
 scrub: resilver completed after 0h0m with 0 errors on Sun Nov  1 15:08:42 2009
config:

        NAME               STATE     READ WRITE CKSUM
        mypool             ONLINE       0     0     0
          mirror           ONLINE       0     0     0
            /var/tmp/c0d0  ONLINE       0     0     0
            /var/tmp/c0d1  ONLINE       0     0     0  73K resilvered

errors: No known data errors

替换硬盘

假设上面第2块硬盘(c0d1)坏了,想用另一块硬盘(c1d0)替换:

# zpool replace mypool /var/tmp/c0d1 /var/tmp/c1d0 
# zpool status
  pool: mypool
 state: ONLINE
 scrub: resilver completed after 0h0m with 0 errors on Sun Nov  1 15:11:33 2009
config:

        NAME               STATE     READ WRITE CKSUM
        mypool             ONLINE       0     0     0
          mirror           ONLINE       0     0     0
            /var/tmp/c0d0  ONLINE       0     0     0
            /var/tmp/c1d0  ONLINE       0     0     0  83.5K resilvered

errors: No known data errors

增加硬盘

如果发现上面两块硬盘不够用了,想在 pool 里增加第3块硬盘(c0d1)只需要简单的 zpool add,这时候需要用到 -f 强制选项,因为 pool 里硬盘类型是 mirror 的,而想要增加的硬盘是 file 的,类型不同所以报错,需要强制执行:

# zpool add mypool /var/tmp/c0d1
invalid vdev specification
use '-f' to override the following errors:
mismatched replication level: pool uses mirror and new vdev is file

# zpool add -f mypool /var/tmp/c0d1
# zpool status
  pool: mypool
 state: ONLINE
 scrub: resilver completed after 0h0m with 0 errors on Sun Nov  1 15:11:33 2009
config:

        NAME               STATE     READ WRITE CKSUM
        mypool             ONLINE       0     0     0
          mirror           ONLINE       0     0     0
            /var/tmp/c0d0  ONLINE       0     0     0
            /var/tmp/c1d0  ONLINE       0     0     0  83.5K resilvered
          /var/tmp/c0d1    ONLINE       0     0     0

errors: No known data errors

现在用第4块硬盘(c1d1)对第3块硬盘(c0d1)做一个镜像;然后加上第5块硬盘(c0d2)和第6块硬盘(c1d0),其中第6块是第5块的镜像:

# zpool attach mypool /var/tmp/c0d1 /var/tmp/c1d1
# zpool add mypool mirror /var/tmp/c0d2 /var/tmp/c1d2 

完成上面的操作以后就会得到类似下面这样的结构,很清晰易懂,c0d0 和 c1d0 是镜像,c0d1 和 c1d1 是镜像,c0d2 和 c1d2 是镜像:

# zpool status
  pool: mypool
 state: ONLINE
 scrub: resilver completed after 0h0m with 0 errors on Sun Nov  1 15:24:51 2009
config:

        NAME               STATE     READ WRITE CKSUM
        mypool             ONLINE       0     0     0
          mirror           ONLINE       0     0     0
            /var/tmp/c0d0  ONLINE       0     0     0
            /var/tmp/c1d0  ONLINE       0     0     0
          mirror           ONLINE       0     0     0
            /var/tmp/c0d1  ONLINE       0     0     0
            /var/tmp/c1d1  ONLINE       0     0     0  14.5K resilvered
          mirror           ONLINE       0     0     0
            /var/tmp/c0d2  ONLINE       0     0     0
            /var/tmp/c1d2  ONLINE       0     0     0

errors: No known data errors

是不是要比 RAID + LVM + EXT3 简单多了?

校验 pool

最好经常时不时的用 zpool scrub 检查一下 pool 和 checksums:

# zpool scrub mypool

删除 pool

# zpool destroy mypool
# rm /var/tmp/c[01]d[012]