俺様サーバー構築記 - ZFS データ送受信で ArchLinux を FreeNAS にバックアップ@デスクトップパソコン

[俺様サーバー構築記 - 基本方針](https://qiita.com/katz_engineer/items/dbabcff4e14e9db2b10f)で挙げたやりたい事「データの自動バックアップシステムの確立」の続きです。

- [前々回](https://qiita.com/katz_engineer/items/f40e84cb8696c00651b8): ssh 互換ソフトの Dropbear をインストール。我が家の ArchLinux 群（デスクトップパソコンとノートパソコン）の間で Dropbear によって公開鍵暗号による接続を確立。
- [前回](https://qiita.com/katz_engineer/items/902824fb70195d41f331): FreeNAS に ssh 接続用ユーザを作成。デスクトップパソコン（我が家のサーバマシン）から公開鍵暗号による接続を確立。

今回は、デスクトップパソコン（我が家のサーバマシン）の全内容を ZFS データ送受信の機能によって FreeNAS にコピーする事を試みます。

# ZFS データ送受信

基本手順は下記のようになります。

0. スナップショットを撮る
0. スナップショットを送信
0. スナップショットを受信、このとき下記に注意
 - 既に存在しているファイルシステム（データセットなど）を指定してはいけない
 - 受信した結果のファイルシステムの mountpoint についてはドキュメントに記述が見当たらない。実際に動かして確認する必要あり。

参考文献: [ZFS データを送信および受信する - 第 7 章 Oracle Solaris ZFS のスナップショットとクローンの操作 - Oracle Solaris ZFS 管理ガイド](https://docs.oracle.com/cd/E24845_01/html/819-6260/gbchx.html)

## IPアドレス等
[前回](https://qiita.com/katz_engineer/items/902824fb70195d41f331)書きましたが、我が家のIPアドレス事情を下記の通りとします。

- デスクトップパソコン（サーバマシン）: 10.0.0.251
- FreeNAS: 10.0.0.108

そして Dropbear のポート番号を 60022 とします。

## デスクトップパソコン → FreeNAS （全データ）

デスクトップパソコン（サーバマシン）で操作します。

```console:デスクトップパソコン（サーバマシン）
# hostname -i
10.0.0.251
# whoami
root
```

スナップショットを撮ります。後程このスナップショットを参照するので、必要な情報をシェル変数に格納しておきます。

```console:デスクトップパソコン（サーバマシン）
# export SNAPSHOT_DATETIME=$(date +%Y%m%d_%H%M%S)
# zfs snapshot tank/main@${SNAPSHOT_DATETIME}_for_backup
```

それでは今回のハイライト！
デスクトップパソコン（サーバマシン）から ZFS データを送信し、FreeNAS で受信します。一応、通信経路を流れるデータは圧縮しておきます。

なお FreeNAS 上でのファイルシステム名は、ユーザ名と同じ bak_server0 にしておきます。こういうのは完全に同一にしておいた方が良い。中途半端に異なる名前にすると、何かと面倒臭い問題が発生します。

本当は、FreeNAS 側で mountpoint を変更したかったんですが、出来ないようです。Linux での ```zfs receive``` コマンドには ```-o``` オプションがあって ZFS データ受信時に属性を変更できるようですが。我が家の FreeNAS はバージョンが古いのでそのせいか、あるいは FreeBSD での ```zfs``` コマンドが Linux とは異なるのか…

```console:デスクトップパソコン（サーバマシン）
# zfs send tank/main@${SNAPSHOT_DATETIME}_for_backup | gzip -c | dbclient -l bak_server0 -p 60022 10.0.0.108 "gzip -cd | sudo zfs recv tank/bak_server0"
```

結果を確認。

```
# dbclient -l bak_server0 -p 60022 10.0.0.108 "zfs list" | grep bak_
tank/bak_server0                                       1.53G   588G  1.53G  /mnt/tank/bak_server0
# dbclient -l bak_server0 -p 60022 10.0.0.108 "zfs list -t snapshot" | grep bak_
tank/bak_server0@20190512_195624_for_backup        0      -  1.53G  -
# dbclient -l bak_server0 -p 60022 10.0.0.108 "ls -dl /mnt/tank/bak_server0"
drwxr-xr-x  16 root  wheel  20 May  4 17:31 /mnt/tank/bak_server0
```

ファイル内容のチェックまではしていませんが、どうやらうまくいったと考えて良いでしょう。

## デスクトップパソコン → FreeNAS （増分）

今度は増分の送受信を試みます。

まずは差分が存在する状況を作ります。今回は ArchLinux をバージョンアップ。

```console:デスクトップパソコン（サーバマシン）
# pacman -Syu
```

ここでまたスナップショットを撮り、先程のスナップショットとの差分を送信します。

スクリプトによる自動化を睨み、シェル変数の事前設定等を一切しない形で一行野郎 one liner にまとめました。その為少々長くなっています。ですが難しい事は何もしていません。

```console:デスクトップパソコン（サーバマシン）
# zfs snapshot tank/main@$(date +%Y%m%d_%H%M%S)_for_backup
# zfs list -t snapshot -o name -s name | grep for_backup | tail -n2 | xargs -r zfs send -i | gzip -c | dbclient -l bak_server0 -p 60022 10.0.0.108 "gzip -cd | sudo zfs recv tank/bak_server0"
```

先程と同様に結果を確認。

```console:デスクトップパソコン（サーバマシン）
# dbclient -l bak_server0 -p 60022 10.0.0.108 "zfs list" | grep bak_
tank/bak_server0                                       1.91G   587G  1.62G  /mnt/tank/bak_server0
# dbclient -l bak_server0 -p 60022 10.0.0.108 "zfs list -t snapshot" | grep bak_
tank/bak_server0@20190512_195624_for_backup     297M      -  1.53G  -
tank/bak_server0@20190512_211013_for_backup        0      -  1.62G  -
# dbclient -l bak_server0 -p 60022 10.0.0.108 "ls -dl /mnt/tank/bak_server0"       
drwxr-xr-x  16 root  wheel  20 May  4 17:31 /mnt/tank/bak_server0
```

# 今後の方針

増分の節で示した手順（スナップショット→増分転送）は、見ていただければわかる通り、手作業が一切入らず決まりきったコマンドの実行だけで済む手順です。これをそのままシェルスクリプトにして crontab を設定すれば、定期的にバックアップできます。

その際には動作ログを残して、エラーメッセージが発生したらメール送信するなどの一工夫が必要です。

更に。FreeNAS 側でのユーザ bak_server0 のコマンドは、全データでも増分でも全く同じになります。手作業による都度修正もありません。従ってこれを authorized_keys に設定しておき、ssh 接続時の自動実行コマンドとして登録しておけば、セキュリティを強化できるでしょう。

ともあれ、ZFS を利用してサーバマシンのバックアップを FreeNAS 上に保存する方法を確認できました。やったね:thumbsup_tone2: