Posts Tagged ‘Scripts’

Go Go Ghetto MySQL Parallel Dump and Import (or Hitting Things With Hammers Is Fun)!

Tuesday, October 18th, 2011

I am not a sophisticated man and my idea of elegance is hitting things with hammers. Bear that in mind when you look over the below code, which is here more for posterity than for any public edification.

One of the backup strategies we have is a complete table level dump and subsequent encryption of those files for storage in S3. Sure, it’s not a scalable solution as we discovered when the first iteration of the script did a complete dump of the db rather than by tables. This version runs them in parallel for the dump and works well enough for me not to sweat the timing at this point (~20 minutes from start to finish).

#!/bin/bash

# set variables
DAYNOW=$(date +%j)
TIMENOW=$(date +%H%M)
RPTIME=$(date +%x-%H:%M)
ME=$(hostname)

# stop the slave
mysql -utears -plamentations -e 'slave stop;'

# create directories
mkdir -p /mnt/tmp/backup/$ME-$DAYNOW-$TIMENOW

function r {
for J in $(cat $1) ; do
mysqldump -utears -plamentations sorrowdb --tables $J | gzip > "/mnt/tmp/backup/$ME-$DAYNOW-$TIMENOW/$J.sql.gz"
done
exit
}

mysql -u tears -plamentations -e 'show tables from sorrowdb' -s --skip-column-names | sort -R | split -d -l 5

for I in $(find x*) ; do
(r $I &)
done

sleep 15s

while [ "$(pgrep -c -f mysqldump)" -gt "0" ]
do
sleep 1s
done

#start slave
mysql -utears -plamentations -e 'slave start;'

cd /mnt/tmp/backup
gpg-zip --encrypt -r yourmom --output $ME-$DAYNOW-$TIMENOW.gpg $ME-$DAYNOW-$TIMENOW

# copy to s3
BACKUP=$(s3cmd --acl-private put /mnt/tmp/backup/$ME-$DAYNOW-$TIMENOW.gpg s3://sorrowdb-db-backups/ | sed \
-e 's/File //g' \
-e 's/\/mnt\/tmp\/backup\///g' \
-e 's/s3:\/\/sorrowdb-db-backups\/'"$HOSTNAME"'-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9].tgz.gpg//g' \
-e 's/\[1 of 1\]//g' \
-e 's/(//g' \
-e 's/)//g' | awk '{ print $1 " " $2 " " $3 " " $5/1048576 "MB" }')

echo $BACKUP | mutt yourmom@sorrowdb.com -s "$ME - $DAYNOW $TIMENOW"

# clean up
rm -r /mnt/tmp/backup/

Now just like the World’s Most Interesting Man, I often only test things after it’s shipped. In this case I’ve tested that I can decrypt the package and see that all the files are there but I never took the time to actually do an import. Today is that day.

Version 0.01-ALPHA-WTFISTHISSHIT-b of the script just handles parallel imports of the tables from the dump file. As a bonus shoddiness, you need to run it in the directory where the files are sitting.

#!/bin/bash
function r {
for J in $(cat $1) ; do
gunzip < $J | mysql -utears -plamentations sorrowdb
done
exit
}

ls $1 | sort -R | split -d -l 18

for I in $(find x*) ; do
(r $I &)
done

Ugly but it works. Feel free to laugh, I always do when I look at my work.

EC2, MySQL Replication, Monitoring, and You!

Monday, December 24th, 2007

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!

Tuesday, October 30th, 2007

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