Torsten Förtsch
IT System Development & Security
Kaum macht man's richtig, schon geht's, ;-)

>> Home >> Tipps & Tricks >> SW RAID >> /boot

Grub auf einem RAID1

Mit yast bei der Installation ist es nicht möglich die /boot Partition auf einem Software-RAID unterzubringen.

Beschäftigt man sich näher mit dem Thema stellt sich heraus, dass einige Bedingungen erfüllt sein müssen, damit eine Boot-Partition auf einem Software-RAID funktioniert und sinnvoll ist.

Besonders der letzte Punkt hat es in sich. Die für Linux verfügbaren Boot-Loader grub und lilo verstehen nämlich von Haus aus kein RAID und wollen alle zu ladenden Blocks auf der selben Platte finden. Damit sind solche RAID-Level, die die Daten zu einer Datei auf unterschiedliche Platten verteilen, für die Boot-Partition nicht verwendbar. Ein kurzer Blick genügt um festzustellen, dass eigentlich nur RAID1 (Spiegelung) verwendbar ist. Lilo stellt keine weiteren Ansprüche; soll jedoch grub verwendet werden, muss ein Filesystem gewählt werden, das Grub versteht. Glücklicherweise steht der RAID-Superblock am Ende jeder Partition, so dass jede Partition für sich genommen wie ein Filesystem auf einer ganz gewöhnlichen Platte aussieht, wie im folgenden Experiment zu sehen ist:

pelmen:/ # mdadm -D /dev/md2
/dev/md2:
        Version : 00.90.01
  Creation Time : Tue Apr 12 20:35:07 2005
     Raid Level : raid1
     Array Size : 3148608 (3.00 GiB 3.22 GB)
    Device Size : 3148608 (3.00 GiB 3.22 GB)
   Raid Devices : 2
  Total Devices : 2
Preferred Minor : 2
    Persistence : Superblock is persistent

    Update Time : Tue Apr 12 22:06:21 2005
          State : clean
 Active Devices : 2
Working Devices : 2
 Failed Devices : 0
  Spare Devices : 0

           UUID : d074e4e9:0ceced94:17001820:f1ccf8e0
         Events : 0.220

    Number   Major   Minor   RaidDevice State
       0       8       17        0      active sync   /dev/sdb1
       1       8       33        1      active sync   /dev/sdc1
pelmen:/ # dd if=/dev/md2 bs=10k count=1 | file -
1+0 records in
1+0 records out
10240 bytes (10 kB) copied, 0.01185 seconds, 864 kB/s
/dev/stdin: Linux rev 1.0 ext3 filesystem data (needs journal recovery)
pelmen:/ # dd if=/dev/sdb1 bs=10k count=1 | file -
1+0 records in
1+0 records out
10240 bytes (10 kB) copied, 0.002784 seconds, 3.7 MB/s
/dev/stdin: Linux rev 1.0 ext3 filesystem data (needs journal recovery)
pelmen:/ # cmp <(dd if=/dev/sdb1 bs=10k count=1) <(dd if=/dev/md2 bs=10k count=1)
1+0 records in
1+0 records out
10240 bytes (10 kB) copied, 5.7e-05 seconds, 180 MB/s
1+0 records in
1+0 records out
10240 bytes (10 kB) copied, 6.3e-05 seconds, 163 MB/s

Hier wird als erstes der Status der Device /dev/md2 erfragt, um zu sehen, dass diese aus /dev/sdb1 und /dev/sdc1 besteht. Mit dem file Kommando wird dann ermittelt, dass die Anfänge sowohl von /dev/sdb1 als auch von /dev/md2 wie ein ext3-Filesystem aussehen. Zum Schluß vergewissern wir uns mit dem cmp Kommando, dass die ersten 10 KByte wirklich gleich sind.

Vorgehensweise

Wie gesagt, die Installation des Boot-Loaders Grub während der Installation auf dem RAID ging schief. Daher installierte ich erstmal mit einer normalen Partition als Boot-Partition. Meine 3 Platten sind gleich partitioniert. Die ersten Partitionen /dev/sda1, /dev/sdb1 und /dev/sdc1 sollen später als RAID1 die Boot-Partition beherbergen.

Zum Installieren des Suse 9.3 wählte ich /dev/sda1 als Boot-Partition. Sie wird als ext3 formatiert und unter /boot eingebunden. Grub wird im Master-Boot-Record von /dev/sda installiert.

Die Partitionen /dev/sdb1 und /dev/sdc1 werden während der Installation in der gleichen Größe erstellt, aber nicht formatiert oder irgendwie eingebunden. Yast bastelt daraus dann /dataN Mountpoints, die man hinterher getrost löschen kann.

Nach der Installation basteln wir uns ein RAID1 aus /dev/sdb1, /dev/sdc1 und einem fehlenden Disk, den wir später durch /dev/sda1 ersetzen:

mdadm -C /dev/md2 -l raid1 -n 3 missing /dev/sdb1 /dev/sdc1

Es kann passieren, dass mdadm dabei etwas schimpft oder warnt, aber wir wissen ja, was wir tun. Dieses RAID mounten wir unter einem temporären Verzeichnis und übertragen wir erstmal den Inhalt von /boot dorthin:

mkfs.ext3 /dev/md2
mkdir /boot.new
mount /dev/md2 /boot.new
tar -C /boot -cf - .| tar -C /boot.new -xvf -

Grub-Installation

Grub benutzt zur Benennung der Partitionen nicht die Linux-typischen Bezeichnungen /dev/sdX oder /dev/hdX, sondern Namen aus der BSD-Welt. Unter /boot/grub/device.map wird jedoch eine Datei angelegt, aus der die Zuordnung ersichtlich ist:

pelmen:/ # cat /boot.new/grub/device.map
(fd0)   /dev/fd0
(hd0)   /dev/sda
(hd2)   /dev/sdc
(hd1)   /dev/sdb

Ich will Grub zuerst auf den 2 Partitionen installieren, die zu /dev/md2 schon gehören. So verändere ich die bestehende Boot-Partition auf /dev/sda1 erstmal überhaupt nicht und habe immer eine Rückfall-Option, falls irgendwas schief geht. Aus obiger Tabelle ist ersichtlich, dass /dev/sdb hd1 und /dev/sdc hd2 und folglich /dev/sdb1 hd1,0 und /dev/sdc1 hd2,0 heißen.

pelmen:~ # grub
grub> root (hd1,0)
 Filesystem type is ext2fs, partition type 0xfd

grub> setup (hd1)
 Checking if "/boot/grub/stage1" exists... yes
 Checking if "/boot/grub/stage2" exists... yes
 Checking if "/boot/grub/e2fs_stage1_5" exists... yes
 Running "embed /boot/grub/e2fs_stage1_5 (hd1)"...  16 sectors are embedded.
succeeded
 Running "install /boot/grub/stage1 (hd1) (hd1)1+16 p (hd1,0)/boot/grub/stage2
/boot/grub/menu.lst"... succeeded
Done.

grub> root (hd2,0)
 Filesystem type is ext2fs, partition type 0xfd

grub> setup (hd2)
 Checking if "/boot/grub/stage1" exists... yes
 Checking if "/boot/grub/stage2" exists... yes
 Checking if "/boot/grub/e2fs_stage1_5" exists... yes
 Running "embed /boot/grub/e2fs_stage1_5 (hd2)"...  16 sectors are embedded.
succeeded
 Running "install /boot/grub/stage1 (hd2) (hd2)1+16 p (hd2,0)/boot/grub/stage2
/boot/grub/menu.lst"... succeeded
Done.

grub> quit

/boot/grub/menu.lst

Zum Schluß muss noch /boot.new/grub/menu.lst angepasst werden. Ich stelle hier schon die Endfassung vor, wenn alle 3 Platten zu dem RAID gehören werden.

color white/blue black/light-gray
default 0
fallback 1
fallback 2

timeout 8
#gfxmenu (hd0,0)/message

title SUSE LINUX 9.3 (/dev/sda) - raid
    kernel (hd0,0)/vmlinuz root=/dev/md0 vga=0x317 selinux=0  splash=verbose console=tty0   showopts
    initrd (hd0,0)/initrd

title SUSE LINUX 9.3 (/dev/sdb) - raid
    kernel (hd1,0)/vmlinuz root=/dev/md0 vga=0x317 selinux=0  splash=verbose console=tty0   showopts
    initrd (hd1,0)/initrd

title SUSE LINUX 9.3 (/dev/sdc) - raid
    kernel (hd2,0)/vmlinuz root=/dev/md0 vga=0x317 selinux=0  splash=verbose console=tty0   showopts
    initrd (hd2,0)/initrd

title Floppy
    root (fd0)
    chainloader +1

Wichtig hierbei sind die default 0 und die beiden fallback Anweisungen. Sie bewirken, dass normalerweise der erste Eintrag zum Booten benutzt und damit von /dev/sda gebootet wird. Sollte die Platte aber einen Fehler haben, dann wird automatisch der nächste und der übernächste Eintrag probiert. Die gfxmenu Zeile ist auskommentiert, da sie eine bestimmte Platte anspricht. So haben wir zwar kein grafisches Boot-Menu, aber ein sicheres.

/etc/fstab nicht vergessen

Genau. In der Fstab ist ja als /boot immer noch /dev/sda1 gemountet. Das müssen wir noch ändern.

/dev/md0             /                    xfs        defaults              1 1
/dev/md2             /boot                ext3       acl,user_xattr        1 2
/dev/md1             /home                xfs        defaults              1 2
/dev/md3             /var/log             xfs        defaults              1 2
/dev/md4             swap                 swap       pri=42                0 0
devpts               /dev/pts             devpts     mode=0620,gid=5       0 0
proc                 /proc                proc       defaults              0 0
usbfs                /proc/bus/usb        usbfs      noauto                0 0
sysfs                /sys                 sysfs      noauto                0 0

Zeit für den Reboot

Wir stellen fest, dass immer noch das grafische Boot-Menu der Suse-Installation erscheint. Was ist falsch? Ahh ..., wir haben vergessen, die Boot-Platte im BIOS zu ändern. Gut, nachdem das geändert ist, erscheint das Boot-Menu unserer geänderten menu.lst im Text-Modus. Hier wählen wir einen Eintrag, der auf das RAID zeigt, also in unserem Fall den zweiten oder dritten.

Wenn der Rechner betriebsbereit ist und wir die verschiedenen Boot-Varianten ein bißchen getestet haben, wird es Zeit, das RAID um die fehlende Platte zu ergänzen. Dazu gibt es natürlich wieder mdadm. Sofort nach dem Hinzufügen der neuen Partition wird sie in das RAID integriert, was in /proc/mdstat verfolgt werden kann.

pelmen:~ # mdadm /dev/md2 -a /dev/sda1
mdadm: hot added /dev/sda1
pelmen:~ # cat /proc/mdstat
Personalities : [raid1] [raid5]
md2 : active raid1 sda1[3] sdc1[2] sdb1[1]
      3148608 blocks [3/2] [_UU]
      [>....................]  recovery =  4.2% (134592/3148608) finish=0.7min speed=67296K/sec
md3 : active raid5 sdc3[2] sdb3[1] sda3[0]
      5253120 blocks level 5, 64k chunk, algorithm 2 [3/3] [UUU]

md4 : active raid1 sdc5[2] sdb5[1] sda5[0]
      4714944 blocks [3/3] [UUU]

md1 : active raid5 sdb7[1] sdc7[0] sda7[2]
      260108160 blocks level 5, 64k chunk, algorithm 2 [3/3] [UUU]

md0 : active raid1 sdc6[2] sdb6[1] sda6[0]
      15735552 blocks [3/3] [UUU]

unused devices: <none>

Ach ja, um ganz sicher zu gehen installieren wir Grub nochmal auf /dev/sda. Das hatte yast während der Installation zwar schonmal gemacht, trotzdem, sicher ist sicher. Die Kommandos sind die gleichen wie oben nur ist hd1 durch hd0 zu ersetzen.

Alarm

Zum Schluß sei noch ein kleines Script vorgestellt, das auf einfache Weise einen Alarm auslöst, falls mit dem RAID irgendwas nicht stimmt. Die Idee ist einfach, /proc/mdstat hat einen konstanten Inhalt. Also merken wir uns irgendwo dessen MD5-Summe und lassen den Rechner piepsen, wenn sie nicht mehr stimmt.

#!/bin/bash

beep () {
  echo -e '\a' >/dev/console
}

alarm () {
  beep
  usleep 200000
  beep
  usleep 300000
  beep
  usleep 200000
  beep
  usleep 200000
  beep
  usleep 300000
  beep
  usleep 200000
  beep
  usleep 200000
  beep
  usleep 300000
  beep
  usleep 200000
  beep
}

md5sum -c /etc/proc-mdstat.md5 >/dev/null 2>&1 || alarm

Die MD5-Summe wird dann einmalig in /etc/proc-mdstat.md5 abgelegt:

md5sum /proc/mdstat >/etc/proc-mdstat.md5

und das Script per /etc/crontab minütlich aktiviert:

pelmen:~ # cat /etc/crontab
...
*  *  * * *    root  /usr/local/sbin/mdalarm
...

So, jetzt können wir einem Plattenausfall entspannt entgegen sehen. Hauptsache im Ernstfall ist schnell eine Ersatzplatte zur Hand. Also evtl. eine in die Schublade legen!

Letzte Aktualisierung: 03.08.2008