×
Community Blog Use Your Storage Space More Effectively with ZFS: Exploring Datasets

Use Your Storage Space More Effectively with ZFS: Exploring Datasets

In this tutorial series, we will learn how to optimize file storage on a Linux server to ensure data integrity using ZFS.

By Alexandru Andrei, Alibaba Cloud Tech Share Author. Tech Share is Alibaba Cloud's incentive program to encourage the sharing of technical knowledge and best practices within the cloud community.

In the previous tutorial, Use Your Storage Space More Effectively with ZFS - Exploring vdevs, we have explored various types of vdevs that make a ZFS storage pool. Of course, a pool is just a building block that wouldn't have much use without datasets such as ZFS filesystems, volumes, snapshots or clones. While we have used the zpool command to interact with pools, for datasets the command is zfs.

Manage ZFS Filesystems

When we have created the previous pool, named "fourth", ZFS already created a dataset for us, as we can see with:

    zfs list

Command output should be:

root@ubuntu:~# zfs list
NAME     USED  AVAIL  REFER  MOUNTPOINT
fourth    67K  38.5G    19K  /fourth

By default, the filesystem will be mounted in /name_of_pool, although this can be changed later on (example: zfs set mountpoint=/fourth fourth). Different mount points can also be chosen when the pool is created, by passing option arguments such as in this example: zpool create -m /other_mountpoint -f fourth vdb vdc.

Let's create another ZFS filesystem:

    zfs create fourth/images

As you can see, the syntax is simple: zfs create name_of_pool/name_of_dataset. These look and act in a similar fashion to regular directories. Children filesystems can be added to the parent filesystems with commands like zfs create fourth/images/store1. We can automatically create multiple non-existing parents by adding the "-p" switch, e.g. zfs create -p fourth/a/b/c/d/e.

But how are multiple ZFS filesystems useful? Besides logically separating data, they offer a way to set different properties on different datasets. For example you could create a dataset to store images and disable compression on it and create another dataset to store text files and enable compression on that one. They also allow the administrator to apply different snapshot policies, set maximum space that each filesystem can use (quotas), optimize properties for different uses (e.g. adjust recordsize to optimize for storing databases) and many other things.

Let's see properties for our two datasets:

    zfs list

Example output of command:

root@ubuntu:~# zfs list
NAME            USED  AVAIL  REFER  MOUNTPOINT
fourth          129K  38.5G    19K  /fourth
fourth/images    19K  38.5G    19K  /fourth/images

As you can see, the filesystems show the same amount of free space on them. This is one of the many advantages of ZFS. Since you don't need to preallocate space for each filesystem, they can each grow freely at their own rate. This means that you don't have to estimate future requirements and you won't need to manually grow filesystems (and maybe partitions) in the future, as is the case with other solutions. Allocating more disk space to your datasets is as simple as growing your pool by adding more devices or replacing them with larger disks. And you don't have to interrupt services, reboot the machine or suspend disk writing.

In some scenarios though, you will want to limit certain filesystems from growing too much. For example, you might have one ZFS filesystem dedicated to each customer, to store their files. They each should be able to access a maximum of 5GB of storage, according to their payment plan. Let's go through an example. Create the following filesystems:

    zfs create -p fourth/customers/1

Now let's set a quota for customer "1":

    zfs set quota=5G fourth/customers/1

zfs list will reflect the newly set maximum space this dataset can use:

root@ubuntu:~# zfs list
NAME                 USED  AVAIL  REFER  MOUNTPOINT
fourth               180K  38.5G    19K  /fourth
fourth/customers      38K  38.5G    19K  /fourth/customers
fourth/customers/1    19K  5.00G    19K  /fourth/customers/1
fourth/images         19K  38.5G    19K  /fourth/images

And that's all there is to it, let's move on to compression. Create another filesystem:

    zfs create fourth/text

Text is highly compressible so let's enable that on this dataset to get all the benefits (saving disk space and faster reading and writing):

    zfs set compression=on fourth/text

Set compression to "on" before storing data on a dataset since ZFS will start to compress only information that is added after the option is enabled. There's almost never a reason to not enable compression so do that for every dataset except those that will only store content that is already compressed (images, video files, etc.) Compression uses the CPU, but the algorithm is so efficient and CPU's are so powerful today, that the performance cost is very small (simply put, it uses very little processing power). And in most cases the CPU is sitting idle anyway (e.g. even on a busy server where it may sit at 70% usage on average, 30% of the time it is doing nothing useful while it could be compressing, saving you disk space and speeding up reading and writing data).

You can take a look at other dataset properties you can adjust, with:

    zfs get all

Manage ZFS Snapshots

Let's explore how useful snapshots can be. Create some files:

    touch /fourth/customers/1/importantdata{1..100}

Let's see the files we created:

    ls /fourth/customers/1/

These will represent important data that a customer may have. Now let's take a snapshot:

    zfs snapshot fourth/customers/1@firstsnapshot

Let's assume, that somehow, 50 of the customer's files have been deleted:

    rm /fourth/customers/1/importantdata{1..50}

The customer complains that he can't find his data. But, you have automatic daily snapshots enabled. You quickly check to see what has changed between the last snapshot and current contents:

    zfs diff fourth/customers/1@firstsnapshot

Which shows you what has been removed, created, modified and renamed, represented by the following leading signs in the output: -, +, M, R.

There is also a special hidden directory in each dataset that allows you to manually explore snapshot contents:

    ls /fourth/customers/1/.zfs/snapshot/

By going to a specific snapshot directory, you can view and copy files selectively if you don't have to restore the entire thing.

We can now see that the customer only has 50 out of the 100 files available:

    ls /fourth/customers/1/

And we can restore his snapshot instantly:

    zfs rollback fourth/customers/1@firstsnapshot

All his files are reinstated:

    ls /fourth/customers/1/

To view all snapshots available:

    zfs list -t snapshot

To view all types of datasets:

    zfs list -t all

To free up disk space, very old snapshots that are no longer needed can be deleted with:

    zfs destroy fourth/customers/1@firstsnapshot

Manage ZFS Clones

To clone a dataset, we first have to snapshot it:

    zfs snapshot fourth/customers/1@firstsnapshot

Now we can create a clone:

    zfs clone fourth/customers/1@firstsnapshot fourth/customers/cloneof1

This will allow you to change content in the clone without changing the original (it's kind of a writable snapshot, whereas a regular snapshot is read-only). Also, content that is identical in both datasets will only be stored once, potentially saving large amounts of disk space. Imagine a scenario where original customer data already occupies 500GB; that will amount to 500GB of space saved. If you now change just a few megabytes of data, only these differences will be stored, so you will only need an additional few megabytes of data to store both of these datasets.

Let's assume that we're testing some type of optimization on customer data and we found a way to reduce the number of files needed:

    rm /fourth/customers/cloneof1/importantdata{1..99}

When we're happy with the changes, we can "promote" the clone:

    zfs promote fourth/customers/cloneof1

This removes the dependency on its origin, which allows us to delete the original content (not possible before promotion):

    zfs destroy fourth/customers/1

Rename the clone:

    zfs rename fourth/customers/cloneof1 fourth/customers/1

We've now improved and replaced the older customer data.

It's worth noting that clones can also be snapshotted, just like a regular ZFS filesystem.

Manage ZFS Volumes

As mentioned in previous tutorials, you can view volumes as a sort of virtual, raw storage devices. These would be used when you need the advantages and features ZFS offers but don't need the ZFS filesystem on top of your storage pool(s). Or, just like we'll do, you can mix them: use filesystems and volumes side by side.

Create a 10GB volume called "virtualdisk":

    zfs create -V 10G fourth/virtualdisk

You will find these volumes added in /dev/zvol. We can use them just like normal disks, for example we can create an ext4 filesystem on top of it:

    mkfs.ext4 /dev/zvol/fourth/virtualdisk

Note: although you can create a filesystem directly on a raw disk, it's recommended you partition it first, but we've skipped that for the sake of simplicity.

Let's also take advantage of some ZFS features, like compression, which ext4 doesn't have natively:

    zfs set compression=on fourth/virtualdisk

This will not let you write more than 10GB on your filesystem because ext4 itself imposes a limit to the size it has been created with. But reads and writes will be faster because there will be less bytes that the disks have to store and retrieve. Also, since the volume size has been preallocated, it will take 10GB out of the space available on your storage pool, even if the data it actually holds is much less than that. Preallocation is recommended for volumes to prevent certain types of errors that might lead to data corruption if the pool would run out of space. If you can avoid/afford the risk, you can get disk space saving benefits of compression and dynamic allocation of data, as the volume grows, by making it "sparse". Simply put: with compression on, if you store 10GB of highly compressible data, the volume may only take 3GB from your storage pool. To create a sparse volume you add the -s parameter, so the previous command would look like this: zfs create -s -V 10G fourth/virtualdisk.

Mount the ext4 filesystem:

    mount /dev/zvol/fourth/virtualdisk /mnt

Check available space on the filesystem:

    df -h /mnt

Now let's see another one of the advantages of ZFS, how easy it is to resize a volume:

    zfs set volsize=20G fourth/virtualdisk

The ext4 filesystem has to be grown to take advantage of the new size:

    resize2fs /dev/zvol/fourth/virtualdisk

Now let's see if it worked:

    df -h /mnt

Check how much space this volume is using in our pool:

    zfs list

The output might look like this:

root@ubuntu:~# zfs list
NAME                 USED  AVAIL  REFER  MOUNTPOINT
fourth              20.6G  17.9G    19K  /fourth
fourth/customers      54K  17.9G    19K  /fourth/customers
fourth/customers/1    35K  17.9G    20K  /fourth/customers/1
fourth/images         19K  17.9G    19K  /fourth/images
fourth/text           19K  17.9G    19K  /fourth/text
fourth/virtualdisk  20.6G  38.4G   126M  -

As mentioned, even if at the moment the volume is empty, space is preallocated so it takes 20GB out of our pool. But even though it wasn't initially created as a sparse volume, we can change it now:

    zfs set refreservation=none fourth/virtualdisk

Now let's see what changed:

    zfs list

Example output:

root@ubuntu:~# zfs list
NAME                 USED  AVAIL  REFER  MOUNTPOINT
fourth               127M  38.4G    19K  /fourth
fourth/customers      54K  38.4G    19K  /fourth/customers
fourth/customers/1    35K  38.4G    20K  /fourth/customers/1
fourth/images         19K  38.4G    19K  /fourth/images
fourth/text           19K  38.4G    19K  /fourth/text
fourth/virtualdisk   126M  38.4G   126M  -

We've regained almost 20GB of usable space on our pool.

Let's see the benefits of compression in action. Create a 1GB file in /mnt, where our ext4 filesystem, backed by the ZFS volume, is mounted:

    dd if=/dev/zero of=/mnt/test status=progress bs=1M count=1024

If we check the contents of /mnt, we'll see that our file, "test" is 1GB in size:

    ls -lh /mnt/

But when we verify how much space the volume is using in the pool:

    zfs list

We'll see that it needs almost no additional space. This is an extreme case, since we've created a file made only of binary zeros and that is extremely compressible. But in real world scenarios, you will still see high compression ratios, especially with text-based content (e.g. website code, databases that use text records, git repositories, etc.)

Tip: when using ext4 on a ZFS volume, you may notice that after deleting data in /mnt, the volume doesn't reflect any gains in usable space. This is because, for efficiency, a lot of filesystems like ext4 don't actually remove the data on disk, they just dereference it. Otherwise, deleting 100GB of information would take a very long time and make your system slow. This means that deleted files continue to exist in random blocks on disk, consequently on the ZFS volume too. To free up space, you would use a command such as fstrim /mnt to actually erase unused data in the ext4 filesystem. Only use the tool when needed, as to not "tire" the physical devices unnecessarily (although the numbers are pretty high these days, devices have a limited number of write cycles).

Don't forget that a lot of the other ZFS specific features are also available on volumes (e.g snapshots and clones). I encourage you to try these features out today on your Alibaba Cloud Elastic Cloud Service (ECS) Linux instance.

0 0 0
Share on

Alibaba Clouder

2,599 posts | 762 followers

You may also like

Comments