Warning: Undefined array key "HTTP_REFERER" in /var/www/html/blogg/include/common.php on line 149

Warning: Undefined array key "HTTP_ACCEPT_LANGUAGE" in /var/www/html/blogg/include/common.php on line 149

Warning: Undefined array key "module_gallery_main_page" in /var/www/html/blogg/modules/gallery/init.php on line 6
root@circe.se

Backup


Warning: Undefined array key "votes" in /var/www/html/blogg/index.php on line 63


Warning: Undefined array key 1 in /var/www/html/blogg/include/parser.php on line 314
Äntligen har jag pillat klart på mitt backupscript för mina MySQL-databaser. Det blev en hel del större än jag tänkt mig, men jag är nöjd och tror att det ska täcka mina behov.

Scriptet har några få inställningar, användarnamn och lösen till databasen, array med databaser som ska backupas, vilka kataloger som datan ska sparas till samt vilken komprimering som ska användas. Scriptet loopar sedan igenom arrayen med databaser och använder mysqldump pipat till komprimeringskommandot för skapandet av själva backupen. Sedan flyttas filen till från tempmappen till backupmappen och en symlänk skapas till senaste backupfilen. Den nya backupfilen replikeras sedan till alla andra valda backupmappar. Som vi alla vet bör man spara backupen på minst två ställen och de två ställerna bör inte vara på samma disk ;)

Själv har jag ställt in det som så att primär backupmappen är min hemmapp på servern och sedan replikeras datan till skrivarservern. Kanske fixar funktioner så att datan kan FTPas till en annan site någon gång i framtiden. Men för tillfället får detta räcka.

Det finns även funktionalltiet för att ta bort gamla backupfiler, dock är det inte 100% tillförlitligt eftersom det kollar mot datumet som finns i filnamnet istället för datumet filen skapades. Dock bör det fungera om man låter systemet arbeta ifred. Filnamnen har detta möster: <databasnamn>.<år>.<månad>.<dag>[.eventuellt sufix].gz. Suffixet läggs på om det redan finns en fil med namnet scriptet vill spara som, suffixet är ett heltal som ökas tills det finns ett namn som inte existerar ;)

Scriptet tar emot två parametrar, -q och -v. -q ges om man vill att det ska hålla helt tyst, bra för cronkörning. -v ges om man vill se så mycket info som möjligt, bra om man vill se exakt vilka kommandon som körs. Notera att databaslösenord kommer att skrivas ut i terminalen om -v används.

Glömde nästan, scriptet är skrivet i perl, använder Digest::MD5 och är enbart testkört på min egna server som kör Debian GNU/Linux 3.1. Det borde fungera att köra det på de flesta andra *nixar, samt på Windows om man har mysqldump, gzip, mv, ln och cp i sin path ;) Visst ja, Windows har ju inte det och kommer inte att ha det (finns ju inget som motsvarar symlänkar i Windows) :D Så det blir till att modifiera om man vill köra det under Windows. Hela alltet är licensierat under truncate_url("http://people.freebsd.org/~phk/", "Beerware-licensen") ;)

MD5-funktion finns för att inte backupa en databas om den inte har ändrats, dock fungerar den inte. Verkar som att gzip spottar ur sig olika data varje körning trots samma indata. Beror kanske på att ett timestamp sparas? Ska läsa igenom mansidan en gång till och se om det finns någon paramter för att motverka detta.

Without further ado, here's the script:

code:

#!/usr/bin/perl

# /*
#  * ----------------------------------------------------------------------------
#  * "THE BEER-WARE LICENSE" (Revision 42):
#  * <dr.slizer@gmail.com> wrote this file. As long as you retain this notice you
#  * can do whatever you want with this stuff. If we meet some day, and you think
#  * this stuff is worth it, you can buy me a beer in return dr slizer
#  * ----------------------------------------------------------------------------
#  */

# How it all works:
# You define the username and password for the database and a set of databases to backup. The script then uses mysqldump to dump
# the contents of all databases. The output from mysqldump is piped through gzip for compression. A symlink is made in the
# backup directory pointing to the latest backed up file.
# 
# There is an MD5 check for checking if the new file is the same as the latest backup, however this doesn't seem to work: gzip seems
# to output different data every time.
# 
# There is functions to replicate the backup to several different dirs, which ofcourse should be on other devices than the primary backup dir ;)
# 
# If you want to se exactly what the script does, use the -v argument, use -q if you want to run it as a cronjob.

# Pass -q as first parameter to the script for no output, e.g. ./backup -q
# Pass -v as first parameter to the script for extra output, e.g. ./backup -v

#--Settings--#
$username = 'username';			#Username to the database server
$password = 'password';			#Password the the database server
@databases = ('db1', 'db2');		#An array with databases to backup
$backupdir = '/var/dbbackup';		#The directory where the backups should be stored
@copydirs = ('/mnt/othermachine');	#An array with directories where the backup files should be copied to, for extra security
$tempdir = '/tmp';			#Temp dir where temp files should be stored during the scrip execution
$maxage = '60';				#The number of days old files should be stored, files older than this will be deleted

$compression = 'gzip -cfqn9';		#The command used to compress the files, the output from mysqldump is piped through this command and the output is written to file

#--Beginning of script--##

use Digest::MD5;

$maxage = $maxage*86400;

$quiet = 0;
$verbose = 0;

if(@ARGV[0] eq '-q'){$quiet = 1;}
if(@ARGV[0] eq '-v'){$verbose = 1;}

foreach $current (@databases)
{
	my($done) = 0;
	
	println('Backing up '.$current.'...', 0);
	my($command) = 'mysqldump -u '.$username.' -p'.$password.' '.$current.' | '.$compression.' > '.$tempdir.'/'.$current.'.tmp.gz';
	println('~$ '.$command, 1);
	system($command);
	
	#Checking if there is a previous backup of this file, if there is we check the md5 sum
	if(-e $backupdir.'/'.$current.'.gz')
	{
		println($backupdir.'/'.$current.'.gz exists', 1);
		
		open(OLDFILE, '<'.$backupdir.'/'.$current.'.gz') || die("Couldn't open $backupdir/$current.gzn");
		binmode(OLDFILE);
		$md5 = Digest::MD5->new;
		$md5->addfile(*OLDFILE);
		my($oldfile_md5) = $md5->b64digest;
		close(OLDFILE);
		
		open(NEWFILE, '<'.$tempdir.'/'.$current.'.tmp.gz') || die("Couldn't open $tempdir/$current.tmp.gzn");
		binmode(NEWFILE);
		$md5->new;
		$md5->addfile(*NEWFILE);
		my($newfile_md5) = $md5->b64digest;
		close(NEWFILE);
		
		println($backupdir.'/'.$current.'.gz MD5: '.$oldfile_md5, 1);
		println($tempdir.'/'.$current.'.tmp.gz MD5: '.$newfile_md5, 1);
		if($newfile_md5 eq $oldfile_md5)
		{
			println('~$ rm '.$tempdir.'/'.$current.'.tmp.gz', 1);
			system('rm '.$tempdir.'/'.$current.'.tmp.gz');
			$done = 1;
		}
		else
		{
			println('~$ rm '.$backupdir.'/'.$current.'.gz', 1);
			system('rm '.$backupdir.'/'.$current.'.gz');
		}
	}
	
	if(!$done)
	{
		#Checking what filename to use
		$filename = $current;
		
		my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
		$mon++;
		if($mon<10) { $mon = '0'.$mon; }
		if($mday<10) { $mday = '0'.$mday; }	
		my($date) = (1900+$year).'.'.$mon.'.'.$mday;
		
		$filename = $filename.'.'.$date;
		
		my($suffix) = 0;
		if(-e $backupdir.'/'.$filename.'.gz')
		{
			$suffix++;
			while(-e $backupdir.'/'.$filename.'.'.$suffix.'.gz')
			{
				$suffix++;
			}
			$filename = $filename.'.'.$suffix;
		}
		println('~$ mv '.$tempdir.'/'.$current.'.tmp.gz '.$backupdir.'/'.$filename.'.gz', 1);
		system('mv '.$tempdir.'/'.$current.'.tmp.gz '.$backupdir.'/'.$filename.'.gz');
		println('~$ ln -s '.$filename.'.gz '.$backupdir.'/'.$current.'.gz', 1);
		system('ln -s '.$backupdir.'/'.$filename.'.gz '.$backupdir.'/'.$current.'.gz');
		
		#Deleting files older than $maxage days
		$olddate = time-$maxage;
		my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($olddate);
		$mon++;
		if($mon<10) { $mon = '0'.$mon; }
		if($mday<10) { $mday = '0'.$mday; }	
		$olddate = (1900+$year).'.'.$mon.'.'.$mday;
		if(-e $backupdir.'/'.$current.'.'.$olddate.'.gz')
		{
			println('~$ rm '.$backupdir.'/'.$current.'.'.$olddate.'.*', 1);
			system('rm '.$backupdir.'/'.$current.'.'.$olddate.'.*');
		}
		
		#Copying the file to all @copydirs
 		foreach $curdir (@copydirs)
 		{
 			println('~$ cp '.$backupdir.'/'.$filename.'.gz '.$curdir.'/'.$filename.'.gz' , 1);
			system('cp '.$backupdir.'/'.$filename.'.gz '.$curdir.'/'.$filename.'.gz');
			
			#Deleting files older than $maxage days
			if(-e $curdir.'/'.$current.'.'.$olddate.'.gz')
			{
				println('~$ rm '.$curdir.'/'.$current.'.'.$olddate.'.*', 1);
				system('rm '.$curdir.'/'.$current.'.'.$olddate.'.*');
			}
		}
	}
	println($current.' backed up to '.$filename.'.gz.');
}

sub println
{
	my($string, $level) = @_;
	if(!$quiet)
	{
		if($verbose)
		{
			print($string."n");
		}
		elsif($level == 0)
		{
			print($string."n");
		}
	}
}

edit: Visst var det timestamp som ställde till det med MD5-checken, scriptet är nu uppdaterat så det skickar -n som paramter till gzip. Det verkar fungera i mina testkörningar.

Cheers.

No comments.

New comment

Name:

Homepage:

E-Mail:

Skriv ordet katt för att bevisa att du inte är en bot:

Message:

  • 1