"""
        (0 0)
---oOO---(_)---------
|  Software RAID 1  |
|    w Linuksie     |
--------------OOo----
       |__|__|
        || ||
       oOO OOo

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.

Czym jest RAID 1?

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.

Dlaczego akurat Software RAID?

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.

Platforma testowa:

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:

   1. Dysk, na którym aktualnie działa system operacyjny musi być 
mniejszy od dysku, na którym będziemy konfigurować macierz.
   2. Na obydwu dyskach musimy zadbać o taką samą adresację (dotyczy 
także identycznych dysków).
      Metodę adresacji wybiera się w BIOSie. W przypadku jakichkolwiek 
problemów można też użyć fdiska. 

# 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% /home

Wyglą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.

--
03.09.2008
Copyright 2008 Damian Pasternok http://www.pasternok.org/