Linux環境での本番向けZFS運用:耐障害性プール構築、データセットチューニング、スナップショットとScrubの自動化

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

背景と導入理由:本番環境におけるサイレントビットロットの脅威

高スループットなPostgreSQLノードにおいて、夜間の定期バッチ処理中に予期せぬI/O読み取りエラーが発生しました。エンタープライズ向けSASドライブ2台にまたがる複数のセクターで、サイレントビットロット(静的なデータ破損)が発生していたことが原因でした。

磁気記録層の物理的な経年劣化によってプラッタレベルでデータが破損していたにもかかわらず、ハードウェアRAIDコントローラーはアラートを一切記録していませんでした。EXT4やXFSなどの標準的なファイルシステムは、下層のブロックデバイスを無条件に信頼します。そのため、ドライブが破損したビットを返した場合でも、ファイルシステムはクラッシュが発生するまで異常なデータをそのままアプリケーションへ渡し続けてしまいます。

OpenZFSはストレージを全く異なるアプローチで扱います。独立したボリュームマネージャーの上にファイルシステムを重ねるのではなく、ZFSは両方のレイヤーを統合しています。

ZFSは書き込まれるすべてのブロックを256ビットのチェックサムで保護します(デフォルトでは極めて高速なFletcher4アルゴリズムを使用)。読み取り時には毎回チェックサムの再計算と検証を実行します。ミラー構成のディスク上で破損ブロックを検出すると、ZFSは正常なドライブから正しいデータを読み取り、バックグラウンドで破損ブロックを自動修復した上で、アプリケーションを停止させることなくクリーンなデータを返します。

ストレージアーキテクチャの選定は長期的な信頼性を大きく左右します。実データを格納する前に適切なストレージプールの設計・構築を行っておくことで、将来的に発生する数テラバイト規模の困難なデータ移行作業を回避できます。

インストール:OpenZFS向けホストの準備

まず、ストレージプール専用の物理ディスクまたはNVMeドライブを特定します。ハードウェアRAIDの仮想ディスク上にZFSプールを構築することは絶対に避けてください。ZFSは、ITモードのHBAまたは直接接続されたSATA/NVMeレーンを介して、ディスクハードウェアと直接通信する必要があります。この構成により、ZFSがディスクの書き込みキャッシュ、電源状態、SMARTヘルス情報を完全に制御できるようになります。

1. ZFSカーネルパッケージのインストール

UbuntuおよびDebianでは、公式パッケージリポジトリからOpenZFSを直接インストールできます。

Ubuntu/Debianの場合:

sudo apt update
sudo apt install -y zfsutils-linux

RHEL、AlmaLinux、Rocky Linux 9の場合:

sudo dnf install -y epel-release
sudo dnf install -y https://zfsonlinux.org/epel/zfs-release-2-3$(rpm -E %dist).noarch.rpm
sudo dnf config-manager --enable zfs-kmod
sudo dnf install -y zfs

2. カーネルモジュールのロードと確認

sudo modprobe zfs
lsmod | grep -i zfs

3. 永続的なディスクIDの特定

/dev/sdbのような動的なブロックデバイス名を使用してプールを作成しないでください。カーネルによるドライブレターの割り当ては、ドライブの交換やホストの再起動によって入れ替わる可能性があります。必ず/dev/disk/by-id/の永続的なシリアル番号ベースのパスを使用します:

ls -l /dev/disk/by-id/

構成:Zpool、データセット、スナップショット、自動Scrub

1. 耐障害性に優れたミラー構成Zpoolの作成

2台の2TBドライブ(ata-ST2000DM008_DISK1_IDおよびata-ST2000DM008_DISK2_ID)を使用して、高性能なミラープールtankを作成するには、以下のコマンドを実行します:

sudo zpool create -f -o ashift=12 -O compression=zstd -O acltype=posixacl -O xattr=sa -O relatime=on tank mirror \
  /dev/disk/by-id/ata-ST2000DM008_DISK1_ID \
  /dev/disk/by-id/ata-ST2000DM008_DISK2_ID

上記の本番向けオプションの役割は以下の通りです:

  • ashift=12: vdevのブロックサイズの指数を12(212 = 4096バイト)に設定します。これにより、近年のAdvanced Format HDDやNVMe SSDの4K物理セクターとI/Oが整合し、大幅な書き込み性能低下を防止します。
  • compression=zstd: Zstandard圧縮を有効化します。混在ワークロードにおいて1.4倍〜1.8倍の圧縮率を実現しながら、実際のディスク書き込み時間を短縮します。
  • xattr=sa: 拡張ファイル属性(xattr)をinode内部に直接格納し、Linuxにおけるアクセス制御チェックを高速化します。

2. ワークロード別データセットの作成とチューニング

本番データをルートプール直下に直接配置することは避けてください。データセットを作成することで、特定のアプリケーションごとにレコードサイズ、クォータ、キャッシュポリシーをきめ細かく制御できます。

データセットのrecordsizeは、使用するデータベースエンジンのネイティブページサイズに合わせて調整します。PostgreSQLのページサイズは8 KB、MySQL InnoDBのデフォルトは16 KBです。これらの値と一致させることで、書き込み増幅(Write Amplification)を抑え、コストの高いリード・モディファイ・ライトの発生を防ぎます。

# PostgreSQL(8kページ)またはMySQL InnoDB(16kページ)向けに調整されたデータセット
sudo zfs create tank/databases
sudo zfs set recordsize=16k tank/databases
sudo zfs set logbias=latency tank/databases
sudo zfs set atime=off tank/databases

# 一般的なコンテナストレージおよびWebアセット用データセット
sudo zfs create tank/appdata
sudo zfs set recordsize=128k tank/appdata
sudo zfs set quota=500G tank/appdata

3. コピーオンライトによる即時スナップショット

ZFSスナップショットは、特定時点のデータ状態を瞬時に記録します。ZFSはコピーオンライトのポインタ管理を行っているため、2 TBのデータベースであってもスナップショットの取得は100ミリ秒未満で完了し、取得直後の追加ストレージ消費はゼロです。

# スキーママイグレーション前に即時スナップショットを取得
sudo zfs snapshot tank/databases@pre_migration_2026_10_01

# スナップショットの容量使用量を確認
sudo zfs list -t snapshot

# スキーマ更新に失敗した場合は即座にロールバック
sudo zfs rollback tank/databases@pre_migration_2026_10_01

元データに影響を与えることなく本番データを用いたステージングテストを実施したい場合は、スナップショットから独立した書き込み可能なデータセットをクローン作成できます:

sudo zfs clone tank/databases@pre_migration_2026_10_01 tank/db_test_sandbox

4. プールScrubの自動化

Scrub(スクラブ)は、プール内のすべてのアクティブなデータブロックを順次読み取ってチェックサムと照合し、サイレントビットロットをミラーのパリティを用いて自動修復します。2 TBのプールの場合、ドライブの性能やI/O負荷にもよりますが、通常30分〜60分程度で完了します。

必要に応じて手動でScrubを実行できます:

sudo zpool scrub tank

軽量なsystemdタイマーを使用して、週次でScrubを自動実行します:

# 1. ワンショットサービスを定義
sudo tee /etc/systemd/system/zfs-scrub-tank.service << 'EOF'
[Unit]
Description=ZFS Storage Pool Scrub (tank)
Documentation=man:zpool-scrub(8)

[Service]
Type=oneshot
ExecStart=/usr/sbin/zpool scrub tank
EOF

# 2. 週次スケジュールを定義(毎週日曜日 02:00 UTC)
sudo tee /etc/systemd/system/zfs-scrub-tank.timer << 'EOF'
[Unit]
Description=Weekly ZFS Scrub Timer for tank

[Timer]
OnCalendar=Sun *-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target
EOF

# 3. デーモンを再読み込みしてタイマーを有効化
sudo systemctl daemon-reload
sudo systemctl enable --now zfs-scrub-tank.timer

検証とモニタリング:ディスクの健全性維持

1. プールの状態とI/Oエラーの確認

ディスクのリアルタイムステータス、実行中のScrubの進捗、チェックサム検証エラー数を確認します:

sudo zpool status tank

CKSUM列の数値を注視してください。ここに正の値が記録されている場合、ZFSがビットレベルの破損を検知し、即座にデータを自動修復したことを示しています。

2. 圧縮率と容量使用状況の確認

sudo zfs get compressratio,used,available,mountpoint tank

3. ZFS Event Daemon (ZED) によるアラート設定

ディスクで修復不可能なエラーが発生した場合やドライブが故障した際に、ZEDから即座にメール通知を送信するように設定します:

# /etc/zfs/zed.d/zed.rc の設定
sudo sed -i 's/#ZED_EMAIL_ADDR="root"/ZED_EMAIL_ADDR="[email protected]"/' /etc/zfs/zed.d/zed.rc
sudo sed -i 's/#ZED_NOTIFY_VERBOSE=0/ZED_NOTIFY_VERBOSE=1/' /etc/zfs/zed.d/zed.rc
sudo sed -i 's/#ZED_EMAIL_PROG="mail"/ZED_EMAIL_PROG="mail"/' /etc/zfs/zed.d/zed.rc

sudo systemctl restart zed

4Kセクターにアライメントされたミラープール、ワークロードに応じたデータセットのレコードサイズ調整、即時スナップショット、自動Scrub、そしてZEDによるアラート通知を組み合わせることで、Linuxサーバーにエンタープライズグレードの堅牢なストレージ信頼性を実現できます。

Share: