[python-env]备份vps文件到邮箱

2022年4月5日 780点热度
概述VPS的备份策略有很多方案选择,对个人博客来说,文件压缩后的体积一般不超过百兆,可以分割后通过附件发送到邮箱做每日备份,使用QQ邮箱可以免费永久性的保存历史版本。(腾讯某些场景被称“南山必胜客”(¬_¬),在云服务上面倒称得上“良心云”(´∀`),对比阿里云)

邮箱选择

国外邮箱附件只支持25M,国内常用是50M;
QQ邮箱容量无限,163邮箱SMTP发件快,因此选用163邮箱做SMTP发件端,QQ邮箱为收件端。

脚本功能

  1. 支持备份自定义目录,可多个。
  2. 支持备份MySql数据库。
  3. 支持备份CronTab计划任务。
  4. 使用独立变量文件保存账号密码和文件路径信息,方便直观修改。

脚本说明

本教程包含两个脚本文件:env-backup-smtplib.py和env.py,
env-backup-smtplib.py为备份脚本的主体,无需修改原样复制写入
env.py为邮箱账号密码、数据库账号密码、自定义目录路径等,需根据自己配置修改写入
新建复制写入 env-backup-smtplib.py
#! /usr/bin env python3
# -- coding:utf-8 --
#ver[2022-03-25]
#list自定义目录导出
#crontab计划任务导出
#mysqldump数据库导出
import env #导入同级目录的env.py变量配置
import os,time
import shutil #删除非空目录
#------变量.引用env.py------#
name = env.name    #邮件标题的识别名称
dir_back = env.dir_back  #压缩包保存位置
#------变量.自动生成------#
date_str = time.strftime('%Y-%m-%d')#日期文本化.#print(time.strftime('%Y-%m-%d'))#时间按照指定格式转成str.
print('[i]'+date_str)
time_str = time.strftime(' %H:%M:%S') #本地时间.字符串格式化
print('[i]'+time_str)
####初始化压缩包保存目录####
if not os.path.exists(dir_back):#判断目录是否存在
    print('[i]检查预写目录不存在:',dir_back)
    os.makedirs(dir_back)#新建目录
else:
    print('[i]检查预写目录已存在即将删除:',dir_back)
    shutil.rmtree(dir_back)#删除目录.支持非空
    os.makedirs(dir_back)#新建目录
####7z压缩部分####
list = [] #初始化list
    #------ files ------#备份的项.文件目录路径添加到list列表#### 
list.extend(env.list)#extend() 将列表元素(或任何可迭代的元素)添加到当前列表的末尾
#list.append('/root/ssj/')#追加目录路径到list
#list.append('-xr!wp2static-processed-site') #7z排除指定目录
    #------ crontab ------#备份的项.导出cromtab计划任务并添加到list列表#### 
cron_path = dir_back + 'crontab-' + date_str +'.txt'
os.system('crontab -l >> ' + cron_path) #-l:显示某个用户的crontab文件内容,如果不指定用户则表示显示当前用户的crontab文件内容.
print('[i]计划任务写入路径:crontab -l >> ' + cron_path)
list.append(cron_path)  #crontab写到文本并将文件路劲追加到list.
    #------ mysql ------#备份的项.导出mysql并添加到list列表#### 
sql_user = env.sql_user         ###自定义.你的mysql用户名 推荐使用root用户,以免出现权限问题
sql_pass = env.sql_pass        ###自定义.上面用户对应的密码
sql_database = env.sql_database ####自定义.要备份的数据库.
sql_host = env.sql_host
sql_port = env.sql_port
sql_cmd = env.sql_cmd
sql_path = dir_back + 'db' + name + date_str +'.sql' #生成数据库备份路径
sql_cmd = 'mysqldump '+sql_user+' '+sql_pass+' '+sql_database+' > '+sql_path+' '+sql_host+' '+sql_port+' '+sql_cmd #数据库命令
print('[i]数据库执行:'+sql_cmd)
os.system(sql_cmd)#导出数据库.如果不存在mysqldump程序,那么会生成0内容的文件.
list.append(sql_path)#数据库导出的路径追加到list.
    ####压缩.推荐使用7z压缩可直接对目标进行分卷
print('[i]list列表原始格式:',list)
#print('[i]list列表转字符串:',str(list))#直接转成字符输出的是['tmp1', 'tmp2']无法作为7z的命令使用,需要用空格格式化.
list_str = ' '.join(list)#join将序列中元素以自定字符连接生成新字符串,7z的命令使用需要生成用空格连接的字符串.
print('[i]list列表转成以空格连接的字符串格式:',list_str)
z7z_title='Backup'+name+''+date_str+'-split-7za' #压缩包名称.变量必须英文开头 因此7z改成z7z
print('[i]压缩包名称:',z7z_title)
#os.system('7z a archive3.zip tmp1 tmp2')#7z压缩多目录的范例语句
#os.system('7za -v45m -md=2M a '+dir_back+z7z_title+' '+list_str) #-md字典越大压缩使用的内存和时间就越多
#os.system('7za -v45m -t7z a '+dir_back+z7z_title+' '+list_str) #有没有-t7z压缩体积没区别因此不再多余申明
#os.system('7za -v1m a '+dir_back+z7z_title+' '+list_str) #测试.可先用小体积测试附件是否能正常发送.
os.system('7za -v45m a '+dir_back+z7z_title+' '+list_str) #
#####邮件部分#######
import glob #搜索匹配文件
import smtplib#邮件支持
import email.mime.multipart#邮件支持
import email.mime.text#邮件支持
from email.mime.text import MIMEText#邮件支持
from email.mime.multipart import MIMEMultipart#邮件支持
from email.mime.application import MIMEApplication#邮件支持
compressed=glob.glob(dir_back+'*split*')#匹配含有split字符的文件,返回list(包含路径).
print("[i]查询类型:compressed返回类型为:",type(compressed)) #查询类型
sum = '['+str(len(compressed))+']' #获取列表数量.字符串化
i = 0
for var in compressed:#循环读取目录中的压缩包作为附件来发送邮件
    print("[i]查询类型:var类型为:",type(var),)#查询var的格式是str字符串而不是数组,可以直接命令调用.
    i=i+1
    num = '['+str(i)+'/'+str(len(compressed))+']'
    print ('[i]List当前元素:',num,var) #str(i)将数字i转成字符才可以用+和字符串连起来
#------ 发送邮件提醒-stmplib模块 ------#
    #----设置邮箱变量信息
    mailsmtp = env.mailsmtp #修改.SMTP服务器.itldc上用QQ发送要用7个小时才能发完50M内容(没成功过),163只需要2分钟
    mailport = env.mailport #修改.SMTP服务器端口号.如果是25端口需要修改smtplib.SMTP()非加密指令
    mailuser = env.mailuser #修改.SMTP用户名
    mailpassword = env.mailpassword #修改.SMTP密码
    mailto = env.mailto #修改.接收方邮箱,可以多个地址用分号分开,send_email()函数中手动设置
    #----自动组合标题和正文变量----#
    subject = 'Backup '+name+date_str+num #邮件标题
    content = "邮件正文:\n" +var +num +':\n'+'脚本运行于: ' +date_str +time_str +'\n邮件构建于: ' +time.strftime('%Y-%m-%d %H:%M:%S')#邮件正文.
    print('[i]邮件content内容:\n',content)
    attach_name = var.split("/")[-1]#附件名称.从附件路径提取
    #----构筑邮件
    msg = email.mime.multipart.MIMEMultipart()
    msg['from'] = mailuser
    msg['to'] = mailto#多个收件人的邮箱应该放在字符串中,用字符分隔, 然后用split()分开,不能放在列表中, 因为要使用encode属性
    msg['subject'] = subject
    #----构筑邮件.正文
    content = content
    txt = email.mime.text.MIMEText(content, 'plain', 'utf-8')
    msg.attach(txt)
    #----构筑邮件.附件
    print("[i]准备添加附件...")
    # 添加附件,从本地路径读取。如果添加多个附件,可以定义part_2,part_3等,然后使用part_2.add_header()和msg.attach(part_2)即可。
    #part = MIMEApplication(open('/root/backup-tmp/Backup[or1]2020-06-16-split-7za.001','rb').read())#测试.指定文件
    part = MIMEApplication(open(var,'rb').read())#读取附件
    part.add_header('Content-Disposition', 'attachment', filename=attach_name)#给附件重命名,一般和原文件名一样.
    #part.set_charset('utf-8')#不知道起不起作用,暂时不用.
    msg.attach(part)
    #----发送邮件.SMTP
    #smtp= smtplib.SMTP(server_address,25)  #連線伺服器,SMTP_SSL是安全傳輸
    smtp = smtplib.SMTP_SSL(mailsmtp, mailport) #需要一个安全的连接,用SSL的方式去登录得用SMTP_SSL,之前用的是SMTP().端口号465或587
    print("[i]准备登录邮件服务器...")
    smtp.login(mailuser, mailpassword) #发送方的邮箱,和授权码(不是邮箱登录密码)
    print("[i]准备发送邮件...")
    smtp.sendmail(mailuser, mailto.split(";"), str(msg)) #注意, 这里的收件方可以是多个邮箱,用";"分开, 也可以用其他符号
    print("[i]已发送邮件:",var+num)
    print("[i]邮箱注销退出,释放邮箱锁定,释放连接\n")
    smtp.quit()#注销退出邮箱
    time.sleep(10) #延迟10秒防止过快的登出又登录而账号冲突
print("=======脚本END=======")
新建修改写入 env.py
#! /usr/bin env python3
# -- coding:utf-8 --#
dir_back = '/root/tmp-backup/'  #自定义.压缩包保存位置
name = '[or1]'    #修改.邮件标题的识别名
#----设置邮箱变量信息
mailsmtp = 'smtp.163.com' #修改.SMTP服务器.用QQ发送大附件用5分钟,163只需要2分钟
mailport = 465 #修改.SMTP服务器端口号.如果是25端口需要修改smtplib.SMTP()非加密指令
mailuser = 'test@163.com' #修改.SMTP用户名
mailpassword = 'test' #修改.SMTP密码
mailto = 'test@qq.com;test@163.com'#修改.接收方邮箱,可以多个地址用分号分开,send_email()函数中手动设置
    #------ mysqldump ------#备份的项.导出mysql并添加到list列表#### 
sql_database = 'blog_wp' ####自定义.要备份的数据库.
sql_user = '--user='+'test'         ###自定义.你的mysql用户名 推荐使用root用户,以免出现权限问题
sql_pass = '--password='+'test'        ###自定义.上面用户对应的密码
sql_host = '--host='+'127.0.0.1' #数据库地址
sql_port = '--port='+'3306' #数据库端口
sql_cmd = '--column-statistics=0' #其他mysqldump参数命令添加到这里
#=============自选需要保存的文件和目录=================
list = [] #初始化list
    #------ files ------#备份的项.文件目录路径添加到list列表#### 
list.append('/root/') #list.append('/root/') #追加目录到list
list.append('/root/.config/rclone/rclone.conf') #追加文件到list
list.append('/root/docker/') #追加目录到list
list.append('-xr!*docker/temp/') #7z排除指定目录
list.append('-xr!*wp-content/updraft/') #7z排除指定目录
list.append('-xr!wp2static-crawled-site') #7z排除指定目录

测试运行:

【使用Python3】
python3 -B ./env-backup-smtplib.py

添加到CronTab计划任务:

【先安装Tmux】
#backup-smtplib.py;-B参数表示不生成__pycache__目录
20 6 * * * tmux new-session -s backup-smtplib -d
25 6 * * * tmux send-keys -t backup-smtplib 'python3 -B /root/crontab/env-backup-smtplib.py' C-m

SSJ

不积跬步无以至千里