Orange Pi 3B 整盘备份与自动恢复方案
Orange Pi 3B 整盘备份与自动恢复方案
本教程将指导您为 Orange Pi 3B 开发板配置全盘镜像备份,支持定时自动备份、多版本保留,并同步到飞牛NAS和阿里云盘。当系统损坏或硬件故障时,可通过烧录备份镜像快速恢复。
📌 方案概览
整盘备份:使用 dd 读取整个 TF 卡(含 U-Boot、内核、根文件系统),压缩为 .img.gz 镜像。
一致性保障:备份前自动停止 Docker 等关键服务,执行 sync 和 fstrim 确保文件系统一致并减小镜像体积。
多副本存储:备份文件同时保存在本地、飞牛NAS(SMB 挂载)和阿里云盘(WebDAV),各保留最近 2 个版本。
自动化:通过 cron 定时执行,支持 tmux 后台运行和进度查看。
完整性校验:生成 MD5 校验文件,恢复前验证镜像完整性。
🧰 准备工作
1. 确认系统盘设备名
lsblk
通常 TF 卡设备为 /dev/mmcblk0,本文统一使用 /dev/mmcblk1(根据实际调整)。
2. 安装必要软件
sudo apt update sudo apt install -y pigz pv rclone cifs-utils tmux
🗂️ 挂载飞牛NAS(SMB)
创建凭据文件(隐藏密码)
sudo mkdir -p /etc/samba/credentials sudo nano /etc/samba/credentials/fnnas
文件内容:
username=你的NAS用户名 password=你的NAS密码 domain=WORKGROUP
设置权限:
sudo chmod 600 /etc/samba/credentials/fnnas sudo chown root:root /etc/samba/credentials/fnnas
编辑 /etc/fstab
sudo nano /etc/fstab
添加挂载条目:
//NAS的IP地址/共享名 /mnt/nas/fnnas cifs credentials=/etc/samba/credentials/fnnas,uid=1000,gid=1000,iocharset=utf8,vers=3.0,nofail,x-systemd.automount 0 0
测试挂载:
sudo mkdir -p /mnt/nas/fnnas sudo mount -a df -h /mnt/nas/fnnas
☁️ 配置 rclone 连接阿里云盘
本方案使用 AList 暴露阿里云盘的 WebDAV 接口,再通过 rclone 对接。
配置 rclone 远程
sudo -i rclone config
交互步骤:
n) New remote name> aliyun Storage> webdav url> http://AList服务器IP:5244/dav vendor> other user> AList用户名 password> AList密码
创建备份目录:
rclone mkdir aliyun:orangepi3b
📜 备份脚本
将以下脚本保存为 /usr/local/bin/orangepi-backup.sh,并赋予执行权限:
sudo nano /usr/local/bin/orangepi-backup.sh sudo chmod +x /usr/local/bin/orangepi-backup.sh
脚本内容(已包含 MD5 校验、多版本保留、服务自动恢复):
#!/usr/bin/env bash set -euo pipefail # ==================== 配置区 ==================== DISK="/dev/mmcblk1" # 整盘设备,不要写分区号 BACKUP_BASE="/mnt/data/backup" # 本地备份目录(非系统盘) NAS_DIR="/mnt/nas/fnnas" # 飞牛NAS挂载目录,留空跳过 ALIYUN_REMOTE="aliyun:orangepi3b" # rclone 远程目录 RETENTION=2 # 保留版本数 SERVICES_TO_STOP=( docker containerd cron rsyslog dnsmasq avahi-daemon bluetooth rpcbind haveged smartmontools unattended-upgrades systemd-resolved orangepi-ramlog vnstat sysstat ) # ==================== 函数 ==================== if command -v pigz >/dev/null; then COMPRESSOR=(pigz -6 -p 3) else COMPRESSOR=(gzip -6) fi STOPPED_SERVICES=() cleanup_old() { local dir="$1" pattern="$2" keep="$3" if [ -d "$dir" ]; then local old_files old_files=$(find "$dir" -maxdepth 1 -type f -name "$pattern" -printf '%T@ %pn' | sort -rn | tail -n +$((keep+1)) | cut -d' ' -f2-) if [ -n "$old_files" ]; then echo "$old_files" | while read -r f; do echo "[$(date)] 删除旧文件: $f" rm -f "$f" "${f}.md5" done fi fi } restart_services() { for (( idx=${#STOPPED_SERVICES[@]}-1 ; idx>=0 ; idx-- )); do srv="${STOPPED_SERVICES[idx]}" echo "[$(date)] 重启服务: $srv" systemctl start "$srv" 2>/dev/null || echo " 启动 $srv 失败" sleep 2 done # 显式确保 Docker 运行 if ! systemctl is-active --quiet docker.service; then echo "[$(date)] 显式启动 docker.service" systemctl start docker.service fi } trap restart_services EXIT # ==================== 1. 停止服务 ==================== if systemctl is-active --quiet docker.socket; then echo "[$(date)] 停止服务: docker.socket" systemctl stop docker.socket STOPPED_SERVICES+=("docker.socket") fi for srv in "${SERVICES_TO_STOP[@]}"; do if systemctl is-active --quiet "$srv"; then echo "[$(date)] 停止服务: $srv" systemctl stop "$srv" STOPPED_SERVICES+=("$srv") fi done # ==================== 2. 同步并 TRIM ==================== sync fstrim -av || true sync sleep 2 # ==================== 3. 生成镜像 ==================== mkdir -p "$BACKUP_BASE/tmp" IMG_NAME="orangepi3b-$(date +%Y%m%d-%H%M%S).img.gz" IMG_PATH="$BACKUP_BASE/tmp/$IMG_NAME" FINAL_PATH="$BACKUP_BASE/$IMG_NAME" echo "[$(date)] 开始整盘备份: $DISK -> $IMG_PATH" dd if="$DISK" bs=4M status=progress | "${COMPRESSOR[@]}" > "$IMG_PATH" sync # ==================== 4. 恢复服务 ==================== restart_services trap - EXIT # ==================== 5. 移动并生成 MD5 ==================== mv "$IMG_PATH" "$FINAL_PATH" echo "[$(date)] 本地镜像完成: $FINAL_PATH" echo "[$(date)] 计算 MD5 校验和..." md5sum "$FINAL_PATH" > "${FINAL_PATH}.md5" echo "[$(date)] MD5 文件: ${FINAL_PATH}.md5" # ==================== 6. 备份到飞牛NAS ==================== if [ -n "$NAS_DIR" ] && [ "$NAS_DIR" != "$BACKUP_BASE" ]; then if [ -d "$NAS_DIR" ]; then echo "[$(date)] 复制到飞牛NAS: $NAS_DIR" cp "$FINAL_PATH" "${FINAL_PATH}.md5" "$NAS_DIR/" sync cleanup_old "$NAS_DIR" "orangepi3b-*.img.gz" "$RETENTION" else echo "[$(date)] 警告: NAS目录 $NAS_DIR 不存在,可能未挂载,跳过。" fi fi # ==================== 7. 上传到阿里云盘 ==================== REMOTE_NAME="${ALIYUN_REMOTE%%:*}" if command -v rclone >/dev/null && rclone config show | grep -Fq "[${REMOTE_NAME}]"; then echo "[$(date)] 上传到阿里云盘: $ALIYUN_REMOTE" rclone mkdir "$ALIYUN_REMOTE" >/dev/null 2>&1 || true rclone copy "$FINAL_PATH" "$ALIYUN_REMOTE" --transfers 1 --retries 3 --bwlimit 4M --progress --stats 1m --stats-one-line --timeout 60s --contimeout 30s rclone copy "${FINAL_PATH}.md5" "$ALIYUN_REMOTE" --transfers 1 --retries 3 --bwlimit 4M --progress --stats 1m --stats-one-line --timeout 60s --contimeout 30s old_images=$(rclone lsf --files-only "$ALIYUN_REMOTE" 2>/dev/null | grep '^orangepi3b-.*.img.gz$' | sort | head -n -${RETENTION}) if [ -n "$old_images" ]; then echo "$old_images" | while read -r img; do echo "[$(date)] 删除阿里云盘旧版本: $img" rclone deletefile "$ALIYUN_REMOTE/$img" rclone deletefile "$ALIYUN_REMOTE/$img.md5" 2>/dev/null || true done fi else echo "[$(date)] 警告: rclone 未配置或远程 ${REMOTE_NAME} 不存在,跳过上传。" fi # ==================== 8. 本地清理 ==================== cleanup_old "$BACKUP_BASE" "orangepi3b-*.img.gz" "$RETENTION" rm -rf "$BACKUP_BASE/tmp" # 最终保障:确保 Docker 和 containerd 运行 systemctl start containerd || true systemctl start docker || true echo "[$(date)] 备份任务完成。"
⏰ 设置定时任务
编辑 root 的 crontab:
sudo crontab -e
添加以下行(每天凌晨 3 点执行,使用 tmux 运行并记录日志):
0 3 * * * tmux kill-session -t backup 2>/dev/null; tmux new-session -d -s backup '/usr/local/bin/orangepi-backup.sh >> /var/log/orangepi-backup.log 2>&1'
🖥️ 后台运行与进度查看
使用 tmux 手动运行
tmux new -s backup sudo /usr/local/bin/orangepi-backup.sh
分离:Ctrl+B 然后按 D
重新连接:tmux attach -t backup
查看实时日志
tail -f /var/log/orangepi-backup.log
♻️ 恢复系统
验证备份完整性
md5sum -c orangepi3b-YYYYMMDD-HHMMSS.img.gz.md5
烧录镜像
Linux 电脑
gunzip orangepi3b-YYYYMMDD-HHMMSS.img.gz sudo dd if=orangepi3b-YYYYMMDD-HHMMSS.img of=/dev/sdX bs=4M status=progress conv=fsync sync
Windows 电脑
使用 balenaEtcher 或 Rufus 烧录解压后的 .img 文件。
恢复后扩展分区(如果换了更大的卡)
sudo resize2fs /dev/mmcblk1p2
🔧 故障排查
问题 可能原因 解决方法 Docker 未自动启动 重启顺序错误 检查脚本中 restart_services 是否逆序启动,并显式启动 docker rclone 上传 403 WebDAV 权限不足 检查 AList 用户权限,确认备份目录可写 上传无进度输出 rclone 未加 --progress 添加 --progress --stats 1m 参数 NAS 挂载失败 凭据文件权限或网络问题 检查 /etc/fstab 和凭据文件,手动 mount -a 测试 备份体积过大 未执行 fstrim 或压缩级别低 确认 fstrim 有效,尝试 pigz -9
🔐 安全建议
所有密码均保存在权限为 600 的文件中(如 /etc/samba/credentials/fnnas 和 rclone 配置)。
定期检查备份日志,确保任务正常执行。
每隔一段时间(如每月)取一个备份镜像实际烧录测试,验证可恢复性。
云端存储建议开启回收站/版本功能,防止误删。
📘 本教程基于 Orange Pi 3B(Ubuntu 22.04)验证,可根据实际环境调整设备名、服务列表和存储路径。
