ZFS snapshotの浪費
ZFSで定期的にsnapshotをとりながら長期間運用していると、snapshotに不要なデータがたまり、ファイルを削除しても実体はsnapshotに残るのでディスクの空き容量が増えないということもよくあります。
$ cd ~/tmp
$ ls
Data
$ du -s Data
528 Data
$ rm -rf *
$ ls
(ファイルは何もない)
$ df -h .
Filesystem Size Used Avail Capacity Mounted on
zvol0/home/minmin/tmp 2.2T 228K 2.2T 0% /home/minmin/tmp
(ディスク使用量は 見かけ上はディレクトリ分のみ)
$ zfs list -o space zvol0/home/minmin/tmp
NAME AVAIL USED USEDSNAP USEDDS USEDREFRESERV USEDCHILD
zvol0/home/minmin/tmp 2.16T 1.01G 1.01G 227K 0 0
(実際は zfsとして 1GB程 snapshot が消費)
このような状態で過去のsnapshotのデータが不要であればsnapshotを削除しないとディスク容量が無駄になります。
zfs destroy で snapshot を指定するときの %
指定したZFSのsnapshotをまとめて削除するシェルスクリプトを作るかなと思い、改めて man zfs していて気づきました。
zfs destroy [-dnpRrv] filesystem|volume@snap[%snap][,snap[%snap]][,...]
削除のときは % でsnapshotの範囲を指定できるわけで、最古のsnapshotと最新のsnapshotを調べて % で挟むめば、その間の不要な snapshot をまとめて削除できます。
zfs destroy ZFS@oldest_snap%newest_snapshot
他にも複数の snapshot のうち不要なものを個別に指定する , (カンマ) のオプションがありますね。これらを活用すると、snapshotを一つづつ削除するのに比べると、かなり効率がよさそうです。
ZFS snapshot をまとめて削除するコマンド
zfs-purge-snapshot というコマンドを作ってみました。使い方は
$ zfs-purge-snapshot zvol0/home/minmin/tmp
のように、引数にzfsのfilesystemを指定します。
実際に使ってみた例です
$ df -h .
Filesystem Size Used Avail Capacity Mounted on
zvol0/home/minmin/tmp 2.2T 228K 2.2T 0% /home/minmin/tmp
$ zfs list -o space zvol0/home/minmin/tmp
NAME AVAIL USED USEDSNAP USEDDS USEDREFRESERV USEDCHILD
zvol0/home/minmin/tmp 2.16T 1.01G 1.01G 227K 0 0
$ zfs-purge-snapshot zvol0/home/minmin/tmp
$ zfs list -o space zvol0/home/minmin/tmp
NAME AVAIL USED USEDSNAP USEDDS USEDREFRESERV USEDCHILD
zvol0/home/minmin/tmp 2.16T 227K 0 227K 0 0
このとおり、df と zfs list それぞれの使用量が一致しました。
プログラムは以下の通りです。
#!/bin/sh
PATH=$PATH:/sbin:/usr/sbin
ZFS_list_snap='zfs list -H -o name -d 1 -t snapshot'
if [ $# -le 0 ]; then
echo "Usage: ${0##*/} zfs [zfs ...]" 1>&2
exit 2
fi
for fs in "$@"; do
if [ $(${ZFS_list_snap} "${fs}" | wc -l) = 0 ]; then
continue
fi
snap0=$(${ZFS_list_snap} "${fs}" | head -1)
snap1=$(${ZFS_list_snap} "${fs}" | tail -1 | sed 's/.*@//')
zfs destroy "${snap0}%${snap1}" || exit
done