Difference between revisions of "Backup NAS Mini"
(No difference)
|
Latest revision as of 00:56, 8 August 2018
Unfinished page...
I wanted to build a small Network Attached Storage (NAS) that is small, portable, cheap, and and doesn't consume a lot of power. I decided to use a Raspberry Pi 2 with a 500GB mechanical hard drive attached via USB. The purpose of this project is to have my most important files backed up on a portable device that I can grab in case of a fire.
Contents
Hardware
Parts list
- Raspberry Pi 2 Model B (link)
- 4GB Sandisk SD card (link)
- 500GB 2.5 inch hard drive (link)
- SATA-to-USB adapter (link)
- LM2596S DC-to-DC buck converter (link)
- CD drive enclosure (thrift store)
- 12V DC power supply (link)
- various parts (USB cable, nuts/bolts, etc.)
Power
The Raspberry Pi requires a little more than 5V and the USB-to-SATA adapter requires 12V, so I'm using a 12V DC power supply and a DC-to-DC buck converter (LM2596S) to convert it to 5V. Notice the placement of glue on the SATA port (second photo below) to prevent things from coming loose. I also put glue on the potentiometer on the buck converter after dialing it in to about 5.4V.
Note: the hard drives are different colors because I switched hard drives half way through the project.
The whole power setup is wired like this:
[ wiring diagram ]
Another thing that I did was add a tactile button to the back as a way of shutting down and restarting the machine. I mapped it to GPIO04 on the Raspberry Pi. For software setup, see Off Button Script.
Hard Drive
The hard drive is a 500GB 2.5 inch mechanical drive, attached via USB with a SATA to USB adapter. I mounted it under the Raspberry Pi using 3D printed brackets (link).
The adapter is a WEme USB 3.0 to SATA Converter Adapter (model number 4328317644) that I bought from Amazon (here's a Google link). I took the casing off of it and just used the bare PCB. The reason I chose this particular adapter is that it uses external power instead of being powered through the USB. The Raspberry Pi can only provide so much current. The adapter came with a 12V power supply, but I replaced it with higher current adapter.
I decided to solder the USB cable to the Pi instead of plugging it into the USB port. I found that I still needed to wire up all four USB pins to the Raspberry Pi because the adapter still expects there to be power on the USB. I also soldered the cable shielding to one of the USB anchor points on the Raspberry Pi.
Notice that I put kapton tape under the wire as well as on top.
Here is the USB wiring diagram:
[ USB pinout diagram ]
Wiring it up this way presents the risk of accidentally plugging something into the USB port in the back that is used for the hard drive, so I 3D printed a USB cover. The port that needs to be blocked is the top right next to the Ethernet port.
Not my design (link).
LED Indicator
I put an RGB LED on the front of the case as an indicator light.
|
Here is the GPIO Pinout:
|
Here are the current ways this LED indicates status:
|
For software setup of the LED indicator, see LED Script.
Enclosure
The enclosure is simply an old external CD/DVD drive enclosure. I cut out a place in the back for the Raspberry Pi USB and Ethernet connectors. I glued on a piece of black acrylic on the front of the case as a face plate. The internals are mounted to a piece of MDF board which is bolted in from the bottom. It was really just trial and error figuring out how to mount everything. I ended up making 3D printed parts for the hard drive.
Notice the stand-offs on the Raspberry Pi have segments of drinking straw on them. This was simply to ensure the nuts were put on at exactly the same height; the straw segments were all cut exactly the same length and were used as guides. I could have cut them off, but it wasn't necessary.
Software
Operating System
I used a minimal install of Raspbian Jessie. You can download the latest version of Raspbian here. There are several configurations I setup for this project. I do these tasks as root.
I installed all the packages that I'm going to use in this project:
apt-get install python python-rpi.gpio python-pylibacl python-pylibattr python-pyxattr rdiff-backup ssmtp
I added the user and the group that will be performing the backup (I typically don't use the system's built-in accounts):
# Assuming your password is 'PICKLES', but you should run passwd manually and enter the password in the prompt
useradd -m backer
echo -e "PICKLES\nPICKLES" | passwd backer
I created a mount point for the backups and setup permissions:
mkdir /backups
chown backer:backer /backups
chmod 770 /backups
I setup the drive:
# Find out the drive name
# Mine was /dev/sda but for basic copy/paste safety I will use /dev/sdx in these examples
fdisk -l | grep "^Disk"
# Create the partition /dev/sdx1
echo -e "n\np\n1\n\n\n\nw\n" | fdisk /dev/sdx
# Create file system with 0% reserved blocks on sdx1
mkfs -t ext4 -m 0 -L Backups01 /dev/sdx1
# Add the mount to fstab. Importantly, I'm using the 'nofail' and 'nobootwait' options.
echo 'LABEL="Backups01" /backups ext4 defaults,nofail,nobootwait 0 0' >>/etc/fstab
mount -a
On the source machine (the remote machine that has my files) I also create the user:
# Assuming your password is 'PICKLES'
useradd -m backer
echo -e "PICKLES\nPICKLES" | passwd backer
The tricky part is making sure that backer has read permissions on all the files that you need to backup, and read/execute permissions on folders. So if I assume your files are contained in /home/timmy/myfiles (on the source machine), you might do:
# Add group ownership to all files/folders
chown -R :backer /home/timmy/myfiles
# Add read/execute to all folders
find /home/timmy/myfiles -type d -print0 | xargs -0 chmod g+rx
# Add read to all files
find /home/timmy/myfiles -type f -print0 | xargs -0 chmod g+r
You can set permissions on a more granular level if you need to. Don't do this on system files.
[ incomplete: source server ssh config and certificate ]
Backup Script
This is the main script that I wrote to perform the backups. Make sure you've installed the packages mentioned in Operating System.
Here's the idea:
- The backup server has a cron job that performs the backup process on a schedule.
- The backup process logs in remotely to the source server using rdiff-backup.
- Rdiff-backup uses SSH. We will be able to log in without a password prompt by using certificate authentication.
- We define included and excluded files and directories to control what get backed up incrementally.
- Each backup process generates a log file and sends text and email notifications if an error occurs.
As root, I created the directory structure:
mkdir -p /scripts/backup-system/config /scripts/config /logs/backups
crontab
10 4 * * * root /scripts/backup-system/backup-run.sh -y
Re-upload scripts after I've fixed everything.
[ incomplete ]
LED Script
Backup Status Script
Restore Script
Text and Email Messaging
I wanted the system to send me text and email notifications.
Make sure you've installed the packages mentioned in Operating System.
crontab
00 12 * * * root /scripts/scheduled-messages.sh
[ incomplete ]
Off Button Script
I added a tactile button to GPIO4 on the Raspberry Pi to act as a power button. When held for a certain number of seconds, it executes the shutdown command. This allows my Shutdown Script to wait for a backup to complete before powering the system down.
Here is the Python script:
#!/bin/python
#
# Script to shutdown Raspberry Pi when GPIO input is held.
# Connect a button between ground (GND) and the GPIO pin
# that you specify below.
#
# If you run this script from your /etc/rc.local file, it
# will be running as root, which eliminates the need to
# enter a password to elevate privileges. Otherwise, the
# sudo command will ask for a password before it will
# execute shutdown.
import RPi.GPIO as GPIO
import sys,os,signal,time
# GPIO pin number
btn = 4
# Button must be held for this many seconds
delay = 3.0
def sense_input(btn,delay):
try:
# Wait for button press
GPIO.wait_for_edge(btn, GPIO.FALLING)
count = 0
while True:
if GPIO.input(btn) == False:
if count < delay:
count = count + 1
time.sleep(0.1)
continue
else:
# Shutdown system
os.system("sudo shutdown -h now")
os._exit(-1)
else:
try:
GPIO.cleanup()
except:
pass
time.sleep(0.1)
break
except KeyboardInterrupt:
try:
GPIO.cleanup()
except:
pass
os._exit(-1)
except:
pass
delay = delay*10
while True:
GPIO.setmode(GPIO.BCM)
GPIO.setup(btn, GPIO.IN, pull_up_down=GPIO.PUD_UP)
sense_input(btn,delay)
Assuming you save this to /scripts/off-button.py go ahead and make it executable:
chmod a+x /scripts/off-button.py
Then add it to /etc/rc.local so it runs on startup. Enter the command above the exit 0 line so that it looks like this:
# ... existing rc.local stuff ...
python /scripts/off-button.py &
exit 0
Shutdown Script
I wanted to have the system wait for a backup to complete if the system is shutdown while a backup is in progress. I created this script called wait-for-backup. It also sends a text message on shutdown (see Text and Email Messaging):
#!/bin/bash
#
# Shutdown script for backup server.
# Waits for rdiff-backup to finish.
#
log=/logs/server-shutdown.log
mount -oremount,rw /
echo "[`date`] TEST: Shutdown issued. Waiting for rdiff-backup to stop..." >>$log
/scripts/sms.sh "backup2" "system shutdown ( `date` )"
sleep 20
echo "[`date`] TEST: Done waiting." >>$log
mount -oremount,ro /
Once you have this script located in /etc/init.d you can create a symbolic link in the appropriate rc directories. Make sure this script is run before any of the VirtualBox services are stopped. For my server, I made it K64. Make sure wait-for-backup is executable.
sudo chmod 755 /etc/init.d/wait-for-backup
sudo ln -s /etc/init.d/wait-for-backup /etc/rc0.d/K64stop-vms
sudo ln -s /etc/init.d/wait-for-backup /etc/rc6.d/K64stop-vms
If you're using Systemd you need to do this differently.
You can put a script in the Systemd system-shutdown script folder. It is located in a different place on different Linux distributions:
- Debian:
/lib/systemd/system-shutdown - Other:
/usr/lib/systemd/system-shutdown
Put the wait-for-backup script in that folder and make sure it's executable.
[ THIS SECTION NEEDS TO BE CORRECTED ]
You actually make script /scripts/backup-system/shutdown-wait-for-backup.sh
contents
Then make file /lib/systemd/system/wait-for-backup.service with:
contents