一个简单的备份脚本,适合小型站点的使用,主要功能
- 使用 tar 打包web目录。有两种工作模式,整个目录全量打包、排除某些目录的快速打包;
- 通过 mysqldump 备份mysql数据库并压缩;
- 把备份文件通过 ssh/scp 传送到远程服务器上(异地备份);
- 本服务器保留最近的数个备份,超过指定数量的备份自动删除;
- 备份后查询远程服务器磁盘使用情况,并写入日志;
- 把整个日志邮件内容发送给指定的邮箱;
脚本内容及使用
正式脚本见后文附件。下面是需要主要要重点关注的部分
#!/bin/bash set -euo pipefail # usage: # bash <this-script.bash> to archive web folder, # # bash <this-script.bash> --full to ignore --exclude xxx # # notice: # require bash, and sh will NOT work # # Written with the assistance of ChatGPT and Gemini. # 2025/12/18 # # ####################################### # 全局配置 ####################################### TZ=Asia/Shanghai NOW=$(TZ=${TZ} date +%Y%m%d-%H%M%S) LOG_FILE=/home/username/auto_backup/log_${NOW}.log LOCAL_SAVE=/home/username/auto_backup/save # archive file kept limitation KEEP_FULL=1 KEEP_BRF=5 # remote via ssh REMOTE_USER=loginname REMOTE_HOST=backup.path8.net REMOTE_DIR='~/auto_backup_store' SSH_KEY='/home/username/.ssh/id_rsa_auth_ssl' # 自动通知到指定的邮件,每行一个,不需要任何分隔符号 MAIL_TO=( username@gmail.com ) # 是否启用排除 ENABLE_EXCLUDE=1 # 1=启用排除,0=完整备份 while [ "$#" -gt 0 ]; do case "$1" in --exclude) ENABLE_EXCLUDE=1 ;; --full) ENABLE_EXCLUDE=0 ;; *) ;; esac shift done touch $LOG_FILE mkdir -p $LOCAL_SAVE ####################################### # 工具函数 ####################################### log() { echo "[$(date '+%F %T')] $*" >> "$LOG_FILE" } remote_df() { ssh -4 -i "$SSH_KEY" "${REMOTE_USER}@${REMOTE_HOST}" df -h -x tmpfs -x devtmpfs >> "$LOG_FILE" \ || log "WARN: ssh failed to retrieve remote disk usage" } send_report_mail() { { echo "Subject: auto backup report ${NOW}" echo cat "$LOG_FILE" } | msmtp "${MAIL_TO[@]}" } ####################################### # 核心备份函数 backup_do() { ...... } # 清理旧备份 cleanup_old() { ...... } ####################################### # suit for your web root # -------- # 待执行的备份项,及调用执行 # # each backup execution is called by backup_do(...) with the following 5 args: # # site_name="blog.path8.net" # also used as prefix of ...tar.gz file-name # site_src="/var/www/html/blog.path8.net" # path your web # site_dir="./html" # the dir in site_src to be archived # cfg_excludes=( # exclude patterns # './html/upload' # ) # savedir=$LOCAL_SAVE # save tar.gz to # backup_do "$site_name" "$site_src" "$site_dir" "$savedir" cfg_excludes[@] ####################################### # site_name="blog.path8.net" site_src="/var/www/html/blog.path8.net" site_dir="./html" cfg_excludes=( './html/wp-content/upgrade' './html/wp-content/wflogs' './html/wp-content/uploads/20[01]*' './html/wp-content/uploads/202[0-4]' './html/wp-content/uploads/2025/0[0-9]' ) savedir=$LOCAL_SAVE backup_do "$site_name" "$site_src" "$site_dir" "$savedir" cfg_excludes[@] # site_name="www.path8.net" site_src="/var/www/html/www.path8.net" site_dir="./html" cfg_excludes=( './html/wp-content/upgrade' './html/wp-content/wflogs' './html/wp-content/uploads/20[01]*' './html/wp-content/uploads/202[0-4]' './html/wp-content/uploads/2025/0[0-9]' ) savedir=$LOCAL_SAVE backup_do "$site_name" "$site_src" "$site_dir" "$savedir" cfg_excludes[@] ############################################################################## # # /etc/my.cnf.d/backup_user.cnf # user to run mysqldump,(at least: SELECT, LOCK TABLES, SHOW VIEW ) , eg. # [client] # user=db_backup # password=backup-user-passwd # host=localhost # port=3306 # ####################################### cleanup_mysqldump() { ... } mysqldump_do() { ... } # 调用部分 site="site1_db" dbs="blog" outdir=$LOCAL_SAVE mysqldump_do "$outdir" "$site" "$dbs" site="site8_db" dbs="mydb1 mydb2 mydb3" outdir=$LOCAL_SAVE mysqldump_do "$outdir" "$site" "$dbs" ####################################### # retrieving remote disk usage ####################################### log "" log "retrieve remote disk usage" remote_df log "" send_report_mail log "All done."
使用
要求必须使用 bash 调用本脚本, 不再参数调用,是快速备份,即排除指定的路径;加 –full 参数则是忽略排除路径
bash <this-script.bash> bash <this-script.bash> --full
基础配置
设置时区TZ,生成日期字符串,用于,备份文件名、日志等。
配置日志文件路径 LOG_FILE, 本地备份文件存储位置 LOCAL_SAVE
KEEP_FULL, KEEP_BRF 完整备份、快速备份,两者各自最多保留个数
异地存储服务器 ssh 用户
备份文件传送到远程服务器上实现异地备份,需要指定一个 ssh 用户(REMOTE_xxx 的参数),通过 ssh-key 登录 (SSH_KEY 的路径)。这里要通过 ssh-key 免密码登录。
MySQL备份用户
给MySQL配置一个用于备份的用户,至少需要 SELECT, LOCK TABLES, SHOW VIEW 三个权限,用户登录信息写到 /etc/my.cnf.d/backup_user.cnf 文件(它就是 mysql 配置文件格式,很直观),mysqldump 命令会读取这个文件里的配置;而不要把用户名、密码写到脚本里。
基于 msmtp 的邮件接口
配置 MAIL_TO 变量,备份完成后,脚本会把日志文件发的内容邮件给这个邮箱。邮件由脚本里的 send_report_mail 函数发送,调用的是 msmtp ,这是个第三方工具,通过其他的 smtp 服务(如 Gmail)发送邮件,要另外安装并配置好,在这里直接调用。
接收可以与发送邮箱相同,对于小规模应用本来也没必要专门开设一个发邮件的账户。
完整脚本
backup_web_html_db.v251224.bash.txt
编写历程
这一节只是随手杂记,可以无视忽略。
脚本的初稿为手工所写,只是使用 tar 打包 web目录(带 –exclude 参数),并调用 scp 传送备份文件 的极其简陋的脚本,因为包含了多个站点,逻辑是完全重复的,所以通过 chatGPT 重构一下,然后再做了些进一步细节改进。
重构要求的提示词如下
评估并重构 bash 脚本,要求 1 使用 tar 打包目录,一个脚本中会有多条打包命令,要封装成函数,方便复用 2 使用 tar 的 --exclude 参数,排除一系列目录,排除的路径以 ./ 开头 3 脚本要同时支持排除或不排除,通过开关变量指定是否开启排除功能 4 打包的 tar 包文件与源文件一致,不做排除的备份文件名后带不同的后缀,以方便区分 5 打包的 tar 包文件默认存储在一个指定的目录中,对每个备份目录做调用时,允许修改该目录的定义 6 每个目录备份完成后通过 scp 复制到远程服务器上,然后本地目录最多只保留最近一个完整备份、及若干个有排除目录的简化备份,删除超过允许数量的旧备份 7 把备份过程中的简报写入本地日志文件,同时使用 ssh 远程调用 df -h 命令,把远程服务器的磁盘使用报告写入本地日志,
把重构结果交给 gemini 做检查验证,再针对性修改,主要是日志格式、内容,文件统计,核实一些细节风险等,增加邮件通知方案等。大体成型后,复用相关函数,改写了个 mysqldump 备份的逻辑,并进一步打磨了结节。再整理脚本,写为此文。
Last Updated on 2025/12/24