如何一键在云端备份自己的小破站

橘子 发布于 2024-11-14 149 次阅读


如何一键优雅钻牛角尖自己写来一键备份自己的小破站呢?

为什么需要自己写……?

WordPress提供了备份指南,并且有很多广泛使用的插件,不过自己总是想捣鼓一下。不使用插件的原因有几个:

  • 备份插件一般需要手动处理,并且下载的文件是下载到本地的(而非服务器)。如果希望执行定时任务,一些插件不支持。
  • 下载的备份文件可能大小受限制,这取决于php的配置,以及nginx的配置。禁止浏览器直接下载、上传大文件是一种保护措施,能够防范一些攻击,处于这些目的站主可能不想上调文件大小上限。顺带一提,这个上限默认是2M,而实际备份大概有40~50M,远超这个大小。(可能也和我的备份方法比较暴力有关,哈哈)
  • 备份不能处理WordPress之外的内容。比如,我希望同时备份nginx配置文件,显然这些东西现有插件都无法做到。

要备份什么?

为了能够实现备份,首先总得知道需要备份什么吧。从内容上,整理如下:

  • WordPress Core,这里包含WordPress的代码。版本更新可能导致插件等不可用,因此备份本体也有必要。
  • WordPress 插件,同上,备份全了能够方便恢复。
  • WordPress 主题,我可不想可爱的Sakurairo消失呜~
  • 图像等媒体文件,主要是构成网页的媒体文件等。此外
  • JavaScript、PHP 和其他代码文件,因为我自己做前后端开发,所以也会写一写自己的代码等。
  • 文章等博客内容,这是重中之重啦,你也不想自己辛辛苦苦写的文章消失吧~
  • 其他,比如前面提到的,我也想备份nginx代理的配置文件等

整理了上述内容,可以简要把需要备份的文件分为如下三类:

  • 网页根目录,这包括了WordPress代码、插件、主题、媒体文件等;我自己写的一些小脚本也在里面。
  • 数据库,我自己是用的是Mysql,需要备份其中WordPress使用到的数据库。
  • 其他,例如nginx代理配置等。

开始备份吧~

主要思路

主要的思路是把所有命令都写到shell脚本里面,这样就可以偷懒啦。结合一些定时任务,定期调用就可以实现自动备份了。既然要写shell脚本,就需要思考几个问题:首先是如何整理需要备份的数据,将它们集中起来;接着,需要将这些文件打包;最后,如果说自己的小站崩溃了,那备份在服务器上的文件也不安全。所以本文提供一个云端备份实践~

汇总需要备份的根目录文件和其他文件

想要备份根目录文件,那也比较容易。复制过来打包就好了。但是有一些细节需要注意,比如你需要拥有WordPress根目录的权限。这是一个一开始很困扰我的问题:Nginx代理以及WordPress的运行环境需要使用www-data用户,而我本身的用户(就假设为ubuntu吧)不能修改这些文件,就很麻烦。所以说我就使用如下命令将ubuntu用户加入了www-data组,并且设定根目录的这些文件由www-data组(注意不是www-data用户本身)的ubuntu用户拥有权限——也就是拥有者是www-data组中的ubuntu。这样平时使用Ubuntu用户就可以直接读写根目录文件了。

注意这里的根目录别忘了换成自己的哦(*σ´∀`)σ

sudo usermod -a -G www-data ubuntu
sudo chown -R ubuntu:www-data /var/www/xxx.com/html

此外,如果你的使用的不是nginx或者用户并非www-data,就试着使用ps aux | grep nginx之类的命令去看看什么用户在负责网页服务吧。

在处理好权限问题之后,只需要简单的复制上述根目录就好了,使用cp命令就好:

cp [options] source dest

在复制完毕之后,使用zip命令打包~

zip [options] output.zip file1 file2 ...

这里就不列出具体代码了,文末有完整代码(*σ´∀`)σ

汇总数据库内容

数据库的内容需要使用工具进行导出。本来可以使用phpmyadmin的,但是鉴于之前所提到的原因,还是决定自己学命令行处理。我使用的数据库时Mysql,因此使用的导出工具就是mysqldump。

这里有几个要点,其一是Mysql 8之后,负责导出的用户需要有PROCESS权限。注意这个权限是一个全局权限,也就是不能只赋予给某个数据库,而是给了这个用户之后他对全部数据库都有这个权限!具体的代码如下,更改完之后别忘记刷新:

GRANT PROCESS ON *.* TO `yourusername`@`localhost`;
flush privileges;

导出数据库内容也有技巧,你也不想每次导出都要输出密码吧~ 所以可以在用户根目录创建一个配置文件(~/.my.cnf),输入如下内容(当然还要自己改一下):

[mysqldump]
user=yourusername
password=yourpassword

~/.my.cnf 配置文件中可以包含多个用户和密码的配置。可以为不同的 MySQL 连接配置不同的凭据,并在执行命令时指定使用哪个配置块。通常情况下,~/.my.cnf 文件会包含 [client] 这一默认配置块,这里没有列出;这里列出了mysqldump配置,这样使用导出工具时就不用每次输入密码啦。在使用导出工具时这样用:

mysqldump --defaults-group-suffix=yourusername databasename > backupfile.sql

这样就可以不用输入密码直接导出了。

自动上传云端

在使用上述方法将所需文件整理、压缩后,还需要备份到云端。这里我使用的是百度云,对应有一个第三方开发的python客户端bypy,挺好用的。安装python、bypy的步骤不再赘述,教程一搜一大把~

不过需要注意的是,截止目前(2024年11月14日),有一个bypy的同步bug,这个同步bug可能是用于百度云API结果返回不当导致的,会引发”同步“(syncup)总是覆盖上传的问题,所以我是用的是”上传“(upload)命令,这个命令,居然,可以,正确对比md5并完成上传!所以就很怪~具体可以看这个issue

定时执行备份

参考这篇文章来设定定时任务吧:使用crontab设置服务器定时任务

我的备份代码

至此,备份代码的思路就完全分享完毕了!其实很简单,但是实现时还是有一些小细节,下面是详细的代码,并且附上一些细节讲解~

自动清除旧的历史备份

可以看到代码中设定了拷贝文件夹的上限数量以及归档的压缩包上限数量,到达上限会自动清除。主要的思路是固定文件夹、归档压缩包的文件(夹)名称前缀,这样使用ls命令就可以得到这些文件(夹)的名称列表,对列表使用wc命令即可计数。此外,使用ls命令时添加-t参数就会按照时间降序排序,但我们希望是升序排序并且清除旧的(前几个)因此额外使用-r参数(来使结果倒序)。

自动备份压缩密码

万一压缩密码忘了咋办,这里把压缩密码也存储到备份文件加了,在上传到云端时会自动备份。但注意这个密码是明文存储的,可得妥善保管。

想知道有多少文件又有多大?

可以使用du命令列出文件,这样就能知道有多少文件又占用了多少空间啦。

完整代码

如下是备份代码:

#!/bin/bash

# ==================== 设置 ====================
# 设置要备份的目录路径
nginx_config_dir="/home/ubuntu/xxx"
html_dir="/var/www/xxx.com/html"
backup_parent_dir="/home/ubuntu/xxx/copied_files"
archive_parent_dir="/home/ubuntu/xxx/archive"
# 设置压缩密码
my_archive_zip_password="******"
auto_add_password_note=true     # 自动在 $archive_parent_dir 下记录压缩密码备忘
# 设置最大备份数量(拷贝文件夹/归档压缩包)
max_backup_copy_count=2
max_backup_archive_count=5
# 是否在备份后删除文件夹,true表示删除
auto_delete_copy_after_archived=true
# =============================================


# 获取当前日期,用于文件名
current_time=$(date +"%Y%m%d_%H%M%S")
backup_dir_single_name="backup_$current_time"
backup_dir="$backup_parent_dir/$backup_dir_single_name"
# 创建备份文件夹(如果不存在的话)
mkdir -p "$backup_dir"
if [ "$auto_add_password_note" = true ]; then
    echo "压缩包密码是: $my_archive_zip_password" > "$archive_parent_dir/password.txt"
fi

# ==================== 备份各类文件 =======================
# 复制 nginx 配置文件夹中的 .conf 文件
echo "正在备份 Nginx 配置文件..."
mkdir -p "$backup_dir/nginx_conf"
cp "$nginx_config_dir"/*.conf "$backup_dir/nginx_conf"

# 复制 HTML 文件夹中的所有文件(包括子文件夹)
echo "正在备份 HTML 文件..."
cp -r "$html_dir" "$backup_dir"

# 备份数据库,密码通过 ~/.my.cnf 文件配置指定
echo "正在备份 MYSQL 数据库..."
mysqldump --defaults-group-suffix=yourusername databasename > "$backup_dir/mysql_$current_time".sql


# 输出基本信息到备份文件夹
echo "这是在 $(date +"%Y年%m月%d日 %H:%M:%S") 自动归档的备份." > "$backup_dir/readme.txt"

# ==================== 压缩文件 =======================
# 切换到备份目录,这样创建的压缩文件只保留相对路径
cd "$backup_parent_dir"
echo "正在创建加密的备份文件..."
# 压缩文件在另一个路径被创建
archive_zip="$archive_parent_dir/archive_$current_time.zip"
# 使用 "./$backup_dir_single_name" 作为当前目录,避免路径过长
zip -r -P "$my_archive_zip_password" "$archive_zip" "./$backup_dir_single_name" 1>/dev/null
# 如果设定了备份后删除文件夹则删除文件夹
if [ "$auto_delete_copy_after_archived" = true ]; then
    echo "正在删除拷贝文件..."
    rm -rf "./$backup_dir_single_name"
fi
# 备份完毕
echo "备份并加密完成,备份文件路径:$archive_zip"
# 指定文件权限为自己可读写他人可读
chmod 664 "$archive_zip"

# ==================== 清理旧的压缩文件 =======================
backup_folders=$(ls -d -tr "$backup_parent_dir"/backup* 2>/dev/null)  # 获取所有备份文件夹 -tr是按照时间升序排序
archive_files=$(ls -tr "$archive_parent_dir"/archive* 2>/dev/null)    # 获取所有备份压缩包
# 检查备份数量是否超过最大值
backup_folder_count=$(echo "$backup_folders" | wc -l)
archive_file_count=$(echo "$archive_files" | wc -l)

# 如果备份文件夹数量超过最大值,则删除最旧的备份文件夹
if [ "$backup_folder_count" -gt "$max_backup_copy_count" ] || [ "$auto_delete_copy_after_archived" = true ]; then
    echo "备份文件夹数量($backup_folder_count)超过最大限制($max_backup_copy_count)或者开启了移除拷贝文件,正在删除最旧的备份文件夹..."
    # 计算需要删除的文件夹数量
    if [ "$auto_delete_copy_after_archived" = true ]; then
        excess_folders=$backup_folder_count
    else
        excess_folders=$((backup_folder_count - max_backup_copy_count))
    fi
    # 删除字典序最小的文件夹直到数量不超过最大备份数量
    echo "$backup_folders" | head -n "$excess_folders" | xargs -I {} rm -rf "{}"
fi

# 如果备份压缩包数量超过最大值,则删除最旧的备份压缩包
if [ "$archive_file_count" -gt "$max_backup_archive_count" ]; then
    echo "备份压缩包数量($archive_file_count)超过最大限制($max_backup_archive_count),正在删除最旧的备份压缩包..."
    # 计算需要删除的压缩包数量
    excess_archives=$((archive_file_count - max_backup_archive_count))
    # 删除字典序最小的压缩包直到数量不超过最大备份数量
    echo "$archive_files" | head -n "$excess_archives" | xargs -I {} rm -f "{}"
fi


# ==================== 收尾 =======================
# 提示备份完成
echo "备份完成. 列出当前归档目录如下:"
du -d 2 -h "$archive_parent_dir"/*

如下是自动上传的脚本:

#!/bin/bash

# ==================== 设置 ====================
# 设置要备份的目录路径
archive_parent_dir="/home/ubuntu/xxx/archive"
# 设置远端备份目录,并且总是创建这个目录
remote_dir="/backup-archive"
python3 -m bypy mkdir $remote_dir
# =============================================

echo "全部的zip列表如下:"
du -h /home/ubuntu/xxx/archive/*.zip

# ====================== 计算最新的文件 ======================
echo "正在同步备份文件中..."
python3 -m bypy upload "$archive_parent_dir" "$remote_dir"
echo "同步完毕."