使用python备份远程FTP上的文件和Mysql

写这个脚本初衷是想借助VPS来定期备份虚拟主机上的文件和数据库。最近对美国VPS的速度有点不满了,想把博客搬到香港的虚拟主机上,但虚拟主机的备份不太方便,于是就冒出了这样一个想法:用VPS备份虚拟主机。

其实备份文件是个简单的事情,但要把数据库都导出,就麻烦了。大部分Mysql有个locahost的限制,只能在本地连接,不能远程连接数据库,想来想去,只能是在localhost弄个php的脚本,用来导出数据库

程序包含2个文件,分别是
db_backup.py:主程序,在里面设置好虚拟主机的FTP地址,用户名和密码信息,然后执行备份程序,如果要定时执行,在crontab里设置这个脚本即可
db_backup.php:导出Mysql的脚本,主程序会把此脚本发送至FTP服务器,然后自动访问http://ftp/db_backup.php把Mysql导出至FTP服务器上的的db_backup目录。因此需要在此文件里填写好Mysql的连接信息。备份完成以后主程序会删除FTP服务器上的此文件和db_backup目录,保证了数据库不被泄露

前提条件:
1,VPS安装Python,这个一般没问题,VPS的虚拟化技术不能是OpenVZ。
2,虚拟主机必须是php型的,且支持passthru()函数,国内的主机一般都支持,不过还是建议事先用探针检测一下

脚本打包下载请点击这里

db_backup.py

#!/usr/bin/python
# -*- coding: utf-8 -*-
import commands
import sys
import os
import re
import zipfile
import urllib2
import shutil
from urllib2 import URLError, HTTPError
from datetime import date,datetime,timedelta

class Package:
    """
    检查fuse-curlftpfs软件包的安装与否,如果没有,则安装之
    """

    def __init__(self,name):
        self.name=name
    def install(self):
        print('execute "yum install ' + self.name + '"...')
	#cmd=commands.getstatusoutput('yum install ' +  self.name + ' -y')
        print os.system('yum install ' +  self.name + ' -y')
        if self.name in os.popen('rpm -qa | grep ' + self.name).read():
	    print('Install ' + self.name + ' successful.')
        else:
            print('Install ' + self.name + ' failed, aborting.')
            sys.exit()
    def checkinstall(self):
	cmd=commands.getstatusoutput('rpm -qa | grep ' +  self.name)
        if self.name in cmd[1]:
            print(self.name + ' has aleady installed. Continue...')
        else:
            print(self.name + ' isn\'t installed. Ready to install it.')
	    self.install()

class Backup:
    """
    使用shell命令挂载之,然后执行备份过程(压缩目录,删除旧文件),压缩完成以后再解除挂载
    """

    def __init__(self,server,user,passwd,webroot,backupname):
        self.server=server
        self.user=user
        self.passwd=passwd
	self.webroot=webroot
        self.backupname=backupname
    def mount(self):
        #判断挂载点是否存在,如果不存在则尝试新建
        try:
            if os.path.isdir('/' + self.backupname):
                print('found mount point /' + self.backupname + ', ready to mount ftp server.')
            else:
                print('doesn\'t found mount point /' + self.backupname + ', build it.')
                os.mkdir('/' + self.backupname)
        except IOError, error:
            print error
            sys.exit()

        #开始挂载
        cmd=commands.getstatusoutput(r'curlftpfs ftp://' + self.user + ':' + self.passwd + '@' + self.server + r' /' + self.backupname + ' -o codepage=utf8')
        print cmd[1]

        #检验是否挂载成功
        pat=r'fuse(.+)/' + self.backupname
        all=os.popen('df -hT').read()
        if re.search(pat,all):
            print('mounted /' + self.backupname + ', continue...')
        else:
            print('mount failed. please check.')
            sys.exit()
		
	#把php脚本发送至FTP
	try:
	    src = file(os.getcwd() + '/db_backup.php', 'r+')
	    des = file('/' + self.backupname + '/' + self.webroot + '/db_backup.php', 'w+')
	    des.writelines(src.read())
            src.close()
	    des.close()
            if not os.path.isdir('/' + self.backupname + '/' + self.webroot + '/db_backup'):
	        os.mkdir('/' + self.backupname + '/' + self.webroot + '/db_backup')
	    #访问php文件(导出数据库)
            try:
	        page=urllib2.urlopen('http://' + self.server + '/db_backup.php')
    		print page.read()
            except (URLError, HTTPError), err:
    	        print err
		print('mysqldump failed! please check.')
	except (IOError, OSError), e:
	    print e
            self.umount()
            sys.exit()
		
    def backup(self):

        self.mount()

        newday = date.today()    #获取今天的日期
        oldday = date.today()-timedelta(5)    #获得5天前的日期
        newfile = self.backupname + '_backup_' + str(newday.year) + '.' + str(newday.month) + '.' +  str(newday.day) + '.zip'    #本次备份的文件名
        oldfile = self.backupname + '_backup_' + str(oldday.year) + '.' + str(oldday.month) + '.' +  str(oldday.day) + '.zip'    #5天前备份的文件名

        print('delete old file...')
        try:
            if os.path.isfile(oldfile):
                os.remove(oldfile)
            else:
                print('don\'t found old file, jumped.')
        except IOError, err:
            print err

	#开始压缩目录
        print('compress directory. it will take a while.')
        try:
            try:
                f = zipfile.ZipFile(newfile,'w',zipfile.ZIP_DEFLATED)
                for dirpath, dirnames, filenames in os.walk('/' + self.backupname + '/' + self.webroot):
                    for filename in filenames:
                        f.write(os.path.join(dirpath,filename))
                print("backup completely! file name is " + newfile)
            except (IOError, OSError), err:
                print err
        finally:
            f.close()
            self.umount()
            sys.exit()

    def umount(self):

	#删除FTP里的数据库目录和文件
        if os.path.isdir('/' + self.backupname + '/' + self.webroot + '/db_backup'):
	    shutil.rmtree('/' + self.backupname + '/' + self.webroot + '/db_backup')
	if os.path.isfile('/' + self.backupname + '/' + self.webroot + '/db_backup.php'):
	    os.remove('/' + self.backupname + '/' + self.webroot + '/db_backup.php')
			
	#判断是否解除挂载成功
        print os.system('umount /' + self.backupname)
        pat=r'fuse(.+)/' + self.backupname
        all=os.popen('df -hT').read()
        if not re.search(pat,all):
            print('umounted /' + self.backupname + '. Done!')
        else:
            print('umount failed. please check.')

if __name__=='__main__':
    ps=Package('fuse-curlftpfs')
    ps.checkinstall()

    #如果不是独立IP的话,FTP地址请填写服务商分配的三级域名,请确保能通过"http://FTP地址"访问网站首页
    #FTP地址开头不要加http://,末尾不要加/
    #网站目录是指FTP上存放网站的目录,有些国内服务商喜欢把网站目录设置成FTP根目录下的wwwroot目录
    #如果网站目录就是FTP根目录,把此项留空即可(填写成'')
    #标示符是挂载到本地的文件名,及压缩以后的文件名,只能用英文且不能有空格,可以用下划线
    #如果要备份多个FTP,把下面2行复制一遍即可,注意每个的标示符不能一样
    bu=Backup('FTP地址','FTP用户名','FTP密码','网站目录','标示符')
    bu.backup()

db_backup.php

<?php
$db_server = 'localhost';  //一般不需要改
$db_user = 'Mysql用户名';
$db_pass = 'Mysql密码';
$save_path = './db_backup';  //导出的数据库备份目录,此目录必须已存在
$suf='.sql';
 
function actionExport($db_server, $db_user, $db_pass, $save_path, $suf){
    echo "Now start exporting databases...\n ";
    $conn = mysql_connect($db_server, $db_user, $db_pass) or die("couldn't connect to mysql.");
    mysql_query("SET NAMES 'utf8'");
    $result_db = mysql_list_dbs($conn);
    for ($i=0;$i<mysql_num_rows($result_db);$i++){
	$db_name = mysql_tablename($result_db,$i);
	echo "dump database: $db_name";
        $cmd = "mysqldump -h $db_server -u $db_user -p$db_pass -q --skip-lock-tables $db_name > $save_path/$db_name$suf";
        dbdump($cmd);
    }
    mysql_close($conn);
}
 
function dbdump($cmd){
    passthru($cmd, $ret);
    if ($ret != 0) {
        echo " : Failed\n ";
    }else{
        echo " : OK \n ";
    }
}
 
if (mysql_connect($db_server, $db_user, $db_pass)) {
    actionExport($db_server, $db_user, $db_pass, $save_path, $suf);
}else{
    echo "Could not connect to mysql.";
}
?>

使用python备份远程FTP上的文件和Mysql》上有2条评论

发表评论

电子邮件地址不会被公开。