Восстанавливаем RAID1 с двумя неисправными жесткими дисками

Долгое время не выдавалось интересных задачек, которые можно было бы освятить в заметке. Вчера всё-таки нашлась интересная с моей точки зрения.

В исходных данных имеем RAID1 массив, построенный на mdadm, оба диска в котором имеют ошибки на чтение в нескольких секторах. Система говорит о том, что в обоих из них Unrecovered read error — auto reallocate failed:

Aug 21 21:53:11 one kernel: [112350.663076] sd 2:0:0:0: [sda] Unhandled sense code
Aug 21 21:53:11 one kernel: [112350.663081] sd 2:0:0:0: [sda] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
Aug 21 21:53:11 one kernel: [112350.663089] sd 2:0:0:0: [sda] Sense Key : Medium Error [current] [descriptor]
Aug 21 21:53:11 one kernel: [112350.663133] sd 2:0:0:0: [sda] Add. Sense: Unrecovered read error - auto reallocate failed
Aug 21 21:53:11 one kernel: [112350.663144] sd 2:0:0:0: [sda] CDB: Read(10): 28 00 3a 38 53 b0 00 00 08 00
Aug 21 21:53:11 one kernel: [112350.663160] end_request: I/O error, dev sda, sector 976769972

Попытки пройти long тест в S.M.A.R.T не увенчиваются успехом. Вырезки из S.M.A.R.T

# smartctl -a /dev/sda
...
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Extended offline    Completed: read failure       90%     32520         976769972
...
# smartctl -a /dev/sdb
...
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Extended offline    Completed: read failure       90%     32519         55084971

Таким образом становиться ясно, что пока в массиве представлено 2-а жестких диска он «малость» работает исправно. Перекидывая неудачные попытки чтения с одного диска на другой диск. Это выражается в следующих сообщениях:

Aug 22 09:12:18 one kernel: [153097.501298] end_request: I/O error, dev sdb, sector 750558140
Aug 22 09:12:18 one kernel: [153097.508526] raid1: sdb4: rescheduling sector 695471160
Aug 22 09:12:18 one kernel: [153097.515736] raid1: sdb4: rescheduling sector 695471408
Aug 22 09:12:30 one kernel: [153109.603257] sd 4:0:0:0: [sdb] Unhandled sense code
Aug 22 09:12:30 one kernel: [153109.603259] sd 4:0:0:0: [sdb] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
Aug 22 09:12:30 one kernel: [153109.603262] sd 4:0:0:0: [sdb] Sense Key : Medium Error [current] [descriptor]
Aug 22 09:12:30 one kernel: [153109.603277] sd 4:0:0:0: [sdb] Add. Sense: Unrecovered read error - auto reallocate failed
Aug 22 09:12:30 one kernel: [153109.603281] sd 4:0:0:0: [sdb] CDB: Read(10): 28 00 2c bc 9b b5 00 00 08 00
Aug 22 09:12:30 one kernel: [153109.603287] end_request: I/O error, dev sdb, sector 750558140
Aug 22 09:12:30 one kernel: [153109.610682] raid1:md3: read error corrected (8 sectors at 695471248 on sdb4)
Aug 22 09:12:30 one kernel: [153110.033913] raid1: sda4: redirecting sector 695471160 to another mirror
Aug 22 09:12:31 one kernel: [153110.177597] raid1: sda4: redirecting sector 695471408 to another mirror

В случае окончательного выхода из строя одного из дисков массив никогда не сможет восстановиться, потому что чтение с любого диска заканчивается провалом.

# dd if=/dev/sda of=/dev/null bs=1M
dd: reading `/dev/sda': Input/output error
476938+1 records in
476938+1 records out
500106223616 bytes (500 GB) copied, 5724.82 s, 87.4 MB/s

Разумеется, существует вариант сделать копию системы штатным tar архивом. После этого заменить жесткие диски и развернуть ранее сохраненную систему. Еще один из вариантов попытаться заменить один жесткий диск, создать на нем изначально RAID1 массив с одним missing диском, после этого попытаться синхронизировать систему с оставшегося диска с помощью tar или rsync.

Однако было замечено, что у дисков недоступные для чтения сектора находятся в различных местах. Таким образом возникла идея попытаться форсировать remapping испорченных секторов на одном из дисков и попытаться после этого восстановить данные в них с использованием другого полудохлого диска. В ходе этой операции мы получим один диск с полной копией наших данных, с которого можно эти данные полностью прочитать без ошибок. Останется заменить второй полудохлый диск новым, mdadm автоматически прочитает данные и синхронизирует их с новым диском. При желании после синхронизации можно заменить диск, на котором произвели remapping секторов.

Для того, чтобы форсировать жесткий диск произвести remapping секторов необходимо произвести запись в указанный сектор. С минимальной математической частью можно ознакомиться в статье Forcing a hard disk to reallocate bad sectors. В нашем случае эмпирическим путем было выяснено, что сектора с 976769972 по 976769979 имеют проблемы на жестком диске sda

# hdparm --read-sector 976769972 /dev/sda

/dev/sda:
reading sector 976769972: FAILED: Input/output error
...
# hdparm --read-sector 976769979 /dev/sda

/dev/sda:
reading sector 976769979: FAILED: Input/output error
#

Таким образом производим фиктивную запись в указанный диапазон секторов

for sector in $(seq 976769972 976769979); do
  hdparm --write-sector $sector --yes-i-know-what-i-am-doing /dev/sda;
done

После этого можно убедиться, что данные сектора доступны для чтения (предыдущей командой командой мы их полностью обнулили)

for sector in $(seq 976769972 976769979); do
  hdparm --read-sector $sector /dev/sda;
done

Теперь необходимо в указанные области записать информацию с соседнего диска из зеркала. Так как нам известно смещение 976769972 и что количество поврежденных секторов равно 8 копируем указанную область целым блоком. Обратите внимание копируем область с диска sdb, записываем на диск sda:

# dd if=/dev/sdb of=copy skip=976769972 count=8
8+0 records in
8+0 records out
4096 bytes (4.1 kB) copied, 9.3381e-05 s, 43.9 MB/s
# dd if=copy of=/dev/sda seek=976769972 oflag=direct count=8
8+0 records in
8+0 records out
4096 bytes (4.1 kB) copied, 0.000874658 s, 4.7 MB/s

Обнуление через вызов hdparm не является обязательным. Можно напрямую записать сектора требуемыми данными через dd командой

for sector in $(seq 976769972 976769979); do
  dd if=/dev/sdb of=/dev/sda skip=$sector seek=$sector oflag=direct count=1
done

Осталось убедиться, что все данные с диска можно прочитать

dd if=/dev/sda of=/dev/null bs=1M
476940+1 records in
476940+1 records out
500107862016 bytes (500 GB) copied, 5622.88 s, 88.9 MB/s

Таким образом в ходе описанных манипуляций у нас есть условно рабочий жесткий диск sda, который мы оставляем в системе и меняем жесткий диск sdb. По завершении синхронизации на диск sdb производим замену sda. По завершении синхронизации имеем полностью исправный массив с целостными данными.

В заключении хочется отметить, что в поиске неисправных секторов незаменимой может оказаться команда

dd if=/dev/sda of=/dev/null bs=512 conv=sync,noerror

При желании можно перенаправить сохранение бинарной копии системы на отдельный носитель в этом случае ключ sync просто незаменим. В случае ошибок сбойные сектора на копии будут заполнены NUL (нулями). Таким образом полученная копия будет полностью соответствовать с точки зрения размера оригиналу.

Вторым незаменимым помощником может служить команда badblocks. Важно помнить, что для ее вызова необходимо указать размер блока равный 512 байтам. По умолчанию программа используется значение 1024. Таким образом вызов будет выглядеть следующим образом

badblocks -v -b 512 /dev/sda

Программа будет считывать сектор за сектором и отображать информацию о секторах, которые не удалось прочитать. Например,

# badblocks -v -b 512 /dev/sda 
Checking blocks 0 to 1565565871
Checking for bad blocks (read-only test): 578915880
578915881
578915882
578915883
578915884
578915885
578915886
578915887
578915888
578915889
578915890
578915891
578915892
578915893
578915894
578915895

В этом случае остается сохранить вывод программы в файл и последовательно перезаписать полученные сектора с другого диска.

Интересные материалы для ознакомления:

One thought on “Восстанавливаем RAID1 с двумя неисправными жесткими дисками

  1. Pingback: debugfs практическое руководство | Обитель UNIX

Добавить комментарий

Ваш e-mail не будет опубликован.

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>