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 自动从云盘恢复,容器、数据、配置一体打包,迁移比你换保温杯还快!
如果你也在用小鸡搭服务,不妨试试这套方案;或有改进建议,欢迎来信交流