Wednesday, November 21, 2012

Cardboard Raspberry Pi NAS and More

Build a network-attached storage (NAS) with your Raspberry Pi and an external USB hard drive.


It's been few months since I've bought Raspberry Pi board and, quite frankly, I haven't been using it as much as I wanted - main reason being too little spare time on my hands.
Anyway, I was thinking recently of how I would really benefit from having some networked storage in my small home office and the thought had occurred of assembling one with my Raspberry Pi and an external 500 GB USB drive that I already have - basically, putting Raspberry Pi to good use :)
As with all DIY projects, one of the main hassles is the enclosure to put all of the hardware in. I wanted a quick solution - didn't have the time to make something fancy. After a while I remembered that I've saved my Amazon Kindle Keyboard cardboard package. The quality of the cardboard is excellent and the construction is quite sturdy. After some measuring and layout prototyping I decided that it is quite good for this purpose so I began to work on my new NAS :)


The Hardware


After reviewing Raspberry Pi (model B) schematics and laying out my requirements I decided to power USB hard drive directly from the power supply unit. Raspberry Pi (model B) has 140 mA poly-fuse on each of the two USB ports while the current requirement of the USB hard drive is certainly higher. This particular USB hard drive (Transcend StoreJet 25M) has Toshiba MK5065GSX HDD inside and according to specifications it consumes 300 mA during read/write operations (and almost full 1 A during start-up!). Anyway, it's safe to say that a single USB hard drive will consume max. 500 mA in general (as that is the max. allowable current on standard USB port). I also plan to add second hard drive eventually, so I'll be needing 1 A for USB hard drives alone. According to wiki, Raspberry Pi (model B) requires ~700 mA of current for active operation so, all in all, I'll be needing at least 1.7 A of current (2 A optimal). Unfortunately, I didn't have any spare 5 V DC power supply with stated current capabilities so I decided to purchase one. I found Mean Well RS-25-5 (5 V at 5 A) switching power supply at my local electronics shop. This is one really stable power supply. I adjusted it's output voltage to 5.05 V while it was unloaded. After I've loaded it with Raspberry Pi and USB hard drive the voltage stayed on exactly the same level - quite nice.
On picture 1, a schematic of all the necessary connections is displayed. Of course, in order to supply power from my new power supply, I had to cut one end of the USB-Micro cable and attach power conductors (Vcc and GND) directly to V+ and V- of the power supply unit (respectively). The same goes for USB hard drive. USB hard drive also requires connections to the Raspberry Pi board - but only the data lines (D+ and D-).

Picture 1: Raspberry Pi NAS Schematic

After a few days of sporadic "high-tech super-glue" cardboard modifications and quick electrical layout, the (almost) finished "device" can be seen on the following pictures. I say "almost" since I didn't install power switch SW1 - quite frankly, I didn't even remember to put it at the time I was assembling all this. I'm also thinking of adding soft turn-off mechanism (using Raspberry Pi's GPIO) instead of SW1 - and will probably do that, we'll see :)

Picture 2: Finished (almost) Raspberry Pi NAS


Picture 3. Raspberry Pi NAS - rear view

Picture 4: Closed box - holes are for ventilation

After all the hardware assembly work was finished, I hooked up new Raspberry Pi NAS box to my LAN router, powered it up and began configuring the software.


The Software


I already had installed Raspbian "wheezy" on my SD card, so I continued with the configuration. I decided to set static IP address for Raspberry Pi (I chose 192.168.1.50). This can be done by editing /etc/network/interfaces on Raspberry Pi and adding following configuration:
iface eth0 inet static
    address 192.168.1.50
    netmask 255.255.255.0
    gateway 192.168.1.1
If the first line is already present (i.e. iface eth0 inet dhcp) then it should be replaced with the above configuration. After this, networking service should be restarted
pi@raspberrypi ~ $ sudo service networking restart
SSH connection will be broken after this step. However, Raspberry Pi should be available on the new address (192.168.1.50).
Since I don't like IP addresses (from aesthetic point of view) I added more aesthetically pleasing hostname to /etc/hosts on my working machine
192.168.1.50 rpi
Yep, much better ;)

Mounting the USB Hard Drive


First step in mounting the USB hard drive is finding it in /dev. One way to find out which device is your target device is with fdisk:
pi@raspberrypi ~ $ sudo fdisk -l
Disk /dev/mmcblk0: 7822 MB, 7822376960 bytes
4 heads, 16 sectors/track, 238720 cylinders, total 15278080 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000108cb

        Device Boot      Start         End      Blocks   Id  System
/dev/mmcblk0p1            8192      122879       57344    c  W95 FAT32 (LBA)
/dev/mmcblk0p2          122880     3788799     1832960   83  Linux

Disk /dev/sda: 500.1 GB, 500107862016 bytes
255 heads, 63 sectors/track, 60801 cylinders, total 976773168 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x4c153c12

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1              63   976768064   488384001    c  W95 FAT32 (LBA)
pi@raspberrypi ~ $ 
By looking at the disk sizes I could deduce which one is my USB drive - highlighted line Disk /dev/sda: 500.1 GB, 500107862016 bytes indicates that this is 500 GB hard drive. Since USB hard drives can be partitioned like any other, all partitions of this device are listed further down. This hard drive has only one partition /dev/sda1. In order to mount it I first had to create a mounting point. I did that by creating empty folder in /mnt:
pi@raspberrypi ~ $ sudo mkdir /mnt/drive1
Now, I can mount /dev/sda1 partition using mount:
pi@raspberrypi ~ $ sudo mount -o umask=0 /dev/sda1 /mnt/drive1
At this point I can access USB hard drive at /mnt/drive1:
pi@raspberrypi ~ $ ls -l /mnt/drive1
total 576
drwxrwxrwx  4 root root 32768 Oct 16  2011 BACKUP
drwxrwxrwx  7 root root 32768 Dec  9  2010 System Volume Information
drwxrwxrwx 47 root root 32768 Nov 12 20:43 Videos
Great, all is working :)
I now added this partition to /etc/fstab which will mount it at boot time (edited /etc/fstab on Raspberry Pi and added following line):
/dev/sda1 /mnt/drive1 auto users,noatime,umask=0 0 0
I now created mounting point on my working machine:
me@localhost:~$ sudo mkdir /mnt/drive1
This is where I'll mount remote Raspberry Pi's /mnt/drive1.


SSHFS/NFS Installation


After much testing with both SSHFS and NFS remote mounts I decided to stay with SSHFS since the testing showed that it's faster than NFS mounted drive and it's more secure (being based on SSH). Another thing to note is that SSHFS is easier to setup - NFS requires installation and configuration of packages on both client and server while SSHFS requires installation and configuration of additional packages only on client (since SSH server is present by default on Raspbian - actually most, if not all, Linux distributions). Testing results are presented in "The Testing" chapter.
For the sake of completeness I'll describe how I configured both SSHFS and NFS. I'm using Crunchbang Linux (Debian based distro) on my working machine.

Method 1: SSHFS


In order to mount remote folder to local /mnt/drive1 mount point via SSH, I installed sshfs utility:
me@localhost:~$ sudo apt-get install fuse sshfs
After the installation I immediately mounted remote drive:
me@localhost:~$ sudo sshfs -o allow_other pi@rpi:/mnt/drive1 /mnt/drive1
pi@rpi's password: 
Please note that if sshfs mounting is not done as root, user that performs the mount has to be a part of fuse group. This can be done like:
me@localhost:~$ sudo usermod -a -G fuse username

Enter Raspberry Pi's password and remote USB drive should be mounted at local /mnt/drive1.
me@localhost:~$ ls -l /mnt/drive1
total 576
drwxrwxrwx  4 root root 32768 Oct 16  2011 BACKUP
drwxrwxrwx  7 root root 32768 Dec  9  2010 System Volume Information
drwxrwxrwx 47 root root 32768 Nov 12 20:43 Videos
Now, I wanted this to be mounted at boot time so I added following to /etc/fstab on my machine:
sshfs#pi@rpi:/mnt/drive1 /mnt/drive1 fuse _netdev,users,allow_other,reconnect 0 0
First field is remote file system (user, remote address, remote path) while the second field is local mount point. Third field is type of the file system (in this case fuse) and fourth field has a list of options. Each of them is important and should not be left out if remote drive is to be automatically mounted at boot time successfully - more info on these options can be found in mount man pages.
As can be seen above, when I mounted remote drive using sshfs, Raspberry Pi asked for a password - like when connecting using standard ssh utlity. This will be a problem when system tries to mount remote drive at boot time. In order to avoid being asked for a password I created SSH authentication key for root user (since fstab will be executed by root):
me@localhost:~$ sudo ssh-keygen -t rsa -C "me@localhost"
[sudo] password for me: 
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
Didn't set any passphrase since, if set, it will ask for that passphrase when trying to establish SSH connection - the very thing I'm trying to avoid with Raspberry Pi's password. I then added it (contents of /root/.ssh/id_rsa.pub) to Raspberry Pi's /home/pi/.ssh/authorized_keys file:
ssh-rsa some_very_long_set_of_characters me@localhost
After the reboot, remote USB drive should be automatically mounted to local /mnt/drive1.

Method 2: NFS


In order to mount remote drive via NFS, NFS packages will need to be installed both on client and server machine. My working machine (client) will only need following packages:
me@localhost:~$ sudo apt-get install portmap nfs-common
This will basically enable me to mount remote drive using standard mount utility. Raspberry Pi will need NFS server package as well:
pi@raspberrypi ~ $ sudo apt-get install portmap nfs-common nfs-kernel-server
After the installation of NFS packages on Raspberry Pi, installer will try to start nfs-common service but will fail since rpcbind service is not running. It will also try to start nfs-kernel-server but will fail since /etc/exports is empty:
...
Creating config file /etc/default/nfs-common with new version
[....] Starting NFS common utilities: statd
[warn] Not starting: portmapper is not running ... (warning).
Setting up nfs-kernel-server (1:1.2.6-3) ...

Creating config file /etc/exports with new version

Creating config file /etc/defaults/nfs-kernel-server with new version
[....] Starting NFS common utilities: statd
[warn] Not starting: portmapper is not running ... (warning).
[warn] Not starting NFS kernel daemon: no exports. ... (warning).
Configurations in /etc/exports file control which remote host can access which local (Raspberry Pi in this case) directory. I want to give access to local /mnt/drive1 so I added following line to /etc/exports (on Raspberry Pi of course):
/mnt/drive1 192.168.1.0/255.255.255.0(rw,sync,no_subtree_check)
First column represents absolute path to local folder to be shared while second column specifies remote client. In this case 192.168.1.0/255.255.255.0 means that access to /mnt/drive1 folder is allowed for anyone on 192.168.1.0 network. Options in the brackets are mounting options i.e. if mounted file system will be only readable, writable or both. More info can be found in man pages for mount and fstab.

It's also not bad to add extra level of security for NFS. This can be done by adding few rules to /etc/hosts.deny and /etc/hosts.allow. In /etc/hosts.deny:
rpcbind: ALL
and in /etc/hosts.allow:
rpcbind: 192.168.1.0/255.255.255.0
Here's some more info on NFS security. Although portmap is mentioned in linked article, newer Debian systems have rpcbind instead.

During the installation of nfs-common and nfs-kernel-server utilities, installer created init scripts which will try to start these services on system boot. However, these services require rpcbind service to be started first and, currently, on Raspbian "wheezy" rpcbind is not started at boot time. This can be remedied with following command:
pi@raspberrypi ~ $ sudo update-rc.d rpcbind enable
Finally, start rpcbind, nfs-common and nfs-kernel-server services:
pi@raspberrypi ~ $ sudo service rpcbind start
[ ok ] Starting rpcbind daemon....
pi@raspberrypi ~ $ sudo service nfs-common start
[ ok ] Starting NFS common utilities: statd idmapd.
pi@raspberrypi ~ $ sudo service nfs-kernel-server start
[ ok ] Exporting directories for NFS kernel daemon...
[ ok ] Starting NFS kernel daemon: nfsd mountd.
Mounting of /mnt/drive1 on client machine should now be possible:
me@localhost:~$ sudo mount rpi:/mnt/drive1 /mnt/drive1
At this point, Raspberry Pi's /mnt/drive1 should be available at /mnt/drive1 on client machine.
me@localhost:~$ ls -l /mnt/drive1
total 576
drwxrwxrwx  4 root root 32768 Oct 16  2011 BACKUP
drwxrwxrwx  7 root root 32768 Dec  9  2010 System Volume Information
drwxrwxrwx 47 root root 32768 Nov 12 20:43 Videos
Yey, all is working fine :) Now, like on Raspberry Pi, I added mount point to /etc/fstab on my working machine:
rpi:/mnt/drive1 /mnt/drive1 nfs rw,hard,intr,async,nodev,noatime 0 0
The first field is remote filesystem while the second field is local mount point. After the reboot, remote USB drive should be automatically mounted at local /mnt/drive1.


The Tests


I performed read and write tests using dd utility (as described here) both with SSHFS mount and with NFS mount. I took three different video files and copied them back and forth to/from /mnt/drive1. I unmounted/mounted remote drive between each copy in order to avoid any caching interfering with measurements. I performed same measurements with standard cp utility, transfer speeds were the same. I must point out that my working machine is connected to LAN router via Broadcom BCM4312 802.11b/g wireless card. Anyway, here are the results:

Read [MB/s] Write [MB/s]
SSHFS 2.3 1.5
NFS 2.1 1.3
Table 1. Throughput measurements

NOTE: Transfer speed MB/s corresponds to 1000000 bytes/s. This is not be confused with MiB/s which corresponds to 1048576 bytes/s. More info on data rate units can be found here.

SSHFS is faster, both at reading and writing data by ~0.2 MB/s. I tried watching video (Video: MPEG-4, 672x512, 25 fps; Audio: 48 kHz, stereo, 128 kb/s) both via SSHFS and NFS and there really is no noticeable difference (I used VLC media player). Seeking (in movie) works fast, video plays smooth all the time (no interruptions whatsoever). However, when watching .mkv video (Video: H264 - MPEG-4, 1280x720, 25 fps; Audio: 48 kHz, stereo, 192 kb/s) interruptions (hiccups) appear every ~2 minutes on SSHFS and every 5-10 minutes on NFS. I have yet to discover why this is happening as, according to above measurements, there is certainly enough bandwidth to support constant stream in both cases.


And More...


NAS is a great thing but still, Raspberry Pi can do more - much more. One of the things that can be installed is torrent client. I'm thinking of i.e. rtorrent client coupled with some web GUI (like ruTorrent or rtgui). This way torrents can be downloaded directly to NAS and are accessible by anyone on the network.
This has been a fun little project and I will probably do a follow up on torrent client configuration and more :)

4 comments:

  1. I think you can improve the configuration: if you put the swap partition at de HDD, you improve de SD lifetime.
    best regards

    ReplyDelete
    Replies
    1. Hi there :)
      Thanks for you comment.
      You are definitely right. To be honest, I didn't consider SD lifetime at all at the time.
      This should also improve read/write speed to swap partition (if I'm not mistaken) :)
      I'll give it a go someday.
      Thanks.

      Delete
  2. Typically the OS can control HDD sleep to conserve power. Does this power management work properly with your split USB cable? I wonder if it would be better to connect the 2 USB power pins to both the Pi and the PSU. Some hard drives come with USB y-cables for that purpose.

    ReplyDelete
  3. Thank you for the article, Predrag. It helped my to configure my Pi as a cheap NAS Server. Meanwhile, I learned a lot about mounting file systems and setting up an NFS Server. :)

    (BTW, in Google Chrome I get Javascript alerts about bugs.)

    ReplyDelete