Rook/Cephで利用しているHDDを交換してみたときのログ

# はじめに

オンプレミスKubernetesで運用しているRook/Cephに接続しているHDDをsmartctlで確認したところ問題がありそうだったので、予防的にHDDを交換することにしました。

後日談として、突然にHDDがクラッシュしてしまった際の対応についてまとめています。

# 環境

次のようなバージョンで稼動している合計5ノードによるk8sクラスターを運用しています。

* Rook/Ceph v1.5.12
* Kubernetes v1.21.6
* Ubuntu 20.04.5
* OSDを構成しているHDD - /dev/sda, 4TB
* Host: Fujitsu TX1320 M4 (CPU: Xeon E-2234, Memory: 64GB)

今回のターゲットにしているHDDのもう少し詳しいデータは次のようになっています。

```bash:smartctlの出力
$ sudo smartctl -a /dev/sda
smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.0-126-generic] (local build)
Copyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF INFORMATION SECTION ===
Model Family:     HGST Deskstar NAS
Device Model:     HGST HDN726040ALE614
Serial Number:    
LU WWN Device Id: 
Firmware Version: APGNW7JH
User Capacity:    4,000,787,030,016 bytes [4.00 TB]
Sector Sizes:     512 bytes logical, 4096 bytes physical
Rotation Rate:    7200 rpm
Form Factor:      3.5 inches
Device is:        In smartctl database [for details use: -P show]
ATA Version is:   ACS-2, ATA8-ACS T13/1699-D revision 4
SATA Version is:  SATA 3.1, 6.0 Gb/s (current: 6.0 Gb/s)
Local Time is:    Wed Feb 15 06:31:50 2023 UTC
SMART support is: Available - device has SMART capability.
SMART support is: Enabled
```

いまとなっては、SeagateのExosや、Western Digital の WD Gold か HGST/Ultrastar にしておかなかったことを少し後悔しています。最近構築したノードのHDDはWD GoldやUltrastarを導入しています。

予算の都合があったとはいえ、それなりの負荷をかける用途ではどのメーカーであってもフラッグシップモデルにしておくべきでした。

# 概要

smartctlでshortテストをしてみたところ少し問題があったので、Rook/CephのOSDから外して smartctl で再度テストを実施していきます。

```bash:smartctlコマンドの出力
$ sudo smartctl -l selftest /dev/sda
smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.0-126-generic] (local build)
Copyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF READ SMART DATA SECTION ===

SMART Self-test log structure revision number 1
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Short offline       Completed: servo/seek failure 70%     20319         0
# 2  Short offline       Completed: servo/seek failure 60%     20318         0
# 3  Short offline       Completed without error       00%      2166         -
```

#2の結果がRook/Cephのクラスターに含まれている時の最初のエラーで、#1がOSDから外して負荷を与えないようにして実施した最新の結果になっています。

"Remaining"の意味は残りのテストの内容になっているので、30〜40%程度のテストが進行した時点でエラーが発生していることが分かります。

"-H"オプションによる出力では、テスト結果はPASSEDになっています。

```bash:
$ sudo smartctl -H /dev/sda
smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.0-126-generic] (local build)
Copyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF READ SMART DATA SECTION ===
SMART overall-health self-assessment test result: PASSED
```

PASSEDの判定基準は、"failing NOW"を含む結果がなければ全部PASSEDになるようなので、即時に対応が必要でなければ全てPASSED扱いになるようです。

とはいえ、servo/seekのエラーは致命的にもみえるので、Rook/CephのOSDから削除して、負荷を与えない状態にしてからlongテストを実施してみます。これには30分ほどの時間が予告されていましたが、実際にはすぐにエラーになって停止しています。


```bash:
$ sudo smartctl -l selftest /dev/sda
smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.0-126-generic] (local build)
Copyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF READ SMART DATA SECTION ===
SMART Self-test log structure revision number 1
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Extended offline    Completed: servo/seek failure 90%     20319         0
# 2  Short offline       Completed: servo/seek failure 70%     20319         0
# 3  Short offline       Completed: servo/seek failure 60%     20318         0
# 4  Short offline       Completed without error       00%      2166         -
```

この状態で交換する必要があるかは微妙です。Raw_Read_Error_Rate値やSeek_Error_Rate値などの各値に問題は確認できません。今回は様子をみるところかもしれませんが、作業手順を確保したい事と、ちょうどメモリ交換のためにクラスターを停止させる予定だったため、このタイミングで確保しておいた交換用のHGST Ultratarと交換することにします。

取り出したHDDは別のバックアップ取得用のNASに移して様子をみる予定です。

```bash:Seek_Error_Rateの値
SMART Attributes Data Structure revision number: 16                                     
Vendor Specific SMART Attributes with Thresholds:                                       
ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE

  1 Raw_Read_Error_Rate     0x000b   100   100   016    Pre-fail  Always       -       0
  2 Throughput_Performance  0x0005   124   124   054    Pre-fail  Offline      -       162
  3 Spin_Up_Time            0x0007   100   100   024    Pre-fail  Always       -       0
  4 Start_Stop_Count        0x0012   100   100   000    Old_age   Always       -       192
  5 Reallocated_Sector_Ct   0x0033   100   100   005    Pre-fail  Always       -       0
  7 Seek_Error_Rate         0x000b   100   100   067    Pre-fail  Always       -       0
  8 Seek_Time_Performance   0x0005   044   044   020    Pre-fail  Offline      -       39      
  9 Power_On_Hours          0x0012   098   098   000    Old_age   Always       -       20321
```

# Rook/Ceph側の作業概要

Rook/Cephでディスクを交換する際には、下記の手順に従って実施しています。

* https://rook.io/docs/rook/v1.10/Storage-Configuration/Advanced/ceph-osd-mgmt/

実際に稼動しているシステムは、v1.5.12なので osd-purge.yaml のファイルは、rook/cluster/examples/kubernetes/ceph/osd-purge.yaml からコピーして変更後、実施しています。


```diff:osd-purge.yamlの変更点
--- rook/cluster/examples/kubernetes/ceph/osd-purge.yaml        2022-08-25 07:11:21.591632585 +0000
+++ osd-purge.yaml      2023-02-15 05:45:52.752190600 +0000
@@ -28,7 +28,7 @@
           image: rook/ceph:v1.5.12
           # TODO: Insert the OSD ID in the last parameter that is to be removed
           # The OSD IDs are a comma-separated list. For example: "0" or "0,2".
-          args: ["ceph", "osd", "remove", "--osd-ids", "<OSD-IDs>"]
+          args: ["ceph", "osd", "remove", "--osd-ids", "1"]
           env:
             - name: POD_NAMESPACE
               valueFrom:
```

この状態では、しばらくrebalancingのためにステータスが HEALTH_WARN になるので、完了するまで暫く待ちます。

```text:
  cluster:
    id:     51464e80-fbf5-44cd-b033-a2e1374526cf
    health: HEALTH_WARN
            Degraded data redundancy: 3405010/19885929 objects degraded (17.123%), 34 pgs degraded, 34 pgs und
ersized
 
  services:
    mon: 3 daemons, quorum k,l,m (age 7w)
    mgr: a(active, since 7w)
    mds: myfs:1 {0=myfs-a=up:active} 1 up:standby-replay
    osd: 4 osds: 4 up (since 97m), 4 in (since 91m); 34 remapped pgs
 
  task status:
 
  data:
    pools:   4 pools, 97 pgs
    objects: 6.63M objects, 164 GiB
    usage:   1.4 TiB used, 13 TiB / 15 TiB avail
    pgs:     3405010/19885929 objects degraded (17.123%)
             203008/19885929 objects misplaced (1.021%)
             63 active+clean
             31 active+undersized+degraded+remapped+backfill_wait
             3  active+undersized+degraded+remapped+backfilling
 
  io:
    client:   1.2 KiB/s rd, 26 KiB/s wr, 2 op/s rd, 2 op/s wr
    recovery: 27 KiB/s, 24 objects/s
```

これが完了するまでは、operatorは起動しないままになっています。

```bash:
$ sudo kubectl -n rook-ceph get deploy -l operator=rook
NAME                 READY   UP-TO-DATE   AVAILABLE   AGE
rook-ceph-operator   0/0     0            0           2y21d
```

## 今後予定している作業

これから HEALTH_OK になってから、Diskを交換し、その後で、operatorを起動することで交換した新しいデイスクを認識させることになります。

その部分は後から追記します。
いまのところ、3%程度のreblancingのために12時間前後を必要としていて、2日以上の時間が経過しています。

# HDDの交換

全体の rebalancing が完了するのに、約3日(72時間)かかりましたが、HEALTH_OK を確認してから、全部のノードを落とし、メモリを増やつつ、問題のあった osd.1 のHDDを交換してから、全部のノードを起動します。

再び HEALTH_OK になるまで数分かかりましたが、この状態になってから operator の pod を起動します。

```bash:
$ kubectl -n rook-ceph scale deployment rook-ceph-operator --replicas=1
```

しばらくすると、交換したHDDが認識され、再び rebalancing が始まりました。

```bash:
  cluster:
    id:     51464e80-fbf5-44cd-b033-a2e1374526cf
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum k,l,m (age 6m)
    mgr: a(active, since 6m)
    mds: myfs:1 {0=myfs-b=up:active} 1 up:standby-replay
    osd: 5 osds: 5 up (since 19s), 5 in (since 19s); 54 remapped pgs
 
  data:
    pools:   4 pools, 97 pgs
    objects: 6.67M objects, 164 GiB
    usage:   1.7 TiB used, 17 TiB / 18 TiB avail
    pgs:     3748057/19997190 objects misplaced (18.743%)
             53 active+remapped+backfill_wait
             43 active+clean
             1  active+remapped+backfilling
 
  io:
    client:   8.2 KiB/s rd, 1.3 MiB/s wr, 2 op/s rd, 135 op/s wr
    recovery: 0 B/s, 300 keys/s, 6 objects/s
 
  progress:
    Rebalancing after osd.1 marked in (16s)
      [............................] 
```

今回交換したHDDは別のNASの交換用にストックしておく予定です。

## 交換したHDDへの追加テスト

別のシステムに接続してもう少し詳しくテストしてみます。

```bash:
$ sudo smartctl -t long /dev/sdb                                                     
smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.0-139-generic] (local build)                             
Copyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org                         
                                                                                                         
=== START OF OFFLINE IMMEDIATE AND SELF-TEST SECTION ===                                            
Sending command: "Execute SMART Extended self-test routine immediately in off-line mode".           
Drive command "Execute SMART Extended self-test routine immediately in off-line mode" successful.   
Testing has begun.                                                                                            
Please wait 571 minutes for test to complete.                                                       
Test will complete after Tue Feb 21 11:35:36 2023 UTC                                               
Use smartctl -X to abort test.
```

10時間(600 minutes)ほど経過した段階で確認したextended (long)テストの結果は次のようになりました。

```bash:
$ sudo smartctl -l selftest /dev/sdb
smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.0-139-generic] (local build)
Copyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF READ SMART DATA SECTION ===
SMART Self-test log structure revision number 1
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Extended offline    Completed without error       00%     20448         -
# 2  Extended offline    Completed: servo/seek failure 90%     20319         0
# 3  Short offline       Completed: servo/seek failure 70%     20319         0
# 4  Short offline       Completed: servo/seek failure 60%     20318         0
# 5  Short offline       Completed without error       00%      2166         -
3 of 3 failed self-tests are outdated by newer successful extended offline self-test # 1
```

検証用のシステムは次のような構成です。

* 本体: Fujitsu製 PRIMERGY MX130 S2
* CPU: AMD FX(tm)-6300 Six-Core Processor
* Memory: DDR3-677 22GB
* Primary Storage (/dev/sda): Intel SSD 535 120GB
* OS: Ubuntu 20.04.5 (LTS) 64bit版

このシステムに/dev/sdbとして接続した上で、smartctlコマンドからlongテストを実施しています。

# 今回の対応は適当だったのか？

一般的に servo/seek failure にはどのように対応するべきなのでしょうか。
最終的なSMARTの結果をみる限り、交換する必要はなさそうでした。

## このシステムでの対応として

今回はCeph/Rookの障害対応の練習としてHDDを交換しました。

手元に同容量のHGSTのUltrastarがあったことと、メモリを増量するためにメンテナンス・ウィンドウを予定していたことから追加でHDD交換の作業実施を決定しました。

HDDが一つ壊れたところでサービスは継続できるので、このシステムについては交換する必要性はほぼなかったといえます。

## 一般的な対応として

SMARTのテスト中に "servo/seek failure" が発生してもテスト自体は終っているので、これだけで交換する必要はないと思います。少なくともRMAには該当しないでしょう。

商用OSが出力するSMARTの結果にはサーボの不調によって予防的な交換を勧めるものもあるので、あまり頻度が上がれば心配ではあるけれど、まずは Seek_Error_RateやSeek_Time_Performanceなどの値が正常であるかどうかを確認するべきだと思われます。

今回のケースでは、負荷がほとんどない検証用のシステムに接続してみると問題は再現しなかったということです。

予防的に交換する判断もあると思いますが、現段階では交換の必要性はほぼないと思いました。

# 後日談 - HDDが突然クラッシュした時の対応ログ

ここでは rook/ceph v1.9.13 を利用しています。

前回は計画的にOSDを停止しましたが、突然にHDDがクラッシュしたので、その際の対応についてログを残しておきます。

基本的には計画交換の際と同様に対応しています。

## 発見の経緯と障害発生ノードの状況

Kubernetesのクラスターを全てv1.27.5にアップグレードしたタイミングで、kube-prometheusを動作させています。

* [kube-prometheusをjsonnet-builderでカスタマイズした時の対応メモ](https://qiita.com/YasuhiroABE/items/dca43cff82d7991658fa)


ここからのメール通知でHDDがダウンしたタイミングで状況を把握していました。

対象のノードにアクセスした時のdmesgは次のような状況でした。

```text:dmesg|grep sdaの出力結果
$ sudo dmesg | grep sda
[    3.843102] sd 0:0:0:0: [sda] 7814037168 512-byte logical blocks: (4.00 TB/3.64 TiB)
[    3.843103] sd 0:0:0:0: [sda] 4096-byte physical blocks
[    3.843107] sd 0:0:0:0: [sda] Write Protect is off
[    3.843108] sd 0:0:0:0: [sda] Mode Sense: 00 3a 00 00
[    3.843114] sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[    3.962646] sd 0:0:0:0: [sda] Attached SCSI disk
[3712876.369100] blk_update_request: I/O error, dev sda, sector 2476925184 op 0x1:(WRITE) flags 0x8800 phys_seg 6 prio class 0
[3712876.372943] blk_update_request: I/O error, dev sda, sector 2563816832 op 0x1:(WRITE) flags 0x8800 phys_seg 16 prio class 0
[3712876.375629] blk_update_request: I/O error, dev sda, sector 2704265728 op 0x1:(WRITE) flags 0x8800 phys_seg 7 prio class 0
[3712876.378941] blk_update_request: I/O error, dev sda, sector 2758165248 op 0x1:(WRITE) flags 0x8800 phys_seg 16 prio class 0
[3712876.381839] blk_update_request: I/O error, dev sda, sector 2761019040 op 0x1:(WRITE) flags 0x8800 phys_seg 12 prio class 0
[3712876.383981] blk_update_request: I/O error, dev sda, sector 2765957968 op 0x1:(WRITE) flags 0x8800 phys_seg 5 prio class 0
[3712876.386381] blk_update_request: I/O error, dev sda, sector 2767521328 op 0x1:(WRITE) flags 0x8800 phys_seg 10 prio class 0
[3712876.388996] blk_update_request: I/O error, dev sda, sector 2782126720 op 0x1:(WRITE) flags 0x8800 phys_seg 16 prio class 0
[3712876.390800] blk_update_request: I/O error, dev sda, sector 2861393664 op 0x1:(WRITE) flags 0x8800 phys_seg 16 prio class 0
[3712876.392684] blk_update_request: I/O error, dev sda, sector 1969594448 op 0x1:(WRITE) flags 0x8800 phys_seg 6 prio class 0
[3712881.733781] sd 0:0:0:0: [sda] tag#10 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
[3712881.733787] sd 0:0:0:0: [sda] tag#10 Sense Key : Illegal Request [current] 
[3712881.733793] sd 0:0:0:0: [sda] tag#10 Add. Sense: Unaligned write command
[3712881.733799] sd 0:0:0:0: [sda] tag#10 CDB: Synchronize Cache(10) 35 00 00 00 00 00 00 00 00 00
[3712881.733814] blk_update_request: I/O error, dev sda, sector 0 op 0x1:(WRITE) flags 0x800 phys_seg 0 prio class 0
[3712881.751068] sd 0:0:0:0: [sda] Synchronizing SCSI cache
[3712881.751123] sd 0:0:0:0: [sda] Synchronize Cache(10) failed: Result: hostbyte=DID_BAD_TARGET driverbyte=DRIVER_OK
[3712881.751127] sd 0:0:0:0: [sda] Stopping disk
[3712881.751141] sd 0:0:0:0: [sda] Start/Stop Unit failed: Result: hostbyte=DID_BAD_TARGET driverbyte=DRIVER_OK
```

この時点でOSから/dev/sdaは認識できない状況になっていました。

## kubectlとcephコマンドから把握できた状況

kubectl上で他のPodに影響がないか確認しておきます。

```bash:
$ sudo kubectl get --all-namespaces pod | grep -v Running | grep -v Completed
NAMESPACE                NAME                                                 READY   STATUS             RESTARTS         AGE
rook-ceph                rook-ceph-osd-2-7db985d8b9-s24p2                     0/1     CrashLoopBackOff   29 (4m33s ago)   43d
```

HDDがクラッシュしただけで、OSDが1つ停止している他には影響はない状態です。

Rook/CephからはOSDがクラッシュしていたので ``ceph status`` の出力結果は次のようになっています。

```text:ceph statusの出力結果
  cluster:
    id:     51464e80-fbf5-44cd-b033-a2e1374526cf
    health: HEALTH_WARN
            Degraded data redundancy: 3381503/22240935 objects degraded (15.204%), 35 pgs degraded, 35 pgs undersized
            1 daemons have recently crashed
 
  services:
    mon: 3 daemons, quorum l,p,q (age 4w)
    mgr: a(active, since 6w)
    mds: 1/1 daemons up, 1 hot standby
    osd: 5 osds: 4 up (since 2h), 4 in (since 111m); 35 remapped pgs
 
  data:
    volumes: 1/1 healthy
    pools:   4 pools, 97 pgs
    objects: 7.41M objects, 177 GiB
    usage:   1.5 TiB used, 13 TiB / 15 TiB avail
    pgs:     3381503/22240935 objects degraded (15.204%)
             62 active+clean
             33 active+undersized+degraded+remapped+backfill_wait
             2  active+undersized+degraded+remapped+backfilling
 
  io:
    client:   852 B/s rd, 748 KiB/s wr, 1 op/s rd, 58 op/s wr
    recovery: 14 KiB/s, 11 objects/s
```

また ``ceph osd status`` や ``ceph health detail`` も確認しておきます。

```text:ceph osd statusの出力結果
ID  HOST       USED  AVAIL  WR OPS  WR DATA  RD OPS  RD DATA  STATE           
 0  node1   386G  3339G     31      343k      1        0   exists,up       
 1  node3   335G  3390G      8     74.3k      2       89   exists,up       
 2  node2     0      0       0        0       0        0   autoout,exists  
 3  node4   388G  3337G     16      190k      0        0   exists,up       
 4  node5   442G  3283G     26      284k      1       15   exists,up
```

```text:ceph health detailの出力
HEALTH_WARN Degraded data redundancy: 3368048/22240995 objects degraded (15.143%), 35 pgs degraded, 35 pgs undersized; 1 daemons have recently crashed
[WRN] PG_DEGRADED: Degraded data redundancy: 3368048/22240995 objects degraded (15.143%), 35 pgs degraded, 35 pgs undersized
    pg 1.0 is stuck undersized for 2h, current state active+undersized+degraded+remapped+backfill_wait, last acting [3,1]
    pg 2.0 is stuck undersized for 2h, current state active+undersized+degraded+remapped+backfill_wait, last acting [1,0]
...
[WRN] RECENT_CRASH: 1 daemons have recently crashed
    osd.2 crashed on host rook-ceph-osd-2-7db985d8b9-s26p2 at 2023-10-27T00:14:59.555441Z
```


## 復旧までの一時対応について

自動的にRook/CephではPGのリカバリが進行しています。

最終的にはOSD.2を削除し、ノードを停止した上でHDDを交換し、再度Rook/Cephのクラスターに加えていきますが、まずは``ceph status``でbackfilling処理が完了するまで待つことにしました。

そして概ね、交換用のHDDが到着してから次のような復旧手順を考えました。

1. OSD.2の削除 (osd-purge.yamlの実行)、crash reportの削除
2. Operatorの停止 (HDDの自動認識やOSDの自動復旧を停止)
3. node2のcordon/drainの実行とノードの停止
4. 同容量HDDとの交換と、ノードの起動
5. uncordonの実行
6. Operatorの起動
7. 稼動確認

とりあえず手持ちの4TBのHDDは中古なので、新品のWD GOLD/Ultrastarを発注していて到着してから作業を進めます。

試しにbackfilling中にosd.2の削除を試してみます。

```bash:
## もし以前に job.batch/rook-ceph-purge-osd を実行していればこれを先に停止する
# $ sudo kubectl -n rook-ceph delete job.batch/rook-ceph-purge-osd 

## ./deploy/examples/osd-purge.yaml を編集し、<OSD-IDs>を2に変更してからapplyすること
$ sudo kubectl apply -f ./deploy/examples/osd-purge.yaml
```

osd-purge.yamlは起動状態のOSDは削除できないことになっていてbackfillingを待たないはずですが、正常状態からのOSD削除とは違うようです。現状では次のようなログを出力してOSDの削除ができません。

```text:
2023-10-27 03:10:36.599304 D | exec: Running command: ceph osd safe-to-destroy 2 --connect-timeout=15 --cluster=rook-ceph --conf=/var/lib/rook/rook-ceph/rook-ceph.config --name=client.admin --keyring=/var/lib/rook/rook-ceph/client.admin.keyring --format json
2023-10-27 03:10:36.869041 W | cephosd: osd.2 is NOT be ok to destroy, retrying in 1m until success
```

このため、一度 osd-purge.yaml の実行は諦めて、backfillingが終るまで待つことにします。

```bash:
$ sudo kubectl -n rook-ceph delete job.batch/rook-ceph-purge-osd 
```

## 他の健全なノードのOSD(HDD,/dev/sda)の状況チェック

backfillingが完了するまで、smartctlを使って他のノードの様子を確認しておきます。

```bash:
$ ansible all -m command -b -a "smartctl -t short /dev/sda"
## wait more than 2 mins
$ ansible all -m command -b -a "smartctl -l selftest /dev/sda" | grep offline
# 1  Short offline       Completed without error       00%     21719         -
# 1  Short offline       Completed without error       00%     26350         -
# 1  Short offline       Completed without error       00%      5913         -
# 1  Short offline       Completed without error       00%     21719         -
```

とりあえず他のノードのHDDには以上はなさそうです。
1台だけ5913時間とLifeTime hoursが短かい出力は、この記事の先頭でUltrastarに交換したサーバーです。

## 作業1. osd.2の削除

72時間程度経過してからceph statusの出力を確認します。

```text:ceph statusの出力
  cluster:
    id:     51464e80-fbf5-44cd-b033-a2e1374526cf
    health: HEALTH_WARN
            1 daemons have recently crashed

  services:
    mon: 3 daemons, quorum l,p,q (age 5w)
    mgr: a(active, since 6w)
    mds: 1/1 daemons up, 1 hot standby
    osd: 5 osds: 4 up (since 2d), 4 in (since 2d)

  data:
    volumes: 1/1 healthy
    pools:   4 pools, 97 pgs
    objects: 7.43M objects, 177 GiB
    usage:   1.8 TiB used, 13 TiB / 15 TiB avail
    pgs:     96 active+clean
             1  active+clean+scrubbing+deep

  io:
    client:   8.5 KiB/s rd, 458 KiB/s wr, 3 op/s rd, 9 op/s wr
```

この状態でosd-purge.yamlを実行します。

```bash:
## 編集済みのosd-purge.yamlを改めて実行
$ sudo kubectl apply -f ./deploy/examples/osd-purge.yaml
```

この時のログは次のようになりました。

```text:
2023-10-29 21:28:19.896058 I | rookcmd: starting Rook v1.9.13 with arguments '/usr/local/bin/rook ceph osd remove --preserve-pvc false --force-osd-removal false --osd-ids 2'
2023-10-29 21:28:19.896124 I | rookcmd: flag values: --force-osd-removal=false, --help=false, --log-level=DEBUG, --operator-image=, --osd-ids=2, --preserve-pvc=false, --service-account= 
...
2023-10-29 21:28:24.828033 I | cephosd: no ceph crash to silence
2023-10-29 21:28:24.828046 I | cephosd: completed removal of OSD 2
```

この処理はすぐに完了し、ceph statusの出力は次のようになっています。

```text:OSD削除後のceph statusの出力
  cluster:
    id:     51464e80-fbf5-44cd-b033-a2e1374526cf
    health: HEALTH_WARN
            1 daemons have recently crashed

  services:
    mon: 3 daemons, quorum l,p,q (age 5w)
    mgr: a(active, since 6w)
    mds: 1/1 daemons up, 1 hot standby
    osd: 4 osds: 4 up (since 2d), 4 in (since 2d); 18 remapped pgs

  data:
    volumes: 1/1 healthy
    pools:   4 pools, 97 pgs
    objects: 7.43M objects, 177 GiB
    usage:   1.8 TiB used, 13 TiB / 15 TiB avail
    pgs:     932805/22290660 objects misplaced (4.185%)
             78 active+clean
             15 active+remapped+backfill_wait
             3  active+remapped+backfilling
             1  active+clean+scrubbing+deep

  io:
    client:   852 B/s rd, 170 KiB/s wr, 1 op/s rd, 12 op/s wr
    recovery: 18 MiB/s, 182 keys/s, 14 objects/s
```

crash reportが残っているので、これは削除しておきます。

```bash:ceph crash reportの削除
$ sudo kubectl -n rook-ceph exec -it $(sudo kubectl -n rook-ceph get pod -l app=rook-ceph-tools -o jsonpath='{.items[*].metadata.name}') -- bash

## 以下はrook-ceph-tools内で実行
# ceph crash ls
ID                                                                ENTITY  NEW  
2023-10-27T00:14:59.555441Z_64635e62-9248-46e4-aa4b-005b421809a1  osd.2    *  

# ceph crash info 2023-10-27T00:14:59.555441Z_64635e62-9248-46e4-aa4b-005b421809a1

# ceph crash archive-all
```

これを実行するとHEALTH_OKになりました。

```bash:ceph statusの実行
    51464e80-fbf5-44cd-b033-a2e1374526cf
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum l,p,q (age 5w)
    mgr: a(active, since 6w)
    mds: 1/1 daemons up, 1 hot standby
    osd: 4 osds: 4 up (since 3d), 4 in (since 3d); 6 remapped pgs
 
  data:
    volumes: 1/1 healthy
    pools:   4 pools, 97 pgs
    objects: 7.43M objects, 178 GiB
    usage:   1.8 TiB used, 13 TiB / 15 TiB avail
    pgs:     794646/22290831 objects misplaced (3.565%)
             91 active+clean
             4  active+remapped+backfill_wait
             2  active+remapped+backfilling
 
  io:
    client:   997 B/s rd, 939 KiB/s wr, 1 op/s rd, 70 op/s wr
    recovery: 18 KiB/s, 13 objects/s
```

## 2. Operatorの停止

以前と同様の手順でOperator Podを停止します。

```bash:
$ sudo kubectl -n rook-ceph scale deployment rook-ceph-operator --replicas=0
```

## 3. ノードの停止

これも特に変更はありません。cordon, drainを実行して十分に時間が経過してからshutdownをします。

```bash:
$ sudo kubectl cordon node2
$ sudo kubectl drain node2 --ignore-daemonsets --delete-emptydir-data
```

しばらく様子を観察してから、ノードを停止します。

```bash:
$ ssh node2
$ sudo shutdown -h now
```

## 4. HDDの交換とノードの起動

電源を停止している間にHDDを交換し、電源を投入します。

しばらく待ち、ノードの状態を確認します。

```bash:
$ sudo kubectl get node
NAME    STATUS                     ROLES           AGE      VERSION
node1   Ready                      control-plane   2y288d   v1.25.6
node2   Ready,SchedulingDisabled   control-plane   2y288d   v1.25.6
node3   Ready                      <none>          2y288d   v1.25.6
node4   Ready                      <none>          2y197d   v1.25.6
node5   Ready                      <none>          2y197d   v1.25.6
```

また新しいHDD(/dev/sda)が認識されていることも確認します。

```bash:
$ lsblk -f
NAME   FSTYPE   LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINT
loop0  squashfs                                                  0   100% /snap/core18/2796
loop1  squashfs                                                  0   100% /snap/core20/1974
loop2  squashfs                                                  0   100% /snap/lxd/24061
loop3  squashfs                                                  0   100% /snap/lxd/23991
loop4  squashfs                                                  0   100% /snap/snapd/20290
loop5  squashfs                                                  0   100% /snap/core18/2790
loop6  squashfs                                                  0   100% /snap/core20/2015
loop7  squashfs                                                  0   100% /snap/snapd/20092
sda                                                                       
sdb                                                                       
├─sdb1 vfat           9E99-F4DC                             504.9M     1% /boot/efi
└─sdb2 ext4           20ee9608-b523-49f3-b0fd-a040aa1d1d1b  351.9G    18% /
sr0                             
```

## 5. uncordonの実行

STATUSがReadyである事を確認し、uncordonして復帰させます。

```bash:
$ sudo kubectl uncordon node2
```

## 6. Operatorの起動

続いてOperatorを起動します。

```bash:
$ sudo kubectl -n rook-ceph scale deployment rook-ceph-operator --replicas=1
```

## 7. 稼動確認

しばらく待ち、operatorが``job.batch/rook-ceph-osd-prepare-node2``を完了させるまで待ちます。

その後、``ceph status``でOSDの状況を確認します。

```text:toolbox Podで実行するceph statusの出力
  cluster:
    id:     51464e80-fbf5-44cd-b033-a2e1374526cf
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum l,p,q (age 3m)
    mgr: a(active, since 7w)
    mds: 1/1 daemons up, 1 hot standby
    osd: 5 osds: 5 up (since 93s), 5 in (since 108s); 55 remapped pgs
 
  data:
    volumes: 1/1 healthy
    pools:   4 pools, 97 pgs
    objects: 7.46M objects, 180 GiB
    usage:   1.8 TiB used, 16 TiB / 18 TiB avail
    pgs:     3057930/22384596 objects misplaced (13.661%)
             54 active+remapped+backfill_wait
             42 active+clean
             1  active+remapped+backfilling
 
  io:
    client:   1.2 KiB/s rd, 43 KiB/s wr, 2 op/s rd, 4 op/s wr
    recovery: 0 B/s, 1.02k keys/s, 10 objects/s
```

OSD数が4から5に増加し、PGsの再配置が開始されました。
ここまでで障害発生時からの一連の作業は完了したことになります。

故障したHDDはHGSTのDeskstar NAS 4TB (May-2018製造)でした。
Deskstar NASは他のNASやKubernetesクラスターでもRook/Cephのbluestoreとして利用しています。

これまでのところ異常が発生したのはこの一台でしたし、SMARTの結果も問題ありません。
保証期間も過ぎていていてRMAもできませんが、よく動いてくれたと思います。

交換したHDDはUltrastar HC310になっています。

## 突発的に発生したHDD故障の対応を終えて

計画的にHEALTH_OKの状態で実施したOSDの削除が、突発的な事象の場合にはすぐに実施できませんでした。

osd-purge.yamlの**--force-osd-removal**オプションを有効にすれば可能だったと思いますが、予備のHDDがなかったことから取り寄せの時間的な余裕もあってこのオプションは使用しませんでした。

ただ、その他の手順は概ね計画交換時と同様だったことと、backfillingが終わってHEALTH_OKとなるまでの時間がおよそ72時間後だということが予測できたことは良かったと思います。

本番環境でRook/Cephを動作させるのであれば、一度はOSDを削除して交換作業を試してみるのも良いでしょう。

以上