ZFSを圧縮・暗号化してリモートバックアップする

# 目的
手元のサーバに保存されている一部のデータを遠隔地にあるリモートサーバにバックアップしたい。しかしリモートサーバは信頼できるユーザだけがアクセスできるとは限らないため、データを暗号化して保存する必要がある。そのため、ZFSのスナップショットをOpenSSLで暗号化する。

# やりかた
## 前提条件
 - CentOS 7でZFS on Linux(0.7.3)を利用する
 - ZFSのスナップショットを使いサービスを停止せずバックアップを転送する
 - リモートサーバにはSSHで接続可能とする
 - リモートサーバでZFSが利用できない可能性が高いとする
 - 転送量を減らすため、pigzで並列して圧縮する
 - データは送信前にOpenSSLで暗号化する
 - バックアップはスナップショットイメージのまま保管したい
 
## バックアップ
スナップショットを作成済みでリモートサーバにはSSHで公開鍵認証設定済みのとき、次のコマンドでスナップショットの差分を暗号化して転送する。
[スナップショット転送] → [gzip圧縮] → [OpenSSLで暗号化] → [SSHで送信] を一括で実施してる。

```
# /sbin/zfs send <pool@snapshot> | /bin/pigz -c | \
/bin/openssl enc -aes-256-cbc -e -pass file:/root/password.txt | \
/bin/ssh <user@remote.server> 'cat > /path/to/archive/<pool@snapshot>.zfsbkup'
```

### 圧縮についての検討
圧縮するのはファイルサイズの低減とバックアップ時間の短縮を目的としている。
2.5GBの色々なファイル（具体的な内容はメモに残っていなかった）でZFSのスナップショットを保存した場合は以下のようになった。
このテストでは2.5" USB2.0 HDD 3台でRAID-Zを構成しているためI/O速度は期待できない。また、CentOS 6、zfs-fuse 0.6.9、AMD FX-8300 Eight-Core Processorの環境となっている。

```
# df -h /zfs
Filesystem      Size  Used Avail Use% Mounted on
tank/pool1      457G  2.5G  454G   1% /zfs

# ls -RF | grep -v / | wc -l
385
```

```:圧縮無し
# time zfs send tank/pool1@snapshot1 > /root/raw.bin
real    2m6.108s
user    0m0.000s
sys     0m0.002s
```

```:gzip
# time zfs send tank/pool1@snapshot1 | gzip -c > /root/comp.zip
real    1m33.384s
user    1m29.602s
sys     0m2.591s
```

```:bzip2
# time zfs send tank/pool1@snapshot1 | bzip2 -c > /root/comp.bz2
real    7m18.095s
user    7m10.998s
sys     0m2.956s
```

```:xz
# time zfs send tank/pool1@snapshot1 | xz -c > /root/comp.xz
real    12m27.685s
user    12m15.869s
sys     0m10.934s
```

無圧縮よりgzip圧縮した場合の方が若干高速になっている。
上記のものは全てシングルプロセスだが、マルチプロセス対応の場合はより高速化が期待できる。この実験環境では8並列の処理となっている。

```:pigz_(parallel_gzip)
# time zfs send tank/pool1@snapshot1 | pigz -c > /root/comp_p.zip
real    0m52.738s
user    1m54.288s
sys     0m2.740s
```

```:pbzip2_(parallel_bzip2)
# time zfs send tank/pool1@snapshot1 | pbzip2 -c > /root/comp_p.bz2
real    1m25.417s
user    10m51.964s
sys     0m14.876s
```

```:pxz_(parallel_xz)
# time zfs send tank/pool1@snapshot1 | pxz -c > /root/comp_p.xz
real    3m55.171s
user    17m37.772s
sys     0m6.262s
```

圧縮後のファイルサイズは以下のようになった。

```
# ll -h
合計 7.2G
-rw-r--r--. 1 root root 1.3G  2月  7 14:46 2014 comp.bz2
-rw-r--r--. 1 root root 1.1G  2月  7 14:59 2014 comp.xz
-rw-r--r--. 1 root root 1.3G  2月  7 15:40 2014 comp.zip
-rw-r--r--. 1 root root 1.3G  2月  7 15:05 2014 comp_p.bz2
-rw-r--r--. 1 root root 1.2G  2月  7 15:10 2014 comp_p.xz
-rw-r--r--. 1 root root 1.4G  2月  7 15:35 2014 comp_p.zip
-rw-r--r--. 1 root root 2.5G  2月  7 14:25 2014 raw.bin
```

この結果からpigzを使うことにした。xzの圧縮率が抜群だが、速度と見合わないと判断した。
OpenSSLでの暗号化を含めた場合の処理時間は以下のようになった。処理時間に対する暗号化の影響はほとんど見られない。

```
# time zfs send tank/pool1@snapshot1 | pigz -c | \
openssl enc -aes-256-cbc -e -k password > /root/pig_e.zip
real    0m54.551s
user    1m58.044s
sys     0m4.474s
```

### 検討その2
Xeon E5620 x2と高速ストレージの実環境下でテストした場合もpigzを利用した方が高速だった。

```
# zfs list -t snapshot
NAME                          USED  AVAIL  REFER  MOUNTPOINT
tank/pool@full_201510242317   364K      -   116G  -

# time zfs send tank/pool@full_201510242317 | \
openssl  enc -aes-256-cbc -e -k password > /nfs/full_201510242317.enc
real    61m24.104s
user    7m29.039s
sys     3m30.057s

# time zfs send tank/pool@full_201510242317 | pigz -c | \
openssl enc -aes-256-cbc -e -k password > /nfs/full_201510242317.zip.enc
real    52m42.727s
user    245m18.443s
sys     13m56.552s

# ll -h
合計 221G
-rw-r--r-- 1 nfsnobody nfsnobody 117G 10月 25 17:54 2015 full_201510242317.enc
-rw-r--r-- 1 nfsnobody nfsnobody 105G 10月 25 00:25 2015 full_201510242317.zip.enc
```


## リストア
リストア時は何らかの方法で手元にファイルを持ってきている状態を想定している。
復元する場所はスナップショット取得時と同一のZFSプールである必要はない。

```
# /bin/openssl enc -aes-256-cbc -d -pass file:/root/password.txt -in /path/to/archive/<pool@snapshot>.zfsbkup | \
/bin/pigz -d  | /sbin/zfs receive <pool/restore>
```