Tag Archive for 'Backups'

Sold on Jungle Disk

Admittedly, it was an easy sell since I’ve been using Amazon’s S3 service on the job for the last 8 months for storing db backups so I’m familiar with the pros, cons, and costs and I had looked at Jungle Disk as a possible solution but disregarded it since it did not support Linux. Mike posted his thoughts about it and pointed out that they are giving some love to The Penguin.

After a quick spin on the free trial client I went ahead and signed up for the Plus service which allows you to browse your files online. Yes, you could use the S3 Firefox plugin but given the way that Jungle Disk writes folders as files it makes for some ugly viewing and the same goes for the S3Sync tools. Anyways, I look at the $12/year as a donation to keep the company afloat and developing.

To give people an idea of the cost I’m going to start with backing up my photos (33GB) and the last three years of eMusic (50GB) which the first transfer cost will be about $30 dollars and after that will cost about $12.50 a month.  This data grows at about 2GB a month which will tack on less than a dollar extra a month.  Not too bad of a proposition though I do see the potential for climbing up to around $35/month though the cost is worth the piece of mind it brings.

EC2, MySQL, Replication, Recovery, and You! (Hammer Time!)

I finally cobbled together an incredibly ugly but functional script for recovering or setting up a slave. The pure hideousness stems from the brute force, lack of error checking, cram that data down the db’s throat method that I am leveraging. See, I know just enough to get the job done but not nearly enough to do it with any elegance, flair, or care and concern for stability. Running with scissors, at night, with a blindfold, through a roomful of children’s toys and cats is my style.

Anyways, here we go…

This script is executed on the slave instance and will fetch the most recent copy of the db from the master, stop the slave, drop the db, recreate the db, read in the backup, issue the change master command, start the slave, and then display the slave status after a minute.

#!/bin/bash
# Recover slave post crash

# run backup from master
# transfer it to the slave
echo "Getting backup, this may take a while."
ssh master "/scripts/slave_recovery.sh WHATSLAVE"

echo

# untar backup
echo "Expanding backup and getting ready to import."
cd /mnt/tmp/recovery
recover=$(ls | grep yourdb)
tar -xf $recover

# set variables
recodir=${recover:0:21}
mastfle=$(ls $recodir/ | grep master)
fullbin=$(cat $recodir/$mastfle | grep A.)
binlog=${fullbin:2}
fullpos=$(cat $recodir/$mastfle | grep B.)
positn=${fullpos:2}

echo "Here's what I have..."
echo $recodir
echo $recover
echo $mastfle
echo $binlog
echo $positn

# stop slave
echo "Stopping slave..."
mysql -e "slave stop;"

# drop database
echo "Dropping the database..."
mysql -e "drop database yourdb;"

# recreate database
echo "Recreating the database..."
mysql -e "create database yourdb;"

# source database from backup
echo "Importing the database..."
mysql yourdb < $recodir/$recodir.sql

# issue change master command
echo "Issuing the change master command..."
mysql -e "CHANGE MASTER TO MASTER_HOST='master', MASTER_USER='USERNAME', MASTER_PASSWORD='PASSWORD', MASTER_LOG_FILE='$binlog', MASTER_LOG_POS=$positn;"

# start slave
echo "I am starting the slave..."
mysql -e "slave start"

# clean up
rm -r *yourdb*

# check status
echo "I'm waiting one minute the checking the status of the slave..."
sleep 1m
mysql -e "show slave status \G;"

echo
echo "I am all done."

Now, you might have noticed that on the seventh line I call another script on the master and you might have noticed a variable trailing it. WHATSLAVE is whatever you called your slaves in the host file on the master in my unimaginative case it is slavea and slaveb but you could have Tom, Dick, and Harry, or the names of your favorite Hostess snackcake characters.

#! /bin/bash
# This script runs on the master and is built off the backup script

# set date variables
DAYNOW=$(date +%j)
TIMENOW=$(date +%H%M)

# grab info about the binlog and position of the database

status1=$(mysql -e 'show master status \G' | grep mysql)
status2=$(mysql -e 'show master status \G' | grep Position)
sql=${status1:18}
posit=${status2:18}

mkdir /mnt/tmp/backup/slave-yourdb-$DAYNOW-$TIMENOW

echo A.$sql >> /mnt/tmp/backup/slave-yourdb-$DAYNOW-$TIMENOW/master-$DAYNOW-$TIMENOW.txt
echo B.$posit >> /mnt/tmp/backup/slave-yourdb-$DAYNOW-$TIMENOW/master-$DAYNOW-$TIMENOW.txt

# dump database
mysqldump yourdb > /mnt/tmp/backup/slave-yourdb-$DAYNOW-$TIMENOW/slave-yourdb-$DAYNOW-$TIMENOW.sql

# tar SQL dump
cd /mnt/tmp/backup

tar -chf - slave-yourdb-$DAYNOW-$TIMENOW | gzip - > slave-yourdb-$DAYNOW-$TIMENOW.tar.gz

rm -r /mnt/tmp/backup/slave-yourdb-$DAYNOW-$TIMENOW/

# copy tar to slaves
scp /mnt/tmp/backup/slave-yourdb-$DAYNOW-$TIMENOW.tar.gz root@$1:/mnt/tmp/recovery/slave-yourdb-$DAYNOW-$TIMENOW.tar.gz
#clean up
rm /mnt/tmp/backup/*.gz*
echo "I'm all done!"

This is just our basic backup script but rather than trying to pass all the variables through ssh I decided to be lazy and just execute the script remotely.

Some of the things I would like to add would be more flexibility in reading the backup name and error checking. Down the line I want to see if I can just backup the schema and import that into the slave db so that I don’t loose all that time reading the db back in (500MB+ can take awhile) and it would help with rapid recovery from data migrations. If you have any comments or suggestions, particularly if they trip your “WTF is wrong with this guy?” sensor I’m all ears.

EC2, MySQL, Backup Recovery, and You! (redux)

Here we go again…

On the heels of the replication monitor, I’ve gone back and fine-tuned the fetch script to let you look back two days in the archives. Now, it is a bit janky because I am setting the days for the first array rather than parsing the actual buckets in S3 but my sed/awk skills are less than none. However, I suppose that the next version could be set up to ask how many days you want to look back easily enough.

#!/bin/bash
# set the environment
export AWS_ACCESS_KEY_ID=xxxyyyzzz
export AWS_SECRET_ACCESS_KEY=xxxyyyzzz
export SSL_CERT_DIR=/opt/s3sync/certs

DAYLST[0]=$(date +%j --date='2 days ago')
DAYLST[1]=$(date +%j --date='1 days ago')
DAYLST[2]=$(date +%j)

DAYNUM=${#DAYLST[@]}

echo

echo "Here are the available days for backup recovery."

echo

# echo each element in array
# for loop
for (( i=0;i<$DAYNUM;i++)); do
echo $i -  ${DAYLST[${i}]}
done

echo

echo -e "What day did you want to parse? \c"
read selectday
listday=${DAYLST[$selectday]}

echo "Ok, I'm going to get the backups from $listday."
echo

echo -e "How many did you want? \c"
read count

echo

# Get the list of backups on the server using s3cmd
dbsets=$(ruby s3cmd.rb list your_db_backups:$listday | tail -n $count)
ARRAY=($dbsets)
# get number of elements in the array
ELEMENTS=${#ARRAY[@]}

# echo each element in array
# for loop
for (( i=0;i<$ELEMENTS;i++)); do
echo $i -  ${ARRAY[${i}]:4}
done

# Prompt user for which backup they want to recover
echo

echo -e "Which backup set would you like to recover? \c"

read numbackup
backup=${ARRAY[$numbackup]:4}

echo "I am fetching your backup $backup now..."
echo

ruby s3cmd.rb get your_db_backups/$listday:$backup /tmp/$backup
cd /tmp
tar -xf $backup
sqlset=${backup:0:14}
mv $sqlset /root

echo "Your backup can be found here /root/$sqlset"

Still on the agenda is getting a slave to recover unassisted after a failure is detected but as my shell scripting abilities improve the possibility of it being realized grows.

EC2, MySQL Replication, Monitoring, and You!

So in a full turn of events I’ve gone back to replication as the the most cost effective solution for creating a high availability environment for MySQL in EC2. The problem of the development team issuing schema changes frequently and without notification hasn’t changed but I have gotten a little more sophisticated about how to deal with them kicking the slave in the teeth when they issue schema changes with impunity.

What I’ve done is build off the backup scripts I have written about prior–especially since they work so well and created the beginnings of a metascript to over see the slaves–it is aptly named slaver. This metascript checks the state of the slave and acts based on it is state: slave up, run backups, or slave down, issue notifications.

#!/bin/bash
### Slaver v0.0.1
### this script is intended to check on the status of the slave
### if the slave is down (IO or SQL) it will send an email out

### set the variables that we are checking for ###
slaver1=$(mysql -Bse ’show slave status \G;’ | grep Slave_IO_Running)
slaver2=$(mysql -Bse ’show slave status \G;’ | grep Slave_SQL_Running)
IO=${slaver1:29}
SQL=${slaver2:29}
COMBO=$IO-$SQL
count=$(mysql -Bse ’show slave status \G;’| grep -c Yes)

### this is sanity check testing stuff ###

#count=1
#echo $IO
#echo $SQL
#echo $COMBO
#echo $count
### this is sanity check testing stuff ###

### run the exception check ###
if [[ "$count" == "2" ]] ; then
/opt/s3sync/db_backup.sh
exit
else
### create status file and mail it ###
date >> status.txt
mysql -Bse ’show slave status \G;’ >> status.txt
mutt you@yourhome.com -s “Slave Status :: DOWN” < status.txt
rm status.txt
fi

The next pieces to build out will be freezing the automated deletion of the old backup sets and attempting recovery of the slave if it is down. To get started on the latter I made some changes to the backup routine on the master:

#! /bin/bash

# Hourly cron job to upload to current bucket
# This is built off what we are currently running

# set date variables
DAYNOW=$(date +%j)
TIMENOW=$(date +%H%M)
# set the environment
export AWS_ACCESS_KEY_ID=xxxyyyzzz
export AWS_SECRET_ACCESS_KEY=xxxyyyzzz
export SSL_CERT_DIR=/opt/s3sync/certs

sleep 1m

# grab info about the binlog and position of the database

status1=$(mysql -e ’show master status \G’ | grep mysql)
status2=$(mysql -e ’show master status \G’ | grep Position)
sql=${status1:18}
posit=${status2:18}

mkdir /mnt/tmp/backup/you-$DAYNOW-$TIMENOW

echo A.$sql >> \
/mnt/tmp/backup/you-$DAYNOW-$TIMENOW/master-$DAYNOW-$TIMENOW.txt
echo B.$posit >> \
/mnt/tmp/backup/you-$DAYNOW-$TIMENOW/master-$DAYNOW-$TIMENOW.txt

# dump database
mysqldump geezeo > \
/mnt/tmp/backup/you-$DAYNOW-$TIMENOW/you-$DAYNOW-$TIMENOW.sql

# tar SQL dump
cd /mnt/tmp/backup

tar -chf - you-$DAYNOW-$TIMENOW | gzip - > you-$DAYNOW-$TIMENOW.tar.gz

rm -r /mnt/tmp/backup/you-$DAYNOW-$TIMENOW/

# copy tar to S3
cd /opt/s3sync
ruby s3sync.rb -vr –ssl /mnt/tmp/backup/ you_db_backups:$DAYNOW

#clean up
rm /mnt/tmp/backup/*.gz*

The key piece here is the capturing of binlog number and position with those two pieces captured it becomes much easier to automate a recovery of the slave from the master’s backup.

More to follow…

EC2, S3, Encrypted MySQL Backups, and You!

With great trepidation I write this as my last attempt earlier in the day saw the utter meltdown of this blog…

The topic of what we are doing to secure user data is one that comes up often and it is completely understandable, so this past week I’ve decided to add an extra layer of security into our database backups by encrypting them. It is a fairly simple process that while still being a work in progress works pretty well.

To get things started I generated a key-pair both on the server and imported my personal key so that I can encrypt the backups so I can open them either on the server or on my laptop. Further down the road I’ll be collecting the keys of the development team and importing them so that they can decrypt locally as well.

Now, I’m a bit wet behind the ears when it comes to shell scripting and while I already had a backup script written I wasn’t really happy with how it performed. I’ve made some tweaks to this one that allowed me to drop the nightly “Create Bucket” procedure as well as gathered the backups into a more logical folder/sub-folder layout.

Here’s the backup script…

#! /bin/bash

# Hourly cron job to upload to current bucket
# This is built off what we are currently running

# set date variables
DAYNOW=$(date +%j)
TIMENOW=$(date +%H%M)
# set the environment
export AWS_ACCESS_KEY_ID=XXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXX
export SSL_CERT_DIR=/opt/s3sync/certs

# dump database
mysqldump YOURDB > /mnt/tmp/backup/YOURDB-$DAYNOW-$TIMENOW.sql

# tar SQL dump
cd /mnt/tmp/backup

tar -chf - YOURDB-$DAYNOW-$TIMENOW.sql | gzip - | \
gpg -r [remote-key-holder] -r [local-key-holder] –encrypt \
> YOURDB-$DAYNOW-$TIMENOW.sql.tar.gz.gpg

rm /mnt/tmp/backup/*.sql

# copy tar to S3
cd /opt/s3sync
ruby s3sync.rb -vr –ssl /mnt/tmp/backup/ YOURDB_db_backups:$DAYNOW

#clean up
rm /mnt/tmp/backup/*.gz*

And the fetch script which will download the backup, decrypt it, and untar it. Now, this script I am working on listing the last X number of backups as determined by the user, dumping them into an array, and then prompting the user to choose which one they want. At the moment, the user need to know the number day of the year and the military time sans colon of the backup. But for the moment running the script is as simple as ./get_db_backup.sh 301 1530.

#! /bin/bash

# set the environment
export AWS_ACCESS_KEY_ID=XXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXX
export SSL_CERT_DIR=/opt/s3sync/certs

echo “Fetching your backup now…”

ruby s3cmd.rb get YOURDB_db_backups/$1:YOURDB-$1-$2.sql.tar.gz.gpg \
/mnt/tmp/recovery/YOURDB-$1-$2.sql.tar.gz.gpg

echo “I’m going to decrypt your backup but will need a passcode…”

gpg -d /mnt/tmp/recovery/YOURDB-$1-$2.sql.tar.gz.gpg \
> /mnt/tmp/recovery/YOURDB-$1-$2.sql.tar.gz

echo “Extracting your backup into /mnt/tmp/recovery…”

cd /mnt/tmp/recovery
tar -xf YOURDB-$1-$2.sql.tar.gz

echo “Cleaning up after myself…”
rm *.tar.gz*

echo “Your file is here: /mnt/tmp/recovery/YOURDB-$1-$2.sql”

Lastly, the “Delete Bucket” script which now thankfully works as advertised.

#! /bin/bash

# Daily cron job to delete old bucket
# set the environment
export AWS_ACCESS_KEY_ID=XXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXX
export SSL_CERT_DIR=/opt/s3sync/certs

DAYTHEN=$(date +%j –date=’2 days ago’)
cd /opt/s3sync
ruby s3cmd.rb -v deleteall YOURDB_db_backups:$DAYTHEN

Since all this is a work in progress I’d love to hear how other people are leveraging S3 for their database backups and if there is an easier way to accomplish what I’m attempting. :-D

Amazon S3, S3Sync, Backups, and You!

With EC2 and S3 Amazon has made available some seriously powerful and flexible tools for server and file hosting. Ec2 allows you to roll whatever flavor OS you want and get it up and running–think of it as virtual dedicated hosting–which while being incredibly cool has one major downside: if your server instance fails you loose your data as it will revert to the most current instance when you get it back online. In other words, a hiccup on EC2 could turn into a blistering nightmare.

This is where S3 comes in handy for storing anything that might change after you create and instance and launch it such as databases, files uploaded or created, and even logs. One of the first projects I hopped into was dumping the MySQL databases, compressing them and tossing them up on S3, a tedious process that can be automated with cron and s3sync (a ruby based tool that approximates rsync, kinda sorta).

Daily cron job to create new bucket

cd /root/s3sync/
DAYNOW=$(date +%j)
ruby s3cmd.rb createbucket WTF_db_$DAYNOW

Daily cron job to delete old bucket

cd /root/s3sync/
DAYTHEN=$(date +%j --date='2 days ago')
ruby s3cmd.rb deletebucket WTF_db_$DAYTHEN

Hourly job to back up the database
# get into the directory
cd /root/s3sync/


# set the environment
export AWS_ACCESS_KEY_ID=XXXX
export AWS_SECRET_ACCESS_KEY=XXXX/XXXX
export SSL_CERT_DIR=/root/s3sync/certs


# set date variables
DAYNOW=$(date +%j)
TIMENOW=$(date +%H%M)

# dump database
mysqldump WTF > /tmp/WTF-$DAYNOW-$TIMENOW.sql

# tar SQL dump
tar -czvf /tmp/backups/WTF-$STAMPD-$STAMPT.tar.gz /tmp/WTF-$DAYNOW-$TIMENOW.sql

# copy tar to S3
ruby s3sync.rb -r --ssl /tmp/backups/ MahBukit:WTF_db_$DAYNOW

# clean up after yourself
rm /tmp/*.sql
rm /tmp/backups/*.gz

Now one of the frustrations I have with this set up is while I am dropping the buckets from 48 hours prior it isn’t actually deleting the files off S3, a bit of a pain in the ass that I need to find some sort of resolution for in the near future. If anyone has an answer to this problem I would love to hear it! For now, I’m cheating by using S3Fox to purge those pesky backups.





Creative Commons Attribution-NonCommercial-ShareAlike 3.0 United States
Creative Commons Attribution-NonCommercial-ShareAlike 3.0 United States