Creating a ZFS pool in a Sparseimage or DMG on OS X

Since I see regular corruption and filesystem errors on my HFS+ partitions it seemed time to give ZFS on OS X a try.

Since there are several ZFS implementations for OS X available you can choose the one you feel most confident that will work. Regardless… here is the script I wrote to automate the creation of my zpools with some data safety against bit rot and filesystem corruption (at least more than HFS+ offers).

ZFS Utils for OS X
ZFS Utils for OS X on PyPI

You can install it like this:

pip install zfs-utils-osx
# Or if you don't have pip available
easy_install zfs-utils-osx

Usage:

# zfs.py -h
usage: zfs.py [-h] {zpool} ...

optional arguments:
  -h, --help  show this help message and exit

Subcommands:
  Please specify one of the following subcommands

  {zpool}
    zpool     zpool creation

Zpool create usage:

usage: zfs.py zpool [-h] [-c COUNT] [-s SIZE]
                    [-t {raidz1,raidz2,raidz3,mirror,raidz}] [-n]
                    [-m MOUNTPOINT] [-o] [-p PATTERN]
                    pool_name

positional arguments:
  pool_name             The name of the pool to create

optional arguments:
  -h, --help            show this help message and exit
  -c COUNT, --count COUNT
                        The amount of images to use (default: 3)
  -s SIZE, --size SIZE  The usable size of the zpool in GiB (default: 10GiB)
  -t {raidz1,raidz2,raidz3,mirror,raidz}, --type {raidz1,raidz2,raidz3,mirror,raidz}
                        The zpool type to use (default: raidz)
  -n, --no-op, --dry-run
                        Show what will be done but dont execute
  -m MOUNTPOINT, --mountpoint MOUNTPOINT
                        Where should the disk be mounted (default:
                        ~/%(pool_name)s
  -o, --overwrite       Overwrite old images if they exist
  -p PATTERN, --pattern PATTERN
                        File name pattern to store the images (default:
                        %(pool_name)s_%(i)02d)

And the actual creation of an image on my local system:

# zfs.py zpool pool --count 4 --size 15
Creating 4 images named pool_00 of size 5.000000GiB for an effective
size of 15.000000GiB. The disk space used will be 20.000000GiB.
# hdiutil create -size 5g -volname pool_00 -type SPARSE -layout NONE  pool_00
# hdiutil attach -nomount  pool_00.sparseimage
# hdiutil create -size 5g -volname pool_01 -type SPARSE -layout NONE  pool_01
# hdiutil attach -nomount  pool_01.sparseimage
# hdiutil create -size 5g -volname pool_02 -type SPARSE -layout NONE  pool_02
# hdiutil attach -nomount  pool_02.sparseimage
# hdiutil create -size 5g -volname pool_03 -type SPARSE -layout NONE  pool_03
# hdiutil attach -nomount  pool_03.sparseimage
# sudo zpool create  -f -m ~/pool pool raidz /dev/disk14 /dev/disk15 /dev/disk16 /dev/disk17

Zpool import usage:

usage: zfs.py load [-h] [-d IMAGE_DIRECTORY] [-p PREFIX] [--postfix POSTFIX]
                       [-n]
                       pool_names [pool_names ...]

    positional arguments:
      pool_names            The name of the pool to import

    optional arguments:
      -h, --help            show this help message and exit
      -d IMAGE_DIRECTORY, --image-directory IMAGE_DIRECTORY
                            File name pattern to store the images (default:
                            ~/zfs/)
      -p PREFIX, --prefix PREFIX
                            File name prefix for the images (default:
                            %(pool_name)s_)
      --postfix POSTFIX     File name postfix for the images (default: )
      -n, --no-op, --dry-run
                            Show what will be done but dont execute

Example run:

# zfs.py load pool
# hdiutil attach -nomount  /Users/rick/zfs/pool_00.sparseimage
# hdiutil attach -nomount  /Users/rick/zfs/pool_01.sparseimage
# hdiutil attach -nomount  /Users/rick/zfs/pool_02.sparseimage
# hdiutil attach -nomount  /Users/rick/zfs/pool_03.sparseimage
# sudo zpool import pool

And to make it mount automatically during boot (make sure you have sudo zpool in your sudoers file as passwordless), add this to /Library/LaunchDaemons/wolph.zfs.plist.

<?xml version="1.0" encoding="UTF-8"?>
< !DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
        "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>Label</key>
        <string>wolph.zfs</string>
        <key>ProgramArguments</key>
        <array>
            <string>zfs.py</string>
            <string>load</string>
            <string>pool</string>
        </array>
        <key>RunAtLoad</key>
        <true></true>
    </dict>
</plist>

In addition, if you want to speed it up a little with a ram drive. You can create and attach one like this:

The size is decided by the number after ram://, simply multiply the amount of megabytes you want by 2048 (in this case 4096 * 2048 = 4Gi of ram)

hdiutil attach -nomount ram://8388608
zpool add -f pool cache /dev/diskX

The X should be replaced with the disk drive you got from the hdiutil command.

Note that using a ram drive as a cache drive is a _very_ bad idea if you have L1ARC available (i.e. all except the current OS X implementation I believe), in that case there is really no point to this as you will be trading really fast memory (L1ARC) for a slower ram drive which will be used slightly differently (L2ARC).

Bookmark and Share

About Rick van Hattem

Rick van Hattem is a Dutch Internet entrepreneur and co-founder of Fashiolista.com
No comments yet.

Leave a Reply