2013年6月29日土曜日

zfs-fuse を使って CentOS 5 でも透過圧縮を利用

CentOS 5 で運用しているサーバで、容量を稼ぐのに透過圧縮可能なファイルシステムを使いたいのですが、ZFS on Linux は非対応(カーネルが古くコンパイル不可)なので、zfs-fuse を利用しようかと思います。しかし、CentOS 5 の fuse は、NFS export に対応していません。
そこで、一工夫して、透過圧縮指定した ZFS 領域にスパースファイルを作って XFS 化して、loop マウントすることで、NFS export することにしました。
※まず圧縮指定
# zfs set compression=gzip-1 tank5/test
# zfs get compression tank5/test
NAME        PROPERTY     VALUE     SOURCE
tank5/test  compression  gzip-1    local

※スパースファイル作成。CentOS 5 では truncate が用意されてないので dd 利用
# dd if=/dev/zero of=/tank5/test/sparsefile bs=1M seek=4096 count=0
0+0 records in
0+0 records out
0 bytes (0 B) copied, 2.2e-05 seconds, 0.0 kB/s

# ls -lsh /tank5/test/sparsefile 
512 -rw-r--r-- 1 root root 4.0G Jun 29 10:21 /tank5/test/sparsefile

※ext4 でもいいけれど、XFS を試したいという気持ちもありまして
# mkfs -t xfs /tank5/test/sparsefile 
meta-data=/tank5/test/sparsefile isize=256    agcount=8, agsize=131072 blks
         =                       sectsz=512   attr=0
data     =                       bsize=4096   blocks=1048576, imaxpct=25
         =                       sunit=0      swidth=0 blks, unwritten=1
naming   =version 2              bsize=4096  
log      =internal log           bsize=4096   blocks=2560, version=1
         =                       sectsz=512   sunit=0 blks, lazy-count=0
realtime =none                   extsz=4096   blocks=0, rtextents=0

# mount -t xfs -o loop /tank5/test/sparsefile /mnt_temp
# df
...
/tank5/test/sparsefile
                       4184064      4384   4179680   1% /mnt_temp

※テスト書き込み
# dd if=/dev/sda of=/mnt_temp/test.dd bs=1M count=500 conv=fsync
500+0 records in
500+0 records out
524288000 bytes (524 MB) copied, 16.7982 seconds, 31.2 MB/s
# ls -lsh /tank5/test/sparsefile 
211M -rw-r--r-- 1 root root 4.0G Jun 29 10:24 /tank5/test/sparsefile
この実験では、500M が 211M に圧縮されました。書き込み速度は、zfs-fuse の遅さに引きずられますが、NFS (NW が 100Mbps の環境) 経由で利用する予定なので、十分そうです。
なお、NFS のセッティング方法等は、普通にやればいいので割愛します。

2013-06-30追記
XFS のサイズ拡張(xfs_growfs)を試してみました。マウントしたまま可能でした。
スパースファイルの拡張、loop デバイスのサイズ拡張、xfs_growfs の順に行います。
※losetup -c も初めてなので、予め loop デバイスのサイズを確認
# grep loop0 /proc/partitions 
   7        0    4194304 loop0

※スパースファイルの拡張、サイズ指定を誤っても変なところに書き込まないように if=/dev/null でね
# dd if=/dev/null of=/tank5/test/sparsefile bs=1M seek=8192 count=0
0+0 records in
0+0 records out
0 bytes (0 B) copied, 1.646e-05 s, 0.0 kB/s
# ls -lsh /tank5/test/sparsefile 
236M -rw-r--r-- 1 root root 8.0G Jun 30 06:06 /tank5/test/sparsefile

※loop デバイスのサイズ認識は自動ではないので、変化なし
# grep loop0 /proc/partitions 
   7        0    4194304 loop0
# df /mnt_temp
Filesystem           1K-blocks      Used Available Use% Mounted on
/tank5/test/sparsefile
                       4184064    545056   3639008  14% /mnt_temp

※loop デバイスのサイズ再認識を実行
# losetup -c /dev/loop0 
# grep loop0 /proc/partitions 
   7        0    8388608 loop0
# df /mnt_temp
Filesystem           1K-blocks      Used Available Use% Mounted on
/tank5/test/sparsefile
                       4184064    545056   3639008  14% /mnt_temp

※XFS のサイズ拡張を実行
# xfs_growfs /mnt_temp
meta-data=/dev/loop0             isize=256    agcount=8, agsize=131072 blks
         =                       sectsz=512   attr=0, projid32bit=0
data     =                       bsize=4096   blocks=1048576, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0
log      =internal               bsize=4096   blocks=2560, version=1
         =                       sectsz=512   sunit=0 blks, lazy-count=0
realtime =none                   extsz=4096   blocks=0, rtextents=0
data blocks changed from 1048576 to 2097152
# df /mnt_temp
Filesystem           1K-blocks      Used Available Use% Mounted on
/tank5/test/sparsefile
                       8378368    545312   7833056   7% /mnt_temp
実験の範囲では大丈夫そうです。

2013-07-03追記
サイズ拡張の検証を、間違って CentOS 6 でやってしまっていました。通常、CentOS 6 を使うことが多いもので・・・
で、残念ながら、CentOS 5 では losetup -c オプションは使えず、いちおう 2.6.18-348.el5 のソースも見てみましたが、サイズ拡張を行うためのインターフェースが未実装でした。したがって、マウントしたまま拡張は不可でした。もちろん、いったんアンマウントしてから、スパースファイル拡張して、再度マウントすればいけます。

2013-08-09追記
この形態で実際使い始めたのですが、ファイル削除の際に一工夫すると、より効果的に運用できます。具体的には次のように、削除対象ファイルをゼロ埋め後に削除するようにします。
# find remove-target-dir -type f -exec shred -n 0 -z -u {} \;
これで不要となったブロックについて、XFS から ZFS へゼロ書き込みが行われ、ZFS 側の圧縮機能により元データが使用していたブロックの大半が回収されることになります。したがって、シン・プロビジョニング(Thin Provisioning)が、より効果的に行えることになります。
なお、上記の find 後には、抜けがら(通常ファイル以外のディレクトリその他のことを言ってます:-)が残りますので、最後に rm -rf remove-target-dir もお忘れなく。

2013-10-22追記
オラクル社提供の UEK を使うことで、CentOS 5 でも ZFS on Linux や btrfs が利用できました。
こちらの記事を参照。

2013年6月11日火曜日

バックアップの暗号化運用手法

ZFS on Linux 上に、バックアップを採っているのですが、暗号化のため sparse ZVOL + cryptsetup + ext4 を使っていました。
しかし、ZVOL 直上の ext4 ならば fstrim が使えるのですが、cryptsetup を挟んだ場合は fstrim を利用できず、使いまわしていると、ZVOL が肥大化してしまいます。ZVOL を再作成するという運用もできますが、ZVOL の解放 (zfs destroy) に結構な時間がかかるようです。わたしの環境では、100GiB 解放するのに 7 分程度かかるようでした。

というわけで、別の方法に切り替えることにしました。

いろいろと調べ、考えた挙句に辿りついた方式は、sparse file + losetup + cryptsetup + xfs です。 sparse file の作成には、truncate コマンドが利用できます。操作手順は、次のようになります。
# truncate -s $((65*1024*1024*1024)) /tank1/backup/vault-2013-06-11
# losetup /dev/loop0 /tank1/backup/vault-2013-06-11
# cryptsetup create -y vault-2013-06-11 /dev/loop0
# mkfs -t xfs /dev/mapper/vault-2013-06-11
# mkdir /mnt_vault-2013-06-11
# mount /dev/mapper/vault-2013-06-11 /mnt_vault-2013-06-11
ファイルシステムには、mkfs が高速で、スペース効率が良い xfs を使うことにしました。
わたしの当該バックアップ運用では、1回のバックアップデータ量が 60GiB ほどで、過去3世代保存できればよいので、毎回、上記操作で 65GiB の暗号化スペースを作って利用することにしました。
なお、バックアップ後には、次の操作を行います。
# umount /mnt_vault-2013-06-11
# cryptsetup remove /dev/mapper/vault-2013-06-11
# losetup -d /dev/loop0
比較的多くのコマンドを、順序を違えずに実行する必要があり、実際やってみると誤操作しがちでしたので、簡単な支援スクリプトを3つ作成しました。ご参考まで。
#!/bin/bash
#
# Name: my-vault-setup
#
# Authors: luna2 <blue3waters at gmail dot com>
#
# This file is subject to the GNU General Public License.
# It comes with NO WARRANTY.
#

usage_exit() {
        echo "Usage: my-vault-setup [-d dir] [-t fs-type] size" 1>&2
        exit 1
}

FS_TYPE=xfs
TARGET_DIR=.

while getopts ":d:t:" opt
do
  case $opt in
  d)
        TARGET_DIR=${OPTARG%/}
        ;;
  t)
        FS_TYPE=$OPTARG
        ;;
  *)    usage_exit
  esac
done

shift $((OPTIND - 1))

if [ $# -ne 1 ] ; then
        usage_exit
elif expr "$1" : '[0-9]*' > /dev/null ; then
        TARGET_SIZE=$1
else
        usage_exit
fi

if [ ! -d $TARGET_DIR ] ; then
        usage_exit
fi

TARGET_NAME=vault-`date +%F-%H%M`
TARGET=$TARGET_DIR/$TARGET_NAME

echo "TARGET=$TARGET  SIZE=$TARGET_SIZE (GiB)"

truncate -s $((${TARGET_SIZE}*1024*1024*1024)) $TARGET
if [ $? -ne 0 ] ; then
        echo "ERROR: truncate fail" 1>&2
        exit 1
fi

losetup -f $TARGET
if [ $? -ne 0 ] ; then
        echo "ERROR: losetup fail" 1>&2
        exit 1
fi

LOOP_DEV=`losetup -j $TARGET | egrep -o '/dev/loop[0-9]+'`

echo "LOOP_DEV=$LOOP_DEV"

cryptsetup create -y $TARGET_NAME $LOOP_DEV
if [ $? -ne 0 ] ; then
        echo "ERROR: cryptsetup fail" 1>&2
        exit 1
fi

mkfs -t $FS_TYPE /dev/mapper/$TARGET_NAME >/dev/null 2>&1
if [ $? -ne 0 ] ; then
        echo "ERROR: mkfs fail" 1>&2
        exit 1
fi

mkdir /mnt_$TARGET_NAME
mount /dev/mapper/$TARGET_NAME /mnt_$TARGET_NAME
if [ $? -ne 0 ] ; then
        echo "ERROR: mount fail" 1>&2
        exit 1
fi

echo "/mnt_$TARGET_NAME is usable for your backup"

exit 0
#!/bin/bash
#
# Name: my-vault-umount
#
# Authors: luna2 <blue3waters at gmail dot com>
#
# This file is subject to the GNU General Public License.
# It comes with NO WARRANTY.
#

usage_exit() {
        echo "Usage: my-vault-umount target_dir" 1>&2
        exit 1
}

if [ $# -ne 1 ] ; then
        usage_exit
fi

TARGET=${1%/}

if [[ $TARGET != /mnt_vault-* ]] ; then
        usage_exit
fi

TARGET_NAME=${TARGET#/mnt_}

if ! grep -wq $TARGET /proc/mounts ; then
        echo "ERROR: $TARGET is not a mount point" 1>&2
        exit 1
fi

umount $TARGET
if [ $? -ne 0 ] ; then
        echo "ERROR: umount fail" 1>&2
        exit 1
fi

LOOP_DEV=`cryptsetup status /dev/mapper/$TARGET_NAME | awk '$1 == "device:" {print $2}'`

cryptsetup remove /dev/mapper/$TARGET_NAME
if [ $? -ne 0 ] ; then
        echo "ERROR: cryptsetup fail" 1>&2
        exit 1
fi

VAULT_FILE=`losetup $LOOP_DEV | awk '{print $NF}'`
VAULT_FILE=${VAULT_FILE#(}
VAULT_FILE=${VAULT_FILE%)}

losetup -d $LOOP_DEV
if [ $? -ne 0 ] ; then
        echo "ERROR: losetup fail" 1>&2
        exit 1
fi

rmdir $TARGET

echo "$VAULT_FILE was locked"

exit 0
#!/bin/bash
#
# Name: my-vault-mount
#
# Authors: luna2 <blue3waters at gmail dot com>
#
# This file is subject to the GNU General Public License.
# It comes with NO WARRANTY.
#

usage_exit() {
        echo "Usage: my-vault-mount vault_file" 1>&2
        exit 1
}

if [ $# -ne 1 ] ; then
        usage_exit
fi

TARGET=$1
if [ ! -f $TARGET ] ; then
        usage_exit
fi

TARGET_NAME=${TARGET##*/}

losetup -f $TARGET
if [ $? -ne 0 ] ; then
        echo "ERROR: losetup fail" 1>&2
        exit 1
fi

LOOP_DEV=`losetup -j $TARGET | egrep -o '/dev/loop[0-9]+'`

echo "LOOP_DEV=$LOOP_DEV"

cryptsetup create $TARGET_NAME $LOOP_DEV
if [ $? -ne 0 ] ; then
        echo "ERROR: cryptsetup fail" 1>&2
        exit 1
fi

mkdir /mnt_$TARGET_NAME
mount /dev/mapper/$TARGET_NAME /mnt_$TARGET_NAME
if [ $? -ne 0 ] ; then
        echo "ERROR: mount fail" 1>&2
        exit 1
fi

echo "/mnt_$TARGET_NAME is usable"

exit 0

2013-06-22追記
上に書いたバックアップ運用に加えて、圧縮処理について、最終格納先 ZFS の透過圧縮 (compression=on) に期待するとうまく行きません。暗号化後のデータは圧縮しにくいためです。そこで、dm-crypt デバイスの上で使うファイルシステムを xfs から Btrfs に変更して、compress=lzo オプションで圧縮を試みました。しかし、残念ながら現状の Btrfs では全くといっていいほど圧縮できませんでした(圧縮オプションを zlib に変えても結果は同じ)。
他に透過圧縮できるファイルシステムは何があるだろ?とサーチしてみて、ntfs-3g でも透過圧縮できるようなので、試してみました。
# mkntfs -Q -C /dev/mapper/vault-xxx
# mount -t ntfs -o compression /dev/mapper/vault-xxx /mnt
# mkdir /mnt/compress_folder
# setfattr -h -v 0x10080000 -n system.ntfs_attrib /mnt/compress_folder
ntfs-3g の場合、こんな具合にセットアップするようです。実際動かしてみると、圧縮はそこそこ出来たのですが、処理スピードは相当に遅めでした。Windows で NTFS 圧縮した経験からも遅いだろうとは予想してましたが、ちょっと、自分の運用には耐えらないほど遅かったです。

2013-06-23追記
ZFS 二段重ねは重いような気がしてましたが、dm-crypt デバイス上で使うファイルシステムも ZFS にして、compression=lz4 を使うとなかなか良好な結果でした。一時もうそれで十分と思いましたが、さらに調べたところ、Btrfs には compress-force というオプションもあることを知りました。試してみると、ZFS 二段重ねよりも高速に処理可能で、compress-force=zlib が最も良好な結果(処理時間が短くて圧縮率高い)でした。compress-force=lzo も試しましたが、処理時間はさほど短くならず、圧縮率も ZFS (compression=lz4) より下でした。なお、Btrfs (compress-force=zlib) の圧縮率は、ZFS (compression=gzip-1) とほぼ同じでした。
以上から、Btrfs (compress-force=zlib) を利用しようかと思います。初めて Btrfs に利点を感じました。
人気ブログランキングへ にほんブログ村 IT技術ブログへ