[RECOVERY]INTERMEDIATE

新しいマシンで読み込んだプールが、古いマシンに二度と戻らなくなった話

`homenas`を新しいハードウェアに載せ替える途中、念のため旧機でも動作確認しようとしたら、二度とインポートできなくなっていました。原因はプールのversion番号ではなく、feature flagという一方通行の仕組みでした。legacy versionとfeature flagsの違い、なぜ一度アップグレードすると戻れないのか、そして詰んだときの読み取り専用インポートによる救出手順を整理します。

homenasを新しいマザーボードに載せ替えたときのことです。念のため旧機の構成もしばらく残しておこうと思い、ディスク一式を新機で一度インポートして動作確認だけしてから、また旧機に戻すつもりでいました。ところが旧機に戻してzpool importを叩いた瞬間、こう言われました。

cannot import 'homenas': pool uses the following feature(s) not supported by this system:
    com.klarasystems:pool_checkpoint

新機で一度インポートしただけのつもりが、勝手に何かがアップグレードされていたようでした。旧機のOSは更新をサボっていて、zfsutils-linuxのバージョンが数世代古かったのです。数分前まで普通に使えていたプールが、旧機からは永久に読めなくなりました。

versionではなくfeature flagという仕組み

ZFSには昔ながらのversion番号(1〜28)がありましたが、OpenZFS移行後はversionは5000に固定され、個々の機能はfeature flag(feature@xxxの形で管理される)単位で有効/無効が切り替わる方式になっています。この方式のポイントは、新しい実装は古いプールを問題なく読めるという上位互換性がある一方、古い実装は自分が知らないfeature flagが有効なプールを読めないという点です。

厄介なのは、この移行が片方向だということです。一度有効化したfeature flagを無効化する手段はありません。新機で開いた瞬間に何かのfeatureが有効化されていれば、それだけでもう旧機とは互換性が切れます。

何が起きたのか

zpool importはデフォルトで、インポート可能なプールに対して自動的にいくつかの補助的な処理を行うことがあります。今回のケースで実際に何が有効化のトリガーになったのかは、当時ログを取っていなかったため正確には特定できていません。ただ、新機のzfsutils-linuxのバージョンが旧機よりかなり新しかったことは確かで、新旧のバージョン差がある状態で一方の環境にディスクを持ち込んだこと自体がそもそもの原因です。

はっきり言えるのは、「とりあえず新しい方で一度開いてみる」という判断が、後戻りの選択肢を消してしまったということです。旧機と新機を行き来させる可能性が少しでもあるなら、先に両方のzpool upgrade -vの出力を見比べて、サポートしているfeatureのリストが一致しているかを確認するべきでした。

zpool upgrade -v

詰んだときにまず見るもの

zpool importが完全に失敗する状態でも、zdb -lでディスクのラベル情報から有効なfeature flagを直接確認できます。

zdb -l /dev/sdX1

出力のfeatures_for_readセクションに並んでいるfeatureが、インポート先の環境でサポートされているかどうかを、zpool upgrade -vの一覧と突き合わせて確認します。今回の自分のケースでは、旧機のzpool upgrade -vに該当のfeatureがそもそも存在せず、パッケージのアップデートなしにはどうにもならないことがその場で分かりました。

読み取り専用インポートという逃げ道

問題のfeatureが「read-only compatible」に分類されているものであれば、書き込みはできなくてもデータの救出は可能です。

zpool import -o readonly=on homenas

自分の場合はこれで事なきを得ました。読み取り専用でマウントし、念のため直近のデータだけ別ディスクにrsyncで退避してから、旧機はそのまま緊急用の予備機として電源を落として保管することにしました。もしfeatureがread-only compatibleでなければ、この手段すら使えず、対応するバージョンのzfsutils-linux/カーネルモジュールを用意するまでお手上げになるところでした。

今なら機材を入れ替える前にやること

  • 新旧の環境を行き来させる可能性があるプールでは、両方のzpool upgrade -vの出力を先に見比べる
  • バージョンに差がある環境では、「とりあえず新しい方で開いてみる」を安易にやらない。片道切符になり得ることを前提に判断する
  • インポートに失敗したら、まずzdb -lでラベルのfeature情報を確認し、zpool upgrade -vと突き合わせる
  • 該当featureがread-only compatibleならzpool import -o readonly=onでデータだけでも救出できる可能性がある
  • 複数ホストで同じ外付けプールを使い回す運用では、全ホストのZFSバージョンを揃えておくのが一番の予防策

special vdevやashiftの話と同じく、これも「後から気づいたときには選択肢が残っていない」タイプの罠でした。今のhomenasと予備機は、OSのアップデートスケジュールを意識して揃えるようにしています。