1.環境說明
系統爲centos 6.5
需要安裝mutt和msmtp並可以發送郵件
需要安裝python 2.6.6
需要安裝xtrabackup
2.備份方案功能模塊介紹
備份:
使用xtrabackup進行備份,每次備份會把備份文件放到一個當前日期和時間的文件夾內。所以創建備份夾new,把備份文件放到new中,並根據new中文件夾的個數判斷是全備還是增備還是需要轉移文件到last中。第一個文件是全備,每次增備是在前一天的基礎上進行增備。備份腳本在把所有的文件從new移動到last的時候 會把所有文件文件打包。以下是mysql備份腳本:
#!/usr/bin/env python #coding:utf-8 #auther:Bran Guo #date:10/23/2015 #description:myql自動備份腳本,添加定時任務自動運行,不要修改mysqlbackup文件夾中的文件 #version:V1.0 import ConfigParser,os,datetime,logger #讀取配置文件 conf = ConfigParser.ConfigParser() conf.read("mysqlbak.conf") bakdir_new = conf.get("file_path","bakdir_new") bakdir_last = conf.get("file_path","bakdir_last") bak_cycle = conf.get('bak_cycle','bak_cycle') bak_output = conf.get('log_file','bak_output') bak_user = conf.get('mysql_set','bak_user') bak_passwd = conf.get('mysql_set','bak_passwd') os.environ['bakdir_new']=str(bakdir_new) os.environ['bak_output']=str(bak_output) os.environ['bak_user']=str(bak_user) os.environ['bak_passwd']=str(bak_passwd) #判斷備份文件夾個數 dirnew_count = int(os.popen('ls %s |wc -l' % bakdir_new).read()) if dirnew_count == 0: os.system('echo %s full backup start ---------------------------------------------------- >> $bak_output ' % datetime.datetime.now()) ret = os.system('innobackupex --user=$bak_user --password=$bak_passwd $bakdir_new &>> $bak_output') logger.logger(ret) elif dirnew_count >= int(bak_cycle): os.system('rm -rf %s/*' % bakdir_last) os.system('mv %s/* %s' % (bakdir_new,bakdir_last)) os.system('tar zcf %s/`date +%%m-%%d-%%Y`.tar.gz %s/*' %(bakdir_last,bakdir_last)) os.system('echo %s full backup start ---------------------------------------------------- >> $bak_output ' % datetime.datetime.now()) ret = os.system('innobackupex --user=$bak_user --password=$bak_passwd $bakdir_new &>> $bak_output') logger.logger(ret) else: full_file = os.popen('ls %s' % bakdir_new).readlines() for file_name in full_file : file_list= [] file_list.append(file_name) basedir = file_list[-1] os.system('echo %s incremental backup start ---------------------------------------------------- >> $bak_output ' % datetime.datetime.now()) os.environ['basedir']=str(basedir) ret = os.system('innobackupex --user=$bak_user --password=$bak_passwd --incremental $bakdir_new --incremental-basedir=$bakdir_new/$basedir &>> $bak_output') logger.logger(ret)
還原:
使用xtrabackup還原需要先準備(perpare)。一般情況下,在備份完成後,數據尚且不能用於恢復操作,因爲備份的數據中可能會包含尚未提交的事務或已經提交但尚未同步至數據文件中的事務。因此,此時數據文件仍處理不一致狀態。“準備”的主要作用正是通過回滾未提交的事務及同步已經提交的事務至數據文件也使得數據文件處於一致性狀態。準備的過程是以第一個完備爲基礎,提交第二個然後是第三個一直到最後一個。整個準備完成後使用第一個完備文件進行還原即可。還原腳本使用cli進行交互,用戶可以選擇還原到最近日期或指定日期。
還原腳本運行界面
還原腳本代碼
#!/usr/bin/python #coding:utf-8 #auther:Bran Guo #date:10/26/2015 #version:V1.0 import os,ConfigParser #讀取配置文件 conf = ConfigParser.ConfigParser() conf.read("mysqlbak.conf") bakdir_new = conf.get('file_path','bakdir_new') bakdir_last = conf.get('file_path','bakdir_last') mysql_data_path = conf.get('mysql_set','mysql_data_path') dirlist_new = os.popen('ls %s' % bakdir_new).readlines() dirlist_last = os.popen('ls %s' % bakdir_last).readlines() #備份函數 def restore(dir_count=len(dirlist_new),bakdir=bakdir_new,dirlist=dirlist_new): if dir_count == 1: print dir_count,bakdir,dirlist ret=os.system('innobackupex --apply-log --redo-only %s/%s' %(bakdir,dirlist[0])) if ret != 0: print "prepare failed" exit() else: ret=os.system('innobackupex --apply-log --redo-only %s/%s' %(bakdir,dirlist[0])) count = 1 while (count < dir_count): incrdir = dirlist[count] basedir = dirlist[0] os.environ['incrdir'] = str(incrdir) os.environ['basedir'] = str(basedir) os.environ['bakdir'] = str(bakdir) ret=os.system('innobackupex --apply-log --redo-only $bakdir/$basedir --incremental-dir=$bakdir/$incrdir') if ret != 0: print "prepare failed" count +=1 os.system('service mysqld stop') os.system('rm -rf %s' % mysql_data_path) os.system('innobackupex --copy-back %s/%s' %(bakdir,dirlist[0])) os.system('chown -R mysql:mysql %s' % mysql_data_path) os.system('service mysqld start') #輸入菜單 while True: user_input = raw_input('Command (m for help):').strip() if user_input == 'm': print '''Warning: The following command will remove mysql datafile, should be used with caution. r restore to recent backup s show backup list n choose backup restore from new l choose backup restore from last q quit ''', elif user_input == 'r': restore() elif user_input == 'q': exit() elif user_input == 's': print 'New:' os.system('ls %s' % bakdir_new) print 'Last' os.system('ls %s' % bakdir_new) elif user_input == 'n': os.system('ls -l %s' % bakdir_new) while True: user_input = raw_input('Please enter line number restore:').strip() if user_input == 'q': exit() try: line_number = int(user_input) dir_count = len(dirlist_new) if line_number <= dir_count: restore(line_number) else: print '''Please enter a number less then line or "q".''' except ValueError: print '''Please enter a number less then line or "q".''' elif user_input == 'l': os.system('ls -l %s' % bakdir_last) while True: user_input = raw_input('Please enter line number restore:').strip() if user_input == 'q': exit() try: line_number = int(user_input) dir_count = len(dirlist_last) if line_number <= dir_count: restore(line_number,bakdir_last,dirlist_last) else: print '''Please enter a number less then line sum or "q".''' except ValueError: print '''Please enter a number less then line sum "q".'''
遠程保存:
寫了個備份腳本,可以自動使用rsync去同步文件,然後判斷週期是否完成,每個每份走起完成後把這一週期的完全備份和增量備份一起打包,寫一條crontab在mysql服務器備份完成執行
備份腳本rsync
#!/usr/bin/env python #coding:utf-8 #auther:Bran Guo #date:11/11/2015 #description:myql備份文件抓取腳本,添加定時任務在mysql備份完成後自動運行,不要修改mysqlback文件夾中的文件 #version:V1.0 import os,ConfigParser,logger conf = ConfigParser.ConfigParser() conf.read("/home/dev/scripts/mysql/mysqlbak.conf") src_dir = conf.get('source','src_dir') dst_user = conf.get('destination','dst_user') dst_ip = conf.get('destination','dst_ip') dst_dir = conf.get('destination','dst_dir') rsync_log = conf.get('log_file','rsync_log') bak_cycle = conf.get('bak_cycle','bak_cycle') his_dir = conf.get('history','his_dir') ssh_port = conf.get('destination','ssh_port') os.environ['src_dir']=str(src_dir) os.environ['dst_dir']=str(dst_dir) os.environ['dst_user']=str(dst_user) os.environ['dst_ip']=str(dst_ip) os.environ['rsync_log']=str(rsync_log) os.environ['ssh_port']=str(ssh_port) os.system('echo `date` sync mysql back file start ------------------------------------ >> $rsync_log') ret = os.system('''rsync -Paz --delete -e "ssh -p $ssh_test" $ddst_page@$dst_ip:$dst_dir $src_dir &>> $rsync_log''') logger.logger(ret) dirnew_count = int(os.popen('ls %s |wc -l' % dst_dir).read()) if dirnew_count >= int(bak_cycle): os.system('tar zcf %s/`date +%%m-%%d-%%Y`.tar.gz %s/new/*' %(his_dir,src_dir)) else: exit()
日誌:
所有xtrabackup的輸出會保存到一個日誌文件便於排查。通過命令執行後返回值判斷成功或失敗後的結果輸出到一個日誌文件中。
#!/usr/bin/python import datetime,os,ConfigParser conf = ConfigParser.ConfigParser() conf.read("mysqlbak.conf") bak_log = conf.get("log_file","bak_log") mail_addr = conf.get('mail','mail_addr') logfile = 'backup.log' def logger(ret): if ret == 0: echo_line = "%s\tBackup mysql data file succes\n" % datetime.datetime.now() else: echo_line = "%s\tBackup mysql data file failed, plz check bakoutput.log\n" % datetime.datetime.now() os.system('echo "%s" | mutt -s "Backup mysql failed" %s' %(echo_line,mail_addr)) f = file(bak_log,'a') f.write(echo_line) f.flush() f.close()
郵件:
每次備份失敗後背自動發送郵件給指定郵件地址,便於運維及時發現問題並進行排查。發送郵件功能寫到日誌腳本中
配置文件:
除遠程保存部分腳本(放在備份服務器)外,所有變量都被抽出來放到配置文件中。
#備份文件路徑 [file_path] bakdir_new = /home/dev/mysqlbackup/new bakdir_last = /home/dev/mysqlbackup/last #備份週期 [bak_cycle] bak_cycle = 7 #日誌文件位置 [log_file] bak_output = bakoutput.log bak_log = backup.log #MySQL設置 [mysql_set] mysql_data_path = /var/lib/mysql bak_user = user bak_passwd = password #備份失敗發送郵件地址 [mail] mail_addr = [email protected]