btrfsのsnapshot/send/receiveの挙動を検証する

スナップショットと容量とリネーム(mv)

最初の状態。スナップショットはsharedとされている

     Total   Exclusive  Set shared  Filename
  40.00KiB       0.00B           -  ./test1/fileA
  56.00KiB       0.00B           -  ./test1/fileB
  96.00KiB       0.00B           -  ./test1
  40.00KiB       0.00B           -  ./snap1/fileA
  56.00KiB       0.00B           -  ./snap1/fileB
  96.00KiB       0.00B           -  ./snap1
 192.00KiB       0.00B    96.00KiB  .

ファイルを追加。追加した分以外がsharedで、サイズが増えているのはsnapshotではなくワーキングボリューム。

     Total   Exclusive  Set shared  Filename
  40.00KiB       0.00B           -  ./test1/fileA
  56.00KiB       0.00B           -  ./test1/fileB
  68.00KiB    68.00KiB           -  ./test1/fileC
 164.00KiB    68.00KiB           -  ./test1
  40.00KiB       0.00B           -  ./snap1/fileA
  56.00KiB       0.00B           -  ./snap1/fileB
  96.00KiB       0.00B           -  ./snap1
 260.00KiB    68.00KiB    96.00KiB  .

新規にスナップショットを取ると増えたファイルもsharedに入った。合計で3ファイル分の容量で無駄がない

     Total   Exclusive  Set shared  Filename
  40.00KiB       0.00B           -  ./test1/fileA
  56.00KiB       0.00B           -  ./test1/fileB
  68.00KiB       0.00B           -  ./test1/fileC
 164.00KiB       0.00B           -  ./test1
  40.00KiB       0.00B           -  ./snap1/fileA
  56.00KiB       0.00B           -  ./snap1/fileB
  96.00KiB       0.00B           -  ./snap1
  40.00KiB       0.00B           -  ./snap2/fileA
  56.00KiB       0.00B           -  ./snap2/fileB
  68.00KiB       0.00B           -  ./snap2/fileC
 164.00KiB       0.00B           -  ./snap2
 424.00KiB       0.00B   164.00KiB  .

もう一度ファイルを増やす。

     Total   Exclusive  Set shared  Filename
  40.00KiB       0.00B           -  ./test1/fileA
  56.00KiB       0.00B           -  ./test1/fileB
  68.00KiB       0.00B           -  ./test1/fileC
  20.00KiB    20.00KiB           -  ./test1/fileD
 184.00KiB    20.00KiB           -  ./test1
  40.00KiB       0.00B           -  ./snap1/fileA
  56.00KiB       0.00B           -  ./snap1/fileB
  96.00KiB       0.00B           -  ./snap1
  40.00KiB       0.00B           -  ./snap2/fileA
  56.00KiB       0.00B           -  ./snap2/fileB
  68.00KiB       0.00B           -  ./snap2/fileC
 164.00KiB       0.00B           -  ./snap2
 444.00KiB    20.00KiB   164.00KiB  .

そしてスナップショット。これは準備段階。

     Total   Exclusive  Set shared  Filename
  40.00KiB       0.00B           -  ./test1/fileA
  56.00KiB       0.00B           -  ./test1/fileB
  68.00KiB       0.00B           -  ./test1/fileC
  20.00KiB       0.00B           -  ./test1/fileD
 184.00KiB       0.00B           -  ./test1
  40.00KiB       0.00B           -  ./snap1/fileA
  56.00KiB       0.00B           -  ./snap1/fileB
  96.00KiB       0.00B           -  ./snap1
  40.00KiB       0.00B           -  ./snap2/fileA
  56.00KiB       0.00B           -  ./snap2/fileB
  68.00KiB       0.00B           -  ./snap2/fileC
 164.00KiB       0.00B           -  ./snap2
  40.00KiB       0.00B           -  ./snap3/fileA
  56.00KiB       0.00B           -  ./snap3/fileB
  68.00KiB       0.00B           -  ./snap3/fileC
  20.00KiB       0.00B           -  ./snap3/fileD
 184.00KiB       0.00B           -  ./snap3
 628.00KiB       0.00B   184.00KiB  .

mvしてみると、ファイル名は異なっているが、sharedの量は増えていない。
mvは理解してくれるらしい。ファイル名ではなく実体の問題。

     Total   Exclusive  Set shared  Filename
  40.00KiB       0.00B           -  ./test1/fileA
  56.00KiB       0.00B           -  ./test1/fileB
  68.00KiB       0.00B           -  ./test1/fileC
  20.00KiB       0.00B           -  ./test1/fileE
 184.00KiB       0.00B           -  ./test1
  40.00KiB       0.00B           -  ./snap1/fileA
  56.00KiB       0.00B           -  ./snap1/fileB
  96.00KiB       0.00B           -  ./snap1
  40.00KiB       0.00B           -  ./snap2/fileA
  56.00KiB       0.00B           -  ./snap2/fileB
  68.00KiB       0.00B           -  ./snap2/fileC
 164.00KiB       0.00B           -  ./snap2
  40.00KiB       0.00B           -  ./snap3/fileA
  56.00KiB       0.00B           -  ./snap3/fileB
  68.00KiB       0.00B           -  ./snap3/fileC
  20.00KiB       0.00B           -  ./snap3/fileD
 184.00KiB       0.00B           -  ./snap3
 628.00KiB       0.00B   184.00KiB  .

rsync fileE fileF; rm fileEとしてみた。
やはり、別ファイルとして扱われ、sharedの量が減っている。

     Total   Exclusive  Set shared  Filename
  40.00KiB       0.00B           -  ./test1/fileA
  56.00KiB       0.00B           -  ./test1/fileB
  68.00KiB       0.00B           -  ./test1/fileC
  20.00KiB    20.00KiB           -  ./test1/fileF
 184.00KiB    20.00KiB           -  ./test1
  40.00KiB       0.00B           -  ./snap1/fileA
  56.00KiB       0.00B           -  ./snap1/fileB
  96.00KiB       0.00B           -  ./snap1
  40.00KiB       0.00B           -  ./snap2/fileA
  56.00KiB       0.00B           -  ./snap2/fileB
  68.00KiB       0.00B           -  ./snap2/fileC
 164.00KiB       0.00B           -  ./snap2
  40.00KiB       0.00B           -  ./snap3/fileA
  56.00KiB       0.00B           -  ./snap3/fileB
  68.00KiB       0.00B           -  ./snap3/fileC
  20.00KiB    20.00KiB           -  ./snap3/fileD
 184.00KiB    20.00KiB           -  ./snap3
 628.00KiB    40.00KiB   164.00KiB  .

ちょっと手直し。3つ目のスナップショットを取る前の状態に。

# btrfs subvol delete test1
# mv snap3 test1
# btrfs property set -ts test1 ro false

mvして、ファイルを追加した。

     Total   Exclusive  Set shared  Filename
  40.00KiB       0.00B           -  ./snap1/fileA
  56.00KiB       0.00B           -  ./snap1/fileB
  96.00KiB       0.00B           -  ./snap1
  40.00KiB       0.00B           -  ./snap2/fileA
  56.00KiB       0.00B           -  ./snap2/fileB
  68.00KiB       0.00B           -  ./snap2/fileC
 164.00KiB       0.00B           -  ./snap2
  40.00KiB       0.00B           -  ./test1/fileA
  56.00KiB       0.00B           -  ./test1/fileB
  68.00KiB       0.00B           -  ./test1/fileC
  20.00KiB       0.00B           -  ./test1/fileE
 112.00KiB   112.00KiB           -  ./test1/fileF
 296.00KiB   112.00KiB           -  ./test1
  40.00KiB       0.00B           -  ./snap3/fileA
  56.00KiB       0.00B           -  ./snap3/fileB
  68.00KiB       0.00B           -  ./snap3/fileC
  20.00KiB       0.00B           -  ./snap3/fileD
 184.00KiB       0.00B           -  ./snap3
 740.00KiB   112.00KiB   184.00KiB  .

shareされていないのは追加されたfileFだけ。ここでスナップショットを取っておく。

     Total   Exclusive  Set shared  Filename
  40.00KiB       0.00B           -  ./snap1/fileA
  56.00KiB       0.00B           -  ./snap1/fileB
  96.00KiB       0.00B           -  ./snap1
  40.00KiB       0.00B           -  ./snap2/fileA
  56.00KiB       0.00B           -  ./snap2/fileB
  68.00KiB       0.00B           -  ./snap2/fileC
 164.00KiB       0.00B           -  ./snap2
  40.00KiB       0.00B           -  ./test1/fileA
  56.00KiB       0.00B           -  ./test1/fileB
  68.00KiB       0.00B           -  ./test1/fileC
  20.00KiB       0.00B           -  ./test1/fileE
 112.00KiB       0.00B           -  ./test1/fileF
 296.00KiB       0.00B           -  ./test1
  40.00KiB       0.00B           -  ./snap3/fileA
  56.00KiB       0.00B           -  ./snap3/fileB
  68.00KiB       0.00B           -  ./snap3/fileC
  20.00KiB       0.00B           -  ./snap3/fileD
 184.00KiB       0.00B           -  ./snap3
  40.00KiB       0.00B           -  ./sendsnap-latest/fileA
  56.00KiB       0.00B           -  ./sendsnap-latest/fileB
  68.00KiB       0.00B           -  ./sendsnap-latest/fileC
  20.00KiB       0.00B           -  ./sendsnap-latest/fileE
 112.00KiB       0.00B           -  ./sendsnap-latest/fileF
 296.00KiB       0.00B           -  ./sendsnap-latest
   1.01MiB       0.00B   296.00KiB  .

send/receive

さて、ここからsend/receiveを試してみる。まずはsnapshot-latestの単純な送信

# btrfs send sendsnap-latest > ~/send1
At subvol sendsnap-latest

# ls -l send1
-rw-r--r-- 1 root root 294193  8月 31 12:42 send1

使用量通りと考えていいだろう。
次にsnap3に基づいて作ってみる。容量は差分容量通りといった感じ。

# btrfs send -p snap3 sendsnap-latest > ~/send2
At subvol sendsnap-latest

# ls -l send*
-rw-r--r-- 1 root root 294193  8月 31 12:42 send1
-rw-r--r-- 1 root root 114925  8月 31 12:44 send2

それぞれの差分を作る。

# btrfs send snap1 > ~/sendsnap1
At subvol snap1
# btrfs send -p snap1 snap2 > ~/sendsnap1-2
At subvol snap2
# btrfs send -p snap2 snap3 > ~/sendsnap2-3
At subvol snap3

# ls -l send*
-rw-r--r-- 1 root root 294193  8月 31 12:42 send1
-rw-r--r-- 1 root root 114925  8月 31 12:44 send2
-rw-r--r-- 1 root root  94926  8月 31 12:45 sendsnap1
-rw-r--r-- 1 root root  66551  8月 31 12:45 sendsnap1-2
-rw-r--r-- 1 root root  18576  8月 31 12:45 sendsnap2-3

全サブボリュームを削除してみる。

# for i in *; do btrfs subvol delete $i; done
Delete subvolume (no-commit): '/mnt/1/sendsnap-latest'
Delete subvolume (no-commit): '/mnt/1/snap1'
Delete subvolume (no-commit): '/mnt/1/snap2'
Delete subvolume (no-commit): '/mnt/1/snap3'
Delete subvolume (no-commit): '/mnt/1/test1'

最新ボリュームのsend2をreceiveしてみる。

# btrfs receive . < ~/send2
At snapshot sendsnap-latest
ERROR: cannot find parent subvolume

元になるサブボリュームがないとだめらしい。
順に戻してみる。

# btrfs receive . < ~/sendsnap1
At subvol snap1
# btrfs receive . < ~/sendsnap1-2
At snapshot snap2
# btrfs receive . < ~/sendsnap2-3
At snapshot snap3
# btrfs receive . < ~/send2
At snapshot sendsnap-latest

順当に行える。では、名前が変わったらどうだろう?
sendsnap-latestを削除して、ベースになるsend3をリネームしてみる。

# btrfs subvol delete sendsnap-latest/
Delete subvolume (no-commit): '/mnt/1/sendsnap-latest'
# mv snap3 sendsnap-base
# btrfs receive . < ~/send2
At snapshot sendsnap-latest
# ls
sendsnap-base  sendsnap-latest  snap1  snap2

sendsnap-latestはsnap3というサブボリュームに基づいて作られていたが、snap3という名前のサブボリュームは、sendsnap-baseという名前に変更されてしまったため、もうない。
だが、一致する名前はなくても正しく書き戻すことができた。

ls -l sendsnap-latest/
合計 296
-rw------- 1 yek yek  40005  8月 31 12:11 fileA
-rw------- 1 yek yek  54006  8月 31 12:20 fileB
-rw------- 1 yek yek  66006  8月 31 12:22 fileC
-rw------- 1 yek yek  18066  8月 31 12:24 fileE
-rw------- 1 yek yek 114066  8月 31 12:35 fileF

ということは名前ではなく、IDなどで判断しているのだろう。元になったサブボリュームが存在していればOKだ。
では中間がなくなったらどうだろう?

元snap3、元sendsnap-baseだけを残してみる

# btrfs subvol delete sendsnap-latest/
Delete subvolume (no-commit): '/mnt/1/sendsnap-latest'
[mint 1]# btrfs subvol delete snap2
Delete subvolume (no-commit): '/mnt/1/snap2'
[mint 1]# btrfs subvol delete snap1
Delete subvolume (no-commit): '/mnt/1/snap1'
# btrfs fi du .
     Total   Exclusive  Set shared  Filename
  40.00KiB    40.00KiB           -  ./sendsnap-base/fileA
  56.00KiB    56.00KiB           -  ./sendsnap-base/fileB
  68.00KiB    68.00KiB           -  ./sendsnap-base/fileC
  20.00KiB    20.00KiB           -  ./sendsnap-base/fileD
 184.00KiB   184.00KiB           -  ./sendsnap-base
 184.00KiB   184.00KiB       0.00B  .

全くシェアはされていないが、シェアフラグを削除し、データは消していないようだ。
ハードリンクのような仕組みだろうか。

サブボリューム名とシンボリックリンク

では、名前においてsubvol名はシンボリックリンクでも良いのだろうか?
send2を戻してリンクしてみる。

# btrfs receive . < ~/send2
At snapshot sendsnap-latest
# ln -s sendsnap-latest/ current
# mount -o subvol=current /dev/mapper/hymaster_1 /mnt/3
# ls /mnt/3
fileA  fileB  fileC  fileE  fileF

こうなった。

マウントするサブボリュームの切り替えは、set-defualtでも良いが、シンボリックリンクを論理的な名前にすることも可能なようだ。

ディスク故障でbtrfsの機能を使い倒す

対応方法

ディスク容量が逼迫しているので何らかの対処が必要である。
とりあえず考えられる方法としては

  • ディスクを大容量のものに交換する
  • USBあるいはeSATA接続のディスクを追加する
  • NASを追加してディスクを追加する
  • AoEを使って他ホストのディスクを追加する
  • 内蔵ディスクを追加する

のいずれかだ。そもそも取り扱うデータ量が多いため、削減ということは考えられない。
もちろん、削減自体は行っているが、その精査にあてる時間のほうが重要で、無効な対策であると考えられる。それほど引き延ばすこともできない。

大容量への交換は効果的だが、現在3TBを使用しており、4TBの場合は33%の容量増となり、不足である。
6TB以上は高額すぎるため却下された。

USB/eSATAを最も有力な候補としていたが、台数搭載するためには金額的にも高く、またいずれの製品も信頼性に劣るようだ。

多数搭載のケースを使用するのであればReadyNASも有力な候補だった。ReadyNASはiSCSIに対応するが、「ReadyNASに搭載されているディスクを指定する」ことはできない。何台搭載しても、btrfs的に見れば1台にしかならないのだ。

そのため、btrfsで管理するためにはReadyNASを複数台追加する必要がある。ReadyNAS全体でbtrfsから見て1台のディスクであり、ReadyNASの台数=ディスク台数になる。
btrfsは不均等なRAID1をサポートするため、例えばReadyNASに12TBを搭載すれば12TB RAID1を構成することが可能だが、あまりうれしくはない状態になる。

AoEを使うのは追加費用のかからない方法だ(ディスクとホストはあるため)。
だが、問題はAoEを使う方法は常に

  • マウント前にターゲットホストを起動していなければならない
  • アンマウント前にターゲットホストを終了してはいけない
  • ネットワークを切断してはいけない

と結構厳しい条件になる。
ターゲットホストが消費電力が大きく、また冗長系システムであり、日常稼働しているものであることを考えるとこれは厳しい。

そこで内蔵ディスク追加を選択した。

Z400の3.5inchシャドウベイの数は2、SATAポート数は6である。
現在、iStarUSAの5.25inchベイ*2を3.5inch HDD*3に変換するリムーバブルラックを使用してシステムSSDを含む計5台を搭載している。
そのため、2台追加となると、マウント的にもSATAポート的にも足りない。

採用した方法はエアリアのSATA*2 PCI-e 1xカードによりポートを2増設し、DVDドライブを除去してiStarUSAの5.25inch*3をHDD*5に変換するリムーバブルラックを搭載するというもの。

かなり無茶だが、さらに無茶なのが、そもそもビデオカードがGeForce GTX750Tiに変更されているために、隣のPCI-e 4xが埋まってしまっており、逆側のPCI-e 4xは使用済みなので、PCI-e 16xに接続した、ということか。

ディスク追加手順

ディスクの追加は、ディスクを装着した状態で

# btrfs device add /dev/sdx /path/to/volume
# btrfs device add /dev/sdy /path/to/volume
# btrfs balance /path/to/volume

のようになる。
balanceの所要時間は果てしなく、うちのケースでは40時間ほどかかった。

そして、balanceがまもなく終わろうという頃に、もともとあったほうのディスクが壊れた。

故障ディスクの除去と交換

故障ディスクが正常にアクセスできない状態にあるとき、btrfsファイルシステムへのアクセスはシステム全体を止めてしまう。
この状態ではbtrfs device deleteもできない。

もしこれが可能な状態であればdeleteよりも新規ディスクも装着した状態でのreplaceのほうが早い。

故障ディスクはオフラインの状態でまず除去してしまい、その上で新規ディスクに交換する。
そして、新規ディスクに対して

# btrfs device add /dev/sdx /path/to/volume

したあと、

# btrfs device delete missing /path/to/volume

するとreplaceに近い挙動になる。

しかし、実際にやるとシステムが死んでしまった。ログにがっつりRIPが残っていた。

ファイルシステム新規作成

もはや修復は不可能なので、ファイルシステムを再作成してバックアップから書き戻す。

dm_crypt上に作成するのであれば、dmデバイスを用意したうえで

$ sudo mkfs.btrfs -L labelname -d raid1 -m raid1 /dev/mapper/crypt_dev_*

こうしてみると気づくのだが、btrfsのミラーリングはミラーレッグの指定ができない。
ジャーナリングの強化版というコンセプトなので当然なのかもしれないが、結構不安。本物のRAID1がほしい人はLVMを使うほうが良さそう。LVM+Btrfsであれば、2台ずつ容量を揃える必要があるが、容量の異なるディスクを混在できる。

バックアップのbtrfsファイルシステムからのリストア

btrfsにはsend/receive機能があり、簡単にバックアップができる。

btrfs sendは差分も可能でストリームで送られる。つまりファイルとして保存することもできる。
この場合復元はcatによって行うことになるだろう。差分だとどうなるのかはわからない。

recieveはsendによって出力されるストリームを元にファイルへと書き出す。

ネットワーク越しのバックアップ手段として行う場合は、権限的な難しさがある。send/receiveはroot権限が必要なためだ。
rsyncであればユーザー権限でのバックアップが可能だが、send/receiveを使う場合はそうはいかない。
受け渡しをssh経由で行おうとすると、rootでのsshアクセスを許していなかったりして結構なネックになる。
別に鍵を作れば良い話ではあるが。

sudoを用いたコマンドラインで行うのであれば、socatを使用する方法がある。receive側は

$ socat tcp-listen:30000 | sudo btrfs receive /path/to/volume

send側は

$ sudo btrfs subvolume snapshot -r /path/to/volume /path/to/snapshot
$ sudo btrfs send /path/to/snapshot | socat stdin tcp-connect:receiverhost:30000

経路安全性を問題にするのであれば、openssl-listenを使うか、ssh -Lを使うなどの方法がある。
インターフェイスを限定できないのが問題なら、rubyワンライナーを使う方法や、もうひとつプログラムを増やし、ソケットで読んで書き込む仕様として、ssh経由でソケットに出力するプログラムを起動するという方法もある。

例えば次のようなスクリプトである。

#!/usr/bin/zsh
zmodload zsh/net/socket

zsocket -l /tmp/btrfs-syncer
sock=$REPLY
while zsocket -a $sock
do
  (
    btrfs receive /path/to/volume <&$REPLY
  ) &
done

こちらはrootで実行する。対してsshで実行すべきは

#!/usr/bin/zsh
zmodload zsh/net/socket
zsocket /tmp/btrfs-syncer
cat >&$REPLY
exec {REPLY}>&-

あとは

$ sudo btrfs subvolume snapshot -r /path/to/volume /path/to/snapshot
$ sudo btrfs send /path/to/snapshot | ssh receivinghost.locadomain btrfs-syncer-client

注意点として、例えばsubvolというサブボリュームに書き戻したい場合、subvolというサブボリューム上にreceiveすると結構困ってしまう。
その場合はそのペアレントボリュームにreceiveした上で(snapshotの名前になってしまうので)mvするのが正しい。

なお、mvしてもreadonlyのままになるため、ロールバックするためには属性変更が必要。

$ sudo btrfs property set -ts subvol ro false

ro trueでreadonly。
IDによるマウントを使っているのであれば、set-default

追加のクリティカルヒット

しかし、またも/dev/sdgの大量エラーという結果になった。一見すると正常だが、ログを見ると1.77TiBの書き込みの間に71456ものblk_update_requestが発行されている。

2度続けてsdgだったため、ケーブル、ラック、ボードのいずれかが問題である可能性も考えられたため、ディスクを入れ替えて(/dev/sdfの位置にあるディスクと入れ替えた)試したところエラーはなく、ディスクの不良と断定。初期不良を申請し、翌日回答、翌々日到着となった(NTT-Xの対応は早い)。

ちなみに、NTT-Xにディスクの初期不良対応を申請したところ、

  • メーカーに確認→メーカー回答を得て応答(交換or返金)→交換の回答を得て発送→到着時に集荷として引き渡し

という手順だった。

交換ディスクは

# dd if=/dev/zero of=/dev/sdf bs=512M

として全域書き込みを行い、

# journalctl --no-pager | grep -F blk_ | tail

としてblk_update_requestが発生していないことを確認した。

そして前述の通り、新規ファイルシステム作成、マスターボリュームのマウント、socatと連動したsend/receive、リネーム、プロパティ変更と行い、エラーがないことを確認した。
なぜか全域でデータ量が5.30TiBまで減少していた。

しかしtopで、kworker/3:2及びksoftirqd/3が張り付いたままになっている。
kworkerはおそらくbtrfs絡みだろうし、ksoftirqdが固まっているということはディスク処理だろうから、しばらく待つことにする。特にkworkerはずっとS欄がR(Running)であり、明らかに忙しくしているので安全を考えて待つ。

結局kworderがしずまらなかったので、syncしてunmountし、それが問題なくunmountされたことを確認してからreboot。

エラーが何もないことを確認して、おそらくこれで完了。
とてもとてもとても大変だった。

Btrfsのバックアップがうまくいかない

sshの問題

「sshdだとダメなのに、sshd -d(デバッグモード)では通る!!」 と言っていた問題は、yum update -yしたあとで再起動したら解決した。 意味不明だ。

send/receiveはよく止まる

2.79TiBに達した時に、send側コンピュータが停止してしまう。 なお、スナップショットの削除を行って再びためすと、さらに短く失敗するようになる。

最初はオーバーヒートかと思ったのだが、2.79TBで止まる、ということが共通しているため、問題があるように疑われる。

これで代替手段というと、スクリプトを組むしかないか。 それなりに複雑なものになりそうだ。もっとも、btrfsが集中的なwriteそのものを受け付けないのなら話は別だが、3TB近く書けるのだから、まさか。

rsyncでなくsend/receiveを使うことでBtrfsに戻す

概要

btrfsがrsyncで死んでしまうので、現在はLVM+XFSを使用しているが、いい加減容量は限界近いのでbtrfsに戻す。

rsyncで詰んでしまう以上、rsyncではなく、send/receiveを用いたものとする。 btrfs wikiによれば、rsyncを用いるよりも遥かに高速なのだという。

手順

まずはアンマウント

umount /MirrorSlave

そして、LVMをインアクティブに

vgchange -a n

btrfsを作る

mkfs.btrfs -L MirrorBtr /dev/mapper/btr*

/etc/fstabを編集する

UUID=0123456-7890-acbd-ef01-234567890ab /MirrorRoot           btrfs   noauto,noatime,autodefrag,compress-force=lzo,space_cache,subvol=mirror 0 0

初期化スクリプトを編集する

#!/bin/zsh

typeset -i index=1

for i in /dev/disk/by-id/{ata-foo,ata-bar,ata-baz,ata-quux}
do
#  print $i
  cryptsetup --hash=sha512 --cipher=twofish-xts-plain --offset=0 --key-file=/home/foo/key --key-size=512 open --type=plain "$i" "btrdm_$(printf "%02d" $index)" || exit 1
(( index++ ))
done

mount /MirrorRoot
	
#mount -U 7ebc7d8d-35c6-4d98-8457-3323c242e9fe -o noatime,autodefrag,compress-force=lzo,space_cache,subvol=mirror /MirrorRoot

#pvscan
#vgscan
#lvscan
#vgchange -a y MirrorVG
#lvchange -a y MirrorVG/MirrorLV
#mount -U "0d09b605-a52b-48f4-8ad5-ed26456ab6cd" /MirrorRoot

クライアント側スクリプト。

#!/usr/bin/zsh

notify-send "BtrSnapSync: Woke"

if ! btrfs subvolume snapshot -r /home/foo/share /home/foo/share/snapshots/snapshot-new 
then
  print "BtrSnapSync: **CRITICAL** Failed to create new snapshot"
  exit 2
fi

sync

notify-send "BtrSnapSync: Snapshot."

if [[ -e /home/foo/share/snapshots/snapshot-old ]]
then
  if btrfs send -v -p /home/foo/share/snapshots/snapshot-old /home/foo/share/snapshots/snapshot-new/  | ssh -v -i /root/.ssh/btrsnapsync daisy.local /usr/local/sbin/btrrecv.sh
  then
    notify-send "Send Snapshot."
    btrfs subvolume delete /home/foo/share/snapshots/snapshot-old
    mv /home/foo/share/snapshots/snapshot-new /home/foo/share/snapshots/snapshot-old
    notify-send "Deleted old Snapshot."
  else
    notify-send "Failed to send Snapshot"
    btrfs subvolume delete /home/foo/share/snapshots/snapshot-new
  fi
else
  if btrfs send -v /home/foo/share/snapshots/snapshot-new  | ssh -i /root/.ssh/btrsnapsync daisy.local /usr/local/sbin/btrrecv.sh
  then
    notify-send "Send Snapshot."
    mv /home/foo/share/snapshots/snapshot-new /home/foo/share/snapshots/snapshot-old
    notify-send "Deleted old Snapshot."
  else
    notify-send "Failed to send Snapshot"
    btrfs subvolume delete /home/foo/share/snapshots/snapshot-new
  fi
fi

サーバー側スクリプト

#!/bin/sh
btrfs receive /MirrorRoot && mv /MirrorRoot/snapshot-new /MirrorRooot/snapshot`date +%y%m%d%H%M`

rootのSSH鍵を生成(適宜)

mkdir .ssh
ssh-keygen -f .ssh/snapshot
chmod 700 .ssh
chmod 600 .ssh/*

鍵を転送(適宜)

scp .ssh/snapshot.pub server:.ssh/authorized_keys
ssh server
chmod 600 .ssh/authorized_keys

鍵をコマンドに結びつける

vim .ssh/authorized_keys

sshd_configでforced-commands-only

vim /etc/ssh/sshd_config

挙動

btrfsのsnapshotやsend/receiveについて知識がなかったため、挙動をひとつひとつ確認することにした。

まず、snapshotはディレクトリに見える。 そのディレクトリにはスナップショットの完全なファイルがあるように見える。常時アクセス可能な凍結された状態が存在するわけだ。

そして、スナップショットは元となるボリュームにネストされたサブボリュームになる。 つまり、/fooにマウントされているサブボリュームのスナップショットは、/foo/snapshot1のようになるというわけだ。この外、例えば/barには置けない。

この/foo/snapshot1はmvは可能だが、rmはできない。rmするのであれば、btrfs subvolume deleteでなければならない。

このスナップショットをsendする時、-pオプションがなければそのsnapshotを構成する全体を送信する。あれば、指定されたサブボリューム(マウントされたディレクトリを指定)との差分を送信する。

内容は標準出力に吐かれ、-fオプションで保存することもできるが、それはリダイレクトで保存しても良いようだ。

receiveについても、およそ似たような挙動である。 標準出力から読んで再編成するか、もしくは-fオプションでファイルから編成する。 これは、sharのようなものではなく、編成されるのはスナップショットである。 つまり、snapshot1というsnapshotをreceiveした場合、snapshot1というsnapshotが作られる。

mvで名前が変わった状態で、-pオプション付きで送りつけたsnapshotがどうなるのかは、まだ試していない。

snapshotにロールバックする場合は、マウントポイントの付け替えと、サブボリュームのデリートでよさそうだ。set-defaultが必要か?

多くのスナップショットを維持することでディスク容量やI/Oにどのように影響するのかはわからない。 英語文書を読めばよいのかもしれないが、なかなか大変そうなので、そこには当面手を付ける予定はない。

思わぬつまづき

rootユーザーでRSA鍵でsshログインできないという問題に遭遇した。 一般ユーザーならばできるし、またサーバー側がsshd -dで起動した場合は通る。 デバッグモードなら通るため、メッセージの確認もできない。 かなり困った。

「update-grubがコケる」問題、ついに解決か

ブログでは4月14日のアーティクルでレポートし、 先日のアーティクルで再インストールという解決方法に至ったことを紹介した。

実際には2月には問題は発生しており、実に4ヶ月以上に渡って、Grubのトラブルということでいえば7ヶ月に渡って悩まされてきた。

この問題はManjaro Forumに、LinuxQuestionに、そしてGoogle+でと訊いていくことになったが、誰ひとりとして解決策を提示することはできなかった。 状況変化に合わせてManjaro Forumで再質問し、再インストールという解決策をとったものの、 結局アップデート時に問題は再発、絶望的な気分を味わった。

だが、そこで気づいたことがあったので、試してみることにした。

  • 再インストール
  • update-grub -> 成功
  • LVM snapshot作成
  • update system -> 失敗
  • LVM snapshotのマージ
  • update-grub -> 成功
  • LVM snapshot作成
  • update-grub -> 失敗
  • LVM snapshot削除
  • update-grub -> 成功
  • update system -> 成功

system upgradeでの失敗によるシステム損傷に備え、巻き戻しを可能にしてきたLVMスナップショット。 実際にこの巻き戻しによってとても救われてきた。 それによってここまで環境を崩壊させず「ダメでした」で済んでいた。

だが、それが原因だったというのか。 LVM snapshotが原因でupdate grubが失敗するなんていうことがありうるのか。 全く関係ないように見えるのに。

とりあえず、完全に解決したのか分からないが、解決したように見える。

これは難題だった…

アップデート&ロールバック

Manjaroのアップデートを実行したところ、またクラッシュした。X Window Systemが起動しない。

しかし、今回は対策をとっていた。

sudo lvcreate -s -L 10G -n preug ManjaroVG/ManjaroRootとすることでLVMスナップショットを取得、lvconvert --merge ManjaroVG/preugとすることでロールバックできる。

ただし、システム起動中は即座にはできないので注意が必要だ。

Manjaro LinuxのXが起動しなくなった

12-20のシステムアップグレードリリースでManjaroを再起動した際、Xが起動しなくなった。

[ 32.722] (II) fglrx(0): Desc: AMD FireGL DRM kernel module
[ 32.722] (II) fglrx(0): Kernel Module version matches driver.
[ 32.722] (II) fglrx(0): Kernel Module Build Time Information:
[ 32.722] (II) fglrx(0): Build-Kernel UTS_RELEASE: 3.18.0-0-MANJARO
[ 32.722] (II) fglrx(0): Build-Kernel MODVERSIONS: no
[ 32.722] (II) fglrx(0): Build-Kernel __SMP__: no
[ 32.722] (II) fglrx(0): Build-Kernel PAGE_SIZE: 0x1000
[ 32.722] (II) fglrx(0): [uki] register handle = 0x00016000
[ 32.722] (II) fglrx(0): DRI initialization successfull
[ 32.722] (II) fglrx(0): FBADPhys: 0xf400000000 FBMappedSize: 0x010e0000
(==) Log file: “/var/log/Xorg.0.log”, Time: Sat Dec 20 23:57:18 2014
(==) Using config file: “/etc/X11/xorg.conf”
(==) Using config directory: “/etc/X11/xorg.conf.d”
(==) Using system config directory “/usr/share/X11/xorg.conf.d”
(WW) fglrx: No matching Device section for instance (BusID PCI:0@0:1:1) found
/usr/bin/Xorg.bin: symbol lookup error: /usr/lib/xorg/modules/drivers/fglrx_drv.so: undefined symbol: GlxInitVisuals2D

検索すると事例はかなり多く見つかるが、そのほとんどはaticonfig –initialしろというものだ。

だが、これでうまくはいかなかった。nomodesetしろという声もあったが、それはすでにしてあるし、確認もした。

そのほかにはflgrxを無効にする方法について記述されたものがある程度で、それ以外はインストールしろか、もしくは解決しないままか、だ。

かなり深く調べた。エラーでは0:1:1となっているが、本当にBusIDの不一致を疑って調べると0:1:0だったので、それが原因かと思って調べたりもした。だが、結局解決にはつながらなかった。

バックアップしたイメージでロールバック、ということも考えたのだが、まずは総再ビルド&インストール。もし、パッケージが壊れたためならこれで解決する。もともと壊れていたのなら、ロールバックしてアップグレードしても同じことになる。

やり方は次のようなもの。

for i in $(pacman -Q | cut -f 1 -d " ")
do
yaourt -S --noconfirm "$i"
done

sudo時間は長くしておくほうが良い。

だが、うちの場合、頻繁に、長時間に渡ってネットワークダウンが生じる。上流で起きていること(おそらく、回線、集合回線部分のせいだろう)なので私には手出しできない。フルビルドで回っている間ネットワークがダウンすると失敗する。これはなかなか恐ろしい。

幸いにもネットワークダウンには遭遇せず2回完走したのだが、改善しなかった。そこでロールバックを試みたのだが、dumpしたイメージをrestore(8)しようとしたが、0 inode fileと言われ、14GBもあるファイルにもかかわらずできなかった。

システム再インストールしかなくなってしまったが、せめてもということで、pacman -Qの結果を外部に保存しておく。

再インストールだが、Manjaro 0.8.11 Xfce JPのイメージのインストーラではkeymap選択画面でフリーズしてしまい、進まない。0.8.10 XFce JPイメージでインストールする。

2014-11-05のarticleにある手順でセットアップ。

  • pacman-keyの再構築、無効なキーの削除
  • aticonfig
  • vigrでusersのGIDを500

そしてパッケージを復帰させる。いくつか試したが、sudoのtimeoutを無効にした上で

for i in $(ruby -e 'list1 = `pacman -Q | cut -f 1 -d " "`.split("\n")' -e 'list2 = `cut -f 1 -d " " paclist`.split("\n")' -e 'list2.each {|i| next if list1.include?(i); puts i}' )
do
yes Y | yaourt -S "$i"
done

--noconfirmではconfrict時に中止を選択されてしまう。基本的にはこれで通るが、MATE関連でgstreamerかpulseaudioか選べと言われてYを返しても通らないため、チェックしてこの時に一旦INTしてmate関連を手動インストールの上、やり直す。ちなみに、依存関係で入るパッケージもこの方法だと再インストールするため、mozc-utなどはfcitx-mozc-utで入るのだから途中で中断してやり直したほうが早い。

これを避ける方法としては

while p=$(ruby -e 'list1 = `pacman -Q | cut -f 1 -d " "`.split("\n")' -e 'list2 = `cut -f 1 -d " " paclist`.split("\n")' -e 'list2.each {|i| next if list1.include?(i); puts i}' | head -n 1)
[[ -n "$p" ]]
do
yes Y | yaourt -S "$i"
done

で、その他の選択肢を要求する場合を除いていけると思う。だだし、そもそもビルドできないパッケージがあると、それはインストールできないため詰んで(ループして)しまう。監視なしにするにはexpectを使うしかない。なお、xpdf関連は無理だった。

この後は次のようにしてセットアップを進める

  • バックアップしたhomeのイメージをrsync。この時、rsync -av /mnt/1/ –exclude share/ /home/としてマウントされるユーザーデータは回避する。
  • このままではユーザーデータがマウントできないので、sudo -u aki mkdir ~aki/share ~aki/.share.encfsとする。
  • /etc/fstabを編集。tmpfsと~/.share.encfs(btrfsサブボリューム)のマウントを設定。 encfsをマウントするためのスクリプトを~/root/binに書く。
  • これでほぼ通常通り。リブートしてakiでログインし、Catalyst Control Centerを起動、設定すればOKだ。復旧に6日もかかってしまった。
  • 今回のようなことがか内容にと、今回はインストール時にLVMを使用する設定とした。ちなみに、ディスクのパスフレーズの強度を大幅に高めた。
  • だが、標準でLVにVG全域を使用してしまうため、「管理やバックアップを容易にするため」と言いながら、実際は全く柔軟性がない。仕方ないので、SystemRescueCDで起動し、luksOpenしてvgscanし、ルートボリュームをe2fsck+resizefs+e2fsckで縮小した後、lvreduce --size 80G ManjaroVG/ManjaroRootでOK。アップデート前にスナップショットを取るようにすればロールバックは容易になる。Manjaroでは運用中にこのような事態に陥ることが既に3回めなので必要だろう。また、rsyncでバックアップしておくことも必要かもしれない。