Resizable Storage file systems under Linux

Today there are many ways to create and maintain a large data storage system that is reliable, resizable, and cheap. The built in software RAID5/6 and LVM(Logical Volume Management) tools in Linux coupled with inexpensive drive arrays make this possible. As of 2007 you can build a Linux based system with over 12TB of usable storage for under $7,000. A NAS or SAN system with similar storage will cost you at least twice as much. In this article I'm going to go over the steps using Linux to setup resizable storage using LVM.

Like many things in linux there is more than one way to build a grow-able file system. You can use multiple hardware RAID arrays with LVM. You can use the Linux software RAID tools with LVM. Or you can use all three together. For example you could get two 16 bay SATA JBOD enclosures and use software RAID5 or 6 with LVM to make one huge storage space. Or you could buy a SATA card that does hardware RAID and all the devices would show up as one combined device under Linux. Use LVM on it and you can grow that in the future.

Using LVM to grow two hardware RAID arrays into one contiguous storage filesystem:

A proper hardware RAID array will show up in Linux as a single drive. The RAID controller is what is used to manage the array. This makes it easy for us on the Linux side since we have less variables to deal with. All we need to do is partition the device and add the partition to a logical volume group. In this example I will use two 2GB disk drives as our two hardware RAID arrays. I will create a logical volume group and use one of the disks. I will then add the other disk and expand the logical volume to include the new space.

Note: The steps in this article work under Red Hat Enterprise Linux 5 and Ubuntu 7.04. For Ubuntu users you will need to have LVM2 and resize2fs installed. Run the following command to install them.

sudo apt-get install lvm2 resize2fs

First thing to do is list the arrays in fdisk to make sure Linux sees them. Use the “fdisk -l” command.

fdisk -l

Disk /dev/sda: 10.7 GB, 10737418240 bytes
255 heads, 63 sectors/track, 1305 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System
/dev/sda1 * 1 1244 9992398+ 83 Linux
/dev/sda2 1245 1305 489982+ 5 Extended
/dev/sda5 1245 1305 489951 82 Linux swap / Solaris

Disk /dev/sdb: 2147 MB, 2147483648 bytes
255 heads, 63 sectors/track, 261 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Disk /dev/sdb doesn't contain a valid partition table

Disk /dev/sdc: 2147 MB, 2147483648 bytes
255 heads, 63 sectors/track, 261 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Disk /dev/sdc doesn't contain a valid partition table  

My two drives show up as /dev/sdb and /dev/sdc. Parallel ATA drives show up as /dev/hdx. Serial ATA drives, USB , Firewire, SCSI, and most disk arrays show up as /dev/sdx. The x at the end donates the order of the drive using alphabet characters a-x.

Now we need to setup a partition on these disks.

fdisk /dev/sdb

Hit the n key then hit return. Hit the p key to choose primary partition then hit return. hit the 1 key then hit return. Hit the return key two more times to select the default of using the whole drive. Hit the t key and type 8e and hit return. This will make the partition as a Linux LVM. Hit the w key and then hit return to write the changes to disk. Repeat the above steps for the other disk array.

After you are done partitioning the disks they will be represented as a 1 next to the device. Do another fdisk -l to see the new partitions.

fdisk -l

Disk /dev/sda: 10.7 GB, 10737418240 bytes
255 heads, 63 sectors/track, 1305 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System
/dev/sda1 * 1 1244 9992398+ 83 Linux
/dev/sda2 1245 1305 489982+ 5 Extended
/dev/sda5 1245 1305 489951 82 Linux swap / Solaris

Disk /dev/sdb: 2147 MB, 2147483648 bytes
255 heads, 63 sectors/track, 261 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System
/dev/sdb1 1 261 2096451 8e Linux LVM

Disk /dev/sdc: 2147 MB, 2147483648 bytes
255 heads, 63 sectors/track, 261 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System
/dev/sdc1 1 261 2096451 8e Linux LVM

We can now add one of the disks to be seen by LVM as a physical volume. Use the pvcreate command.

pvcreate /dev/sdb1
Physical volume "/dev/sdb1" successfully created

Create the volume group using the vgcreate command. I will name mine array-pool.

vgcreate array-pool /dev/sdb1
Volume group "array-pool" successfully created

Create a logical volume using the lvcreate command.

lvcreate -L 2000 array-pool
Logical volume "lvol0" created

The above command created a logical volume of 2GB with a default name lvol0.

Format the logical volume with your desired filesystem. Here I will use ext3.

mkfs.ext3 /dev/array-pool/lvol0

mke2fs 1.40-WIP (14-Nov-2006)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
256000 inodes, 512000 blocks
25600 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=524288000
16 block groups
32768 blocks per group, 32768 fragments per group
16000 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912

Writing inode tables: done
Creating journal (8192 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 32 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.

Create a mount point for the volume.

mkdir /mnt/storage

Mount the new volume.

mount /dev/array-pool/lvol0 /mnt/storage

Check to see that the new storage is available using the df -h command.

df -h

Filesystem Size Used Avail Use% Mounted on
/dev/sda1 9.4G 2.4G 6.6G 27% /
varrun 252M 104K 252M 1% /var/run
varlock 252M 0 252M 0% /var/lock
procbususb 252M 92K 252M 1% /proc/bus/usb
udev 252M 92K 252M 1% /dev
devshm 252M 0 252M 0% /dev/shm
lrm 252M 33M 219M 14% /lib/modules/2.6.20-16-generic/volatile
/dev/mapper/array--pool-lvol0
2.0G 35M 1.8G 2% /mnt/storage

We will now add our other disk to be seen by LVM. Adding /dev/sdc1 as a physical volume

pvcreate /dev/sdc1
Physical volume "/dev/sdc1" successfully created

Adding the second disk to the array-pool volume group.

vgextend array-pool /dev/sdc1
Volume group "array-pool" successfully extended

Checking the volume group using the pvs command.

pvs
PV VG Fmt Attr PSize PFree
/dev/sdb1 array-pool lvm2 a- 2.00G 44.00M
/dev/sdc1 array-pool lvm2 a- 2.00G 2.00G

To use all the space of the combined disks check to see how many physical extents are available using the vgdisplay command.

vgdisplay array-pool


--- Volume group ---
VG Name array-pool
System ID
Format lvm2
Metadata Areas 2
Metadata Sequence No 3
VG Access read/write
VG Status resizable
MAX LV 0
Cur LV 1
Open LV 1
Max PV 0
Cur PV 2
Act PV 2
VG Size 3.99 GB
PE Size 4.00 MB
Total PE 1022
Alloc PE / Size 500 / 1.95 GB
Free PE / Size 522 / 2.04 GB
VG UUID 4YFBMT-18kP-jLRP-Pmsb-X7bU-GC0J-c10qvX

The section that shows Free PE/Size is what we are interested in. Add the extra space to the logical volume.

lvextend -l+522 /dev/array-pool/lvol0
Extending logical volume lvol0 to 3.99 GB
Logical volume lvol0 successfully resized

Unmount the volume to resize the ext3 filesystem.

umount /mnt/storage/

Grow the ext3 filesystem to use the new space.

resize2fs /dev/array-pool/lvol0

Remount the volume.

mount /dev/array-pool/lvol0 /mnt/storage/

Check the size of the volume.

df -h

Filesystem Size Used Avail Use% Mounted on
/dev/mapper/array--pool-lvol0 4.0G 36M 3.7G 1% /mnt/storage

Using LVM on top of software RAID 5.

Note: I prefer to use RAID6 for more fault tolerance, but unfortunately at the time of this article Ubuntu 7.04 nor RHEL5 include a kernel that supports growing a RAID6. You could compile one of the newer kernels but it would not be supported by the distribution providers. I will substitute the extra parity drive I would have with a hot spare.

First thing we want to do is create partitions on each disk. In my example I have five 2GB drives.

fdisk -l

Disk /dev/sda: 10.7 GB, 10737418240 bytes
255 heads, 63 sectors/track, 1305 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System
/dev/sda1 * 1 1244 9992398+ 83 Linux
/dev/sda2 1245 1305 489982+ 5 Extended
/dev/sda5 1245 1305 489951 82 Linux swap / Solaris

Disk /dev/sdb: 2147 MB, 2147483648 bytes
255 heads, 63 sectors/track, 261 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System

Disk /dev/sdc: 2147 MB, 2147483648 bytes
255 heads, 63 sectors/track, 261 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System

Disk /dev/sdd: 2147 MB, 2147483648 bytes
255 heads, 63 sectors/track, 261 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Disk /dev/sdd doesn't contain a valid partition table

Disk /dev/sde: 2147 MB, 2147483648 bytes
255 heads, 63 sectors/track, 261 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Disk /dev/sde doesn't contain a valid partition table

Disk /dev/sdf: 2147 MB, 2147483648 bytes
255 heads, 63 sectors/track, 261 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Disk /dev/sdf doesn't contain a valid partition table

I will put one partition of Linux RAID that takes up all the space on every disk.

fdisk /dev/sdb

Hit the n key then hit return. Hit the p key to choose primary partition then hit return. hit the 1 key then hit return. Hit the return key two more times to select the default of using the whole drive. Hit the t key and then hit return to set the System ID. You can hit the L key to show all the hex code choices. The hex code choice for linux raid auto is fd. Type fd and hit return. It should say “Changed system type of partition 1 to fd (Linux raid autodetect)”. hit the w key and then hit return to write the changes to disk. Repeat these steps for the other drives you will be using in the RAID.

After you are done partitioning the disks they will be represented as a 1 next to the device. Do another fdisk -l to see the new partitions.

fdisk -l

Disk /dev/sda: 10.7 GB, 10737418240 bytes
255 heads, 63 sectors/track, 1305 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System
/dev/sda1 * 1 1244 9992398+ 83 Linux
/dev/sda2 1245 1305 489982+ 5 Extended
/dev/sda5 1245 1305 489951 82 Linux swap / Solaris

Disk /dev/sdb: 2147 MB, 2147483648 bytes
255 heads, 63 sectors/track, 261 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System
/dev/sdb1 1 261 2096451 fd Linux raid autodetect

Disk /dev/sdc: 2147 MB, 2147483648 bytes
255 heads, 63 sectors/track, 261 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System
/dev/sdc1 1 261 2096451 fd Linux raid autodetect

Disk /dev/sdd: 2147 MB, 2147483648 bytes
255 heads, 63 sectors/track, 261 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System
/dev/sdd1 1 261 2096451 fd Linux raid autodetect

Disk /dev/sde: 2147 MB, 2147483648 bytes
255 heads, 63 sectors/track, 261 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System
/dev/sde1 1 261 2096451 fd Linux raid autodetect

Disk /dev/sdf: 2147 MB, 2147483648 bytes
255 heads, 63 sectors/track, 261 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System
/dev/sdf1 1 261 2096451 fd Linux raid autodetect

Using the mdadm command to create the RAID.

I want to use three of the drives in a RAID5 configuration with one hot spare.

Create the RAID using a command similar to the following.

mdadm --create --verbose /dev/md0 --level=5 --raid-devices=3 /dev/sdb1 /dev/sdc1 /dev/sdd1

The output should look something like below.

mdadm: layout defaults to left-symmetric
mdadm: chunk size defaults to 64K
mdadm: size set to 2096384K
mdadm: array /dev/md0 started.

You can use two commands to view the status of the RAID. mdadm –detail /dev/md0 and cat /proc/mdstat

mdadm --detail /dev/md0
/dev/md0:
Version : 00.90.03
Creation Time : Thu Sep 13 23:12:59 2007
Raid Level : raid5
Array Size : 4192768 (4.00 GiB 4.29 GB)
Device Size : 2096384 (2047.59 MiB 2146.70 MB)
Raid Devices : 3
Total Devices : 3
Preferred Minor : 0
Persistence : Superblock is persistent

Update Time : Thu Sep 13 23:12:59 2007
State : clean, degraded, recovering
Active Devices : 2
Working Devices : 3
Failed Devices : 0
Spare Devices : 1

Layout : left-symmetric
Chunk Size : 64K

Rebuild Status : 96% complete

UUID : f5c24ee9:27cb0d42:e368bf24:bd0fce41 (local to host ubuntu)
Events : 0.1

Number Major Minor RaidDevice State
0 8 17 0 active sync /dev/sdb1
1 8 33 1 active sync /dev/sdc1
3 8 49 2 spare rebuilding /dev/sdd1

Lets add one more disk to the RAID as a hot spare using the mdadm –add command.

mdadm --add /dev/md0 /dev/sde1
mdadm: added /dev/sde1

Use the mdadm –detail command to make sure it is added.

mdadm --detail /dev/md0
/dev/md0:
Version : 00.90.03
Creation Time : Thu Sep 13 23:12:59 2007
Raid Level : raid5
Array Size : 4192768 (4.00 GiB 4.29 GB)
Device Size : 2096384 (2047.59 MiB 2146.70 MB)
Raid Devices : 3
Total Devices : 4
Preferred Minor : 0
Persistence : Superblock is persistent

Update Time : Thu Sep 13 23:35:23 2007
State : clean
Active Devices : 3
Working Devices : 4
Failed Devices : 0
Spare Devices : 1

Layout : left-symmetric
Chunk Size : 64K

UUID : f5c24ee9:27cb0d42:e368bf24:bd0fce41 (local to host ubuntu)
Events : 0.10

Number Major Minor RaidDevice State
0 8 17 0 active sync /dev/sdb1
1 8 33 1 active sync /dev/sdc1
2 8 49 2 active sync /dev/sdd1

3 8 65 - spare /dev/sde1

Now that you have created a RAID we need to add it to a logical volume group and create a logical volume with it.

We can now add one of the disks to be seen by LVM as a physical volume. Use the pvcreate command.

pvcreate /dev/md0
Physical volume "/dev/md0" successfully created

Create the volume group using the vgcreate command. I will name mine raid-pool.

vgcreate raid-pool /dev/md0
Volume group "raid-pool" successfully created

Use the vgdisplay command to see how many physical extents are available to use.

vgdisplay

--- Volume group ---
VG Name raid-pool
System ID
Format lvm2
Metadata Areas 1
Metadata Sequence No 3
VG Access read/write
VG Status resizable
MAX LV 0
Cur LV 0
Open LV 0
Max PV 0
Cur PV 1
Act PV 1
VG Size 4.00 GB
PE Size 4.00 MB
Total PE 1023
Alloc PE / Size 0 / 0
Free PE / Size 1023 / 4.00 GB
VG UUID 96bd50-9yn2-bzpc-C4kV-3W1L-rkbN-bO3o3e

Create a logical volume using the lvcreate command.

lvcreate -l 1023 raid-pool -n lv1
Logical volume "lv1" created

The above command created a logical volume using all 4GB with a default name lv1.

Format the logical volume with your desired filesystem. Here I will use ext3.

mkfs.ext3 /dev/raid-pool/lv1

mke2fs 1.40-WIP (14-Nov-2006)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
524288 inodes, 1047552 blocks
52377 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=1073741824
32 block groups
32768 blocks per group, 32768 fragments per group
16384 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736

Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 29 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.

Create a mount point for the volume.

mkdir /mnt/storage

Mount the new volume.

mount /dev/raid-pool/lv1 /mnt/storage

Check to see that the new storage is available using the df -h command.

df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/raid--pool-lv1 4.0G 73M 3.7G 2% /mnt/storage

Now lets add another disk to the RAID.

First we should unmount or lv to be safe.

umount /mnt/storage

Use the –grow option to expand the RAID and then add another hot spare.

mdadm --grow /dev/md0 --raid-devices=4
mdadm: Need to backup 384K of critical section..
mdadm: ... critical section passed.

Again, check the status of the RAID to make sure everything is in order.

mdadm --detail /dev/md0
/dev/md0:
Version : 00.91.03
Creation Time : Thu Sep 13 23:12:59 2007
Raid Level : raid5
Array Size : 4192768 (4.00 GiB 4.29 GB)
Device Size : 2096384 (2047.59 MiB 2146.70 MB)
Raid Devices : 4
Total Devices : 4
Preferred Minor : 0
Persistence : Superblock is persistent

Update Time : Thu Sep 13 23:40:24 2007
State : clean, recovering
Active Devices : 4
Working Devices : 4
Failed Devices : 0
Spare Devices : 0

Layout : left-symmetric
Chunk Size : 64K

Reshape Status : 43% complete
Delta Devices : 1, (3->4)

UUID : f5c24ee9:27cb0d42:e368bf24:bd0fce41 (local to host ubuntu)
Events : 0.648

Number Major Minor RaidDevice State
0 8 17 0 active sync /dev/sdb1
1 8 33 1 active sync /dev/sdc1
2 8 49 2 active sync /dev/sdd1
3 8 65 3 active sync /dev/sde1

You can also use the cat /proc/mdstat command.

cat /proc/mdstat
Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10]
md0 : active raid5 sde1[3] sdd1[2] sdc1[1] sdb1[0]
4192768 blocks super 0.91 level 5, 64k chunk, algorithm 2 [4/4] [UUUU]
[============>........] reshape = 60.0% (1258880/2096384) finish=1.2min speed=10877K/sec

unused devices: <none>

Once the rebuild is complete we should add the new hot spare disk also.

mdadm --add /dev/md0 /dev/sdf1
mdadm: added /dev/sdf1

Check to make sure it is really there.

mdadm --detail /dev/md0

/dev/md0:
Version : 00.90.03
Creation Time : Thu Sep 13 23:12:59 2007
Raid Level : raid5
Array Size : 6289152 (6.00 GiB 6.44 GB)
Device Size : 2096384 (2047.59 MiB 2146.70 MB)
Raid Devices : 4
Total Devices : 5
Preferred Minor : 0
Persistence : Superblock is persistent

Update Time : Fri Sep 14 00:01:20 2007
State : clean
Active Devices : 4
Working Devices : 5
Failed Devices : 0
Spare Devices : 1

Layout : left-symmetric
Chunk Size : 64K

UUID : f5c24ee9:27cb0d42:e368bf24:bd0fce41 (local to host ubuntu)
Events : 0.1434

Number Major Minor RaidDevice State
0 8 17 0 active sync /dev/sdb1
1 8 33 1 active sync /dev/sdc1
2 8 49 2 active sync /dev/sdd1
3 8 65 3 active sync /dev/sde1

4 8 81 - spare /dev/sdf1

Now we can expand the logical volume to use the extra storage space. Use the pvresize command.

pvresize /dev/md0
Physical volume "/dev/md0" changed
1 physical volume(s) resized / 0 physical volume(s) not resized

You can now see the unused space with the vgdisplay comand.

vgdisplay

--- Volume group ---
VG Name raid-pool
System ID
Format lvm2
Metadata Areas 1
Metadata Sequence No 5
VG Access read/write
VG Status resizable
MAX LV 0
Cur LV 1
Open LV 0
Max PV 0
Cur PV 1
Act PV 1
VG Size 6.00 GB
PE Size 4.00 MB
Total PE 1535
Alloc PE / Size 1023 / 4.00 GB
Free PE / Size 512 / 2.00 GB
VG UUID 96bd50-9yn2-bzpc-C4kV-3W1L-rkbN-bO3o3e

Use the lvresize command to add the new physical extents to the volume

lvresize -l+512 /dev/raid-pool/lv1
Extending logical volume lv1 to 6.00 GB
Logical volume lv1 successfully resized

Check the volume with e2fsck before growing the ext3 filesystem.

e2fsck -f /dev/raid-pool/lv1
e2fsck 1.40-WIP (14-Nov-2006)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/raid-pool/lv1: 11/524288 files (9.1% non-contiguous), 34912/1047552 blocks

Grow the ext3 filesystem to use the new space.

resize2fs /dev/raid-pool/lv1
resize2fs 1.40-WIP (14-Nov-2006)
Resizing the filesystem on /dev/raid-pool/lv1 to 1571840 (4k) blocks.
The filesystem on /dev/raid-pool/lv1 is now 1571840 blocks long.

Remount the volume.

mount /dev/raid-pool/lvol0 /mnt/storage/

Check the size of the volume.

df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/raid--pool-lv1 6.0G 73M 5.6G 2% /mnt/storage

As you can see we went from having 4GB of storage to 6GB.

linux/linux-howto/linuxlvm.txt · Last modified: 2008/12/29 09:00 (external edit)