ぽよメモ

ファッション情報学徒の備忘録.

ZFSのashiftを変更した話

結論から言うと途中からashiftの値を変更する方法はありません。

サーバにPT3を追加し録画を始めたため、従来の2TB×3本の4TB相当RAID-Z1では容量が不安になってしまいました。そこで3TBに順次リプレースし、合計6TB相当にしようと考え、WD30EFRXを2本と3TBの外付けHDD1つを購入しました。

そこでトラップにひっかかりました。

以前挿していたHDDはHGSTのHUA722020ALA330という古いHDDで、セクタサイズが「512B(論理)/512B(物理)」と非AFTなHDDでした。

AFTとは、

AFTとは、ハードディスク製品の種類の一つで、従来より大きなサイズの物理セクタを採用したもの。セクタはディスクに記録するデータの最小単位で、長らく512バイトだったが、AFTでは8倍の4,096バイト(4KB/4KiB)のセクタを使用する。コンピュータで利用するにはBIOS/EFIやOSが対応している必要がある。 ハードディスクは使用前のフォーマット操作によってセクタ単位に細かく分割されるが、セクタとセクタの間には何も記録されない空白の領域ができるほか、セクタごとに誤り訂正符号(ECC)が付加されるため、セクタの数が多い(セクタあたりの容量が小さい)ほど、本来の記憶容量からのロスが大きくなる。近年はディスクの高記録密度化が進み、こうしたロスが無視できなくなってきたため、物理セクタ容量を4KBに拡張したAFTが登場した。http://e-words.jp/w/AFT-2.htmlより

ZFSではオプションを何も指定しなければセクタサイズに応じてashiftという値がpool作成時に決定されます。このashift値は512BなHDDを利用して作成したpoolでは通常ashift=9に設定されています。
AFTなHDDは論理セクタサイズが512Bなため、一応AFTと非AFTのHDDのが混在したpoolを作成することもできるようです。(実際僕のサーバでは内蔵2台、外付け1台でRAIDZを組んでいましたが、外付けはセクタサイズが4096でした) ただし調べた限りでは混在したpoolのパフォーマンスは大きく下がるようです。

そして何も考えずに古い方のHDDをofflineにし、新しい方を挿してreplaceしようとしたら…セクタサイズでエラーを吐かれてしまいました。何をしてもちゃんとreplaceしてくれず、pool自体のahift値を何とかする必要があるようです。

もちろんそんな設定はないので、今回限り使えるであろう手段を用います。
現在poolに保存されている容量はまだ数百GB程度であり、今度挿すHDDはは3TBだったからこそ強引にできた手段なのでおそらく大抵の人は使えないと思います…タイトル詐欺ですいません…

手段としてはとりあえず

  1. 新しく挿したWD30EFRX(以下HDD1)を3分割する
  2. HDD1の3つのパーティションを使ってashift=12なpoolを作成する
  3. 古いpoolから新しいpoolへデータを移行する
  4. 新しいpoolを構成するデバイスを順次新しいHDD(HDD2、HDD3)に差し替え
  5. HDD1の最後のパーティションをオフラインにし、パーティションを全て削除
  6. 新しいpoolを構成する最後のHDD1のパーティション(offline済)をHDD1でreplace

現状以上のHDDを追加できない状況でpoolのデータを保持したままashift値を変更するためには、「同じ構成」「古いpoolのデータを入れきれる」「セクタサイズ4096Bなデバイスで構成」な別のpoolを作成する必要があります。
新しいpoolは3TB HDDを3分割したRAID-Z1であり、容量はおよそ1.3TB程度となるはずです。これなら古いpoolのデータを許容できるはず。

まずpartedコマンドで3分割します。同じくらいのサイズであれば問題無いでしょう。

# ディスクのIDを確認
$ ls /dev/disk/by-id
$ parted [ディスクID]

この辺りはググればいくらでも情報が出てくると思います。

$ sudo zpool create -m none -o ashift=12 [新しいpool名] /dev/disk/by-id/[ディスクID]-part1 /dev/disk/by-id/[ディスクID]-part2 /dev/disk/by-id/[ディスクID]-part3
$ sudo zpool set autoexpand=on [新しいpool名]

-m noneは作成後にマウントしないオプションで -o ashift=12は見ての通りashiftを12に設定するオプションになります。構成するデバイスのセクタサイズが全て4096Bなら自動でashift=12に設定されるという記述も見かけたのですが、不安だったので手動で設定しました。 autoexpand=onは、より大きな容量のHDDにreplaceした際に自動で容量を拡張するオプションです。

$ sudo zdb | grep ashift
            ashift: 12

一応ashift値を確認しておきます。ちゃんと12になっています。

$ sudo zfs snapshot -r [古いpool名]@backup
$ sudo zfs umount -a
$ sudo zfs send -vRp [古いpool名]@backup | sudo zfs receive -uvdF [新しいpool名]

snapshotを作成し、zfs sendとzfs receiveで新しいpoolにデータを移します。 zfs sendのオプションはそれぞれ、-v: verbose、-R: 再帰実行、-p: 属性情報をコピー。 同様にzfs receiveのオプションはそれぞれ-u: 転送先をマウントしない、 -v: verbose、-d: ファイルシステムの新規作成、-F: 強制書き込み。 恐ろしく時間がかかります。531GB分の転送で31時間11分かかりました。

$ sudo zpool scrub [新しいpool名]

一応scrubしておきます。必要ない気はしますが。これは寝て起きたら終わっていました。

$ sudo zpool destroy [古いpool名]

複製が終われば古いpoolは破棄します。 シャットダウンし、古いpoolを構成していたHDDを引っこ抜いて新しいHDDを挿します。

$ sudo zpool replace [新しいpool名] [ディスクID]-part1 [新しく挿したディスクID]

リプレースして放置します。進捗はstatusを見ることで確認できます。

$ sudo zpool status
  pool: data
 state: ONLINE
status: One or more devices is currently being resilvered.  The pool will
    continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.
  scan: resilver in progress since Thu Apr  7 13:22:21 2016
    181G scanned out of 793G at 20.7M/s, 8h25m to go
    60.3G resilvered, 22.81% done
config:

    NAME                                                  STATE     READ WRITE CKSUM
    data                                                  ONLINE       0     0     0
      raidz1-0                                            ONLINE       0     0     0
        replacing-0                                       ONLINE       0     0     0
          ata-WDC_WD30EFRX-68AX9N0_WD-WMC1T0534235-part1  ONLINE       0     0     0
          ata-WDC_WD30EFRX-68EUZN0_WD-WCC4N0YZ2854        ONLINE       0     0     0  (resilvering)
         ata-WDC_WD30EFRX-68AX9N0_WD-WMC1T0534235-part2    ONLINE       0     0     0
         ata-WDC_WD30EFRX-68AX9N0_WD-WMC1T0534235-part3    ONLINE       0     0     0

一本目は8時間で終了しました。replace作業を最後の一本まで行います。

$ sudo zpool status
  pool: data
 state: ONLINE
  scan: resilvered 280G in 1h32m with 0 errors on Fri Apr  8 12:47:28 2016
config:

    NAME                                           STATE     READ WRITE CKSUM
    data                                           ONLINE       0     0     0
      raidz1-0                                     ONLINE       0     0     0
        ata-WDC_WD30EFRX-68EUZN0_WD-WCC4N0YZ2854   ONLINE       0     0     0
        usb-I-O_DATA_HDCL-UT_000277B591400351-0:0  ONLINE       0     0     0
        ata-WDC_WD30EFRX-68AX9N0_WD-WMC1T0534235   ONLINE       0     0     0

最後の一本は1時間半で終了しました(速い)

デフォルトのままであれば、この状態ではまだ新しいpoolの容量はreplace前と同じだと思います。autoexpand=onを設定し忘れてましたーっていう場合も簡単に容量を拡張できます。

$ sudo zpool online -e [新しいpool名] [ディスクID]

online -eをするデバイスはどれか一つでよく、全てのHDDに対して行う必要はありません。

以上で手順は完了です。途中何度も冗長性を失うタイミングがあり、しかもその時間が長いのでHDDが一本でも死んだ瞬間全ておじゃんです。
それでもまぁ、諦めて新しいpoolで上書きするよりはマシかなと。