事象発生日:2026-10-25
記事公開日:2026-10-27
アクセス数:11
10 年ほど使ってきた自宅サーバーをシステム用 HDD の交換に合わせてクリーンインストールしました.
そのついでに自宅サーバー 2 台分の HDD の SMART を確認したところ,長年ほぼ不具合なしで動いていることに感心しつつも,気になる点や読めないセクタが見つかったりしました.
それらの設定見直しと,読めないデータへの対処の備忘録です.
最近,セキュリティ事情が厳しくなっていることなどもあり,集中的に自宅サーバー群のメンテナンスをしています.
自宅サーバーのうち,最も古いマシン(サーバー A とする)は,2016 年 5 月に構築したもので,それから 10 年以上ほぼ絶え間なく動き続けています.
このサーバーなのですが,10 年以上様々な用途で使ってきており(実はこのブログもこのサーバー上で動いている),長年の試行錯誤や実験の残骸が残ってしまっていることもあるので,きれいさっぱりクリーンインストールさせることにしました.
今回更新するサーバーには 3 台の HDD が搭載されています.
2 台はサーバー構築時から,もう 1 台は 2018 年 1 月に追加したものです.
また,隣で動いている 2020 年 4 月に構築した別サーバー(サーバー B とする)にも,3 台の HDD が搭載されており,今回はこれも合わせて計 6 台の HDD の状況を確認しました.
以下はメンテナンス前の HDD の記録です.
| HDD | 通電時間 | ヘッド退避回数 | 不良セクタ等 | 用途 |
| TOSHIBA DT01ACA200 (2 TB) | 85,297 時間 | 284 回 | 0 / 0 / 0 | A: システム |
| Seagate ST2000DM001 (2 TB) | 約 85,000 時間 ※ | 361 回 | 0 / 0 / 0 | A: 録画・共有 |
| WD WD40EZRZ (4 TB) | 75,801 時間 | 56,774 回 | 0 / 0 / 0 | A: ファイル共有 |
| WD WD20EZRZ (2 TB) | 57,178 時間 | 5,200,245 回 | 0 / 5 / 4 | B: システム |
| WD WD60EZAZ (6 TB) | 57,159 時間 | 51,877 回 | 0 / 0 / 0 | B: 録画・共有 |
| Seagate ST6000DM003 (6 TB) | 57,222 時間 | 7,802 回 | 0 / 0 / 0 | B: ファイル共有 |
なお,表の備考は,
「不良セクタ等」は,代替処理済み / 保留中 / オフライン訂正不能の値です.A・B は 2 台のサーバーを表します. | |
ST2000DM001 の通電時間(※)は,表示に不整合があるため,Head_Flying_Hours の 85,304 時間を参考にした推定です. |
です.
今回取り外した旧システム用の DT01ACA200 は,10 月 22 日時点で 85,297 時間でした.約 9.7 年分です.
メンテナンスや引っ越しなどで電源を落としていた時間もあるため,10 年には少し届いていないですね.
WD40EZRZ も約 8.7 年分,サーバー B の 3 台もそれぞれ約 6.5 年分と,どれもだいぶ長いこと動いています.
個人的にすごいなぁ(というか,頑張ったなぁ)と思うのは,これが温度も湿度も電源も何も管理していない自宅での運用だったことです.
人間が家にいれば空調が効いていますが,コロナ禍を除きほぼリモートをしていないことや,ここ数年は仕事の都合で 3 週間連続で出張しているようなこともしばしばありました.
空調の効いていない夏場などは,なかなかの劣悪な環境になっていたと思います.
なお,SMART の全体判定は 6 台とも PASSED でした.
ただし,細かく見ると気になる項目はあります.
ST2000DM001 には過去の ATA エラーが 2 件記録されていました.
どちらもログ上の通電時間が 43,186 時間のときのもので,同じセクタ位置を示しています.
一方は UNC(訂正不能な読み取りエラー),もう一方は WP(書き込み保護を示す表示)でしたが,当時の詳しい原因までは OS 側のログが残っておらず分かりません.
さらにサーバー B のシステム HDD である WD20EZRZ には,
| ヘッド退避回数が約 520 万回と,定格の 17 倍を超えていた | |
| 代替処理の保留中として記録されたセクタが 5 つあった |
という見過ごせない記録がありました.
次から,この 2 つの問題の対処記録を残します.
まず気になったのが,サーバー B のシステム用 HDD の WD20EZRZ のヘッド退避回数です.
SMART の Load_Cycle_Count が約 520 万回になっていました.
HDD のメーカー仕様に記載されたロード/アンロード回数は 30 万回なので,その約 17 倍で,さすがに多すぎです.
この HDD には,一定時間アクセスがないとヘッドを退避させる idle3 タイマーという設定があり,今回の HDD ではこれがデフォルト値の 8 秒になっていました.
システムログなど短い間隔で少しずつアクセスが発生するような使い方だと,ヘッドを退避させては戻す,という動作を繰り返してしまいます.
実際,設定変更前に観測したところ,28 分間で 39 回,1 時間あたり約 84 回のペースで増えていました.
もともとは省電力のための設定のようでした.
デスクトップ向け HDD をサーバーのシステム用に使うことの弊害をきちんと認識できていなかったのが反省ポイントです.
今回は,idle3-tools の idle3ctl を使い,idle3 タイマーを 300 秒に変更しました.
サーバー A に今回新しく取り付けた交換後のシステム用 HDD も同じ WD20EZRZ で,こちらも短時間に退避回数が増えていたため,同様に変更しました.
設定変更後,10 月 27 日に再確認した結果は次のとおりでした.
| HDD | 電源を入れ直した直後 | 約 58 時間後 | 増加回数 |
| A: 交換後のシステム用 | 1,516 回 | 1,516 回 | 0 回 |
| B: システム用 | 5,200,352 回 | 5,200,403 回 | 51 回 |
サーバー B は,この間の平均では 1 時間あたり約 0.9 回となり,設定変更前に観測した約 84 回と比べると,かなり落ち着きました.
今後,通常運用に戻してしばらく様子を見ることにします.
なお,データ用の WD 4 TB と 6 TB は,もともと退避回数の増加が 1 時間に 1 回未満だったため,設定を変えていません.
もう 1 つの問題は,同じ WD20EZRZ に代替処理保留中のセクタが 5 つあったことです.
こちらは実際に読めない箇所があったので,場所を調べて対処しました.
最初に確認した値は,次のとおりでした.
Reallocated_Sector_Ct 0 : 代替処理済み Current_Pending_Sector 5 : 保留中 Offline_Uncorrectable 4 : オフライン訂正不能
さらに過去の ATA エラーログにも,訂正不能な読み取りエラー UNC が 5 件残っていました.
エラーの発生したディスク上の位置 (LBA) から,ファイルシステムのブロック番号を計算し,debugfs でどのファイルに属するかを調べました.
すると,ログに残っていた 5 個所は,すべて /swap.img の中でした.
そこで,メモリーの空き容量を確認してから swap を停止し,既存の swap ファイルを残したまま全体をゼロで上書きしました.
その後,ディスクから全体を読み戻せることを確認し,swap を再初期化して有効に戻しました.
ファイルを削除して作り直すと,別の場所に割り当てられる可能性がありますが,今回は問題のあった場所に書き込みたかったので,既存のファイルを上書きし,前後でディスク上の配置が変わっていないことも確認しています.
この作業で,保留中のセクタは 5 から 4 に減りました.代替処理済みセクタは 0 のままです.
ただし,残りの 4 個所がどこにあるのかは,過去のエラーログだけでは分かりませんでした.
過去にエラーが起きた場所が,現在も保留中とは限らない,ということのようです.
残りの不良セクタの場所を調べるため,バックアップを取ってから long self-test を実行しました.
以下のコマンドは,実際に行った作業から抜粋・簡略化したものです.
$ disk=/dev/disk/by-id/XXXXXXXXXXXXXXXX # 長時間の自己テストを開始 $ sudo smartctl -t long "$disk" # 進行状況と結果を確認 $ sudo smartctl -c "$disk" $ sudo smartctl -l selftest "$disk"
テスト開始時には約 277 分かかると表示されましたが,結果を確認すると,読み取りエラーで終了していました.
# Test_Description Status Remaining LifeTime(hours) LBA_of_first_error Extended offline Completed: read failure 90% 57182 14032584
最初に見つかったのは LBA 14,032,584(先頭から約 7.2 GB の位置)で,この結果だけでは,その先にも読めない場所があるかどうかは分かりません.
そこで,読めなかった物理セクタの次から末尾までを,範囲指定の selective self-test で調べました(今回の HDD では,4096 バイトの物理セクタ 1 個に 512 バイト単位の LBA が 8 個分含まれる.エラーが出た位置は物理セクタの先頭だったため,その物理セクタを飛ばした次の位置,つまり LBA に 8 を加えた位置からテストを再開している).
# 最初に読めなかった物理セクタの次から調べる sudo smartctl -t select,14032592-max "$disk" # 終了後に結果を確認 sudo smartctl -l selftest "$disk"
またエラーで終了したら,その場所を記録して次から読む,ということを繰り返した結果,次の 4 箇所のエラーが見つかりました.
$ sudo smartctl -l selftest "$disk" # 1 Selective offline Completed without error 00% 57201 - # 2 Selective offline Completed: read failure 90% 57195 14217304 # 3 Selective offline Completed: read failure 90% 57195 14041568 # 4 Selective offline Completed: read failure 90% 57195 14039280 # 5 Extended offline Completed: read failure 90% 57182 14032584
次に,読めなかった場所が何に使われているかを調べます.
今回のファイルシステムは /dev/sda2 上の ext4 で,パーティションの開始位置は 1,050,624 セクタ,ファイルシステムのブロックサイズは 4096 バイトでした.
$ cat /sys/block/sda/sda2/start 1050624 $ sudo tune2fs -l /dev/sda2 | grep 'Block size' Block size: 4096
LBA はディスク全体の先頭から数えた位置なので,パーティションの開始位置を引いてからファイルシステムのブロック番号に直します.
例えば LBA 14039280 なら,ブロック番号は (14039280 - 1050624) / 8 = 1623582 となります.
その結果をもとに,debugfs の icheck で inode を調べ,ncheck でファイル名を引きます.
実行結果はこのような感じです.
$ sudo debugfs -R "icheck 1623582" /dev/sda2 debugfs xx.xx.xx Block Inode number 1623582 52698848 $ sudo debugfs -R "ncheck 52698848" /dev/sda2 debugfs xx.xx.xx Inode Pathname 52698848 /usr/lib/x86_64-linux-gnu/libboost_wave.so.xx.xx.xx
調べた結果は次のとおりでした.
| LBA | 使われていた場所 |
| 14,032,584 | 空き領域 |
| 14,039,280 | /usr/lib/x86_64-linux-gnu/libboost_wave.so.xx.xx.xx |
| 14,041,568 | /usr/bin/libtoolize |
| 14,217,304 | 空き領域 |
4 箇所とも,約 94 MB の範囲に固まっていました.
ファイルの領域と重なっていたものも,普段使わなさそうなファイルだったため,これまで気づかなかっただけのよう?です.
幸い,不良セクタによって破損した 2 つのファイルは,どちらも Ubuntu のパッケージに含まれるファイルでした.
そこで,インストール済みのものと同じバージョンのパッケージを取得し,その内容を使って読めない部分だけを書き直しています.
ざっくり,次のような手順です.
| 対象ファイルが使用中でないことと,ディスク上の配置を確認する. | |
| 同じバージョンのパッケージからファイルを取り出し,チェックサムを照合する. | |
| 各ファイルの読めない 4,096 バイトのブロックだけを正しいデータで上書きする. | |
| キャッシュを通さずにファイル全体を読み戻し,パッケージの内容と一致することを確認する. | |
| 修復した範囲の selective self-test を実行する. |
具体的なコマンドを簡単に記録しておきます(以下は libtoolize 側を例にしたコマンド).
まず,インストール済みのものと同じバージョンのパッケージを取得し,展開したファイルのサイズと MD5 がパッケージ管理情報に記録された値と一致することを確認しました.
# インストール済みのバージョンを調べ,同じものを取得
$ pkg=libtool
$ version=$(dpkg-query -W -f='${Version}' "$pkg")
$ work=$(mktemp -d)
$ cd "$work"
$ apt-get download "$pkg=$version"
# パッケージを展開
$ dpkg-deb -x ./*.deb extracted
# 正しい内容の照合に使用
$ dpkg-query --control-show "$pkg" md5sums | grep ' usr/bin/libtoolize$'
$ md5sum extracted/usr/bin/libtoolize
swap ファイルのときと同様に問題のあった場所のみに書き込むために,単にパッケージを再インストールするのではなく,既存ファイルの読めないブロックだけを書き直しました.
書き込み前には,対象ファイルが使用中でないこととパッケージの更新処理が動いていないことを確認しています.
また,filefrag でファイル内の位置とディスク上の位置を照合しました.
$ sudo lsof /usr/bin/libtoolize $ sudo filefrag -v -b4096 /usr/bin/libtoolize
libtoolize の読めない部分は,(ファイル先頭を 0 とした)28 番目の 4096 バイトのブロックでした.そこで,正常なファイルの同じ位置から 1 ブロックを取り出し,上書きしました.
sudo dd \ if="$work/extracted/usr/bin/libtoolize" \ of=/usr/bin/libtoolize \ bs=4096 skip=28 seek=28 count=1 \ iflag=fullblock oflag=direct \ conv=notrunc,nocreat,fsync
conv=notrunc はファイルの残りを切り詰めないため,nocreat は出力先が存在しない場合に新規作成しないための指定です.
もう一方のライブラリーも同様に,30 番目のブロックだけを書き直しました.実際の作業では,ファイルの inode やサイズの確認,更新日時の復元も行っています.
読み戻したファイルのチェックサムは一致し,dpkg --verify でも差分は出ませんでした.
修復した範囲の selective self-test も,Completed without error で完了し,問題のあったブロックも正しく読み取れることを確認しました.
SMART の値の変化は次のとおりでした.
| 時点 | 代替処理済み | 保留中 | オフライン訂正不能 |
| 作業前 | 0 | 5 | 4 |
| swap ファイルの書き直し後 | 0 | 4 | 4 |
| 2 つのファイルの修復後 | 0 | 2 | 4 |
保留セクタは 5 → 4 → 2 と減りました.
代替処理済みセクタは増えておらず,書き直した箇所は代替処理なしで再び読み取れる状態になっています.
残りの 2 個所は空き領域だったため,今回はそのままにしました.
その領域にファイルを書き込もうとした時に,正常に書き込まれるか,代替処理が発生する可能性があります.
オフライン訂正不能の 4 という値も残っています.
これは以前の検査結果が残っている可能性がありますが,詳細は不明です.
なお,ヘッド退避回数の多さが今回の読み取り不良の原因だったかまでは分かりません.
修復したファイルが読めることを確認できたので,ディスクの交換も含めて今後の扱いを考えることにします.
サーバーのクリーンインストールのついでに HDD の状態を確認しただけだったのですが,結構なボリュームの作業となってしまいました.
また,普通にサーバー B のシステムディスクの状態が不安なこともわかったので,定期的に状況を確認することは大事だなと改めて気付かされました.
データは突然吹っ飛びますからね.
名前
Email (※公開されることはありません)
コメント