Linux  mdraild のHDD故障時の交換手順

## 概要

mdraid の構成HDDが壊れたら交換作業を実施しなければならない。
データ領域にかかわる作業なので手順書なしで実施するのは不安。かつ、稀にしか発生しないイベントなので手順を記憶しておくのも難しい。
都度手順を調べるのも面倒なのでここにまとめておく。

なお、同時期に買ったHDDは同時期に故障する可能性がやや高い。
1つのHDDが壊れたと思ったら数日後に2つ目が壊れた、みたいな事象も起こりうるので、普段から監視や予備資材の確保など、故障にそなえておきたい。

たとえばHDDの故障気配を検知するには smartctl が使える。

```bash
# smartctl -A /dev/sdx | grep -i error
  1 Raw_Read_Error_Rate     0x002f   172   172   051    Pre-fail  Always       -       1236
  7 Seek_Error_Rate         0x002e   200   200   000    Old_age   Always       -       0
199 UDMA_CRC_Error_Count    0x0032   200   200   000    Old_age   Always       -       0
200 Multi_Zone_Error_Rate   0x0008   180   180   000    Old_age   Offline      -       9736
```

Raw_Read_Error_Rate が 0 より大きいと故障しやすいあるいは故障した状態。

smartd が動いていれば、以下のようなログにも要注意である。

```
Dec  2 17:54:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 695 Currently unreadable (pending) sectors (changed +1)
Dec  2 22:24:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 696 Currently unreadable (pending) sectors (changed +1)
Dec  2 22:54:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 769 Currently unreadable (pending) sectors (changed +73)
Dec  3 00:54:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 770 Currently unreadable (pending) sectors (changed +1)
Dec  3 01:54:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 772 Currently unreadable (pending) sectors (changed +2)
Dec  3 02:54:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 775 Currently unreadable (pending) sectors (changed +3)
Dec  3 03:24:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 784 Currently unreadable (pending) sectors (changed +9)
Dec  3 03:54:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 810 Currently unreadable (pending) sectors (changed +26)
Dec  3 04:24:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 835 Currently unreadable (pending) sectors (changed +25)
Dec  3 04:54:37 localhost smartd[1240]: Device: /dev/sdh [SAT], 870 Currently unreadable (pending) sectors (changed +35)
Dec  3 05:24:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 887 Currently unreadable (pending) sectors (changed +17)
Dec  3 05:54:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 889 Currently unreadable (pending) sectors (changed +2)
Dec  3 06:24:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 890 Currently unreadable (pending) sectors (changed +1)
Dec  3 06:54:37 localhost smartd[1240]: Device: /dev/sdh [SAT], 896 Currently unreadable (pending) sectors (changed +6)
Dec  3 07:24:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 921 Currently unreadable (pending) sectors (changed +25)
Dec  3 07:54:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 1188 Currently unreadable (pending) sectors (changed +267)
Dec  3 08:24:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 1384 Currently unreadable (pending) sectors (changed +196)
Dec  3 08:54:37 localhost smartd[1240]: Device: /dev/sdh [SAT], 1415 Currently unreadable (pending) sectors (changed +31)
Dec  3 09:24:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 1456 Currently unreadable (pending) sectors (changed +41)
Dec  3 09:54:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 1667 Currently unreadable (pending) sectors (changed +211)
Dec  3 10:24:36 localhost smartd[1240]: Device: /dev/sdh [SAT], 1744 Currently unreadable (pending) sectors (changed +77)
Dec  3 10:54:37 localhost smartd[1240]: Device: /dev/sdh [SAT], 1974 Currently unreadable (pending) sectors (changed +230)
```
これは完全な故障の直前のログで、unreadable sector が増加していることがわかる。
（そもそも正常なHDDであれば unreadable sectors のログは出ないので、これが出ていたら上記のように激しく増加していないとしても、いつ故障してもおかしくないと思われる）

また、 /dev/mdstat も監視しておくのが良い。


## 具体的な手順


### Step1. 故障したHDDを確認

```bash

# cat /proc/mdstat
Personalities : [raid0] [raid6] [raid5] [raid4] [linear] 
[multipath] [raid1] [raid10]
md6 : active raid6 sde1[7] sdd1[6] sdf1[8] sdh1[11](F) sdg1[9] 
 sdc1[10]
      23441864704 blocks super 1.2 level 6, 512k chunk, algorithm 2 [6/5] [UUUUU_]
      bitmap: 4/11 pages [16KB], 262144KB chunk
```
`(F)` とついているのが故障したHDD. ここでは sdh が故障しているのがわかる


### Step2. シリアル番号を確認

さきほど確認したデバイス名をもとに、HDDのシリアル番号を確認する。 hdparm コマンドで確認可能。

```bash
# hdparm -i /dev/sdh|grep Serial
 Model=WDC WD60EZRZ-00GZ5B1, FwRev=80.00A80, SerialNo=WD-WX11A12BCD3E
```

故障状況によっては hdparm がエラーになってシリアルを確認できない場合がある。その場合は他のHDDのシリアルのリストを作って特定する。（筐体のHDDベイにあるHDDのなかでリストにないシリアルを持つものが故障HDDである）

### Step3. 電源断

```bash
# poweroff
```

### Step4. （物理作業）筐体を開けてHDDを交換する

* HDDにはられたシール等でシリアル番号を確認。
* Step2で確認したHDDのシリアル番号と一致するHDDを取り外す
* 新しいHDDのシリアルを確認し、代替品と入れ替える。

### Step5. Step2と同様のコマンドで、新しいHDDのシリアル番号をもとにデバイス名を特定する。

* /dev/sdh のままではないかもしれないことに注意が必要。
* 最近のLinuxではうまい具合に処理してくれるのか、変わらないことが多い。（はずして消えたHDDのデバイス名が新しいHDDに割り当てられる）
* ここでは /dev/sdh のままだったと仮定する。

### Step6. parted にてパーティションテーブルとRAID用パーティションを作成する

まず DEVICE変数をセットする。（デバイス名は必要に応じて変更する）

```bash
# export DEVICE=/dev/sdh
```

なお、以降のコマンドラインではデバイス名を `${DEVICE}` ではなく `${DEVICE:?not set}` と参照しているが、これは DEVICE が空あるいは未定義の場合に "not set" というメッセージを表示してエラーにする bash 系の変数参照の記法である。DEVICEを設定し忘れたままコピペして実行してもコマンドが実行されないためのガードとして、ここではこの記法を用いる。

``` bash
# parted ${DEVICE:?not set} print
Error: /dev/sdh: unrecognised disk label
Model: ATA WDC WD60EFAX-68J (scsi)
Disk /dev/sdh: 6001GB
Sector size (logical/physical): 512B/4096B
Partition Table: unknown
Disk Flags:
```

新品時は上のようにパーティションテーブルが存在しないことが多い（`Partition Table: unknown`の部分）

mklabel してGPTラベルをつける

```bash
# parted ${DEVICE:?not set} mklabel gpt
Information: You may need to update /etc/fstab.

# parted ${DEVICE:?not set} print
Model: ATA WDC WD60EFAX-68J (scsi)
Disk /dev/sdh: 6001GB
Sector size (logical/physical): 512B/4096B
Partition Table: gpt
Disk Flags:

Number  Start  End  Size  File system  Name  Flags
```

パーティションテーブルが作成されたが、パーティションはまだ1つもない状態。
RAID用パーティションを作成する。

```bash
# parted -a optimal ${DEVICE:?not set} mkpart primary ext4 0% 100%
Information: You may need to update /etc/fstab.

# parted ${DEVICE:?not set} print
Model: ATA WDC WD60EFAX-68J (scsi)
Disk /dev/sdh: 6001GB
Sector size (logical/physical): 512B/4096B
Partition Table: gpt
Disk Flags:

Number  Start   End     Size    File system  Name     Flags
 1      1049kB  6001GB  6001GB               primary
```

パーティションをキャパシティ全体をつかってひとつ作成した。現状では ext4 用なので、これをRAID用に切り替える。

```bash
# parted ${DEVICE:?not set} set 1 raid on
Information: You may need to update /etc/fstab.

# parted ${DEVICE:?not set} print
Model: ATA WDC WD60EFAX-68J (scsi)
Disk /dev/sdh: 6001GB
Sector size (logical/physical): 512B/4096B
Partition Table: gpt
Disk Flags:

Number  Start   End     Size    File system  Name     Flags
 1      1049kB  6001GB  6001GB               primary  raid
```

`Flags` が `raid` になっていればOK

これで `/dev/sdh1` が作成され、 mdraid に組み込む準備ができた。

### Step7. mdraid へ新しいHDDデバイスを組み込む


まず現状の確認

```
# mdadm -D /dev/md6
/dev/md6:
           Version : 1.2
     Creation Time : Sat Dec 22 02:27:41 2012
        Raid Level : raid6
        Array Size : 23441864704 (22355.90 GiB 24004.47 GB)
     Used Dev Size : 5860466176 (5588.98 GiB 6001.12 GB)
      Raid Devices : 6
     Total Devices : 5
       Persistence : Superblock is persistent

     Intent Bitmap : Internal

       Update Time : Thu Jul 15 09:24:29 2021
             State : clean, degraded
    Active Devices : 5
   Working Devices : 5
    Failed Devices : 0
     Spare Devices : 0

            Layout : left-symmetric
        Chunk Size : 512K

Consistency Policy : bitmap

              Name : localhost.local:6
              UUID : 7d2efe2c:584b4fd:7b65d1e42:2395932b
            Events : 1676565

    Number   Major   Minor   RaidDevice State
      10       8       33        0      active sync   /dev/sdc1
       6       8       49        1      active sync   /dev/sdd1
       7       8       65        2      active sync   /dev/sde1
       8       8       81        3      active sync   /dev/sdf1
       9       8       97        4      active sync   /dev/sdg1
       -       0        0        5      removed
```

HDDデバイスが1つ `removed` になっている。
この状態であれば `mdadm --manage <mdデバイス> -a <追加HDDデバイス>` すればOK
(完全には故障してないHDDを予備的にとりはずす場合等、明示的にデバイスの故障と取り外しを教えてやる必要がある場合もある。詳細は mdadm(8) を参照)

文字列 `${DEVICE:?not set}` の後ろに `1` が追加されていることに注意。これにより先ほど作成したパーティション番号1のパーティションを指定している。

```bash
# mdadm --manage /dev/md6 -a ${DEVICE:?not set}1
mdadm: added /dev/sdh1
# mdadm -D /dev/md6
/dev/md6:
           Version : 1.2
     Creation Time : Sat Dec 22 02:27:41 2012
        Raid Level : raid6
        Array Size : 23441864704 (22355.90 GiB 24004.47 GB)
     Used Dev Size : 5860466176 (5588.98 GiB 6001.12 GB)
      Raid Devices : 6
     Total Devices : 6
       Persistence : Superblock is persistent

     Intent Bitmap : Internal

       Update Time : Thu Jul 15 09:45:13 2021
             State : clean, degraded, recovering
    Active Devices : 5
   Working Devices : 6
    Failed Devices : 0
     Spare Devices : 1

            Layout : left-symmetric
        Chunk Size : 512K

Consistency Policy : bitmap

    Rebuild Status : 0% complete

              Name : fuji.local:6
              UUID : 7d2efe2c:584b4fd:7b65d1e42:2395932b
            Events : 1676568

    Number   Major   Minor   RaidDevice State
      10       8       33        0      active sync   /dev/sdc1
       6       8       49        1      active sync   /dev/sdd1
       7       8       65        2      active sync   /dev/sde1
       8       8       81        3      active sync   /dev/sdf1
       9       8       97        4      active sync   /dev/sdg1
      11       8      113        5      spare rebuilding   /dev/sdh1
```

追加直後に `mdadm -D` で確認すると早速再構築が開始されたことがわかる。

### Step8. 再構築完了まで待つ

`/dev/mdstat` を眺めると進捗状況や再構築時間の目安がわかる。
今回の場合、 10時間以上かかるようだ。（予測時間は増えることもよくあるので過信しないほうがよい）

```
# cat /proc/mdstat
Personalities : [raid0] [raid6] [raid5] [raid4] [linear] [multipath] [raid1] [raid10]
md6 : active raid6 sdh1[11] sdg1[9] sdc1[10] sdf1[8] sde1[7] sdd1[6]
      23441864704 blocks super 1.2 level 6, 512k chunk, algorithm 2 [6/5] [UUUUU_]
      [>....................]  recovery =  0.1% (10861272/5860466176) finish=622.8min speed=156518K/sec
      bitmap: 4/11 pages [16KB], 262144KB chunk

```

以上。