raid mdadm debian
Afin de garantir la disponibilité de mes données sur mon serveur perso, j'ai décidé de me lancer dans la mise en place d'un volume de taille conséquente (1 tera octets) réparti sur 3 disques durs en RAID 5. Il est possible, pour ce type de volume, d'utiliser des cartes controlleurs parfaitement supportées sous Linux, mais faute de budget, et aussi car le RAID logiciel de Linux est tout à fait fiable, je n'ai pas retenu cette solution.
Le RAID 5 associe le striping et un système à parité répartie, il permet une bonne disponibilité (même en cas de défaillance d'un des périphériques de stockage. Exemple pratique : soit trois disques durs de taille identique A, B et C. Le système va enregistrer sur les disques A et B les données (Strip) comme en mode RAID 0 et, sur le disque C, le résultat de l'opération ou-exclusif entre A et B (A xor B).
Ainsi, en cas de défaillance du disque A, les données qui y étaient accessibles le sont toujours avec les disques B et C, par l'opération B xor C (B xor C = A). Il en va de même pour le disque B. Et si le disque C tombe en panne, les informations sont toutes sur A et B.
Important : les disques doivent être de même taille. On ne stocke que sur les deux tiers de la place totale des disques (le dernier tiers étant la parité). .
L'architecture est simple : un serveur sous Debian avec un noyau 2.6.26-1-686 SMP, 2 disques durs Western Digital 640GB et un disque dur Western Digital 500GB.
Sur les 3 disques, je vais créer des partitions de tailles égales : 255 heads, 63 sectors/track (512 bytes per sector), 60801 cylinders qui vont êtres reliées entre elles par mdadm pour obtenir un volume logique, sur lequel le système de fichier sera créé.
fdisk permet de créer des partitions de tailles égales. Sur chacun des disques, il faut effacer les partitions existantes et en créer une nouvelle de taille voulue, disons 500GB ou 60801 cylindres dans mon cas.
Pour info : un disque est généralement constitué de secteurs de 512 octets. Dans mon cas, un disque possède 255 têtes (heads), 63 secteurs (sectors) de 512 octets et 60801 cylindres. Lorsque l'on multiplie le tout, on obtiens la taille du disque en octets : 255*63*512*60801 = 500105249280 octets, soit 465 gigaoctets.
Donc, sur les disques, ont crée des partitions de 60801 cylindres (commande « n ») auquelles on donne le type « fd » (commande « t ») qui correspond à Linux Raid Autodetect. Enfin, on écris cela sur le disque et on quitte (commande « w »)
Disk /dev/sdc: 640.1 GB, 640135028736 bytes 255 heads, 63 sectors/track, 77825 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Disk identifier: 0x5956f77c Device Boot Start End Blocks Id System Command (m for help): n Command action e extended p primary partition (1-4) p Partition number (1-4): 1 First cylinder (1-77825, default 1): 1 Last cylinder or +size or +sizeM or +sizeK (1-77825, default 77825): 60801 Command (m for help): t Selected partition 1 Hex code (type L to list codes): fd Changed system type of partition 1 to fd (Linux raid autodetect) Command (m for help): w The partition table has been altered! Calling ioctl() to re-read partition table. Syncing disks.
mdadm doit être présent sur le système pour réaliser les opérations suivantes. Les partitions fraîchement formattées sont reliées dans un volume virtuel de type /dev/mdX (ou X varie de 0 à n selon le nombre de devices sur votre système).
On demande a mdadm de créer (–create) un device nommé /dev/md2 de niveau 5 (–level=5) qui contiendra 3 disques (–raid-devices). On précise également une taille de bloc (–chunk)par défaut de 64Ko et on donne un petit nom à notre volume, ce sera « teradata ». Enfin, on précise les noms des partitions, à savoir /dev/hda1, /dev/sdc1 et /dev/sdd1.
Un petit mot sur la Chunk Size : cela correspond a la taille des blocs que l'on va ventiler sur les disques. Dans notre cas, il y a 3 disques D1, D2 et D3. Si l'on considère une chunk size de 64Ko et que l'on souhaite écrire 256Ko de données, on va avoir :
--> bloc #1 de 128Ko découpage en 2 blocs de 64 Ko stockage du bloc 1 sur D1, stockage du bloc 2 sur D2, stockage du XOR de bloc 1 et bloc 2 sur D3 --> bloc #2 de 128Ko découpage en 2 blocs de 64 Ko stockage du bloc 1 sur D2, stockage du bloc 2 sur D3, stockage du XOR de bloc 1 et bloc 2 sur D1
Par contre, si l'on souhaite écrire moins de 64 Ko de données, on se retrouve quand même à devoir utiliser cette espace sur les 3 disques. Il faut donc bien évaluer la chunk size en fonction de l'utilisation du volume RAID. Ici, je souhaite stocker des fichiers de grandes tailles (photos, videos, …) donc une taille de 64Ko est un bon compromis.
C'est parti pour la création :
# mdadm --create --verbose /dev/md2 --level=5 --raid-devices=3 --chunk=64 --name="teradata" /dev/hda1 /dev/sd[cd]1 mdadm: layout defaults to left-symmetric mdadm: size set to 488383936K mdadm: array /dev/md2 started.
On peut vérifier l'état du volume nouvellement créé :
# cat /proc/mdstat md2 : active (auto-read-only) raid5 sdd1[3](S) sdc1[1] hda1[0] 976767872 blocks level 5, 64k chunk, algorithm 2 [3/2] [UU_]
Ou en détail :
# mdadm --detail /dev/md2 /dev/md2: Version : 00.90 Creation Time : Sat Feb 7 22:03:34 2009 Raid Level : raid5 Array Size : 976767872 (931.52 GiB 1000.21 GB) Used Dev Size : 488383936 (465.76 GiB 500.11 GB) Raid Devices : 3 Total Devices : 3 Preferred Minor : 2 Persistence : Superblock is persistent Update Time : Sat Feb 7 22:03:34 2009 State : clean, degraded Active Devices : 2 Working Devices : 3 Failed Devices : 0 Spare Devices : 1 Layout : left-symmetric Chunk Size : 64K UUID : ba111286:ca1a6ce5:f890ba8d:d38797f9 (local to host zerhuel) Events : 0.1 Number Major Minor RaidDevice State 0 3 1 0 active sync /dev/hda1 1 8 33 1 active sync /dev/sdc1 2 0 0 2 removed 3 8 49 - spare /dev/sdd1
Apparemment, /dev/sdd1 est reconnu en spare. C'est relativement étrange, et je n'ai pas trouvé de raison à ce problème… Par contre, je l'ai corrigé assez facilement en enlevant puis en remettant la partition dans le volume :
# mdadm /dev/md2 -r /dev/sdd1 mdadm: hot removed /dev/sdd1 # cat /proc/mdstat md2 : active raid5 sdc1[1] hda1[0] 976767872 blocks level 5, 64k chunk, algorithm 2 [3/2] [UU_] # mdadm /dev/md2 -a /dev/sdd1 mdadm: re-added /dev/sdd1 # cat /proc/mdstat md2 : active raid5 sdd1[3] sdc1[1] hda1[0] 976767872 blocks level 5, 64k chunk, algorithm 2 [3/2] [UU_] [>....................] recovery = 0.0% (115968/488383936) finish=70.1min speed=115968K/sec
Une fois la construction démarrée, on peut suivre son évolution dans /proc/mdstat ou via mdadm :
# mdadm --detail /dev/md2 /dev/md2: Version : 00.90 Creation Time : Sat Feb 7 22:03:34 2009 Raid Level : raid5 Array Size : 976767872 (931.52 GiB 1000.21 GB) Used Dev Size : 488383936 (465.76 GiB 500.11 GB) Raid Devices : 3 Total Devices : 3 Preferred Minor : 2 Persistence : Superblock is persistent Update Time : Sat Feb 7 22:14:24 2009 State : clean, degraded, recovering Active Devices : 2 Working Devices : 3 Failed Devices : 0 Spare Devices : 1 Layout : left-symmetric Chunk Size : 64K Rebuild Status : 1% complete UUID : ba111286:ca1a6ce5:f890ba8d:d38797f9 (local to host zerhuel) Events : 0.4 Number Major Minor RaidDevice State 0 3 1 0 active sync /dev/hda1 1 8 33 1 active sync /dev/sdc1 3 8 49 2 spare rebuilding /dev/sdd1
Ca prend du temps…..
Question existencielle : Que choisir ???? Du reiserfs ? Non, je vais plutôt y mettre de gros fichiers. JFS alors ? Pas confiance, je sais pas pourquoi, une mauvaise expérience par le passé… Reste XFS, ext3 et le très tentant mais jeune ext4… Je ne retiendrais pas ext4 pour deux raisons : la première c'est que le noyau de ma distrib est 2.6.26 et que ext4 a été intégré comme stable dans la 2.6.28, et la seconde c'est que je préfère ne pas trop jouer sur un serveur censé être fiable. Ext3 étant trop ancien, ce sera donc XFS, qui une fois optimisé devrait donner de bons résultats.
La lecture de [Optimisation XFS] donne quelques options de montages intéressantes. Pour plus d'infos, se reporter à la [XFS kernel doc].
La création et le montage de la partition se font de la façon suivante (il faut disposer des « xfsprogs »):
# mkfs.xfs -f -d su=64k,sw=2 -l lazy-count=1,version=2 -i attr=2 /dev/md2 meta-data=/dev/md2 isize=256 agcount=32, agsize=7630992 blks = sectsz=4096 attr=2 data = bsize=4096 blocks=244191744, imaxpct=25 = sunit=16 swidth=32 blks naming =version 2 bsize=4096 log =internal log bsize=4096 blocks=32768, version=2 = sectsz=4096 sunit=1 blks, lazy-count=1 realtime =none extsz=131072 blocks=0, rtextents=0
Les options sont détaillées dans le man mkfs.xfs. On précise ici la chunk size et le nombre de volumes actifs (2) dans les options « su » et « sw ». Le lazy-count et la version concernent la journalisation, et l'on précise également la version des attr que l'on souhaite.
Parmi les options de montage XFS, on a :
95 logbsize=value 96 Set the size of each in-memory log buffer. 97 Size may be specified in bytes, or in kilobytes with a "k" suffix. 98 Valid sizes for version 1 and version 2 logs are 16384 (16k) and 99 32768 (32k). Valid sizes for version 2 logs also include 100 65536 (64k), 131072 (128k) and 262144 (256k). 101 The default value for machines with more than 32MiB of memory 102 is 32768, machines with less memory use 16384 by default.
Celui la est positionné a la valeur la plus haute, la consommation mémoire n'est pas un problème.
119 noatime 120 Access timestamps are not updated when a file is read.
On gagne du temps lors de l'accès à un fichier.
84 logbufs=value 85 Set the number of in-memory log buffers. Valid numbers range 86 from 2-8 inclusive. 87 The default value is 8 buffers for filesystems with a 88 blocksize of 64KiB, 4 buffers for filesystems with a blocksize 89 of 32KiB, 3 buffers for filesystems with a blocksize of 16KiB 90 and 2 buffers for all other configurations. Increasing the 91 number of buffers may increase performance on some workloads 92 at the cost of the memory used for the additional log buffers 93 and their associated control structures.
On a un chunk size à 64Ko, on positionne donc 8 buffers de log.
157 sunit=value and swidth=value 158 Used to specify the stripe unit and width for a RAID device or 159 a stripe volume. "value" must be specified in 512-byte block 160 units. 161 If this option is not specified and the filesystem was made on 162 a stripe volume or the stripe width or unit were specified for 163 the RAID device at mkfs time, then the mount system call will 164 restore the value from the superblock. For filesystems that 165 are made directly on RAID devices, these options can be used 166 to override the information in the superblock if the underlying 167 disk layout changes after the filesystem has been created. 168 The "swidth" option is required if the "sunit" option has been 169 specified, and must be a multiple of the "sunit" value.
Il n'est pas vraiment nécessaire de renseigner ces valeurs, car XFS peut aller les chercher tout seul dans les superblocks des membres du RAID… Pour l'aspect pédagogique, ils sont ici renseignés afin de coller a la chunk size.
134 osyncisosync 135 Make O_SYNC writes implement true O_SYNC. WITHOUT this option, 136 Linux XFS behaves as if an "osyncisdsync" option is used, 137 which will make writes to files opened with the O_SYNC flag set 138 behave as if the O_DSYNC flag had been used instead. 139 This can result in better performance without compromising 140 data safety. 141 However if this option is not in effect, timestamp updates from 142 O_SYNC writes can be lost if the system crashes. 143 If timestamp updates are critical, use the osyncisosync option.
Améliore le comportement synchrone/asynchrone de XFS pour améliorer les performances globales.
Au final, la ligne dans /etc/fstab est la suivante :
# grep xfs /etc/fstab /dev/md2 /data/teradata xfs noatime,nodiratime,logbufs=8,logbsize=262144,osyncisdsync 0 0
hdparm permet de faire des tests de base. Tout d'abord, sur les disques membres du RAID :
# hdparm -tT /dev/sdc1 /dev/sdc1: Timing cached reads: 1758 MB in 2.00 seconds = 879.86 MB/sec Timing buffered disk reads: 310 MB in 3.02 seconds = 102.71 MB/sec # hdparm -tT /dev/sdd1 /dev/sdd1: Timing cached reads: 1766 MB in 2.00 seconds = 883.56 MB/sec Timing buffered disk reads: 314 MB in 3.00 seconds = 104.54 MB/sec # hdparm -tT /dev/hda1 /dev/hda1: Timing cached reads: 1734 MB in 2.00 seconds = 867.99 MB/sec Timing buffered disk reads: 228 MB in 3.00 seconds = 75.88 MB/sec
On a des perfs de cache similaires mais des différences entre les disques SATA plus récents (les 640GB) et le disque IDE de 500GB. Ca reste tout de même honorable, entre 75Mo/s pour l'IDE et 104Mo/s pour le SATA.
Maintenant concernant le volume :
# hdparm -tT /dev/md2 /dev/md2: Timing cached reads: 1728 MB in 2.00 seconds = 864.53 MB/sec Timing buffered disk reads: 452 MB in 3.00 seconds = 150.57 MB/sec
150Mo/s en lecture ! + 50% de gain grace au RAID5, c'est plutôt une bonne affaire.
Un petit test brutal pour regarder la vitesse d'écriture :
$ dd if=/dev/zero of=bigfile count=512K bs=64K 524288+0 enregistrements lus 524288+0 enregistrements écrits 34359738368 bytes (34 GB) copied, 310,462 s, 111 MB/s
111Mo/s… je suis satisfait !
Après, il faut regarder du coté de bonnie++ :
Bonnie++ benchmarks three things: data read and write speed, number of seeks that can be performed per second, and number of file metadata operations that can be performed per second. Metadata operations include file creation and deletion as well as getting metadata such as the file size or owner (the result of a fstat(2) call).
$ /usr/sbin/bonnie++ -d /data/teradata/foo/ -s 6G Writing with putc()...done Writing intelligently...done Rewriting...done Reading with getc()...done Reading intelligently...done start 'em...done...done...done... Create files in sequential order...done. Stat files in sequential order...done. Delete files in sequential order...done. Create files in random order...done. Stat files in random order...done. Delete files in random order...done. Version 1.03d ------Sequential Output------ --Sequential Input- --Random- -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks-- Machine Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP /sec %CP zerhuel 6G 40584 94 120436 25 47101 14 47423 95 156913 22 400.2 1 ------Sequential Create------ --------Random Create-------- -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete-- files /sec %CP /sec %CP /sec %CP /sec %CP /sec %CP /sec %CP 16 5959 31 +++++ +++ 2927 15 5905 32 +++++ +++ 3074 18 zerhuel,6G,40584,94,120436,25,47101,14,47423,95,156913,22,400.2,1,16,5959,31,+++++,+++,2927,15,5905,32,+++++,+++,3074,18
Pour interpréter les résultats, se reporter à cet article.
Sur mes 3 disques, j'en ai 2 de 640GB. Il me reste donc un peu de place à utiliser. Cette place va servir à un gros volume RAID 0 de 240GB pour stocker des données non importantes. Pour créer ce volume, il faut stopper /dev/md2, modifier les partitions avec fdisk, créer le RAID0 dans /dev/md3 et créer le système de fichiers. Puis, on redémarre /dev/md2.
Let's go:
# mdadm --stop /dev/md2 mdadm: stopped /dev/md2 # fdisk /dev/sdc [...] Device Boot Start End Blocks Id System /dev/sdc1 1 60801 488384001 fd Linux raid autodetect /dev/sdc2 60802 77825 136745280 fd Linux raid autodetect Command (m for help): w The partition table has been altered! Calling ioctl() to re-read partition table. Syncing disks. root@zerhuel:/home/julien/# fdisk /dev/sdd [...] Device Boot Start End Blocks Id System /dev/sdd1 1 60801 488384001 fd Linux raid autodetect /dev/sdd2 60802 77825 136745280 fd Linux raid autodetect Command (m for help): w The partition table has been altered! Calling ioctl() to re-read partition table. Syncing disks. # mdadm --create --verbose /dev/md3 --level=0 --raid-devices=2 --chunk=64 /dev/sd[cd]2 mdadm: array /dev/md3 started. # cat /proc/mdstat Personalities : [raid1] [raid6] [raid5] [raid4] [raid0] md3 : active raid0 sdd2[1] sdc2[0] 273490432 blocks 64k chunks md1 : active raid1 sda3[0] sdb3[1] 97683136 blocks [2/2] [UU] md0 : active raid1 sda1[0] sdb1[1] 18554944 blocks [2/2] [UU] unused devices: <none> root@zerhuel:/home/julien/# mdadm --detail /dev/md3 /dev/md3: Version : 00.90 Creation Time : Sun Feb 8 10:54:50 2009 Raid Level : raid0 Array Size : 273490432 (260.82 GiB 280.05 GB) Raid Devices : 2 Total Devices : 2 Preferred Minor : 3 Persistence : Superblock is persistent Update Time : Sun Feb 8 10:54:50 2009 State : clean Active Devices : 2 Working Devices : 2 Failed Devices : 0 Spare Devices : 0 Chunk Size : 64K UUID : 774ba314:72323e5b:f890ba8d:d38797f9 (local to host zerhuel) Events : 0.1 Number Major Minor RaidDevice State 0 8 34 0 active sync /dev/sdc2 1 8 50 1 active sync /dev/sdd2 # mkfs.xfs -f -d su=64k,sw=2 -l lazy-count=1,version=2 -i attr=2 /dev/md3 meta-data=/dev/md3 isize=256 agcount=16, agsize=4273296 blks = sectsz=512 attr=2 data = bsize=4096 blocks=68372608, imaxpct=25 = sunit=16 swidth=32 blks naming =version 2 bsize=4096 log =internal log bsize=4096 blocks=32768, version=2 = sectsz=512 sunit=16 blks, lazy-count=1 realtime =none extsz=131072 blocks=0, rtextents=0
Un rapide test :
# hdparm -tT /dev/md3 /dev/md3: Timing cached reads: 1776 MB in 2.00 seconds = 888.17 MB/sec Timing buffered disk reads: 440 MB in 3.01 seconds = 146.29 MB/sec
Pas mal ! Reste plus qu'a redémarrer /dev/md2 et remonter les partitions. L'avantage des superblocks, c'est qu'il permettent à mdadm de retrouver les partitions tout seul en faisant un scan. Il suffit donc de lui dire :
# mdadm --assemble --scan mdadm: /dev/md2 has been started with 3 drives.
Et on retrouve notre /dev/md2 :
# cat /proc/mdstat [...] md2 : active (auto-read-only) raid5 hda1[0] sdd1[2] sdc1[1] 976767872 blocks level 5, 64k chunk, algorithm 2 [3/3] [UUU]
References :
Howto : http://tldp.org/HOWTO/Software-RAID-HOWTO-5.html
Benchmark : http://home.comcast.net/~jpiszcz/20080607/raid-benchmarks-decimal-fix-and-right-justified/disks.html
Chunk size : http://forums.storagereview.net/index.php?showtopic=26857
XFS kernel doc : http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=Documentation/filesystems/xfs.txt;hb=HEAD
Optimisation XFS : http://www.mail-archive.com/linux-raid@vger.kernel.org/msg09204.html
Bonnie++ : http://www.linux.com/feature/139742
~~DISCUSSION~~