ストレージングの再構成

AoEにしてみたり、複数ボリュームにしてみたり、内蔵で盛ってみたりと様々な手法で拡張を続けてきたストレージだが、ようやくNASを含む構成に変更された。 これにより片側最大192TBのボリュームを確保できるようになった。

NAS ReadyNAS528

NASはReadyNAS 528。

8ベイのNASである。 箱のサイズが随分大きくて驚いてしまったが、本体は8ベイとしては最小限のサイズになっている。

普通に構成するとRAID6になってしまい、選択の余地がない。 一旦ボリュームを削除してから再度作る必要がある。

この際RAID5を選択すると6台以上でのRAID5を推奨しないことを警告される。 この場合は、バックアップもされているので、RAID5で強行する。安心感で言えばRAID6のほうが良いと思う。

そしてiSCSI LUNを作成し、グループに追加する。 残念ながらLUNの最大サイズは全容量の90%とかなり少なく、10%もの領域が余る上に、ディスク全容量の21.25%も削られてしまうことになり、なかなかつらい。フルディスク使える場合はRAID6を使うのとあまり変わらないと考えると余計に辛い。

ReadyNASには暗号化機能があり、起動時に暗号化キーを含むUSBディスクを挿すことで起動できる仕組み。 どうもこれはLUKSによるものであるらしい。

また、ReadyNASは4つものNICを搭載している。 チーミングも可能だが、「グローバルLANへの経路を持つネットワーク」と「ストレージ専用ネットワーク」の2つに同時に接続できるという点は大きい。

Btrfsボリュームの構成

従来はなんらかのディスク(基本的には物理1台に対して1デバイス)に対してdm-cryptデバイスを作成し、dm-cryptデバイスをBtrfsディスクとして使用していた。

今回はRAIDレイヤー及び暗号化レイヤーはブロックデバイスが保証しているものとして取り扱う。 つまり、「このブロックデバイスは冗長化され、暗号化されたブロックデバイスである」という前提が成り立つようにするわけである。

実際にReadyNSAのボリュームはハードウェア的にRAID化され、その上で暗号化もされている。 Linux上で行う場合はmdまたはLVM RAIDデバイスをLUKSで暗号化するのが有力だろう。

この方式をとることで、「冗長化され暗号化されたiSCSIディスク」を用意することができればいくらでもBtrfsを拡張できるようになり、 従来よりも堅牢性、可用性が向上した。

snashotをsendすると壊れる

ところが、実際に使おうとreadonly snapshotをsend/receiveを使って転送すると途中で「読み込み専用ファイルシステム」となってしまい、ファイルシステムが壊れる。

ネットワークデバイスなども疑ったのだが、ログを見る限りiSCSIディスクが応答しなくなって失敗している。 Btrfs的な理由を疑ってMLでも質問したのだが、追求したところどうも「Linuxカーネルの書き込みにネットワークが追いつかず失敗する」ようだった。

この対処方法としては/sys/block/<disk>/queue/max_sectors_kbの値を変更するというものだった。 Manjaro Linuxのデフォルトは8192だが、これを4096とするとトラブルは解消された。 Red Hat Enterprise Linuxでも同様の変更によりトラブルが起きたこともあるようだ。

このディスクについては/dev/diskを使うことができないので自動化が難しい。 やるとしたら次のようなところか。

おわりに

レイヤーを分担させることができるようになり、ぐっとわかりやすくなった。 また、パフォーマンス面でもそれなりに向上が見られた。コンピュータ自体も刷新されたため体感としては小さいが、起動するコンピュータを問わず簡単にworldストレージをマウントできるようになった点は大きい。

max_sectors_kbの問題は盲点で発見に苦労したか、無事問題が解消できてよかった。

誤家庭のLinuxで大規模ストレージを取り扱う

ストレージ規模がどんどん大きくなる当システム。次期再編成のためのメモである。 なお、話を一般化するために特殊な箇所は変更してあり、このまま適用できるわけではない。

前提

メインとなるホスト(Host Master A)と冗長ホスト(Host Master B)から60TBに及ぶ巨大なストレージを取り扱えるようにする。

バックアップとしてサーバー(Host Slave)を用いてデータの冗長化を行う。

基本的には無停止が可能なようにするが、HAのような完全無停止を要求するわけではない。

機材

安価に拡張可能なストレージ群を構築するには、NetGear ReadyNAS RN316が良いようだ。 これにNetGear ReadyNAS EDA500を追加することで10万円以内で筐体を用意することができる。ディスクとあわせると、20台60TBで約40万円が必要だ(2017年6月現在)。

バックアップは実際には現在はサーバーによる内蔵とAoEの組み合わせ、とするが、将来的には同様の方法をとるのが望ましいだろう。

構成

NetGearで用意したディスクを、NetGearの機能でRAID5化し(つまり有効ディスク数は18となる)、これをiSCSIで提供する。

iSCSIディスクはHost Master Anyがイニシエータとなり、これをLUKSデバイスとして暗号化/復号化し、その上にbtrfsを構築して利用する。

2台ひと組と考えることで、-m1 -d1オプションを使って無停止性を確保することができる。 だが、実際は一般の利用ではディスク故障を除いたデータが損失するファイルシステム事故は割と少ないので、-m1 -d0あるいは-m1 -dsingle構成にしたほうが良いと考えられる。 -m1 -d1とした場合、ディスク30台を使用して使える容量は9台分となる。

バックアップも基本的には同様に構築する。異なるグループのiSCSIを用意し、btrfsボリュームを作成する。

構築

接続

iSCSIによる通信でほぼネットワークはあふれることになるため、ネットワークは分離することにする。

上流側は主にはクライアントユースになる、と考えれば、上流側をUSBアダプタとすることでホストの切り替えを容易にすることが考えられる。

Host Master AをHost Masterと考えた場合、Host Master A及びHost Slaveが2つのNICを持っている状態 となる。 このとき、両者が所属するネットワーク(サブネット1)はホストネットワークであり、下流側はストレージのみを接続する、という形態にすることでiSCSIの通信を混在することを避けられる。

Host Master Bを使用する場合はHost Master AからUSB NICを取り外し、Host Master Aに接続されていたUSB NICにHost Master Bにささっている(サブネット1の)ケーブルを接続し、Host Master B内蔵NICにはHost Master Aに接続されていたサブネット2のケーブルを接続すれば良い。

NASの用意

NASのディスクを各NAS上でRAID5で構成する。

2台ひと組にしておけば、btrfsは-d1であってもディスクを使い切ってくれる。 -dsingleにする場合は、NAS内のディスクは容量を揃える必要があるが、NASの合計容量を揃える必要はなくなる。

全ディスクを連結してRAID5を作ってしまうと、ディスク容量の制約が厳しくなるので、この方法のほうが良いだろう。 性能面でも、暗号化とRAIDの組み合わせはかなり重いので、速度を考えればハードウェアRAIDのほうが望ましい。

RAID5を構成したディスク上にLUNを作る。 グループはMasterとSlaveで異なるものにしたほうが良いが、Master NASはサブネット2、Slave NASはサブネット3に置かれるため、実際に混在することはない。

ホストのセットアップ

iSCSIイニシエータとして利用するため、iSCSIイニシエータutilのインストールと、iscsiサービスの起動が必要になる。

利用方法については、archwiki参照のこと。

全てのiSCSIターゲットにログインした上、これをluksFormatする。 ディスクは増えていくことになるので、キーファイルを使ったほうがスクリプトにする際に良いだろう。

# cryptsetup luksFormat /dev/sdb /path/to/keyfile

そしてオープンする

# cryptsetup luksOpen --keyfile-/path/to/keyfile /dev/sdb Crypt1
# cryptsetup luksOpen --keyfile-/path/to/keyfile /dev/sdc Crypt2

btrfsファイルシステムの作成

# mkfs.btrfs -L MasterBtrfs -m1 -dsingle /dev/mapper/Crypt[12]

追加で、サブボリュームの作成や、fstabの編集などを行っておく。

iSCSIターゲットの探索とログイン、ディスクの復号化とマウントの一連の流れはスクリプトにしておいたほうが良いだろう。

利用

まずNASを起動し、次にホストを起動する。そして、スクリプトによってマウントする。 セットアップが終われば非常にシンプルである(ただし、スクリプトにしていないと打つコマンドの量は多い。それはそれでセキュアだが)

スクリプトをsystemdユニットにしておくのは良い考えだが、コケる可能性の高いユニットなので、enableにはしないほうが良いだろう。

NFSとファイル配置

NFS v4を提供する。Manjaro Linuxでの手順である。

まず、btrfsのマウントポイントは/mnt/masterであるとする。 しかし、実際には特定ユーザーのデータが配置されるのであり、~foo/.masterfsがマウントポイントである。

そこで、これを反映できるようにするため、スクリプトの先頭で次のようにしておく

mount --bind /mnt/master /mnt/master
mount --make-rshared /mnt/master
mount --bind /mnt/master ~foo/.masterfs

これにより、ふたつのマウントポイントを意識せずに共有することができるようになる。

さらに、ここではNFSのマウントポイントも別に用意する。

mount --bind /mnt/master /srv/nfs4/masterfs
mount --make-slave /srv/nfs4/masterfs

ここまではスクリプトに含めると良いだろう。

NFSパッケージのインストール。

# pacman -S nfs-utils

/etc/exportsの設定

/srv/nfs4/masterfs 192.168.1.0/24(rw,no_subtree_check,nohide)

v2, v3の無効化(/etc/conf.d/nfs-server.conf)

NFSD_OPTS="-N 2 -N 3"

起動。マウントを手動で行っているため、enableは非推奨。

# systemctl start nfs-server

他ホスト(サブネット1のホスト)からマウントするためには、次のようにする。

# mount -t nfs -o vers=4 masterhost1:/masterfs /mnt/masterfs

-t nfs4とする必要がある場合、サーバー側パスにエクスポートルートを含める必要がある場合があるという。

CIFS

Arch系ディストリビューションにおけるSambaのセットアップ手順はArchwikiが参考になるだろう。

Linuxを中心に使っているユーザーにとっては、常時マウントするわけではないから、ファイルマネージャでのusershareシェアで十分かもしれない。

ただし、AndroidユーザーにとってはCIFSで構築したほうがメディアファイルの再生も容易になる。

DLNA

メディアファイルを広く再生する方法として有効なDLNA。

DLNA対応のプレイヤーがあり、それによって大画面で見られるといった場合には役立つだろう。

主にHost Masterを使い、Host Masterにログインすることが前提であるならばRygelで良いと思う。 そうでない場合は、ReadyMedia(minidlnaパッケージ)を利用することが推奨されているようだ。

Archのminidlnaはminidlnaユーザー, minidlnaグループで動作する。 そのため、ライブラリはこのユーザーでアクセス可能でなくてはいけない。

また、面倒を避けるためには、/etc/minidlna.confinotifynoにしておくのが良いだろう。

あるいはユーザーで実行したい場合は

$ install -Dm644 /etc/minidlna.conf ~/.config/minidlna/minidlna.conf
$ vim ~/.config/minidlna/minidlna.conf
$ minidlnad -f ~/.config/minidlna/minidlna.conf -P ~/.config/minidlna/minidlna.pid

のようにする。

DAAP

iTunesを使用しているユーザーにとっては軽快に動作する音楽ストリーミング方法。 forked-daapdが推奨されているが、AURにしか存在しない。

forked-daapdをインストールし、libraryセクションのdirectoriesを設定、当該ディレクトリをdaapd実行ユーザー(デフォルトでdaapd:daapd)がアクセス可能な状態にして

# systemctl start forked-daapd

で動作する。

LinuxではAmarokあるいはRhythemboxで再生できる。 Androidスマートフォンでは、DAAP Media Playerなどがあるようだ。

2008年頃に流行したDAAPだが、既にだいぶ廃れている印象はある。

ストレージとの分離

ユーザーデータ(dotfileなど)にも容量の大きなものがあるため、ストレージに退避したいと考えるかもしれないが、割とそれはリスキーである。

dotfileの場合、ファイルシステムをまたぐと動作しないもの、シンボリックリンクをこえられないものがあったりする。 また、ログイン時においてはストレージから切り離された状態にある可能性もあり、またストレージがダウンする可能性もあると考えなくてはいけない。

つまり、ストレージが切り離されても動作する状態を保ったほうが良い、ということである。

また、作業中のファイルなどは、ストレージとは別にシステム側にもあったほうが良い。ストレージが切り離された時に作業不能になる度合いを軽減するためである。

さらに、xdgディレクトリはシンボリックリンクが切れているとログイン時に変更されてしまう。 そのため、データが大きい~/Pictures~/Videoなどは、単純なディレクトリとして、その下にシンボリックリンクを配置したほうが良い。

運用の一例としてだが、次のようなストレージ構成だとする

sda      Internal SSD      System
sda1     Internal SSD      System, /
sda2     Internal SSD      System, Swap
sdb      iSCSI 1GbE        Data, Btrfs
sdc      iSCSI 1GbE        Data, Btrfs

次のようなマウント結果となる

/dev/mapper/luks-xxxxxxxxxxxxxxxxx on / type ext4 (rw,relatime,data=ordered)
/home/foo/.storage on type btrfs (rw,noatime,compress=lzo,space_cache,subvolid=258,subvol=/master)

ストレージへ同期されるべきものとして以下のように構成する

WorkSpace
+ Documents
+ Dotfiles

製作中のメディアファイルはDocumentsではなく~/.storage/user/art/以下に配置し、Documentsは文書ファイルのみを配置することで軽量なディレクトリにすることができる。

次のようなスクリプトを作っておく

#!/bin/zsh

WS=$HOME/WorkSpace

update() {
  rsync -avH "$@"
}

sync() {
  rsync -avH --delete "$@"
}

gitup() {
  (
      cd "$1"
        git add .
        git commit -m "Synced at $(date +%y%m%d)"
        git push
    )
}

hgup() {
  (
      cd "$1"
        hg add .
        hg commit -m "Synced at $(date +%y%m%d)"
        hg push
    )
}

gitup $WS/Documents
for i in $WS/Dotfiles/*
do
  hgup $i
done
sync ~/Pictures ~/.storage/XDG/
sync ~/Videos ~/.storage/XDG/
sync ~/Mail ~/.storage/

slave系

これでmaster系は54TB(3TB * 20 disks)ものスペースを一台に集約し、そのファイルを他のホストでアクセスするようにすることができた。 NFS/CIFS/DLNA/DAAPでアクセスできるため、ファイルの取り扱いは非常に良いはずだ。

加えてRAID5によりディスク故障やディスク破壊にも対応する。

だが、これでも壊れる可能性はある。誤消去や誤った更新・上書き、NASの故障、Btrfsの修復不能なクラッシュにどう対応するのだろうか?

ここでRAIDとバックアップは違う、という基本に立ち戻る必要がある。 つまり、このように堅牢なシステムを作っても、あるいはBtrfsクラッシュに備えてRAID5 NASにBtrfs RAID1を組み合わせたとしても、バックアップは別途必要なのである。

基本的にバックアップはメインストレージと同等の設備が必要になる。 もしメインストレージをBtrfs RAID1としているのであれば、バックアップにおける無停止性はそれほど重要ではないため、その場合はメインストレージの半分で済むということになる。

つまりおおよそシンメトリカルな2つのストレージ群とホストが編成されることになる。 ただし、slave系のコントローラホストはサーバーであり、処理性能よりも省電力性や静音性が重要となるだろう。 とはいえ、あまりにも処理性能をおざなりにすると、SSHの処理でもたついて時間がかかることになる。

現状で当環境においてはProLiant MicroServerを使用しているが、どちらかといえばラップトップのほうが適性があるとされている。 標準でコンソールを備え、無停電電源装置を内蔵した状態になるからだ。しかも省電力で静音性も高い。 メインホストからみた速度向上のためにはメモリは多目であったほうがいいだろう。

構築、ストレージへの接続、Btrfsの作成は同様である。

バックアップはBtrfsのsend/receive機能を使うことができる。 差分バックアップも用意で、本当に変更されたものだけを転送するため、非常に効率が良い。

次のようなスクリプトを用意する。

#!/bin/zsh

notify-send "BtrSnapSync: Woke"

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

sync

notify-send "BtrSnapSync: Snapshot."

if [[ -e /mnt/master/snapshots/snapshot-old ]]
then
  if btrfs send -v -p /mnt/master/snapshots/snapshot-old /mnt/master/snapshots/snapshot-new/
  then
    notify-send "Send Snapshot."
    btrfs subvolume delete /mnt/master/snapshots/snapshot-old
    mv /mnt/master/snapshots/snapshot-new /mnt/master/snapshots/snapshot-old
    notify-send "Deleted old Snapshot."
  else
    notify-send "Failed to send Snapshot"
    btrfs subvolume delete /mnt/master/snapshots/snapshot-new
  fi
else
  if btrfs send -v /mnt/master/snapshot-new
  then
    notify-send "Send Snapshot."
    mv /mnt/master/snapshots/snapshot-new /mnt/master/snapshots/snapshot-old
    notify-send "Deleted old Snapshot."
  else
    notify-send "Failed to send Snapshot"
    btrfs subvolume delete /mnt/master/snapshots/snapshot-new
  fi
fi

また、slave側は次のようなスクリプトを用意する。

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

あとは次のようにすることで転送する。

$ btrfssend.zsh | ssh -i /root/.ssh/btrfssnapsync root@slave.local /usr/local/sbin/btrfsreceiver.sh

暗号化を行わず処理を軽減するには、まずslave側で次のように実行する(OpenBSD Netcatを使用している)

# nc -l 12358 | btrfsreceiver.sh

続いてmaster側。ここではbashを使っている。

$ btrfssend.zsh > /dev/tcp/slave.local/12358

暗号化の必要がないとしても、危険だと考えられるため、SSHの利用が推奨される。

AoEとNBD(とiSCSI)の違いを調べた

ハブの口が3つも足りなくなってしまったため、追加購入するために、AoE(ATA over Ethernet)でフレームを損失した場合にどうなるのかということを含めて調べた。

日本語情報がないため、参考としてまとめておく。

  • AoEはATAコマンドの直接発行。Ethernetフレームを利用
  • NBDはTCPなので、iSCSIとほとんど同じ
  • NBDがBlock Deviceもしくはfile,iSCSIはdiskが下層、という説明もあるけれど、確認できず
  • NBDのほうがパフォーマンス的に有利らしい
  • AoEはWindows, Macでも利用可能。NBDは無理っぽい
  • AoEはフレームを損失した場合、それを回復する機能はない
  • AoEの通信はMACアドレスで行うから外部からの攻撃に強いよ、という説明もある

AoEがデータを壊す可能性があるのか?信頼できるのか?
については、このあたりが論争。

Clearing up some misconceptions about the AoE protocol

興味深いけれど、結局どうなのか私には判別しにくい。

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

sshの問題

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

send/receiveはよく止まる

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

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

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

ZFSが遅い

先日、ZFS+rsyncでやっていくことにした、と報告したが、挫折した。

最初の7時間で300GBの進捗。まぁ、これはいい。だが、いくらなんでもそのあと3時間で40GBの進捗はない。それはひどすぎる。

これでは実用にならないのでBtrfsに変更。結果としては出だしで18倍以上の差となり、15時間ほどで2TBの転送を完了した。

ちなみに、サーバーはProliant MicoroServerで、メモリは4GB。Swapは使われていなかった。

不安定なZFSの劣化コピーとみなされがちなBtrfsだが、こうしてみるとZFSを凌ぐ部分が結構みつかる。特に、ZFS on Linuxと比較するとかなりだ。

Btrfsは既にかなり安定しているし、また速度的なアドバンテージがある。また、自動でストライピングするが、ミラーの場合でも1台単位で追加でき、1+0でもリバランスできる。これはZFSにはない特徴だ。

そして新たに知ったこととして、BtrfsにもZFS同様のSend/Recieveがあるということだ。Btrfs、かなり良い。

ストレージワーク:btrfs+EncFS / dm-crypt+ZFSでのリモートミラー

Btrfs上にEncFSを構成したマスターから、dm-crypt上にZFSを構築したスレーブへとミラーする、しかもそれらのホストはシャットダウンされる。これはかなり厳しい条件だった。

やはりシャットダウンされるためにHA(高可用)システムは使えない。シャットダウンする時点で障害発生とみなされるし、切り離された状態で単独でスタートアップして動けない。

さらに、EncFSはrootであってもアクセスを許さないため、非常にセキュアではあるが、GlusterFS GeoReplicationも使えないなど障害になった。

やはり無理な要求である、というのは

LinuxQuestionで聞いてみても
明らかになるだけだった。だが、ここでrsyncが大規模システムに耐えるということが分かったため、rsync(1)at(1)でいこうと決意を固めることができた。

rsync+sshはごくごく単純だ。

rsync -e ssh fromdir user@host:destdir

でいける。だが、まずはZeroconfでアクセスしたい。

Manjaroで/etc/nsswitch.confmdns_minimalを指定しても解決できない。これでホスト名を解決しようと思うと、nss-mdnsパッケージをインストールし、avahi-daemonを動かさなくてはいけない。

さらに、CentOS側が受け入れてくれない。これは、NICがひとつだとそのNICをpublicなゾーンのインターフェイスとみなすが、Avahiはhomeインターフェイスにしか許されていない。そのため、firewall-cmd --add-service=avahi --zone=public --permanentとしてAvahi-daemonへのアクセスを透過する。

これでZeroconfでのアクセスに成功、.localのホスト名でsshアクセスできる。ssh-keygenでパスフレーズなしのキーを作り、アクセスしようとする。ところが、ssh-copy-idしようとした段階でToo many authentication failures for …となり、sshアクセスできない。これは、sshの管理下にある鍵が多すぎる場合に生じるようだ。その数はsshdが登録、管理している鍵とファイルとして~/.ssh以下にある鍵の総数。とりあえずの方法としては、鍵ファイルがあるものだけを鍵として扱うため、~/.ssh/config

IdentitiesOnly yes

と書くと改善される。ただし、ファイル自体が多くなるとこれでもダメだろう。

これで鍵によるアクセスまでできるようになった。

鍵による実際のアクセスの前に、atとrsyncについて確認する。rsyncについては前述の通りで大丈夫。atは

$ <kbd>at now + 1 minutes &lt;&lt;&lt; ‘zsh -c "notify-send \"$(id)\""'</kbd>

すると自身のuidになっているので、atを実行したユーザーで実行されることが分かる。EncFSに対してアクセスするためにはこれは絶対条件だ。

そしてこのままrsyncするとうまくいく。だが、パスフレーズなしで自由にアクセスできる鍵というのは危険だ。

コマンドで制限すべきなのだが、rsyncのコマンドを受ける側がどうなっているのか、というのは非常にわかりにくい。rsync -vvv -au --delete-after -e ssh from destして、パスワードの前に表示されたconnectionの中からrsyncより前を削り、互いにvを削って設定する。

ただし、このままでは外部からファイルを消されるリスクがあるため、ホストも限定したほうがいいかもしれない。ただし、鍵がなければできないのだから、その時はバックアップ側は仕方がない、とも見れる。

今回の成果物も

GitHub
にて公開。

新しいストレージワーク(GlusterFS, AoE, lsyncd)

問題点

分かってはいたことだが、先日の件でbtrfs mirrorでは不十分だ。データの冗長化は次のようなことが考えられる。

  • ストレージの故障
  • ファイルシステムの故障
  • RAIDハードウェアの故障
  • 誤操作による喪失
  • コンピュータノードの故障
  • コンピュータノードの喪失(災害や盗難など)

基本的な対応は次の通り。

ストレージの故障
ミラーリングを行い、故障したストレージを置き換える
ファイルシステムの故障
異なるファイルシステム間でミラーするか、バックアップする。
RAIDハードウェアの故障
同一のRAIDハードウェアに再接続するか、バックアップによって復元する
誤操作
gitやpdumpfs、あるいはLogFSなどスナップショットの活用
ノードの故障
新規ノードにストレージを移設する。可用性が不要ならばデータの冗長化は不要
ノードの喪失
可用性の損失は避けられない。同一箇所に保管していると同時に損失する可能性は高いため、クラウドバックアップが有効

一般にノードの損失は稀なケースだと考えられる。だが、実際はそこそこ可能性はある。落雷によっても起こりうる。この場合のことを考えれば、データを諦められないのであればバックアップは必要だ。

これまでは、btrfsの2レッグミラーを採用していた。これはストレージの故障に対する冗長性・可用性を担保する。しかし、それ以外には対応できていない。特に怖いのは、未だexperimentalであるbtrfs自体の異常だ。ファイルシステムが壊れてしまえばミラーされていたものも含めてすべてのデータがアクセス不能になる。

少々複雑な話に聞こえるかもしれないが、btrfsは「壊れにくいファイルシステム」だ。日常的な読み書きで起こるビットエラーをbtrfsは発見し、ミラーリングを行っていれば訂正することすら可能だ。そのため、他のファイルシステムと比べ非常に壊れにくい。それは他のジャーナリングファイルシステムと比べてもだ。そのためた信頼性が高いと言われ、この点を買って私は採用している。一方で、btrfsはまだ完成度が低く、btrfs自体が壊れる挙動を示す可能性がある。そのため信頼性が低いとされている。

データが既に2TiBを超えている現状にあっては、バックアップやその復元も容易ではない。ちなみに、この次にハードルが上がるのは、ストレージ1台ではすまなくなる時だ。コストを度外視すれば、8TBということになる。

基本的にはバックアップというよりも、ミラーリングによって冗長性を持たせ、データが損失しないことを前提とする方向としている。今後も情報増加は進む予定だし、それに対応する構成としておかなくてはならないからだ。そして、データ量は一般的なシングルストレージで済む量に収まる見込みはない。ストレージ増加よりもデータ増加の方がはるかに速い。

現在の対応は次の通りだ。

  • btrfsによるジャーナリング、自動訂正によるビットエラー対策
  • btrfsミラーによる冗長化
  • btrfsによる柔軟なストレージ追加。1台単位でディスク容量を問わず追加し任意のボリュームを切り出せる
  • 必要な箇所をgitとすることで誤操作による損失の防止
  • 一部データのリモートgitへのclone

だが、先日はそのストレージを格納するコンピュータノードがダウンした。そのため、データ自体にアクセスできなくなった。現在でも最低限、4台のストレージを搭載しなければデータを使うことができない。Proliant Microserverは搭載ストレージ台数は4なので、システムドライブを含めるとそれを搭載することができない。

もちろん、データは損失していないのだからコンピュータノードを追加すれば良いのだが、その間仕事は完全に停止してしまう。やはりこの点も冗長性が必要だろう。

機能問題は別として、別のコンピュータがデータを触れれば良いのだ。コンピュータノード全体がダウンしても機能するようにすれば、1台のコンピュータが損失しても問題ない、ということになる。もっとも、実際に必要となるのは完全なクラウドバックアップだが、現在のところそれは月額で8万円程度が必要になるため、現実的でない。

GlusterFS

GlusterFSはペタバイト規模に対応するクラスタストレージ技術だ。ユーザースペースで動作するデーモンであり、大きなファイルの読み書きはネイティブなコードで行う。FUSEでマウントできるだけでなく、NFSやCIFSでもマウント可能。

分散ファイルシステムだがネイティブファイルシステムではなく、各コンピュータはマウントされた特定のディレクトリをGlusterFSデーモンによって公開する(blickと呼ぶ)。クライアントはblickを束ねてvolumeとしてマウントすることができる。中央サーバーがないのがGlusterFSの特徴だ。

CephやGFSと比べかなりお手軽に使えそうに見えるGlusterFSだが、実際には意外と厳しい。それは、blickの容量をみずにファイルベースで振り分けていくこと、GlusterFSのレプリケーションはblickを組みとして複製するものであることによる。このことから、実質的にはblickはすべて同一容量でなくてはならない。しかも、blickの追加はRAID化している数をunitとして行わなくてはいけない。例えばstriped replicated volumeだとしたら、4 blicksが追加単位となるのだ。ディスク単位のblickにしても4台のディスク、コンピュータ単位だと4台のコンピュータだ!

また、このような技術はノードの停止は「障害」であるため、HAの理屈に基づき稼働停止が許されなくなる。電気代で考えても厳しいし、デスクトップユースでの無停止はそもそも難しい。結構よく見えたのだが、GlusterFS適用はかなり難しいように見えた。

ただし、適用方法はある。それは、「そもそも2組のストレージにしてしまい、2 blicks GlusterFS volumeにする」という方法だ。例えばiSCSI+LVMなどで複数のコンピュータノードからなるひとつのファイルシステムを編成し、それをマウントし、それをblickにすれば良い。そうすればコンピュータを2組に分けることができ、片方の組のコンピュータが停止しても全体はダウンしない。ストレージ追加はLVMなどの単位で行えるためかなり柔軟だ。容量の問題は、単に合計容量が小さいほうの組を上限としてそれを越えないように運用する、という制約があるだけだ。

lsyncd

だが、やはりこのようなHA技術は少々重い。それであれば少なくともデスクトップは使う側にしてデスクトップにストレージをもたせるのはやめるべきだ。

任意に停止しゆるい同期を行えれば良い。つまり、「手動で同期サービスを開始・停止し、ファイル更新時に反映してくれれば良い」という考えだ。可用性については、短時間の停止なら十分許容できる。

原始的には定期的にrsyncを回す方法もあるが、せめてファイル更新を監視したい。別にそれをスクリプトにしてもいいのだが、ionotifyというLinuxの機能があるのだから、それを活かしたいもの。そこで「ファイル変更を監視して同期する」というものを探したところ(正確には「なんて名前だっけ」だった)、lsyncdが見つかった。

lsyncdはファイル同期デーモンだが、実際にはミラーリングを行う機能はなく、rsyncなどをバックエンドとして使う。つまり、lsysncdはionotifyによってファイル更新を受け取り、それをトリガーとしてrsyncなどを起動するデーモンだ。

デスクトップレベルではこれが順当なところではないだろうか。台数が増えるとオンオフも困難になるので普通にHAストレージでいいのかもしれないが。

台数が少ないうちは普通にイーサネットケーブルでつなぎ、それをまとめて2つのストレージを編成すればいいのだが、台数が増えてきた場合、それぞれキーになるノード(片方はデスクトップ)をシングルにつなぎ、それぞれがストレージ用ハブを持っておくと良いだろう。構成手順は次のとおり

  • デスクトップは現在btrfsミラー
  • キーサーバーAを接続し、ZFSでストレージを束ねる
  • キーサーバーAにrsyncで全データをバックアップ
  • デスクトップのbtrfsのミラーをやめて構成しなおす
  • キーサーバーAからrsyncでデスクトップにデータを戻す
  • デスクトップとキーサーバーAにGbEインターフェイスを追加する
  • 追加されたGbEインターフェイスをハブに接続する
  • ストレージサーバーノードをGbEでストレージ側ハブに接続する
  • ストレージサーバーノードのディスクをAoEを用いてそれぞれのキーノードのブロックデバイスとしてみえるようにする
  • 追加されたストレージサーバーノードのディスク(AoE)をbtrfs/ZFSノードに加える
  • btrfs/ZFSをリバランス

10GbEに置き換えるところまで考えればかなりの規模までこれでいけるように思う。ただし、グループノードのいずれかがダウンすると障害発生なので、「故障率」は当然あがる。これはグリッドシステムでは常に起こることだ。