9820 文字
49 分

QNAP NAS を卒業して mergerfs + SnapRAID で「壊れても理解できる」ストレージを作った

3秒まとめ#

  • QNAP TS-431P に不満が溜まり、mergerfs + SnapRAID で DIY NAS を構築した
  • 異なる容量の HDD(4TB×1 + 2TB×2)を無駄なくプールしつつ、パリティで冗長性を確保
  • 3-2-1 バックアップの考え方に基づいて、クラウドへの定期バックアップも組み合わせた

どんな人向けの記事?#

  • NAS アプライアンス(QNAP、Synology)に不満を感じている人
  • RAID の仕組みに不安があり、もっとシンプルなストレージ構成を探している人
  • 異なる容量の HDD を無駄なく活用したい人
  • 自宅サーバーで写真やメディアファイルを長期保管したい人

はじめに#

@matsubokkuri です。

自宅のストレージ環境を QNAP NAS から mergerfs + SnapRAID に移行しました。この記事では、移行の背景から実際の設定ファイルまでをまとめます。

「NAS アプライアンス、なんか微妙だな…」と感じている方の参考になれば嬉しいです。

色々な AI に聞いても、mergerfs + SnapRAID の組み合わせをおすすめされたので、この構成にしました。


背景:これまでのストレージ遍歴#

第1世代:Dell PowerEdge T320 + ハードウェア RAID#

最初の自宅ストレージは Dell PowerEdge T320 に 2TB HDD を 6 本載せて、ハードウェア RAID で運用していました。サーバーらしいサーバーで、それなりに満足していたのですが、ある日 RAID カードが壊れかけて 危うくデータをロストしかける事件が発生しました。

Dell PowerEdge T320

ハードウェア RAID の怖いところは、コントローラが死ぬとデータにアクセスできなくなる ということです。同じ型番の RAID カードを調達して差し替えればいける——理論上は。でも実際に障害が起きたときにそれを冷静にやれるかというと、かなり厳しい。

この経験から「ハードウェア RAID は信用できない」という教訓を得ました。

第2世代:QNAP TS-431P(RAID5)#

PowerEdge の反省を踏まえて、次は手軽さを重視して QNAP TS-431P を導入しました。4 ベイの ARM ベース NAS で、2TB HDD × 4 を RAID5 で構成。利用可能容量は約 6TB です。

QNAP TS-431P

「アプライアンスなら管理が楽だろう」と思って導入したのですが、使い続けるうちに不満が溜まっていきました。

iOS からの写真バックアップがつらい。 QNAP 公式の Qfile アプリで iPhone の写真を自動バックアップしていたのですが、バックアップが途中で止まる、重複ファイルが量産される、HEIF の扱いが怪しい——控えめに言ってひどい。一眼レフの写真も含めて 1 万枚以上あるので、ここのバックアップ体験が悪いのは致命的です。

HDD がスタンバイにならない。 何もしていないのに HDD のアクセスランプが頻繁に点滅 しています。QTS のバックグラウンドプロセスが何かしら動いているようで、HDD がスタンバイに移行しません。日本の電気代は ¥25〜35/kWh と安くないので、使っていない時間帯にはちゃんと止まってほしい。

ベンダーロックイン。 QNAP の RAID 構成は独自フォーマットに依存しています。本体が壊れたら HDD を取り出して別のマシンでマウントすることが容易ではありません。PowerEdge の RAID カード障害と本質的に同じリスクを抱えています。さらに QNAP は過去に Deadbolt ランサムウェア の標的になった実績もあり、セキュリティ面でも不安がありました。

RAID の根本的な問題#

振り返ると、PowerEdge のハードウェア RAID も QNAP のソフトウェア RAID も、共通の問題を抱えていました。

管理が複雑で、壊れたときに詰む。 ハードウェア RAID はコントローラ依存。ZFS のようなソフトウェア RAID はプール管理が複雑で、異なるサイズのドライブを混在させにくい。QNAP の QTS RAID は GUI こそ綺麗だけど、中身はブラックボックスで、障害時に何が起きているのか分からない。

そして最大の問題:RAID アレイが崩壊したら、全データが道連れになる。 RAID5 で 2 台同時に壊れたらおしまい。QNAP の場合、ストレージプール内に複数の RAID グループがあって 1 つが壊れると、プール全体のデータが失われる という仕様もあります。

もっとシンプルで、壊れても理解できて、壊れても被害が限定的なストレージが欲しい。


目的:こんなストレージが欲しかった#

  • 異なる容量の HDD を無駄なくプール できる(手持ちの 4TB×1、2TB×2 を活かしたい)
  • 1台の HDD が故障しても復旧 できる冗長性
  • ドライブ故障時に生き残ったデータは即座にアクセス可能 であること
  • HDD が使われていない時はスタンバイ に入る省電力設計
  • 壊れたときに何が起きているか理解できる シンプルな仕組み
  • Docker で自由にアプリを動かせる 汎用 Linux 環境

アプローチ:mergerfs + SnapRAID#

mergerfs:異なるサイズの HDD を 1 つに束ねる#

mergerfsFUSE ベースのユニオンファイルシステム です。複数のディスクのマウントポイントを、1 つの仮想的なディレクトリとして統合します。

最大のポイントは ファイル単位 で動作すること。RAID のようにブロックレベルでストライピングするのではなく、各ファイルは物理的にどれか 1 台の HDD にそのまま保存されます。

/mnt/data1 (4TB) ─┐
/mnt/data2 (2TB) ─┼─→ /mnt/storage (8TB)
/mnt/data3 (2TB) ─┘

これの何が嬉しいかというと:

  • 異なる容量の HDD を混在可能。 4TB + 2TB + 2TB = 8TB がそのまま使える。RAID5 だと最小ドライブに合わせて 2TB × 3 = 6TB になってしまう
  • ドライブを 1 台追加するだけで容量拡張。 RAID の再構築は不要
  • 各 HDD は普通の ext4 なので単体でマウントして読める。 mergerfs が消えてもデータは無事
  • 1 台が故障しても、他の HDD 上のファイルは無傷。 被害は壊れたドライブに載っていたファイルだけ

RAID5 だとアレイが崩壊したら全データ喪失のリスクがありますが、mergerfs なら 被害が限定的 です。PowerEdge で RAID カードが壊れかけた経験がある身としては、これがめちゃくちゃ大きい。

SnapRAID:あとからパリティを計算する#

SnapRAIDスナップショットベースのパリティシステム です。リアルタイム RAID と違い、snapraid sync コマンドを実行したタイミングでパリティを計算します。

重要なルール:パリティドライブは最大のデータドライブ以上の容量が必要 です。今回は最大データドライブが 4TB、パリティドライブが 6TB なので余裕があります。

データ: data1(4TB) + data2(2TB) + data3(2TB) = 8TB 利用可能
パリティ: parity(6TB) → シングルパリティ保護

SnapRAID は 「書き込んだらほとんど変更しない」データ に最適化されています。一眼レフの RAW 写真やメディアファイルの保管にはぴったり。逆にデータベースのように頻繁に書き換わるデータには向きません(sync 間隔の間に書き込まれたデータはパリティで保護されない)。

SnapRAID のもう一つの強みは ビットロット検出 です。128bit ハッシュで全ファイルのチェックサムを管理しているので、定期的に scrub を実行することでサイレントデータ破損を検出できます。長期間の写真アーカイブには地味に重要な機能です。

サイレントデータ破損(ビットロット)とは? HDD や SSD の記録媒体は、経年劣化や宇宙線の影響で保存されたビットが勝手に反転することがあります。これが サイレントデータ破損(bit rot) です。「サイレント」と呼ばれる理由は、OS やファイルシステムがエラーを報告しないまま、ファイルの中身が静かに壊れていくからです。たとえば 3 年前に撮った写真を開いたら画像の一部が変色していた——というケースがこれにあたります。通常のファイルシステム(ext4 など)はデータの整合性を自動チェックしない(メタデータのチェックサムはあるがデータ本体にはない)ため、ビットロットは検出されずに放置されがちです。SnapRAID は全ファイルのハッシュを記録しているので、scrub コマンドで定期的にチェックすれば破損を早期発見し、パリティから修復できます。

従来の構成との比較#

特徴mergerfs + SnapRAIDRAID5/6QNAP QTS
異なるサイズの HDD✅ 完全対応❌ 最小ドライブに制限❌ 最小ドライブに制限
パリティ計算スナップショット(定期実行)リアルタイムリアルタイム
書き込み性能ディスクネイティブ速度パリティオーバーヘッドパリティオーバーヘッド
アレイ崩壊時生存ディスクのデータは保持全データ喪失全データ喪失
ドライブ追加いつでも追加可能アレイ再構築が必要RAID グループ単位
個別ドライブの可読性✅ 標準 FS(ext4)
ビットロット検出✅ 128bit ハッシュ限定的
HDD スタンバイ✅ 未使用ドライブは停止❌ 全 HDD 常時回転❌ 全 HDD 常時回転
管理CLIコントローラ依存Web GUI

ハードウェア選定と予算#

方針:ミニ PC + USB DAS#

最初は Raspberry Pi で組みたかったんですが、写真管理に使っている Immich の機械学習機能(顔認識・セマンティック検索)は ARM では荷が重い。仕方なく、もともと自宅サーバーとして使っていた Beelink SER5 MAX を NAS 兼用にすることにしました。

CPU: AMD Ryzen 7 5800H(8C/16T、最大 4.4GHz)
RAM: 16GB DDR4
SSD: 1TB NVMe(OS + Docker用)
USB: 3 × USB 3.2 Gen2(10Gbps)
LAN: 1GbE
TDP: 45W(実測アイドル ~15W)
サイズ: 126 × 113 × 42mm

Passmark スコアは約 21,000。QNAP TS-431P の ARM CPU とは比較にならない処理能力です。Docker で複数のサービス(写真管理、監視、ファイル共有など)を同時に動かしても余裕があります。

日本のマンションだとサイズも重要で、126 × 113mm の手のひらサイズは PowerEdge のタワー型と比べると天と地の差です。

USB DAS の選び方#

mergerfs では各ドライブが OS から 独立したブロックデバイス として認識される必要があります。RAID 機能のない JBOD モデルなら何も設定せず、HDD を入れるだけで /dev/sda, /dev/sdb… として認識されます。

今回は ORICO 4 ベイ HDD ケース(USB 3.0、RAID なし) を選びました。

ORICO HDD ケース

選定基準は 「安い」「壊れなさそう」 の 2 点だけ。正直なところ、USB DAS(特に RAID なしの JBOD モデル)は選択肢が少なく、Amazon のレビューを見ても評価の高い製品がなかなか見つかりません。QNAP や Synology のような NAS アプライアンスと違って、「ただの箱」にあたる DAS はメーカーもユーザーも少なく、品質の当たり外れも大きい印象です。

その中で ORICO を選んだ理由:

  • RAID 機能なしの JBOD モード がある(RAID コントローラ付きのモデルは mergerfs と相性が悪い)
  • 4 ベイ でデータ 3 台 + パリティ 1 台を収容可能
  • ¥10,000 前後 と比較的安価
  • ファン付き で HDD の冷却ができる
  • ORICO は USB ストレージ周辺機器メーカーとしてはそこそこ実績がある

「消去法で選んだ」感は否めませんが、数ヶ月使ってみて今のところ問題は出ていません。

USB 3.0(5Gbps)で理論上 ~400MB/s。HDD 自体が 150〜200MB/s なのでボトルネックにはなりません。

USB DAS は SATA 直結と比べると接続の信頼性は劣ります。ケーブルの抜けや電源トラブルには注意が必要です。dmesg を定期的にチェックする運用が求められます。

コスト#

パーツモデル価格
ミニ PCBeelink SER5 MAX(Ryzen 7 5800H / 16GB / 1TB)¥50,000(自宅サーバーを流用)
DASORICO 4ベイ HDD ケース(USB 3.0、RAID なし)¥10,000
HDD(データ)4TB × 1 + 2TB × 2(手持ち)¥0(既存在庫)
HDD(パリティ)6TB × 1(新規購入)¥25,000
合計¥85,000

ミニ PC は自宅サーバーの流用なので、NAS 構築のための 実質追加コストは約 35,000 円(DAS + パリティ用 HDD)です。

参考までに、QNAP TS-464(現行 4ベイモデル)を新品で買うと本体だけで ¥70,000〜90,000。HDD 4 本を足すと ¥150,000 超え になります。

構成概算コスト
QNAP TS-464 + NAS用 HDD × 4~¥150,000〜170,000
DIY(SER5 + ORICO + HDD)~¥85,000(うち流用 ¥50,000)
DIY 追加分のみ~¥35,000

データ量と HDD 構成#

保管しているデータ#

主に一眼レフ(RAW)と iPhone の写真・動画です。現時点で約 2TB。

24MP の一眼で RAW 1 枚あたり 25〜40MB、動画ファイルも含めるとそれなりの容量になります。8TB の利用可能容量があれば当面は余裕です。

ドライブ構成#

/dev/sdb → /mnt/data1 (4TB) [データ]
/dev/sdc → /mnt/data2 (2TB) [データ]
/dev/sdd → /mnt/data3 (2TB) [データ]
/dev/sda → /mnt/parity (6TB) [パリティ] ← 新規購入

mergerfs で 4TB + 2TB + 2TB = 8TB の利用可能容量。SnapRAID のシングルパリティでどれか 1 台が壊れても復旧可能。

RAID5 だと 2TB × 3(最小ドライブに合わせる)= 6TB にしかならないので、mergerfs のほうが 2TB 多く 使えます。


3-2-1 バックアップ戦略#

ここで超重要なことを書きます。

SnapRAID のパリティはバックアップではありません。

SnapRAID はドライブ故障からの 冗長性 を提供しますが、以下のケースからは守ってくれません:

  • 誤削除 → sync が走ると削除がパリティに反映され、復旧不可能
  • ランサムウェア → 暗号化されたファイルが sync で記録される
  • 火災・盗難 → 物理的にすべてのドライブが失われる
  • sync 間の書き込み → まだパリティに反映されていないデータ

冗長性(= ドライブが壊れてもシステムが動き続ける)と、バックアップ(= どんな障害からもデータを復旧できる)は 別物 です。両方必要です。

3-2-1 ルールとは#

データ保護の基本原則として広く知られているルールです。

意味今回の実装
3データのコピーを 3 つ 持つ原本 + ローカルバックアップ + クラウド
22 種類 以上の異なるメディアに保存HDD アレイ + クラウドストレージ
11 つ はオフサイト(物理的に離れた場所)AWS S3 Glacier

今回のバックアップ構成#

なぜ rclone copy なのか#

クラウドバックアップのツールとして rclone を採用しています。rclone は クラウドストレージ向けの rsync とも呼ばれるオープンソースの CLI ツールで、AWS S3、Google Cloud Storage、Backblaze B2 など 70 以上のクラウドストレージに対応しています。

rclone には copysync の 2 つのモードがあります。

コマンド動作リモートの削除
rclone copyローカルにあるファイルをリモートにコピーしない
rclone syncローカルとリモートを完全に同期(ミラーリング)する

今回は copy を選択 しました。理由は明確で:

  • 誤削除からの保護。 sync だとローカルで誤ってファイルを消した瞬間、次の同期でクラウドからも消えます。バックアップの意味がなくなる。copy ならローカルで削除してもクラウド側は残るので、「あの写真消しちゃった!」というときにクラウドから復旧できます
  • ランサムウェア対策。 ランサムウェアにやられてローカルが暗号化されても、copy なら暗号化前のファイルがクラウドに残っています。sync だと暗号化されたファイルでクラウドが上書きされるリスクがあります

なぜ Glacier Deep Archive なのか#

ストレージクラスは Glacier Deep Archive を選択。理由:

  • 写真・動画は write-once データ。 一度保存したらほぼ変更しない。頻繁にアクセスする必要もない。この特性は Glacier Deep Archive の「低頻度アクセス・長期保存」にぴったり合致します
  • 圧倒的に安い。 S3 Standard の約 1/23 のコスト(~$1/TB/月 vs ~$23/TB/月)
  • rclone から直接 PUT できる。 --s3-storage-class DEEP_ARCHIVE を指定するだけ。ライフサイクルポリシーの設定は不要

取り出しに 12〜48 時間かかりますが、これは「火災・盗難で物理ドライブが全滅したときの最終手段」なので問題ありません。

なぜサムネイルとドットファイルを除外するのか#

バックアップ対象を 写真・動画の本体データだけ に絞っています。

  • thumbs/ — Immich などの写真管理ツールが生成するサムネイルキャッシュ。元データから再生成できるのでバックアップ不要
  • .*(ドットファイル).DS_Store.Thumbs.db などの OS やアプリが生成するメタデータ。これもバックアップ不要

これにより、バックアップ対象が約 2TB → 約 1.5TB に減少。年間のストレージコストが ¥900 程度削減されます。

初回バックアップ前にディレクトリ構造を整理してください。 rclone はファイルパスでリモートのファイルを管理しています。バックアップ開始後にディレクトリを mv で移動すると、rclone は「旧パスのファイルが消えて、新パスに別のファイルが現れた」と認識します。結果として、同じファイルが新パスで再アップロードされ、旧パスのファイルは年次クリーンアップまでクラウドに残り続けます。つまり 転送量と保管料が二重にかかります。 ディレクトリ構成の変更は初回バックアップの前に済ませておきましょう。

実際のコマンド#

Terminal window
rclone copy /mnt/storage s3-remote:nas-backup/ \
--s3-storage-class DEEP_ARCHIVE \
--exclude "thumbs/**" \
--exclude ".*" \
--transfers 4 --checkers 8

rclone はファイルサイズとタイムスタンプで差分を検知するため、変更のないファイルは再転送されません。毎月実行しても、転送されるのは新しく追加された写真・動画だけです。

年 1 回のクリーンアップ#

rclone copy はリモートのファイルを削除しないので、ローカルで整理・削除したファイルがクラウドに溜まり続けます。これを放置するとストレージコストが増え続けるので、年 1 回のクリーンアップを行います。

ただし Glacier Deep Archive は 最低保存期間が 180 日 で、180 日未満で削除すると残り期間分の料金が発生します。そこで、ローカルに存在せず、かつアップロードから 180 日以上経過したファイル だけを削除対象にします。

Terminal window
# 1. S3 にあるがローカルに存在しないファイルを検出
rclone lsf s3-remote:nas-backup/ -R > /tmp/remote.txt
rclone lsf /mnt/storage/ -R --exclude "thumbs/**" --exclude ".*" > /tmp/local.txt
comm -23 <(sort /tmp/remote.txt) <(sort /tmp/local.txt) > /tmp/orphaned.txt
# 2. 180 日以上経過したファイルだけ削除
rclone delete s3-remote:nas-backup/ \
--files-from /tmp/orphaned.txt \
--min-age 180d

この運用なら:

  • 普段の rclone copy — 追加のみ。誤削除・ランサムウェアから保護
  • 年 1 回のクリーンアップ — 本当に不要になったファイルだけ削除。180 日ルールに抵触しない
  • 結果 — クラウド上には「ローカルに存在するファイル + 削除後 180 日未満のファイル」だけが残り、コストが膨らみ続けることはない

クラウドバックアップのコスト見積もり#

サムネイルやドットファイルを除外すると、実際のバックアップ対象は写真・動画データの約 1.5TB 程度になります。

サービスストレージ単価月額(1.5TB)年額取り出し
AWS S3 Glacier Deep Archive~$1/TB/月~$1.5(≒ ¥225)≒ ¥2,700取り出しに 12〜48 時間、別途料金
Backblaze B2$6/TB/月~$9(≒ ¥1,350)≒ ¥16,200即時アクセス、3× ストレージまで egress 無料
AWS S3 Standard$23/TB/月~$34.5(≒ ¥5,175)≒ ¥62,100即時アクセス

rclone copy で月 1 回の差分転送なら、PUT リクエストは毎月の新規ファイル分だけ(月数百〜数千件程度)。API コストはほぼ無視できます。

アーカイブ用途で「普段は取り出さない、災害時の最終手段」と割り切るなら Glacier Deep Archive が圧倒的に安い です。年額 ¥3,000 以下で 3-2-1 ルールのオフサイトコピーを実現できます。

ランニングコストの全体像#

項目月額年額
電気代(ミニ PC + DAS、~30W 平均)≒ ¥650≒ ¥7,800
クラウドバックアップ(Glacier Deep Archive)≒ ¥225≒ ¥2,700
合計≒ ¥875≒ ¥10,500

比較として、QNAP TS-431P は公称消費電力 36W(HDD 4 台搭載時)ですが、HDD がスタンバイに入らない問題があったので実質もう少し高いはず。加えて QNAP のクラウドバックアップ機能を使うにしても別途クラウドストレージ費用がかかります。


実際の設定#

ここからは実際に構築した設定ファイルを掲載します。OS は Ubuntu 24.04 LTS です。

ディスクのマウント#

各 HDD を UUID で個別にマウントします。USB DAS は接続順でデバイス名が変わることがあるため、UUID 指定が必須です。

Terminal window
lsblk -o NAME,SIZE,FSTYPE,UUID,MOUNTPOINT
NAME SIZE FSTYPE UUID MOUNTPOINT
sda 5.4T ext4 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /mnt/parity
sdb 3.6T ext4 yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy /mnt/data1
sdc 1.8T ext4 zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz /mnt/data2
sdd 1.8T ext4 wwwwwwww-wwww-wwww-wwww-wwwwwwwwwwww /mnt/data3

/etc/fstab:

# --- データドライブ ---
UUID=yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy /mnt/data1 ext4 defaults,nofail 0 2
UUID=zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz /mnt/data2 ext4 defaults,nofail 0 2
UUID=wwwwwwww-wwww-wwww-wwww-wwwwwwwwwwww /mnt/data3 ext4 defaults,nofail 0 2
# --- パリティドライブ ---
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /mnt/parity ext4 defaults,nofail 0 2
# --- mergerfs プール ---
/mnt/data1:/mnt/data2:/mnt/data3 /mnt/storage fuse.mergerfs defaults,allow_other,use_ino,cache.files=off,moveonenospc=true,dropcacheonclose=true,minfreespace=20G,category.create=mfs,fsname=mergerfs 0 0

nofail は USB DAS の起動が OS のブートより遅れても起動が止まらないようにするためのオプションです。USB 接続では必須。

mergerfs のポリシー#

category.create=mfs(most free space)は mergerfs の デフォルトポリシー で、空き容量が最も大きいドライブに新規ファイルを書き込みます。特にこだわりがないので、まずはデフォルトのままで運用してみることにしました。

ポリシー動作ユースケース
mfs空き容量が最大のドライブに書き込み今回採用。 ドライブ間で均等に分散
epmfs既存パスがあるドライブ優先、なければ mfsディレクトリ構造を維持したい場合
lfs空きが最小のドライブに書き込み1 台ずつ順番に埋めたい場合

moveonenospc=true は書き込み先が満杯の場合に自動で別ドライブにリトライする設定。minfreespace=20G で各ドライブを使い切らないようにガードします。

SnapRAID の設定#

Terminal window
sudo apt install snapraid

/etc/snapraid.conf:

# パリティファイルの場所
parity /mnt/parity/snapraid.parity
# コンテンツファイル(複数ドライブに分散配置を推奨)
content /var/snapraid/snapraid.content
content /mnt/data1/.snapraid.content
content /mnt/data2/.snapraid.content
content /mnt/data3/.snapraid.content
# データドライブ
data d1 /mnt/data1/
data d2 /mnt/data2/
data d3 /mnt/data3/
# 除外パターン
exclude *.unrecoverable
exclude /tmp/
exclude /lost+found/
exclude .DS_Store
exclude .Thumbs.db
exclude .snapraid.content
# 250GB ごとに中間セーブ(初回 sync 中に中断しても途中から再開可能)
autosave 250

コンテンツファイルは SnapRAID のメタデータで、最低 2 箇所 に配置する必要があります。パリティドライブ以外のデータドライブに分散させるのがベストプラクティスです。

初回の sync:

Terminal window
sudo snapraid sync
Self test...
Loading state from /var/snapraid/snapraid.content...
Scanning disk d1...
Scanning disk d2...
Scanning disk d3...
...
Syncing...

初回は全データのハッシュ計算が走るのでそれなりに時間がかかります。autosave 250 のおかげで 250GB ごとに中間保存されるので、途中で中断しても最初からやり直しにはなりません。

SnapRAID の自動化#

毎日深夜に sync + scrub を自動実行します。安全のため、大量削除を検知したら sync を中止する しきい値を設けます。

/usr/local/bin/snapraid-sync.sh:

#!/bin/bash
set -euo pipefail
THRESHOLD=250
LOG="/var/log/snapraid-sync.log"
echo "=== SnapRAID sync started at $(date) ===" >> "$LOG"
# diff で変更量を確認
DIFF_OUTPUT=$(snapraid diff 2>&1)
REMOVED=$(echo "$DIFF_OUTPUT" | grep -E 'removed' | awk '{print $1}' || echo "0")
if [ "$REMOVED" -gt "$THRESHOLD" ]; then
echo "WARNING: $REMOVED files removed (threshold: $THRESHOLD). Sync aborted." >> "$LOG"
# ここに通知処理を追加(メール、Discord webhook など)
exit 1
fi
snapraid sync >> "$LOG" 2>&1
# 30日以上前にチェックしたデータの 8% をスクラブ
snapraid scrub -p 8 -o 30 >> "$LOG" 2>&1
echo "=== SnapRAID sync completed at $(date) ===" >> "$LOG"
Terminal window
sudo chmod +x /usr/local/bin/snapraid-sync.sh
sudo crontab -e
# 毎日 AM 3:00 に実行
0 3 * * * /usr/local/bin/snapraid-sync.sh

削除しきい値(THRESHOLD=250)がポイントです。ランサムウェアに感染してファイルが大量に暗号化・削除されたケースでは、snapraid diff が異常な削除数を検出して sync を止めてくれます。パリティが上書きされる前に対処できるわけです。

scrub -p 8 -o 30 は「30 日以上前にチェックしたデータのうち 8% をスクラブ」。毎日実行すれば約 2 週間でアレイ全体を 1 巡し、ビットロットを早期発見できます。

Samba によるネットワーク共有#

LAN 内の他のマシンから mergerfs プールにアクセスするための設定です。

/etc/samba/smb.conf(抜粋):

[pool]
comment = mergerfs pool
path = /mnt/storage
browseable = yes
read only = no
valid users = @smbusers
create mask = 0664
directory mask = 0775
Terminal window
sudo useradd -M -s /usr/sbin/nologin smbuser
sudo smbpasswd -a smbuser
sudo usermod -aG smbusers smbuser
sudo systemctl restart smbd

rclone によるクラウドバックアップの設定#

rclone のインストールとリモート設定#

Terminal window
sudo apt install rclone

S3 リモートを設定します。rclone config は対話式で進みます。

Terminal window
rclone config
# 以下の順で入力:
# n) New remote
# name> s3-remote
# Storage> s3
# provider> AWS
# env_auth> false
# access_key_id> (IAM ユーザーの Access Key ID)
# secret_access_key> (IAM ユーザーの Secret Access Key)
# region> ap-northeast-1
# endpoint> (空欄のまま Enter)
# location_constraint> ap-northeast-1
# acl> private
# storage_class> DEEP_ARCHIVE
# (残りはデフォルトのまま Enter)

IAM ユーザーには以下のポリシーを付与しておきます。

{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject", "s3:ListBucket", "s3:DeleteObject"],
"Resource": [
"arn:aws:s3:::nas-backup",
"arn:aws:s3:::nas-backup/*"
]
}]
}

設定が完了したら動作確認します。

Terminal window
# リモートへの接続テスト
rclone lsd s3-remote:
# バケットがなければ作成
rclone mkdir s3-remote:nas-backup
# dry-run でバックアップ対象を確認(実際には転送しない)
rclone copy /mnt/storage s3-remote:nas-backup/ \
--s3-storage-class DEEP_ARCHIVE \
--exclude "thumbs/**" \
--exclude ".*" \
--dry-run -v

バックアップスクリプト#

月次バックアップスクリプト(/usr/local/bin/backup-cloud.sh):

#!/bin/bash
set -euo pipefail
LOG="/var/log/backup-cloud.log"
REMOTE="s3-remote:nas-backup"
LOCK="/var/run/backup-cloud.lock"
# 多重起動の防止
if [ -f "$LOCK" ]; then
echo "ERROR: Backup already running (lockfile exists)" >> "$LOG"
exit 1
fi
trap 'rm -f "$LOCK"' EXIT
touch "$LOCK"
echo "=== Cloud backup started at $(date) ===" >> "$LOG"
# rclone copy: 新規・変更ファイルだけアップロード、リモートのファイルは削除しない
rclone copy /mnt/storage "$REMOTE" \
--s3-storage-class DEEP_ARCHIVE \
--exclude "thumbs/**" \
--exclude ".*" \
--transfers 4 \
--checkers 8 \
--bwlimit 50M \
--log-file "$LOG" \
--log-level INFO \
--stats 1h \
--stats-log-level INFO \
&& echo "=== Cloud backup completed successfully at $(date) ===" >> "$LOG" \
|| { echo "=== Cloud backup FAILED at $(date) ===" >> "$LOG"; exit 1; }

スクリプトのポイント:

  • --bwlimit 50M — 帯域を 50MB/s に制限。バックアップ中に他のネットワーク通信が遅くならないようにします。回線速度に応じて調整してください
  • ロックファイルbackup-cloud.lock で多重起動を防止。初回の 1.5TB アップロードは数時間以上かかるため、cron の次の実行と重複しないようにします
  • --stats 1h — 1 時間ごとに転送状況をログに出力。長時間実行の進捗がわかります
Terminal window
sudo chmod +x /usr/local/bin/backup-cloud.sh

クリーンアップスクリプト#

年次クリーンアップスクリプト(/usr/local/bin/backup-cleanup.sh):

#!/bin/bash
set -euo pipefail
LOG="/var/log/backup-cloud.log"
REMOTE="s3-remote:nas-backup"
echo "=== Cleanup started at $(date) ===" >> "$LOG"
# S3 上のファイル一覧を取得
rclone lsf "$REMOTE" -R > /tmp/remote-files.txt
# ローカルのファイル一覧を取得(バックアップと同じ exclude ルールを適用)
rclone lsf /mnt/storage/ -R \
--exclude "thumbs/**" \
--exclude ".*" > /tmp/local-files.txt
# S3 にあるがローカルに存在しないファイルを抽出
comm -23 <(sort /tmp/remote-files.txt) <(sort /tmp/local-files.txt) > /tmp/orphaned-files.txt
ORPHAN_COUNT=$(wc -l < /tmp/orphaned-files.txt)
echo "Found $ORPHAN_COUNT orphaned files" >> "$LOG"
if [ "$ORPHAN_COUNT" -gt 0 ]; then
# 180 日以上経過したファイルだけ削除(Glacier の最低保存期間を回避)
rclone delete "$REMOTE" \
--files-from /tmp/orphaned-files.txt \
--min-age 180d \
--log-file "$LOG" \
--log-level INFO
fi
# 一時ファイルの削除
rm -f /tmp/remote-files.txt /tmp/local-files.txt /tmp/orphaned-files.txt
echo "=== Cleanup completed at $(date) ===" >> "$LOG"
Terminal window
sudo chmod +x /usr/local/bin/backup-cleanup.sh

cron 設定#

Terminal window
sudo crontab -e
# --- SnapRAID ---
# 毎日 AM 3:00 に sync + scrub
0 3 * * * /usr/local/bin/snapraid-sync.sh
# --- クラウドバックアップ ---
# 毎月 1 日 AM 4:00 に rclone copy(SnapRAID sync の後に実行)
0 4 1 * * /usr/local/bin/backup-cloud.sh
# 毎年 1 月 15 日 AM 4:00 にクリーンアップ(1 日のバックアップ完了後に実行)
0 4 15 1 * /usr/local/bin/backup-cleanup.sh

SnapRAID sync(AM 3<00>)→ rclone copy(AM 4<00>)の順番にしています。sync が完了してパリティが最新になった状態でクラウドバックアップを実行するためです。

ドライブの監視#

SMART データの監視を設定しています。Reallocated Sector Count や Current Pending Sector が増加傾向にあるドライブは早めに交換する判断材料になります。

Terminal window
sudo apt install smartmontools
# 各ドライブの SMART 情報を確認
sudo smartctl -a /dev/sda
sudo smartctl -a /dev/sdb
sudo smartctl -a /dev/sdc
sudo smartctl -a /dev/sdd

Zabbix などの監視ツールでカスタム UserParameter を設定し、温度やセクタエラーの推移を記録しておくと、障害の予兆を捉えやすくなります。


運用してみての所感#

良かったこと#

HDD がちゃんとスタンバイに入る。 QNAP の謎のアクセスランプ点滅から解放されました。使っていない時間帯は静かで、電気代も下がった実感があります。

異なる容量のドライブを無駄なく使い切れる。 RAID5 だと 2TB に揃えなきゃいけなかったところ、手持ちの 4TB + 2TB + 2TB がそのまま 8TB として活きています。

壊れても理解できる。 mergerfs + SnapRAID の仕組みはシンプルなので、何か問題が起きても dmesgsnapraid status を見れば状況が分かります。QNAP の QTS や PowerEdge の RAID コントローラではこうはいかなかった。

1 台壊れても慌てない安心感。 仮に disk2 が死んでも、disk1 と disk3 のファイルはそのまま ext4 として読めます。PowerEdge で RAID カード障害を経験した身としては、この安心感は本当に大きい。

注意点・トレードオフ#

mergerfs の CPU 使用率がそこそこある。 これは運用してみて気づいたことですが、mergerfs は FUSE(ユーザー空間ファイルシステム)で動作するため、ファイルアクセスのたびにカーネル空間とユーザー空間を往復するオーバーヘッドがあります。btop で見ると、特に大量のファイルを読み書きしている時に mergerfs プロセスが CPU をそれなりに食っているのが分かります。Ryzen 7 5800H なら実用上問題にはなりませんが、Raspberry Pi のような低スペック環境だとボトルネックになる可能性があります。mergerfs の開発者も「CPU を使っているように見えるが、実際にはリクエストのディスパッチがほとんど」と説明しており、実 I/O に比べれば軽微とのこと。とはいえ、カーネルモジュールとして実装された場合と比べるとオーバーヘッドがあるのは事実です。

USB DAS の信頼性。 SATA 直結と比べると USB 接続はどうしても一段劣ります。ケーブルの抜けや DAS の電源トラブルには注意が必要で、dmesg を定期的にチェックする運用が求められます。

SnapRAID はリアルタイム保護ではない。 sync の間隔(今回は 1 日 1 回)の分だけデータ損失リスクがあります。重要な写真を大量に取り込んだ直後は手動で snapraid sync を走らせるのが安心です。

GUI がない。 QNAP の QTS のような Web UI はありません。すべて CLI です。Linux に慣れていない人にはハードルが高いですが、逆に言えばすべてテキストファイルで設定が完結するので、バージョン管理やドキュメント化がしやすいです。

初期セットアップの手間。 QNAP は箱から出して 30 分で使い始められますが、この構成は数時間〜数日かかります。そのぶん自由度と「何が起きているか理解できる」度は桁違いです。


mergerfs FAQ#

mergerfs + SnapRAID の運用事例はあまり記事になっていなかったので、運用していく中で自分が気になったことをチェックリストとしてまとめておきます。

Q. ドライブが 1 台壊れたらどうなる?#

mergerfs プール自体は動き続けます。 壊れたドライブに載っていたファイルにアクセスするとエラーになりますが、他のドライブのファイルは何事もなく読み書きできます。RAID5 のように「アレイが degraded になって全体のパフォーマンスが落ちる」ということはありません。

復旧手順としては、壊れたドライブを新しいドライブに交換し、SnapRAID の fix コマンドでパリティからデータを復元します。

Terminal window
# 新しいドライブをフォーマットしてマウント
sudo mkfs.ext4 /dev/sdX
sudo mount /dev/sdX /mnt/data2
# SnapRAID でデータを復元
sudo snapraid fix -d d2

Q. 新しいドライブを追加するには?#

fstab を 1 行書き換えるだけ です。mergerfs のソースパスに新しいドライブを追加して mount -a するか、再マウントすれば即座に反映されます。RAID のような再構築は一切不要。

Terminal window
# 例: /mnt/data4 を追加
sudo mkfs.ext4 /dev/sdX
sudo mkdir /mnt/data4
sudo mount /dev/sdX /mnt/data4
# fstab の mergerfs 行を編集
/mnt/data1:/mnt/data2:/mnt/data3:/mnt/data4 /mnt/storage fuse.mergerfs ...
# 再マウント
sudo umount /mnt/storage && sudo mount -a

SnapRAID 側も /etc/snapraid.confdata d4 /mnt/data4/ を追加して snapraid sync を実行すればOK。

ライブで追加したい場合は mergerfs.ctlxattr 経由で再マウントなしに追加できます。

Terminal window
sudo xattr -w user.mergerfs.srcmounts +/mnt/data4 /mnt/storage/.mergerfs

Q. ドライブを取り外すには?#

mergerfs は ドライブ取り外し時にデータを自動移動しません。 手動でデータを退避する必要があります。

ポイントは 先にプールから除外してから rsync する ことです。除外前に rsync すると、コピー先の /mnt/storage/ に取り外し対象ドライブ自身が含まれているため、ファイルが「既に存在する」と判定されてコピーがスキップされてしまいます。

Terminal window
# 1. mergerfs からドライブを除外(プール上でこのドライブのファイルが見えなくなる)
sudo xattr -w user.mergerfs.srcmounts -/mnt/data3 /mnt/storage/.mergerfs
# 2. 除外したドライブのデータをプールにコピー(他のドライブに書き込まれる)
sudo rsync -avxHAXWE --numeric-ids --info=progress2 /mnt/data3/ /mnt/storage/
# 3. コピー漏れがないか dry-run で確認(出力がなければ OK)
sudo rsync -avxHAXWE --numeric-ids --dry-run /mnt/data3/ /mnt/storage/
# 4. ドライブをアンマウント
sudo umount /mnt/data3
# 5. fstab から該当行を削除し、SnapRAID の設定から d3 を削除して再 sync
sudo snapraid sync

プール全体に十分な空き容量がある前提です(取り外すドライブの使用量 < 残りドライブの空き容量)。ドライブの容量アップグレード(2TB → 4TB に交換など)もこの手順で対応できます。

Q. あるファイルがどのドライブに保存されているか確認できる?#

できます。mergerfs はファイル単位で保存先が分かれるので、物理的にどこにあるか確認したい場合は個別のマウントポイントを find で探します。

Terminal window
# /mnt/storage/photos/2024/IMG_001.RAF がどのドライブにあるか
find /mnt/data1 /mnt/data2 /mnt/data3 -path "*/photos/2024/IMG_001.RAF"
/mnt/data1/photos/2024/IMG_001.RAF

Q. ext4 と xfs など異なるファイルシステムを混在できる?#

できます。 mergerfs はファイルシステムの種類に依存しません。ext4 と xfs を混在させても問題なく動作します。ただし、運用のシンプルさを考えると統一しておくのが無難です。NTFS や FAT のような非 POSIX ファイルシステムはパーミッションの問題が出る可能性があるので非推奨。

Q. mergerfs 自体がクラッシュしたらデータは消える?#

消えません。 mergerfs はデータを変換・加工しているわけではなく、既存のファイルシステムの上にマウントポイントを仮想的に統合しているだけです。mergerfs が落ちたら /mnt/storage にアクセスできなくなりますが、各ドライブ(/mnt/data1, /mnt/data2, /mnt/data3)は普通の ext4 なので、そのままマウントしてファイルにアクセスできます。

最悪の場合、mergerfs を一切使わなくても全データにアクセスできるというのが、RAID に対する最大のメリットの 1 つです。

Q. Docker のボリュームとして mergerfs プールを使える?#

使えます。 Docker の volumes/mnt/storage のパスをそのまま指定すれば OK です。

volumes:
- /mnt/storage/data:/app/data

ただし、データベース(PostgreSQL、MySQL など)のデータディレクトリを mergerfs 上に置くのは 非推奨 です。データベースは小さなファイルを頻繁に読み書きするワークロードで、FUSE のオーバーヘッドがパフォーマンスに影響します。データベースのデータは SSD(OS ドライブ)に置いて、mergerfs プールには写真やメディアなどの大きなファイルを保存する、という使い分けがおすすめです。

Q. パフォーマンスはどのくらい出る?#

シーケンシャルな Read/Write であれば、ほぼディスクネイティブの速度 が出ます。USB 3.0 接続の HDD で 150〜200MB/s 程度。mergerfs のオーバーヘッドよりも HDD や USB の帯域がボトルネックになるので、実質的な速度低下は体感しにくいです。

ただし、大量の小さなファイルを扱うメタデータ操作findls -Rdu など)は FUSE のオーバーヘッドが効いてきます。ファイルアクセスのたびにカーネル↔ユーザー空間を往復するので、ファイル数が多いディレクトリでは遅延を感じることがあります。写真やメディアファイルのような「数は多いが個々のファイルは大きい」ワークロードなら問題になることは少ないです。

Q. 1 台が壊れたとき、他のディスクに新しい書き込みがあってもパリティから復元できる?#

最後に snapraid sync が成功した時点のデータは復元できます。 ただし、いくつか注意点があります。

SnapRAID のパリティはリアルタイムではなく スナップショットベース です。パリティが保護しているのは「最後の sync 時点でのデータ」なので、sync 後に他のディスクへ書き込みがあっても、壊れたディスクの復元には影響しません。パリティ計算のベースラインが変わっていないからです。

絶対にやってはいけないこと:故障したディスクがある状態で snapraid sync を実行すること。 sync を実行すると、壊れたディスクの欠損を「正しい状態」としてパリティが再計算されてしまい、復元できなくなります。必ず fix → ディスク交換 → sync の順番 を守ってください。

まとめると:

状況復元
sync 後、他ディスクへの書き込みなし✅ 完全復元
sync 後、他ディスクへの新規ファイル追加あり✅ 壊れたディスクは sync 時点で復元可能。新規ファイルはパリティ未保護
sync 後、他ディスクの既存ファイルを変更⚠️ 壊れたディスクの復元精度が下がる可能性あり(変更されたファイルとパリティの整合性がずれるため)
故障後に sync を実行してしまった❌ 復元不可能

運用のコツとしては、ディスク故障に気づいたら すぐに自動 sync の cron を止めて、snapraid fix を実行する ことです。自動化スクリプトで snapraid diff の結果をチェックしてディスク異常を検知→ sync を中止する仕組みを入れておくと安心です(本記事の自動化スクリプトでは削除しきい値でこれに近い保護をしています)。


まとめ#

Dell PowerEdge T320(ハードウェア RAID)→ QNAP TS-431P(ソフトウェア RAID)→ mergerfs + SnapRAID on Ubuntu 24.04 と渡り歩いてきて、ようやく「壊れても理解できる」ストレージ構成にたどり着きました。

3 世代の経験から言えるのは:

  • ハードウェア RAID はコントローラが SPOF。 壊れたら詰む
  • NAS アプライアンスは便利だが、ベンダーロックインと品質のばらつきがある。 特に周辺アプリの完成度は過信できない
  • mergerfs + SnapRAID は「理解できるシンプルさ」が最大の価値。 各ドライブが普通の ext4 で読めるという安心感は他に替えがたい
  • SnapRAID はバックアップではない。 3-2-1 ルールに基づくオフサイトバックアップは別途必須。Glacier Deep Archive なら年額 ¥3,000 以下で実現可能

CLI 運用と USB DAS の割り切りが必要なので万人向けとは言えませんが、NAS のブラックボックスに不安を感じている人、過去に RAID で痛い目に遭った人には自信を持っておすすめできます。

次回の記事では、この NAS 上に構築した 写真管理環境(Immich)について書く予定です。

QNAP NAS を卒業して mergerfs + SnapRAID で「壊れても理解できる」ストレージを作った
https://blog.teraren.com/posts/nas-diy-mergerfs-snapraid/
作者
Yuki Matsukura
公開日
2026-02-25
ライセンス
CC BY-NC-SA 4.0
この記事が役に立ったら
GitHub Sponsorsで応援できます

コメント