[RECOVERY]ADVANCED

アラートメールが本当に届くか、一度も確認していなかった

友人宅のNASでディスクが1本壊れたとき、設定していたはずのzedからの通知メールが結局届いていなかったと聞いて青ざめました。`homenas`のアラートも設定しただけで一度も動作確認していなかったからです。本物のディスクが壊れるのを待つ代わりに、`zinject`で疑似的に故障状態を作り、監視・通知が本当に動くかを検証したときの記録です。コマンドの意味を1つずつ追いながら整理します。

友人がNASのディスクを1本壊したとき、「zedからのメール通知を設定していたはずなのに、結局何も届かなかった」とこぼしていました。原因を聞くと、zed.rcの設定を書いた後に一度も動作確認をしていなかったそうです。聞きながら青ざめました。homenasのアラートも、数ヶ月前に設定してから一度もテストしていなかったからです。

本物のディスクが壊れるのを待って初めて「アラートが来ない」と気づくのは、さすがに間が悪すぎます。そこで、ZFSに標準で付いてくる**障害注入ツールzinject**を使って、実際にディスクを壊さずに「壊れたふり」をさせ、監視・通知が動くかを検証してみました。この記事では、そのとき使ったコマンドの意味を1つずつ追いながら整理します。

zinjectは「壊す」ためのデバッグツール

zinjectは、ZFS自身のテストスイートのために作られた障害注入ツールで、多くのOpenZFS環境ではzfsutilsパッケージに同梱されています。実行にはroot権限が必要です。

zinject

引数なしで実行すると、現在アクティブな注入(ハンドラ)の一覧が表示されます。何も注入していなければ空です。まず覚えておきたいのはこのコマンドで、これから何かを注入するたびに、ここで状態を確認する癖をつけます。

大前提として、これは本番データに直接手を出せてしまうツールです。 検証は本番のfootageやプロジェクトが載っているデータセットではなく、使い捨ての検証用プール(以前の記事でlabtestと呼んでいたようなもの)で行うことを強くおすすめします。うちも今回はhomenas本体ではなく、検証用に切り出した小さいプールでテストしました。

デバイスを「壊れたことにする」いちばん簡単な方法

実際のI/Oエラーを起こす前に、まずは一番安全な方法から試しました。デバイスの状態そのものを、実際のI/Oを一切介さずに書き換える方法です。

zinject -d /dev/sdb -A degrade labtest

それぞれの意味は次の通りです。

  • -d /dev/sdb: 対象デバイスの指定。zpool statusに表示されているパスやデバイス名を使います
  • -A degrade: automatic fault handlingのモード指定。degrade(DEGRADED状態にする)かfault(FAULTED状態にする、完全に切り離されたことにする)を選べます
  • 末尾のlabtest: 対象プール名

このコマンドを実行すると、実際には何も壊れていないのにzpool status上ではそのデバイスがDEGRADED(またはFAULTED)として表示されます。ZFS内部のFault Management(FMA)が本物の故障を検知したときと同じ状態遷移を強制的に起こしているだけなので、I/Oエラーを積み重ねる必要がなく、一番手っ取り早く「監視が状態変化を拾うか」を確認できます。

zpool status labtest

この状態変化はzed(ZFS Event Daemon)がイベントとして拾い、/etc/zfs/zed.d/以下のスクリプトが発火する起点になります。うちのhomenasではzed.rcにZED_EMAIL_ADDRを設定してメール通知を組んでいたので、ここで初めて実際にメールが届くかどうかを確認できました。結果は……案の定、届きませんでした。原因はSMTPの認証設定が変わっていたことに気づかず放置していたことで、友人のケースと同じパターンでした。

注入は必ず消す: zinject -c

検証が終わったら、注入した状態を元に戻す必要があります。これを忘れると、実際のデバイスは健全なのに、プールはいつまでもDEGRADEDのままになります。

zinject -c all

-cはクリアのオプションで、zinject -l(または引数なしのzinject)で表示されるIDを個別に指定することも、allで一括指定することもできます。注入を消した後は、念のためzpool clearでエラーカウンタもリセットしておくと状態がきれいになります。

zpool clear labtest sdb

検証手順の最後に必ずこの2行を書いておく、というのが今回一番身につけた習慣です。

実際のI/Oエラーを注入する

デバイス状態の強制書き換えだけでなく、実際の読み書きに対してエラーを発生させることもできます。

zinject -d sdb -e io -T write -f 100 labtest
  • -e io: 注入するエラーの種類。ioはI/Oエラー(読み書きの失敗)、checksumはチェックサム不一致(データ破損)を模擬します
  • -T write: 対象となる操作の種類。read/write/allなどを指定でき、ここでは書き込みだけを失敗させています
  • -f 100: 発生頻度(%)。100を指定すると該当する操作は必ず失敗します。値を下げれば「たまに失敗する」不安定なディスクの挙動も再現できます

この状態で実際に書き込みを発生させると、zpool statusのWRITEエラーカウンタが増え、閾値を超えればZFSが自動的にそのデバイスをDEGRADEDへ遷移させます。デバイス状態を直接書き換える-Aと違い、こちらは実際にエラーハンドリングの経路そのものをたどらせる検証ができます。監視だけでなく、アプリケーション側が書き込み失敗にどう反応するかまで含めて確認したい場合はこちらが向いています。もちろん検証後は同じくzinject -c allを忘れずに。

チェックサムエラーとscrubの修復を確認する

以前scrub/resilverの記事で「scrubは本当に直してくれる」と書きましたが、実際に自分の目で確認したことはありませんでした。これもzinjectで試せます。

zinject -t data -e checksum -f 20 /labtest/testfile
  • -t data: 注入対象の種類。データブロックを対象にする指定です(メタデータ側を狙うdnode指定などもあります)
  • 末尾はプール名ではなく、対象となるファイルパスを指定します
  • -f 20: 該当ブロックの20%程度にチェックサム不一致を発生させる、という頻度指定

この状態でファイルを読み出す(catするなど)と、ZFSはチェックサム検証で異常を検出します。冗長性のあるプールであれば、健全なコピーから自動修復されるはずです。zpool statusのCKSUMカウンタが増えているのを確認し、zinject -c allで注入を消してから改めてファイルを読み、正常に読めることまで確認して一区切りです。

今の運用: 四半期に一度の疑似障害訓練

一度動作確認して終わりにせず、homenasでは四半期に一度、labtestプールでzinject -A degradeによるアラート発火テストを行うことにしました。

  • 検証は必ずlabtestのような使い捨てプールで行い、本番データセットには近づけない
  • テスト前にzinject(引数なし)で注入が残っていないか確認する
  • テスト後はzinject -c allとzpool clearをセットで実行し、状態を元に戻したことを確認する
  • メール/通知が実際に届くところまで確認して初めて「監視が機能している」と言えることを忘れない

友人のNASの一件がなければ、homenasのアラートもきっと今も「設定しただけ」のままだったはずです。本物の故障で初めて気づくより、疑似的にでも一度壊してみる方がずっと安心できます。