使用 Docker + Rclone 实现轻量主机自动备份

hello world ! and say something say anything~

最近买了便宜的小鸡(廉价 VPS),性能还可以,但不太稳定,偶尔崩溃是常有的事。

好在我早就用上了 Docker,重新部署已经非常方便。每次重新部署都得去找 compose.yaml、数据文件什么的,如果能“一包在手,说走就走”不是更舒服?

于是我把 Docker Compose 配置文件和所有挂载数据都放在同一个文件夹里,只需要打包整个目录,就能实现一键迁移和快速恢复。

思路简述

  • 使用 rclone 挂载 B2(或其他对象存储)为远程目录 /rclone
  • 备份脚本停止 Docker 容器 → 打包目录为加密 ZIP → 上传到 /rclone → 启动容器
  • 保留多个历史备份(每类保留 3 份)
  • 使用 Gotify 推送失败通知

环境准备

安装 rclone

sudo curl https://rclone.org/install.sh | sudo bash

配置远程对象存储

rclone config

配置好如 b2:/my-backup-bucket 的远程盘。

系统准备和挂载

sudo apt update && sudo apt install fuse3 -y sudo sed -i 's/^#user_allow_other/user_allow_other/' /etc/fuse.conf sudo mkdir -p /rclone && sudo chmod a+w /rclone

挂载远程存储:

rclone mount b2:/my-backup-bucket /rclone --allow-other --vfs-cache-mode writes --daemon

❗你可以写入 /etc/fstab 实现开机自动挂载。


多项目通用备份脚本:/root/backup/bd.sh

脚本说明

  • 自动遍历多个目录,每个目录含有完整的 Docker 环境(compose.yaml + 数据文件)
  • 停止容器、压缩打包、上传到远程盘、重启容器
  • 每个目录可配置保留的备份数量
  • 失败时 Gotify 通知推送

脚本内容(简略版)

#!/bin/bash

# 自定义路径
BACKUP_DIR=("/root/")

# 备份目录
BACKUP_DIRS=(
    "${BACKUP_DIR}gotify"
    "${BACKUP_DIR}hobby"
)

# 定义每个目录需要保留的备份数量
declare -A BACKUP_COUNTS=(
    ["hobby"]=1
    ["gotify"]=3
)

# 定义其他变量
RCLONE_DIR="/rclone"
LOG_FILE="${BACKUP_DIR}backup/backup.log"
ZIP_PASSWORD="xxxxxxx"
GOTIFY_URL="https://gotify.1az.cc/message?token=xxxxxxx"

# 获取当前日期
DATE=$(date +%Y%m%d)

# 初始化日志文件和状态变量
echo "备份开始时间: $(date)" >> $LOG_FILE
declare -A BACKUP_STATUS
ALL_SUCCESS=true

# 遍历每个备份目录
for DIR in "${BACKUP_DIRS[@]}"; do
    # 获取目录名
    DIR_NAME=$(basename $DIR)
    BACKUP_STATUS["$DIR_NAME"]="成功"  # 默认状态为成功
    
    # 进入目录
    cd $DIR
    
    # 执行 docker compose down
    echo "正在停止 $DIR 中的 Docker 容器..." >> $LOG_FILE
    docker compose down >> $LOG_FILE 2>&1
    if [ $? -eq 0 ]; then
        echo "$DIR 中的 Docker 容器已成功停止。" >> $LOG_FILE
    else
        echo "$DIR 中的 Docker 容器停止失败。" >> $LOG_FILE
        BACKUP_STATUS["$DIR_NAME"]="失败"
        ALL_SUCCESS=false
        continue
    fi
    
    # 使用 zip 加密压缩
    BACKUP_FILE="${RCLONE_DIR}/${DIR_NAME}${DATE}.zip"
    echo "正在为 $DIR 创建备份..." >> $LOG_FILE
    zip -r -P "$ZIP_PASSWORD" "$BACKUP_FILE" . > /dev/null 2>> $LOG_FILE
    if [ $? -eq 0 ]; then
        echo "$DIR 的备份已成功创建: $BACKUP_FILE" >> $LOG_FILE
    else
        echo "$DIR 的备份创建失败。" >> $LOG_FILE
        BACKUP_STATUS["$DIR_NAME"]="失败"
        ALL_SUCCESS=false
        continue
    fi

    # 启动 Docker 容器
    echo "正在启动 $DIR 中的 Docker 容器..." >> $LOG_FILE
    docker compose up -d >> $LOG_FILE 2>&1
    if [ $? -eq 0 ]; then
        echo "$DIR 中的 Docker 容器已成功启动。" >> $LOG_FILE
    else
        echo "$DIR 中的 Docker 容器启动失败。" >> $LOG_FILE
        BACKUP_STATUS["$DIR_NAME"]="失败"
        ALL_SUCCESS=false
    fi
    
    # 删除旧的备份文件,根据配置的数量保留
    echo "正在清理 $DIR 的旧备份文件..." >> $LOG_FILE
    BACKUP_COUNT=${BACKUP_COUNTS["$DIR_NAME"]}
    if [ -z "$BACKUP_COUNT" ]; then
        BACKUP_COUNT=5  # 默认保留5份
    fi
    ls -t ${RCLONE_DIR}/${DIR_NAME}*.zip 2>/dev/null | tail -n +$(($BACKUP_COUNT + 1)) | xargs rm -f >> $LOG_FILE 2>&1
    if [ $? -eq 0 ]; then
        echo "$DIR 的旧备份文件已成功清理。" >> $LOG_FILE
    else
        echo "$DIR 的旧备份文件清理失败。" >> $LOG_FILE
        BACKUP_STATUS["$DIR_NAME"]="失败"
        ALL_SUCCESS=false
    fi
done

# 备份完成时间
echo "备份完成时间: $(date)" >> $LOG_FILE

# 判断是否全部成功
if $ALL_SUCCESS; then
    echo "所有目录备份成功,不发送通知。" >> $LOG_FILE
else
    HOSTNAME=$(hostname)
    TITLE="${HOSTNAME} 主机备份失败"

    MESSAGE=""
    for DIR_NAME in "${!BACKUP_STATUS[@]}"; do
        MESSAGE+="$DIR_NAME: ${BACKUP_STATUS["$DIR_NAME"]},"
    done

    # 发送通知
    curl -X POST "$GOTIFY_URL" \
        -F "title=$TITLE" \
        -F "message=$MESSAGE" >> $LOG_FILE 2>&1

    echo "Gotify 通知已发送。" >> $LOG_FILE
fi

echo "Gotify 通知已发送。" >> $LOG_FILE

添加定时任务(crontab)

crontab -e # 每天凌晨3点(北京时间) 0 3 * * * /root/backup/bd.sh >> /root/backup/cron.log 2>&1


WordPress 快速备份脚本:/root/backup/bdwp.sh

当你只需要备份某个 WordPress 项目时,这个脚本更轻量:

脚本特性

  • wp 文件夹(网页代码)直接打包(不停服务)
  • mysql 文件夹(数据库挂载)在容器暂停时打包
  • 所有 ZIP 文件加密保存到 /rclone
  • 每类备份仅保留最近 3 个
  • 无需日志文件,干净利落

脚本内容(简洁高效)

#!/bin/bash

# === 基本配置 ===
ZIP_PASSWORD="xxxxxxx"
RCLONE_DIR="/rclone"
DATE=$(date +%Y%m%d)

# 路径配置
WP_DIR="/root/hobby/wp"
MYSQL_DIR="/root/hobby/mysql"
COMPOSE_FILE="/root/hobby/compose.yaml"

# Gotify 推送地址(Token 已含在 URL 中)
GOTIFY_URL="https://gotify.1az.cc/message?token=xxxxxxx"

# 状态记录
declare -A BACKUP_STATUS
ALL_SUCCESS=true

# === WordPress 文件直接打包备份 ===
WP_BACKUP_FILE="${RCLONE_DIR}/hobby_wp${DATE}.zip"
zip -r -P "$ZIP_PASSWORD" "$WP_BACKUP_FILE" "$WP_DIR" > /dev/null 2>&1
if [ $? -eq 0 ]; then
    BACKUP_STATUS["wp"]="成功"
else
    BACKUP_STATUS["wp"]="失败"
    ALL_SUCCESS=false
fi

# 删除旧的 WP 备份,只保留最新 3 个
ls -t ${RCLONE_DIR}/hobby_wp*.zip 2>/dev/null | tail -n +4 | xargs rm -f

# === 关闭容器,备份数据库,再启动容器 ===
cd $(dirname "$COMPOSE_FILE")
docker compose -f "$COMPOSE_FILE" down

MYSQL_BACKUP_FILE="${RCLONE_DIR}/hobby_mysql${DATE}.zip"
zip -r -P "$ZIP_PASSWORD" "$MYSQL_BACKUP_FILE" "$MYSQL_DIR" > /dev/null 2>&1
if [ $? -eq 0 ]; then
    BACKUP_STATUS["mysql"]="成功"
else
    BACKUP_STATUS["mysql"]="失败"
    ALL_SUCCESS=false
fi

docker compose -f "$COMPOSE_FILE" up -d

# 删除旧的 MySQL 备份,只保留最新 3 个
ls -t ${RCLONE_DIR}/hobby_mysql*.zip 2>/dev/null | tail -n +4 | xargs rm -f

# === Gotify 推送函数 ===
send_gotify() {
    local title="$1"
    local message="$2"
    curl -X POST "$GOTIFY_URL" \
        -F "title=$title" \
        -F "message=$message"
}

# === 判断是否需要通知 ===
if ! $ALL_SUCCESS; then
    HOSTNAME=$(hostname)
    TITLE="${HOSTNAME} 主机备份失败"
    MESSAGE=""
    for KEY in "${!BACKUP_STATUS[@]}"; do
        MESSAGE+="$KEY: ${BACKUP_STATUS["$KEY"]},"
    done
    MESSAGE=${MESSAGE%,}
    send_gotify "$TITLE" "$MESSAGE"
fi

添加执行权限

chmod +x /root/backup/bdwp.sh


总结

虽然 VPS 小鸡便宜不稳定,但配合 Docker 和这套脚本,恢复部署只需要:

bash

复制编辑

unzip -P xxx backup.zip docker compose up -d

甚至可以通过 rclone 自动从云盘恢复,容器、数据、配置一体打包,迁移比你换保温杯还快!

如果你也在用小鸡搭服务,不妨试试这套方案;或有改进建议,欢迎来信交流

上一篇