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 に利点を感じました。

0 件のコメント:

コメントを投稿

人気ブログランキングへ にほんブログ村 IT技術ブログへ