Raid software con User Mode Linux

Condividi:

In questo articolo abiliteremo il RAID software su User Mode Linux. Compileremo il driver nel kernel della macchina virtuale, creeremo i file per alcuni dispositivi virtuali, installeremo il software per l’amministrazione degli array e testeremo varie tipologie RAID supportate. Utilizzeremo il root_fs di una virtual machine già creata con sufficiente spazio disco per l’installazione del software necessario ed il supporto di rete per eseguire apt

Ambiente di lavoro

Supponiamo di avere preparato il root_fs per la virtual machine di test nel file uml-ubda nella cartella ~/uml/uml-raid

Per la compilazione del kernel abbiamo bisogno di alcuni pacchetti

# apt install -y gcc make bc libncurses5-dev
Supporto RAID

Il supporto multi device non è abilitato nella versione normalmente distribuita con il pacchetto debian di user mode linux, quindi lo compiliamo monoliticamente all’interno del file linux che usiamo per eseguire le virtual machine. Visualizziamo la versione di kernel utilizzata dall’ uml che abbiamo installato

# linux --version
4.9.25

Quindi scarichiamo da kernel.org l’archivio con lo stesso numero di versione salvandolo in una cartella di lavoro, decomprimiamo l’archivio ed entriamo nella cartella

# mkdir uml-src

# cd uml-src/

# wget https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.9.25.tar.gz

# tar -xzf linux-4.9.25.tar.gz

# cd linux-4.9.25

Creiamo una configurazione di default

# make defconfig ARCH=um

Ora modifichiamo la configurazione

# make menuconfig ARCH=um

Ci troviamo nella root del menù di configurazione

Entriamo in “Device Drivers”, spostiamoci su “Multiple devices driver support (RAID and LVM)” ed abilitiamo il supporto con Y

Entriamo nel menù, abilitiamo con Y il menù “RAID support” e tutte le voci che si aprono sotto di esso, sempre con Y

Usciamo dando 3 volte Exit e salviamo la configurazione

Avviamo la compilazione

# make all ARCH=um

Terminata la compilazione, copiamo l’eseguibile linux risultante nella cartella in cui abbiamo preparato la macchina virtuale da utilizzare per i test. Per avviare la virtual machine, dovremo lanciare questo eseguibile al posto di quello distribuito con il pacchetto uml. Copiamo quindi l’eseguibile rinominandolo in modo da distinguerlo da quello originale

# cp linux ~/uml/uml-raid/linux-raid
Preparazione dei dischi

Prepariamo i file per i dischi virtuali che ci servono. Per i nostri test creiamo 4 file di cui uno da 100 MB e tre da 200 MB per mostrare le differenze tra i vari livelli di array

# dd if=/dev/zero of=disk0 bs=1M count=100
# dd if=/dev/zero of=disk1 bs=1M count=200
# dd if=/dev/zero of=disk2 bs=1M count=200
# dd if=/dev/zero of=disk3 bs=1M count=200
# dd if=/dev/zero of=disk4 bs=1M count=200
Avvio della Virtual Machine

Avviamo la nostra uml, assegnandole il nome uml-raid con il supporto di rete di navigazione tramite l’host sull’ interfaccia eth0 che avremo già opportunamente configurato al suo interno

# ./linux-raid \
  umid=uml-raid \
  ubda=uml-ubda \
  root=/dev/ubda1 \
  ubdb=disk0 ubdc=disk1 ubdd=disk2 ubde=disk3 ubdf=disk4 \
  mem=128M \
  con0=fd:0,fd:1 \
  con=pts \
  eth0=tuntap,,fe:fd:00:00:01:01,10.0.1.1

In un secondo terminale entriamo nella vm facendo login

# screen /dev/pts/4

Nella vm possiamo vedere che in /dev è presente il dispositivo multi device md0 ed i 5 dischi virtuali

# ls /dev/ubd?
/dev/ubda  /dev/ubdb  /dev/ubdc  /dev/ubdd  /dev/ubde /dev/ubdf

# ls /dev/md*
/dev/md0

Creiamo altri due device md che ci serviranno per i test con alcune configurazioni ibride

# mknod -m 0660 /dev/md1 b 9 1

# mknod -m 0660 /dev/md2 b 9 2

# ls /dev/md*
/dev/md0  /dev/md1  /dev/md2

Installiamo il software per l’amministrazione dei dispositivi raid

# apt install mdadm

Creiamo una cartella in cui montare il raid

# mkdir -p /mnt/raid
Test dei livelli RAID supportati

Eseguiamo alcune prove sui livelli di RAID supportati dal driver. Ogni volta che costruiremo un array, ripuliremo prima i dischi utilizzati dai meta-dati contenuti in questo modo

# mdadm --zero-superblock /dev/ubdb
# mdadm --zero-superblock /dev/ubdc
# mdadm --zero-superblock /dev/ubdd
# mdadm --zero-superblock /dev/ubde
# mdadm --zero-superblock /dev/ubdf

Dopo aver costruito l’array, formatteremo il device /dev/md0 a cui è collegato in ext2 senza creare prima una partizione al suo interno. E’ un passaggio inutile ai fini dei test, servirebbe solo se volessimo montare quella partizione al boot della vm

# mke2fs /dev/md0

Monteremo il device sul mount point  /mnt/raid

# mount /dev/md0 /mnt/raid

Dopo aver eseguito i test lo smonteremo e lo fermeremo l’array in modo da liberare i dischi per il test successivo

# umount /dev/md0
# mdadm --stop /dev/md0
RAID 0 ( striping )

Questo livello di raid crea un dispositivo con una capienza che è la somma dello spazio disponibile ai dischi montati nell’ array. Non viene riservato spazio per la ridondanza, ed i blocchi vengono distribuiti tra tutti i nodi. Questo permette una lettura in parallelo tra tutti i nodi incrementando la velocità in lettura e scrittura, ma il guasto ad un solo nodo compromette la coerenza di tutto il raid

Utilizziamo i primi due dischi, quello da 100 MB e quello da 200 MB passando a mdadm rispettivamente /dev/ubdb e /dev/ubdc

# mdadm --create --verbose /dev/md0 \
--level=0 \
--raid-devices=2 \
/dev/ubdb /dev/ubdc

mdadm: chunk size defaults to 512K
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.

Il raid presenta queste caratteristiche

# mdadm --detail /dev/md0
/dev/md0:
        Version : 1.2
  Creation Time : Wed Mar  6 14:35:48 2019
     Raid Level : raid0
     Array Size : 305152 (298.00 MiB 312.48 MB)
   Raid Devices : 2
  Total Devices : 2
    Persistence : Superblock is persistent

    Update Time : Wed Mar  6 14:35:48 2019
          State : clean
 Active Devices : 2
Working Devices : 2
 Failed Devices : 0
  Spare Devices : 0

     Chunk Size : 512K

           Name : uml-base:0  (local to host uml-base)
           UUID : 4ec8d2d6:ecd2f855:eb8a0cf5:172fc080
         Events : 0

    Number   Major   Minor   RaidDevice State
       0      98       16        0      active sync   /dev/ubdb
       1      98       32        1      active sync   /dev/ubdc

Creiamo il filesystem in /dev/md0, montiamo il device su /mnt/raid e visualizziamo lo spazio disponibile

# df -h /mnt/raid/

Filesystem      Size  Used Avail Use% Mounted on
/dev/md0        289M  2.1M  272M   1% /mnt/raid

Nel raid ci sono circa 300 MB

Smontiamo il device, terminiamo l’array e ripuliamo i dischi, dopodiché ripetiamo le stesse operazioni con 2 dischi da 200MB, ad esempio ubdbc ed ubdd. Riformattiamo il device /dev/md0 e verifichiamo lo spazio disponibile

# mdadm --detail /dev/md0
/dev/md0:
        Version : 1.2
  Creation Time : Wed Mar  6 14:39:50 2019
     Raid Level : raid0
     Array Size : 407552 (398.00 MiB 417.33 MB)
   Raid Devices : 2
  Total Devices : 2
    Persistence : Superblock is persistent

    Update Time : Wed Mar  6 14:39:50 2019
          State : clean
 Active Devices : 2
Working Devices : 2
 Failed Devices : 0
  Spare Devices : 0

     Chunk Size : 512K

           Name : uml-base:0  (local to host uml-base)
           UUID : e65c3076:0194e40e:55ccd945:c2006f47
         Events : 0

    Number   Major   Minor   RaidDevice State
       0      98       32        0      active sync   /dev/ubdc
       1      98       48        1      active sync   /dev/ubdd

# df -h /mnt/raid/
Filesystem      Size  Used Avail Use% Mounted on
/dev/md0        386M  2.3M  364M   1% /mnt/raid

La capienza ora è di quasi 400 MB

Aggiungendo altri dischi, la capacità totale continua a crescere

# mdadm --create --verbose /dev/md0 \
--level=0 \
--raid-devices=4 \
/dev/ubdb /dev/ubdc /dev/ubdd /dev/ubde

# ...

# df -h /mnt/raid/
Filesystem      Size  Used Avail Use% Mounted on
/dev/md0        686M  712K  650M   1% /mnt/raid
RAID 1 ( mirroring )

Questo livello di array è dotato di ridondanza. I nodi che partecipano all’ array, che possono essere dischi o array raid, sono uno lo specchio degli altri. Questo implica che la capacità totale dell’array è limitata dal nodo di dimensioni inferiori. Utilizziamo l’opzione –level=1 e montiamo i primi due dischi

# mdadm --create --verbose /dev/md0 \
--level=1 \
--raid-devices=2 \
/dev/ubdb /dev/ubdc

Nella fase di creazione dell’ array il driver ci avverte che i due dischi sono di dimensioni molto diverse, cosa che con il raid 0 non succede.

mdadm: size set to 102272K
mdadm: largest drive (/dev/ubdc) exceeds size (102272K) by more than 1%

Dopo le operazioni viste prima, visualizziamo le caratteristiche del raid e lo spazio disponibile

# mdadm --detail /dev/md0
/dev/md0:
        Version : 1.2
  Creation Time : Wed Mar  6 15:15:17 2019
     Raid Level : raid1
     Array Size : 102272 (99.88 MiB 104.73 MB)
  Used Dev Size : 102272 (99.88 MiB 104.73 MB)
   Raid Devices : 2
  Total Devices : 2
    Persistence : Superblock is persistent

    Update Time : Wed Mar  6 15:15:17 2019
          State : clean, resyncing
 Active Devices : 2
Working Devices : 2
 Failed Devices : 0
  Spare Devices : 0

  Resync Status : 35% complete

           Name : uml-base:0  (local to host uml-base)
           UUID : ab91e115:e4eba8d1:383cada0:0728f313
         Events : 3

    Number   Major   Minor   RaidDevice State
       0      98       16        0      active sync   /dev/ubdb
       1      98       32        1      active sync   /dev/ubdc


# df -h /mnt/raid/
Filesystem      Size  Used Avail Use% Mounted on
/dev/md0         97M  1.6M   91M   2% /mnt/raid

Ora sono disponibili quasi 100MB  limitati dal disco ubdb. Ripetiamo l’operazione utilizziamo due dischi da 200 MB

mdadm --create --verbose /dev/md0 \
--level=1 \
--raid-devices=2 \
/dev/ubdc /dev/ubdd

Visualizziamo lo spazio disponibile

# mdadm --detail /dev/md0
/dev/md0:
        Version : 1.2
  Creation Time : Wed Mar  6 15:19:52 2019
     Raid Level : raid1
     Array Size : 204608 (199.81 MiB 209.52 MB)
  Used Dev Size : 204608 (199.81 MiB 209.52 MB)
   Raid Devices : 2
  Total Devices : 2
    Persistence : Superblock is persistent

    Update Time : Wed Mar  6 15:19:52 2019
          State : clean, resyncing
 Active Devices : 2
Working Devices : 2
 Failed Devices : 0
  Spare Devices : 0

  Resync Status : 21% complete

           Name : uml-base:0  (local to host uml-base)
           UUID : 54ffdff2:cc8b09fb:4490eaf5:ac57f4cf
         Events : 1

    Number   Major   Minor   RaidDevice State
       0      98       32        0      active sync   /dev/ubdc
       1      98       48        1      active sync   /dev/ubdd

# df -h /mnt/raid/
Filesystem      Size  Used Avail Use% Mounted on
/dev/md0        194M  1.6M  182M   1% /mnt/raid

Ora ci sono quasi 200M

Ripetiamo l’operazione includendo tutti i 4 dischi, vediamo che ubdb limita lo spazio di tutto l’array poichè solo 100 MB possono essere replicati

# mdadm --create --verbose /dev/md0 \
--level=1 \
--raid-devices=4 \
/dev/ubdb /dev/ubdc /dev/ubdd /dev/ubde

# ...

# df -h /mnt/raid/
Filesystem      Size  Used Avail Use% Mounted on
/dev/md0         97M  1.6M   91M   2% /mnt/raid
RAID 4

Il Raid 4 è molto simile al Raid 0 perchè distribuisce i blocchi tra i nodi sommando le capacità di ciascuno, la differenza è che utilizza l’ultimo dei rami per memorizzare i blocchi di parità.

Ogni blocco di parità viene calcolato tramite una XOR tra tutti i corrispondenti blocchi dati sugli altri rami, questo significa che detto N il numero di nodi, e D la dimensione del ramo più piccolo, la capacità dell’array sarà dell’ ordine di ( N – 1 ) * D

In questo modo nel caso in cui uno dei dischi si dovesse rompere è sempre possibile ricostruirne il contenuto grazie agli altri

E’ veloce in lettura perchè può sfruttare il parallelismo su rami diversi, però in scrittura è leggermente più lento perchè il driver oltre a scrivere il blocco, deve leggere i blocchi sugli altri rami, calcolare e poi scrivere la parità. La capacità viene limitata dal ramo di dimensione minore, invece se i sono tutti della stessa dimensione, vengono sommate le capacità di tutti i rami dedicati ai dati

Possiamo creare l’array con soli 2 dischi, come prima proviamo con due dischi di dimensioni diverse, utilizziamo quello da 100 MB per i dati e quello da 200MB per la parità

# mdadm --create --verbose /dev/md0 \
--level=4 \
--raid-devices=2 \
/dev/ubdb /dev/ubdc

Lo spazio disponibile è di circa 100 MB

# df -h /mnt/raid/
Filesystem      Size  Used Avail Use% Mounted on
/dev/md0         96M  1.6M   90M   2% /mnt/raid

Aggiungiamo un terzo disco da 200 MB

# mdadm --create --verbose /dev/md0 \
--level=4 \
--raid-devices=3 \
/dev/ubdb /dev/ubdc /dev/ubdd

Lo spazio disponibile è passato a 2 * 100 MB = 200 MB ( circa )

# df -h /mnt/raid/
Filesystem      Size  Used Avail Use% Mounted on
/dev/md0        192M  1.6M  181M   1% /mnt/raid

Aggiungiamo il quarto disco da 200MB

# mdadm --create --verbose /dev/md0 \
--level=4 \
--raid-devices=4 \
/dev/ubdb /dev/ubdc /dev/ubdd /dev/ubde

Lo spazio disponibile è passato a 3 * 100 MB = 300 MB ( circa )

# df -h /mnt/raid/
Filesystem      Size  Used Avail Use% Mounted on
/dev/md0        288M  2.1M  271M   1% /mnt/raid
RAID 5

E’ molto simile al raid 4, solo che i blocchi di parità sono distribuiti tra tutti i nodi cosa che permette di sfruttare meglio il parallelismo in lettura ed in scrittura tra i rami. Eseguire questi test sul raid 5 darebbe gli stessi risultati

RAID 6

Questo livello è simile al 5, però utilizza 2 rami per la parità quindi permette il guasto simultaneo fino a 2 nodi

Come prima, detto N il numero di nodi, e D la dimensione del ramo più piccolo, la capacità dell’array sarà dell’ ordine di ( N – 2 ) * D

Se prendiamo l’esempio del livello 4 con 4 dischi e creiamo un array di livello 6, vediamo che la capacità totale scende a circa 200 MB, ma possiamo perdere fino a 2 dischi senza perdere i dati contenuti

# mdadm --create --verbose /dev/md0 \
--level=6 \
--raid-devices=4 \
/dev/ubdb /dev/ubdc /dev/ubdd /dev/ubde

( 4 – 2 ) * 100MB = 200 MB ( circa )

# df -h /mnt/raid/
Filesystem      Size  Used Avail Use% Mounted on
/dev/md0        192M  1.6M  181M   1% /mnt/raid
RAID 10 ( mirrored striping )

Anche detto raid 1+0 è una combinazione dei due livelli, dove ogni blocco dati viene duplicato un certo numero di volte e distribuito sui diversi rami

Avendo i blocchi dati e di mirror distribuiti su tutti i nodi dell’array, questa configurazione è molto performante sia in lettura che in scrittura ed è particolarmente indicata nello storage dei database

# mdadm --create --verbose /dev/md0 \
--level=10 \
--raid-devices=4 \
/dev/ubdb /dev/ubdc /dev/ubdd /dev/ubde

Dopo aver montato il raid con uno dei dischi da 100 MB vediamo come si presenta e lo spazio disponibile

# mdadm --detail /dev/md0
/dev/md0:
        Version : 1.2
  Creation Time : Thu Mar  7 16:05:48 2019
     Raid Level : raid10
     Array Size : 202752 (198.00 MiB 207.62 MB)
  Used Dev Size : 101376 (99.00 MiB 103.81 MB)
   Raid Devices : 4
  Total Devices : 4
    Persistence : Superblock is persistent

    Update Time : Thu Mar  7 16:06:41 2019
          State : clean
 Active Devices : 4
Working Devices : 4
 Failed Devices : 0
  Spare Devices : 0

         Layout : near=2
     Chunk Size : 512K

           Name : uml-base:0  (local to host uml-base)
           UUID : a32e357e:678256e9:0c517078:5a7cb46d
         Events : 17

    Number   Major   Minor   RaidDevice State
       0      98       16        0      active sync set-A   /dev/ubdb
       1      98       32        1      active sync set-B   /dev/ubdc
       2      98       48        2      active sync set-A   /dev/ubdd
       3      98       64        3      active sync set-B   /dev/ubde

# df -h /mnt/raid/
Filesystem      Size  Used Avail Use% Mounted on
/dev/md0        192M  1.6M  181M   1% /mnt/raid

Se invece utilizziamo i 4 dischi da 200MB

# mdadm --create --verbose /dev/md0 \
--level=10 \
--raid-devices=4 \
dev/ubdc /dev/ubdd /dev/ubde /dev/ubdf

Dopo aver montato il raid con uno dei dischi da 100 MB vediamo come si presenta e lo spazio disponibile

# mdadm --detail /dev/md0
/dev/md0:
        Version : 1.2
  Creation Time : Thu Mar  7 16:28:11 2019
     Raid Level : raid10
     Array Size : 407552 (398.00 MiB 417.33 MB)
  Used Dev Size : 203776 (199.00 MiB 208.67 MB)
   Raid Devices : 4
  Total Devices : 4
    Persistence : Superblock is persistent

    Update Time : Thu Mar  7 16:28:11 2019
          State : clean, resyncing
 Active Devices : 4
Working Devices : 4
 Failed Devices : 0
  Spare Devices : 0

         Layout : near=2
     Chunk Size : 512K

  Resync Status : 8% complete

           Name : uml-base:0  (local to host uml-base)
           UUID : 96c71cf1:c8f779be:54de6776:1709799d
         Events : 1

    Number   Major   Minor   RaidDevice State
       0      98       32        0      active sync set-A   /dev/ubdc
       1      98       48        1      active sync set-B   /dev/ubdd
       2      98       64        2      active sync set-A   /dev/ubde
       3      98       80        3      active sync set-B   /dev/ubdf


# df -h /mnt/raid/
Filesystem      Size  Used Avail Use% Mounted on
/dev/md0        386M  2.3M  364M   1% /mnt/raid
Configurazioni ibride

Montando assieme raid diversi, possiamo creare diverse configurazioni in modo da sfruttare tutte le caratteristiche dei vari livelli raid. La più banale che si può implementare è il raid 10, combinando due raid 1 ed un raid 0. Per farlo utilizziamo i due device /dev/md1 e /dev/md2 creati all’inizio

# mdadm --create --verbose /dev/md1 \
--level=1 \
--raid-devices=2 \
/dev/ubdc /dev/ubdd

# mdadm --create --verbose /dev/md2 \
--level=1 \
--raid-devices=2 \
/dev/ubde /dev/ubdf

# mdadm --create --verbose /dev/md0 \
--level=0 \
--raid-devices=2 \
/dev/md1 /dev/md2

La situazione è identica a quella mostrata per il raid 10, solo che l’array viene gestito con un totale di 3 raid, uno per lo striping e due per il mirroring in cui è contenuto un disco mirror per ciascun ramo

# mdadm /dev/md1
/dev/md1: 199.81MiB raid1 2 devices, 0 spares. Use mdadm --detail for more detail.
/dev/md1: device 0 in 2 device active raid0 /dev/md0.  Use mdadm --examine for more detail.
# mdadm /dev/md2
/dev/md2: 199.81MiB raid1 2 devices, 0 spares. Use mdadm --detail for more detail.
/dev/md2: device 1 in 2 device active raid0 /dev/md0.  Use mdadm --examine for more detail.
# mdadm /dev/md0
/dev/md0: 397.00MiB raid0 2 devices, 0 spares. Use mdadm --detail for more detail.

# df -h /mnt/raid/
Filesystem      Size  Used Avail Use% Mounted on
/dev/md0        385M  2.3M  363M   1% /mnt/raid

In questo modo possiamo anche creare configurazioni atipiche, ad esempio un raid 0 che fa striping su un raid 1 ed un raid 5

# mdadm --create --verbose /dev/md1 \
--level=1 \
--raid-devices=2 \
/dev/ubdb /dev/ubdc

# mdadm --create --verbose /dev/md2 \
--level=5 \
--raid-devices=3 \
/dev/ubdd /dev/ubde /dev/ubdf

# mdadm --create --verbose /dev/md0 \
--level=0 \
--raid-devices=2 \
/dev/md1 /dev/md2

Combina la capacità di 100 MB del raid 1 con un disco da 100 MB con quella da 400 MB del raid 5, i due rami hanno un disco di mirror ed un disco di parità

# mdadm /dev/md1
/dev/md1: 99.88MiB raid1 2 devices, 0 spares. Use mdadm --detail for more detail.
/dev/md1: device 0 in 2 device active raid0 /dev/md0.  Use mdadm --examine for more detail.
# mdadm /dev/md2
/dev/md2: 398.00MiB raid5 3 devices, 0 spares. Use mdadm --detail for more detail.
/dev/md2: device 1 in 2 device active raid0 /dev/md0.  Use mdadm --examine for more detail.
# mdadm /dev/md0
/dev/md0: 495.50MiB raid0 2 devices, 0 spares. Use mdadm --detail for more detail.

# df -h /mnt/raid/
Filesystem      Size  Used Avail Use% Mounted on
/dev/md0        480M  2.3M  453M   1% /mnt/raid