Poniższy artykuł ma na celu przedstawienie - krok po kroku - w jaki sposób skonfigurować i uruchomić prostą macierz dyskową w trybie RAID 1 na działającym już systemie operacyjnym Linux.
Jednym słowem: mirroring. W praktyce oznacza to równoległy zapis tych samych danych na dwóch oddzielnych dyskach. W przypadku awarii jednego z dysków system operacyjny może działać dalej korzystając z drugiego, sprawnego dysku.
O wyborze programowego rozwiązania decyduje przede wszystkim cena. Nie musimy inwestować pieniędzy w drogi, sprzętowy kontroler macierzy dyskowych. Ponadto większość "tanich i dobrych" kontrolerów często wymaga dodatkowych sterowników, które nie zawsze lubią współpracować z Linuksem. No i też nie jest to rozwiązanie w pełni sprzętowe.
Na potrzeby tego artykułu uruchomiona została macierz składająca się z dwóch dysków 120 GB (Maxtor 6Y120P0), działająca pod kontrolą dystrybucji Slackware Linux (gałąź -current, jądro w wersji 2.6.26.3-grsec). Każdy z dysków podłączony jest do innego kanału ATA, co wpływa na większą wydajność macierzy.
Co do wyboru dysków zazwyczaj zaleca się tworzenie macierzy na dwóch identycznych dyskach, jednak nie ma większych przeszkód przy stworzeniu takiej macierzy na dyskach różnej pojemności. Wpływa to nieco na wydajność z racji różniących się parametrów. W przypadku różnych dysków musimy zadbać o dwie rzeczy:
# fdisk -l /dev/hda | grep heads 255 heads, 63 sectors/track, 14946 cylinders
# fdisk -l /dev/hdc | grep heads 255 heads, 63 sectors/track, 14946 cylinders
Jest to przykład dla dwóch identycznych dysków. W tym przypadku wszystkie wartości są te same.
# fdisk -l /dev/hda | grep heads 255 heads, 63 sectors/track, 9729 cylinders
# fdisk -l /dev/hdc | grep heads 255 heads, 63 sectors/track, 14946 cylinders
A to przykład użycia dwóch różnych dysków.
Wartości, które muszą być identyczne to liczba głowic (heads) oraz liczba sektorów na ścieżkę (sectors/track). Liczba cylindrów jest różna z powodu różnych pojemności (hda to 80 GB, hdc 120 GB).
Adresacja jest o tyle ważna, gdyż przy tworzeniu partycji programy takie jak fdisk czy cfdisk zaokrąglają rozmiary partycji. W przypadku tej samej adresacji nie ma ryzyka, że np. partycja 20 GB na jednym dysku będzie o 500 kB mniejsza od tej na drugim.
Przejdźmy więc do właściwej konfiguracji. Podstawowa kwestia to odpowiednie opcje wkompilowane w jądro, a więc wybieramy:
Device Drivers ---> [*] Multiple devices driver support (RAID and LVM) ---> <*> RAID support <*> RAID-1 (mirroring) mode
Nie będę opisywał samej rekompilacji jądra. Pomijam także zabawę z initrd, dlatego sterownik wkompilujemy w jądro.
Dysk, na którym zainstalowany jest mój system ma taką strukturę:
# fdisk -l /dev/hda Disk /dev/hda: 122.9 GB, 122942324736 bytes 255 heads, 63 sectors/track, 14946 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Device Boot Start End Blocks Id System /dev/hda1 1 524 4208998+ 83 Linux # / /dev/hda2 525 14946 115844715 5 Extended /dev/hda5 525 907 3076416 83 Linux # /tmp /dev/hda6 908 1290 3076416 83 Linux # /var /dev/hda7 1291 2183 7172991 83 Linux # /usr /dev/hda8 2184 14831 101595028+ 83 Linux # /home /dev/hda9 14832 14946 923706 82 Linux swap
Pierwsze co musimy zrobić to stworzyć na drugim dysku takie same partycje, zmieniając ich typ na Linux raid autodetect. Efekt końcowy wygląda tak:
# fdisk -l /dev/hdc Disk /dev/hdc: 122.9 GB, 122942324736 bytes 255 heads, 63 sectors/track, 14946 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Device Boot Start End Blocks Id System /dev/hdc1 1 524 4208998+ fd Linux raid autodetect /dev/hdc2 525 14946 115844715 5 Extended /dev/hdc5 525 907 3076416 fd Linux raid autodetect /dev/hdc6 908 1290 3076416 fd Linux raid autodetect /dev/hdc7 1291 2183 7172991 fd Linux raid autodetect /dev/hdc8 2184 14831 101595028+ fd Linux raid autodetect /dev/hdc9 14832 14946 923706 fd Linux raid autodetect
Oznaczenia partycji Extended nie zmieniamy. Jest ona informacją o istnieniu dysków logicznych.
Kolejny krok to stworzenie macierzy:
# mdadm -C /dev/md0 -l 1 -n 2 missing /dev/hdc1
Tworzymy w ten sposób macierz /dev/md0 pierwszego poziomu (RAID 1), składającą się z dwóch dysków. Na początku konfigurujemy w niej tylko partycję nowego dysku (hdc1), ponieważ hda1 jest używana przez nas system (oznaczamy ją jako missing).
Czynność powtarzamy dla pozostałych partycji:
# mdadm -C /dev/md1 -l 1 -n 2 missing /dev/hdc5 # mdadm -C /dev/md2 -l 1 -n 2 missing /dev/hdc6 # mdadm -C /dev/md3 -l 1 -n 2 missing /dev/hdc7 # mdadm -C /dev/md4 -l 1 -n 2 missing /dev/hdc8 # mdadm -C /dev/md5 -l 1 -n 2 missing /dev/hdc9
Nowe partycje musimy sformatować (oczywiście wybór systemu plików wedle upodobań, ja używam ext3):
# mkfs.ext3 /dev/md0 # mkfs.ext3 /dev/md1 # mkfs.ext3 /dev/md2 # mkfs.ext3 /dev/md3 # mkfs.ext3 /dev/md4 # mkswap /dev/md5
Tworzymy katalogi, pod które będziemy montować partycje oraz /dev, /proc, /sys:
# mkdir -p /raid/{tmp,var,usr,home,dev,proc,sys} # chmod 555 /raid/proc
Montujemy je:
# mount /dev/md0 /raid # mount /dev/md1 /raid/tmp # mount /dev/md2 /raid/var # mount /dev/md3 /raid/usr # mount /dev/md4 /raid/home
Czas na przekopiowanie naszego systemu na nowy dysk. Możemy użyć do tego celu rsync:
# rsync -auH --progress --stats --exclude=/raid --exclude=/dev --exclude=/proc --exclude=/sys / /raid
Na nowym dysku trzeba jeszcze przeprowadzić kilka modyfikacji. Pierwsza to dopisanie macierzy do pliku konfiguracyjnego mdadm. Edytujemy więc /raid/etc/mdadm.conf i dopisujemy do niego linię:
DEVICE /dev/hda[156789] /dev/hdc[156789]
Tajemnicze cyfry w nawiasach to oznaczenia kolejnych dysków (hda1, hda5, itd.). Musimy także umieścić informacje o poszczególnych partycjach macierzy. Dane te wygeneruje nam mdadm:
# mdadm --detail --scan >> /raid/etc/mdadm.conf
Kolejną modyfikacją jest zmiana wpisów w fstab. Edytujemy więc /raid/etc/fstab i zamieniamy oznaczenia starych partycji na nowe (hda1 na md0, hda5 na md1, itd.):
/dev/md5 swap swap defaults 0 0 /dev/md0 / ext3 defaults 1 1 /dev/md1 /tmp ext3 defaults 1 2 /dev/md2 /var ext3 defaults 1 2 /dev/md3 /usr ext3 defaults 1 2 /dev/md4 /home ext3 defaults,usrquota 1 2
Ostatnia zmiana to LILO. Nowy wpis w /raid/etc/lilo.conf powinien wyglądać następująco:
image = /boot/bzImage-2.6.26.3 root = /dev/md0 label = Linux-RAID read-only
Można oczywiście zostawić także stary wpis na wypadek jakiegoś niepowodzenia:
image = /boot/bzImage-2.6.26.3 root = /dev/hda1 label = Linux-Normal read-only
Oczywiście w przypadku GRUB-a zmiany są analogiczne. Instalujemy nowe LILO:
# lilo -C /raid/etc/lilo.conf
Taka konfiguracja pozwoli nam na uruchomienie systemu z obsługą macierzy bez potrzeby zmiany kolejności bootowania dysków w BIOS-ie (LILO zainstaluje się w MBR dysku /dev/hda), a w razie problemów uruchomienie systemu bez RAIDa.
Nadszedł więc czas zrestartowania systemu. Wybieramy oczywiście jądro Linux-RAID. Po uruchomieniu systemu sprawdźmy czy RAID rzeczywiście działa:
# df -h Filesystem Size Used Avail Use% Mounted on /dev/root 4.0G 701M 3.1G 19% / /dev/md/1 2.9G 173M 2.6G 7% /tmp /dev/md/2 2.9G 297M 2.5G 11% /var /dev/md/3 6.8G 2.0G 4.5G 32% /usr /dev/md/4 96G 46G 45G 51% /homeWygląda w porządku. Jeszcze szybki rzut oka na status naszego RAID-a:
# cat /proc/mdstat Personalities : [raid1] md0 : active raid1 hdc1[1] 4208896 blocks [2/1] [_U] md1 : active raid1 hdc5[1] 3076352 blocks [2/1] [_U] md2 : active raid1 hdc6[1] 3076352 blocks [2/1] [_U] md3 : active raid1 hdc7[1] 7172864 blocks [2/1] [_U] md4 : active raid1 hdc8[1] 101594944 blocks [2/1] [_U] md5 : active raid1 hdc9[1] 923584 blocks [2/1] [_U]
Jak widać aktualnie w naszej macierzy działa jeden dysk. U (up) oznacza dysk działający. _ to dysk niepodłączony.
Dodajmy więc pierwszy dysk do macierzy. Tu także musimy zmienić Id partycji na Linux raid autodetect:
# fdisk -l /dev/hda Disk /dev/hda: 122.9 GB, 122942324736 bytes 255 heads, 63 sectors/track, 14946 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Device Boot Start End Blocks Id System /dev/hda1 1 524 4208998+ fd Linux raid autodetect /dev/hda2 525 14946 115844715 5 Extended /dev/hda5 525 907 3076416 fd Linux raid autodetect /dev/hda6 908 1290 3076416 fd Linux raid autodetect /dev/hda7 1291 2183 7172991 fd Linux raid autodetect /dev/hda8 2184 14831 101595028+ fd Linux raid autodetect /dev/hda9 14832 14946 923706 fd Linux raid autodetect
Ostatni krok to dodanie nowych partycji do naszej macierzy. Wpisujemy więc kolejno:
# mdadm /dev/md0 -a /dev/hda1 # mdadm /dev/md1 -a /dev/hda5 # mdadm /dev/md2 -a /dev/hda6 # mdadm /dev/md3 -a /dev/hda7 # mdadm /dev/md4 -a /dev/hda8 # mdadm /dev/md5 -a /dev/hda9
Po czym możemy obserwować jak RAID automatycznie kopiuje dane na nowy dysk:
# watch -n 0.1 cat /proc/mdstat Every 0.1s: cat /proc/mdstat Wed Sep 3 18:35:46 2008 Personalities : [raid1] md0 : active raid1 hda1[0] hdc1[1] 4208896 blocks [2/2] [UU] md1 : active raid1 hda5[0] hdc5[1] 3076352 blocks [2/2] [UU] md2 : active raid1 hda6[0] hdc6[1] 3076352 blocks [2/2] [UU] md3 : active raid1 hda7[0] hdc7[1] 7172864 blocks [2/1] [_U] resync=DELAYED md4 : active raid1 hda8[0] hdc8[1] 101594944 blocks [2/1] [_U] [====>................] recovery = 20.2% (20581248/101594944) finish=69.4min speed=19430K/sec md5 : active raid1 hda9[0] hdc9[0] 923584 blocks [2/2] [UU] unused devices: <none>
Proces kopiowania danych może oczywiście zająć kilka godzin, w zależności od rozmiaru dysków.
Po zakończeniu kopiowania warto zainstalować LILO na obydwu dyskach (możemy już skasować stary wpis dotyczący /dev/hda1) oraz ustawić ich bootowanie kolejno po sobie w BIOSie. W razie awarii któregoś z dysków, gdy będziemy restartować system, drugi dysk automatycznie podejmie pracę.
Pozwoliłem sobie także napisać niewielki skrypt CGI, który w przejrzysty sposób wyświetla status naszej macierzy:
--- cut here --- #!/bin/sh function print() { i=1 while [ $i -le `grep -o 'md[0-9]*' /proc/mdstat | wc -l` ]; do if [ `grep -o '\[..\]' /proc/mdstat | head -$i | tail -1 | head -c$1 | tail -c1` == "U" ]; then echo "[ <span style=\"color:#006400\">up</span> ]" else echo "[ <span style=\"color:#8b0000\">down</span> ]" fi ((i++)) done } echo -e "Content-type: text/html\n" echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" echo "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">" echo -e "<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />" echo "<title>mdstat - RAID 1 - $HOSTNAME</title>" echo -e "<style type=\"text/css\">\n<!--" echo -e "\ttd { text-align:center;padding:0 20px 0 20px }" echo -e "-->\n</style>\n</head>\n<body>" if [ ! -e /proc/mdstat ]; then echo "<pre>/proc/mdstat doesn't exist...</pre>" else echo "<table><tr>" echo -e "<th>device</th>\n<th>#0</th>\n<th>#1</th>\n<th>status #0</th>\n<th>status #1</th></tr><tr>" echo -e "<td><pre>\n`grep -o 'md[0-9]*' /proc/mdstat`\n</pre></td>" echo -e "<td><pre>\n`grep -o '[a-z0-9]*\[0\]' /proc/mdstat | sed 's/\[0\]//'`\n</pre></td>" echo -e "<td><pre>\n`grep -o '[a-z0-9]*\[1\]' /proc/mdstat | sed 's/\[1\]//'`\n</pre></td>" echo "<td><pre>" if [ `grep -o '\[[0-9]\]' /proc/mdstat | head -c3` == "[0]" ]; then print 2 else print 3 fi echo -e "</pre></td>\n<td><pre>" if [ `grep -o '\[[0-9]\]' /proc/mdstat | head -c3` == "[1]" ]; then print 2 else print 3 fi echo -e "</pre>\n</td>\n</tr></table>" fi echo -e "</body>\n</html>" --- cut here ---
Tym oto akcentem temat Software RAID 1 można uznać za wyczerpany. Wszelkie sugestie i uwagi odnośnie tego artykułu będą mile widziane. W razie pytań proszę o kontakt e-mailowy. Adres e-mail podany jest na mojej stronie.
Copyright 2008 Damian Pasternok.
Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.
Zezwala się na wykonywanie i dystrybucję wiernych kopii tego tekstu, niezależnie od nośnika, pod warunkiem zachowania niniejszego zezwolenia.
Aktualizowane: $Date: 2008/09/03 19:47:21 $ $Author: crh $