takaha.siの技術メモ

勉強したことをお伝えします。ちょっとでも誰かの役に立てればいいな…

絵描きのzfs snapshot活用術

あっ、線画レイヤーに色塗ってる!

突然ですが、私、趣味で絵を描きます(NSFWです)

twitter.com

絵描きあるあるが「線画レイヤーに色を直接塗ってしまっており、それに気づくことなく作業をガンガン進めてしまい、気づいたら手遅れなところまで進んでしまっている」という事故です。ご丁寧に保存しちゃってたりするともはや後には戻れません。なんとか、お絵かきソフトを駆使して線画と中の色塗りの部分を分離するみたいなことをするしかないです。とてもとても面倒くさいです。

なので考えました。せっかくzfsNASを組んだのだから、zfs snapshotで一時間ごとに絵のファイルが保存されてる作業フォルダのスナップショットを取っておけばよいのでは?と。そしたら、ミスに気づいた時点で過去のスナップショットをさかのぼって、そこから色が塗られちゃう前の古い作業データの線画レイヤだけ取り出して現作業ファイルにコピペすればらくらく回復できます。

というわけで、さっそくやってみました。

なお、zfsNASの話は以下のリンクから読めます。

tkh86.hateblo.jp

まず、定期zfs snapshot。cronとかつかって自分でやってもいいですが面倒ですよね。ここはsanoidっていうツールがあるのでこれを使います。

github.com

Pythonで書かれてるっぽいです。まあ、Debianだったらapt-getでインストールできますので楽ちん。

$ sudo apt install sanoid

んで、次に設定ファイル。/etc/sanoid/sanoid.confに設定ファイルあるので、emacsだかviだかで編集します。私はこんな感じにしました。

[tank/working]
        use_template = template_daily

[template_daily]
        hourly = 24
        daily  = 7
        monthly = 0
        yearly = 0
        autosnap = yes
        autoprune = yes

tank/workingというzfsがイラストのファイルが入ってる領域です。これをsambaで外部に公開して、Windowsからマウントして使ってる感じです。

hourly = 24で一日に一時間に一回snapshotを取り、daily = 7で一日一個。保存する感じにしてます。多分これであってる。。。sanoid.confを編集したら、sanoidを再起動しときます。

$ sudo systemctl restart sanoid

これで、定期的にsnapshotが取れます。

root@liella:/home/takahashi# zfs list -t snapshot
NAME                                                USED  AVAIL     REFER  MOUNTPOINT
tank/working@autosnap_2022-03-13_00:22:35_monthly     0B      -     4.81G  -
tank/working@autosnap_2022-03-13_00:22:35_daily       0B      -     4.81G  -
tank/working@autosnap_2022-03-13_00:22:35_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_01:00:01_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_02:00:24_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_03:00:24_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_04:00:24_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_05:00:24_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_06:00:24_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_07:00:24_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_08:00:01_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_09:00:01_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_10:00:01_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_11:00:24_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_12:00:24_hourly      0B      -     4.81G  -

この設定で一時間に一枚snapshot取れてます。

特定の場所だけsnapshot取りたい!

余談ですが、zfsでスナップショットを取る場合は、zfsファイルシステム(か、zvol)単位になります。つまりzfs内部にある特定のディレクトリだけsnapshotを取る!みたいなことはできないわけです。

root@liella:/home/takahashi# zfs list
NAME                         USED  AVAIL     REFER  MOUNTPOINT
tank                        1.65T  12.8T     1.64T  /tank

例えば、こんなzfsがあったとしましょう。ここで、/tankディレクトリの下にoekakiみたいなディレクトリを作り、このoekakiディレクトリ内部だけのsnapshotを取りたい!みたいなことはできないんですね。上の例だとtank全体でしかsnapshotがとれません。

tankの下にoekakiとは別のディレクトリもたくさんあって、そこにデータもたくさんある場合はそこもまとめてスナップショットされてしまいます。すると、スナップショットの容量が大きくなってzfs poolの容量を圧迫します。

スナップショット領域は最も重要なファイルがあるディレクトリだけに限定したいですね。

とはいえ、スナップショット対象領域を別にした構成に新たに作り直すのも面倒です。後付で特定のディレクトリだけ、例えば/tank/samba/public/workingの場所だけsnapshot取りたい場合は一工夫すれば一応それっぽいことが可能です。

まず、tankの下に新しいzfsを後付します。

root@liella:/home/takahashi# zfs create tank/working
root@liella:/home/takahashi# zfs list
NAME                         USED  AVAIL     REFER  MOUNTPOINT
tank                        1.65T  12.8T     1.64T  /tank
tank/working                4.81G  12.8T     4.81G  /tank/working

これで、tank/workingという新しいzfsが作れました。あとはMOUNTPOINTを変更します。

root@liella:/home/takahashi# zfs set mountpoint=/tank/samba/public/working tank/working
root@liella:/home/takahashi# zfs list
NAME                         USED  AVAIL     REFER  MOUNTPOINT
tank                        1.65T  12.8T     1.64T  /tank
tank/working                4.81G  12.8T     4.81G  /tank/samba/public/working

これで、tank/workingのスナップショットをとれば/tank/samba/public/workingサブディレクトリの中身のみスナップショットを取るみたいなことが事実上できます。あとはtank/workingをマウントすればいいです。

root@liella:/home/takahashi# zfs mount tank/working
root@liella:/home/takahashi# df -h
Filesystem                  Size  Used Avail Use% Mounted on
tank                         15T  1.7T   13T  12% /tank
tank/working                 13T  4.9G   13T   1% /tank/samba/public/working

こんな感じで。もちろん、もともと/tank/samba/public/workingに存在していたファイルはmount時点で見えなくなってしまうので(消えるわけではなくて見えなくなるだけです。mount解除すればまた見えるようになります)もともとあったファイルはどこかに退避しといた上でzfs mount後に移動し直せばいいです。

もともとあったファイルの移動のひと手間がありますが、zfsを作り直すよりは楽だと思いますので、特定ディレクトリだけスナップショット取りたい場合はこのやり方はおすすめです。

さて、/tank/samba/public/workingのスナップショットを取ったとして、スナップショットもWindowsから楽に参照できればいいですよね?というわけで、このスナップショットをマウントしてSambaで公開します。

まず、スナップショットのマウントから。

root@liella:/home/takahashi# zfs list
NAME                         USED  AVAIL     REFER  MOUNTPOINT
tank                        1.65T  12.8T     1.64T  /tank
tank/working                4.81G  12.8T     4.81G  /tank/samba/public/working
root@liella:/home/takahashi# zfs set snapdir=visible tank/working

さて、zfs set snapdir=visible tank/workingを実行したあとで/tank/samba/public/workingの中身を見てみると。。。

root@liella:/tank/samba/public/working# ls -ahlF
total 1.7G
drwxrwxrwx 9 101000 101000   10 Mar 13 09:18 ./
drwxrwxr-x 9 101000 101000   13 Mar 13 16:46 ../
drwxr-xr-x 2 101000 101000   55 Mar 13 09:18 0.下描き/
drwxr-xr-x 2 101000 101000   16 Mar 13 09:18 1.ペン入れ/
drwxr-xr-x 2 101000 101000    6 Mar 13 09:21 2.彩色/
drwxr-xr-x 3 101000 101000   10 Mar 13 09:18 3.QA_Review/
drwxr-xr-x 3 101000 101000   42 Mar 13 09:21 4.Done/
drwxr-xr-x 2 101000 101000   19 Mar 13 09:18 done_drawing/
-rw-r--r-- 1 101000 101000  70M Mar 13 01:16 knowhow.pur
-rw-r--r-- 1 101000 101000 1.6G Mar  9 22:03 myboard.pur
drwxrwxrwx 1 root   root      0 Mar 13 17:26 .zfs/

.zfsという隠しディレクトリができてることがわかります。この中身を見てみると。。。

root@liella:/tank/samba/public/working# cd .zfs/
root@liella:/tank/samba/public/working/.zfs# ls -ahlF
total 17K
drwxrwxrwx 1 root   root    0 Mar 13 17:26 ./
drwxrwxrwx 9 101000 101000 10 Mar 13 09:18 ../
drwxrwxrwx 2 root   root    2 Mar 13 17:26 shares/
drwxrwxrwx 2 root   root    2 Mar 13 22:00 snapshot/
root@liella:/tank/samba/public/working/.zfs# cd snapshot/
root@liella:/tank/samba/public/working/.zfs/snapshot# ls -ahlF
total 264K
drwxrwxrwx 2 root   root    2 Mar 13 22:00 ./
drwxrwxrwx 1 root   root    0 Mar 13 17:26 ../
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_00:22:35_daily/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_00:22:35_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_00:22:35_monthly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_01:00:01_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_02:00:24_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_03:00:24_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_04:00:24_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_05:00:24_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_06:00:24_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_07:00:24_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_08:00:01_hourly/
drwxrwxrwx 8 101000 101000 10 Mar 13 09:18 autosnap_2022-03-13_09:00:01_hourly/
drwxrwxrwx 8 101000 101000 10 Mar 13 09:18 autosnap_2022-03-13_10:00:01_hourly/
drwxrwxrwx 8 101000 101000 10 Mar 13 09:18 autosnap_2022-03-13_11:00:24_hourly/
drwxrwxrwx 8 101000 101000 10 Mar 13 09:18 autosnap_2022-03-13_12:00:24_hourly/
drwxrwxrwx 8 101000 101000 10 Mar 13 09:18 autosnap_2022-03-13_13:00:01_hourly/

はい、このように、snapshotが見えます。

なので、この.zfs領域をSambaで公開してそれをWindowsから見ればOKです。こんな感じで見れます。

f:id:tkh86:20220313221750p:plain

ここで問題発生

ここで問題発生。なんかスナップショットのファイル名が妙ちくりんな名前になってます。これは、Windowsがファイル名に:を使えないことに起因します。sanoidが取るスナップショット名に:が入っちゃってるので、Sambaが変換をかけてこうなっちゃうんですね。

sanoidのほうでsnapshotの名前を:を使わないなにか別のフォーマットに変えられないのか?と思ったんですが、これは無理です。sanoidにその機能はないとのこと

なのでsamba側でなんとかする

なので、samba側でなんとかします。sambaにはwindowsでは表示できない記号がファイル名に使われてた場合、それを置き換える機能vfs objects = catiaというものがあって、それを使います。

[snapshot]
    comment = zfs snapshots
    vfs objects = catia
    mangled names = no
    catia:mappings = 0x22:0xa8,0x2a:0xa4,0x2f:0xf8,0x3a:0xf7,0x3c:0xab,0x3e:0xbb,0x3f:0xbf,0x5c:0xff,0x7c:0xa6
    path = /samba/public/working/.zfs
    writable = no
    guest ok = no

この0x22:0xa8,0x2a:0xa4,0x2f:0xf8,0x3a:0xf7,0x3c:0xab,0x3e:0xbb,0x3f:0xbf,0x5c:0xff,0x7c:0xa6ってのが変換テーブルなわけですが、これの内訳はこういう意味です。

0x22:0xa8 = ダブルクオーテーション (") -> ウムラウト(¨)
0x2a:0xa4 = アスタリスク(*) -> 汎用通貨記号(¤)
0x2f:0xf8  = スラッシュ(/) ->スラッシュ付きオー(⊘)
0x3a:0xf7 = コロン(:) ->除算記号(÷)
0x3c:0xab = 山括弧左(<) ->二重山括弧左(《)
0x3e:0xbb = 山括弧右(>) ->二重山括弧右(》)
0x3f:0xbf  = クエスチョンマーク(?) ->逆さクエスチョン(¿)
0x5c:0xff   = Yenマーク(\) ->Yの分音記号(ÿ)
0x7c:0xa6 = 垂直線(|) ->分割垂直線(¦)

この設定で。いい感じに表示できます。

f:id:tkh86:20220313222712p:plain

2つ注意点。

この変換テーブルをいじらないほうがいいです。例えば、÷(除算記号)はいやだから_(アンダーバー)に変換したい!と思ってもいじっちゃダメです。理由はあらゆるファイルの_がすべて:に変換されてしまうので、_を含むファイルやディレクトリをみようとした瞬間全体がおかしくなります。_はファイルパスとしてわりかし普通に使うので変換先として使うのはやめましょう。また、このvfs objects = catiaをsambaの[global]セクションに入れるのもやめましょう。理由はsnapshot以外のすべての領域において万が一除算記号(÷)といった記号がファイル名に使われてしまうと、上述した理由でこれまたSamba全体がおかしくなるからです。snapshotの部分は除算記号(÷)などがパスに使われることはないと保証できますので(sanoidはそんな記号使わないから)snapshot領域のみにvfs objects = catiaを適用しましょう。

はい、これで一時間おきに自動的にスナップショットがとられていつでも過去のデータを参照することができるようになります!

これでHappyお絵かきライフが捗るんじゃぁ~

次はバックアップだねぇ。。。