【备份】Docker一键批量备份恢复脚本

脚本分为两步骤,一个是备份,一个是恢复

备份

备份流程如下

1.打开终端,新建备份目录/backup ,并给权限

mkdir /backup & chmod -R 777 /backup

2.新建备份脚本,名字为backup.sh ,粘贴以下内容 (脚本是AI写的)

#!/bin/bash
echo "开始备份..."
docker ps -a --format '{{.Names}}' | while read container; do
    echo "正在备份 $container ..."
    docker commit "$container" "$container"
    docker save "$container" > "/backup/$container.tar"
    volumes=$(docker inspect --format='{{range .Mounts}}{{println .Name .Destination}}{{end}}' $container)
    echo "$volumes" | while read volume destination; do
        if [ -n "$volume" ]; then
            echo "正在备份 $container 的数据卷 $volume ..."
            docker run --rm --volumes-from "$container" -v /backup:/backup ubuntu tar cvf "/backup/${container}_data.tar" "$destination"
            echo "备份 $container 的数据卷 $volume 完成."
        fi
    done
    echo "备份 $container 完成."
    docker inspect $container > "/backup/$container.json"
done
echo "所有容器备份完成."

3.回到终端,输入以下代码运行脚本,开始备份

bash backup.sh

文件夹内会出现3类文件

json是由docker inspect生成的该容器的配置

tar是镜像

_data.tar是容器数据卷的数据

4.小雅需要备份token,在任意文件夹内新建文件xiaoya_backup.sh,粘贴以下内容:(该脚本集备份+恢复为一体)

#!/bin/bash

# 检查 /etc/xiaoya 目录是否存在
if [ -d /etc/xiaoya ]; then
  echo "目录 /etc/xiaoya 已存在。"

  # 将 /etc/xiaoya 目录下的所有文件打包
  echo "正在打包 /etc/xiaoya 目录..."
  tar -cf xiaoya_backup.tar -C /etc xiaoya

  # 显示打包信息
  echo "打包完成,压缩包名为 xiaoya_backup.tar。"
  echo "压缩包保存在当前目录下。"

else
  echo "目录 /etc/xiaoya 不存在。"

  # 创建 /etc/xiaoya 目录
  echo "正在创建目录 /etc/xiaoya..."
  mkdir /etc/xiaoya

  # 设置 /etc/xiaoya 目录权限
  echo "正在设置目录 /etc/xiaoya 的权限..."
  chmod 755 /etc/xiaoya

  # 解压缩 xiaoya_backup.tar 到 /etc/xiaoya 目录
  echo "正在解压缩 xiaoya_backup.tar 到目录 /etc/xiaoya..."
  tar -xf xiaoya_backup.tar -C /etc/xiaoya

  # 显示解压缩信息
  echo "解压缩完成。"
fi

5.终端输入bash xiaoya_backup.sh 执行,该脚本会备份/etc/xiaoya目录下所有文件到当前目录下

6.生成的备份文件为xiaoya_backup.tar ,如果当前目录下有这个文件,脚本会自动解压缩token文件到对应目录

恢复

1.如果在新机器上,首先需要安装docker

2.portainer管理容器需要提前手动恢复,hub的地址:https://hub.docker.com/r/6053537/portainer-ce

3.把备份好的文件拷贝到待恢复的机器上的/backup目录下

4.新建文件restore.sh,并粘贴以下内容:

#!/bin/bash
echo "开始恢复..."
for file in /backup/*.tar; do
    if [[ $file == *"_data.tar" ]]; then
        container=$(basename "$file" _data.tar)
        echo "正在恢复 $container 的数据卷..."
        docker run --rm --volumes-from "$container" -v /backup:/backup ubuntu tar xvf "$file"
        echo "恢复 $container 的数据卷完成."
    else
        container=$(basename "$file" .tar)
        echo "正在恢复 $container ..."
        docker load < "$file"
        echo "恢复 $container 完成."
    fi
done
for file in /backup/*.json; do
    container=$(basename "$file" .json)
    echo "正在恢复 $container 的配置..."
    config=$(jq -r '.[0] | "-d --name " + .Name + " --network " + .HostConfig.NetworkMode + " " + (.Config.Env | map(" -e " + .) | join("")) + " " + (.HostConfig.PortBindings | to_entries | map(" -p " + .value[0].HostIp + ":" + .value[0].HostPort + ":" + .key) | join("")) + " " + .Config.Image' $file)
    docker run $config
    echo "恢复 $container 的配置完成."
done
for file in /backup/*_data.tar; do
    container=$(basename "$file" _data.tar)
    echo "正在覆盖 $container 的数据卷..."
    docker run --rm --volumes-from "$container" -v /backup:/backup ubuntu bash -c "cd / && tar xvf /backup/${container}_data.tar"
    echo "覆盖 $container 的数据卷完成."
done
echo "所有容器恢复完成."

5.保存后,在终端执行命令开始恢复:

bash restore.sh

6.恢复后,docker需要重启,执行命令

systemctl restart docker

7.部分docker容器无法自动恢复,需要手动恢复,例如小雅xiaoya(详细的配置指南请点击打开 ),恢复前需要将上文备份的xiaoya的token备份文件xiaoya_token_backup.tar跟xiaoya_backup.sh复制在同个目录,然后输入以下执行恢复小雅token文件

bash xiaoya_backup.sh

8.恢复完token后再,参考 详细的配置指南 手动恢复小雅xiaoya

一键安装和更新容器,标准模式,打开端口 5678
bash -c "$(curl http://docker.xiaoya.pro/update_new.sh)"
一键安装和更新容器,host模式(推荐,软路由和NAS上更少网络故障,打开端口 5678
bash -c "$(curl http://docker.xiaoya.pro/update_new.sh)" -s host
webdav 账号密码
用户: guest 密码: guest_Api789
重启就会自动更新数据库及搜索索引文件
docker restart xiaoya

9.小雅keeper清理缓存命令

每天自动清理一次。如果系统重启需要手动重新运行或把命令加入系统启动。

bash -c "$(curl -s https://xiaoyahelper.zengge99.eu.org/aliyun_clear.sh | tail -n +2)" -s 3 -tg

附:备份、恢复脚本的注释

备份脚本

  1. 列出所有的 Docker 容器。
  2. 对每个容器执行以下操作:
    • 使用 docker commit 命令创建一个新的镜像,该镜像的状态与当前运行的容器相同。
    • 使用 docker save 命令将新创建的镜像保存为 tar 文件。
    • 使用 docker inspect 命令获取容器的所有数据卷和它们在容器内的目标路径。
    • 对每个数据卷执行以下操作:
      • 如果数据卷存在,使用 docker run 命令启动一个新的临时容器,该容器从当前容器中挂载数据卷,并将备份目录挂载到 /backup
      • 在新的临时容器中,使用 tar 命令将数据卷的内容打包为 tar 文件。
    • 使用 docker inspect 命令将容器的配置信息保存为 json 文件。

恢复脚本

  1. 对备份目录中的每个 tar 文件执行以下操作:
    • 如果文件名以 _data.tar 结尾,那么这是一个数据卷的备份文件。使用 docker run 命令启动一个新的临时容器,该容器从对应的容器中挂载数据卷,并将备份目录挂载到 /backup。然后在新的临时容器中,使用 tar 命令将 tar 文件的内容解压到数据卷中。
    • 否则,这是一个容器的备份文件。使用 docker load 命令从 tar 文件中加载镜像。
  2. 对备份目录中的每个 json 文件执行以下操作:
    • 这是一个容器的配置文件。使用 jq 命令从 json 文件中提取容器的配置信息,然后使用 docker run 命令根据这些配置信息启动新的容器。
  3. 对备份目录中的每个以 _data.tar 结尾的文件执行以下操作:
    • 这是一个数据卷的备份文件。使用 docker run 命令启动一个新的临时容器,该容器从对应的容器中挂载数据卷,并将备份目录挂载到 /backup。然后在新的临时容器中,使用 tar 命令将 tar 文件的内容解压到数据卷中。

备份方法2

检查目录/backup/docker_data/是否存在,如果不存在则创建它,并创建脚本文件 docker_data_backup.sh,并设置权限为755:

mkdir -p /backup/docker_data/ && touch /backup/docker_data/docker_data_backup.sh && chmod 755 /backup/docker_data/docker_data_backup.sh

打开该目录

cd /backup/docker_data

执行脚本命令:docker_data_backup.sh

bash docker_data_backup.sh

docker_data_backup.sh脚本内容如下:

#!/bin/bash

# Helper function for countdown timer
countdown() {
   local seconds=$1
   local temp

   while [ $seconds -gt 0 ]; do
      echo -ne "倒计时 $seconds\033[0K\r"
      read -t 1 -n 1 temp
      if [ "$temp" = "" ]; then
          echo -e "\n"
          return
      fi
      : $((seconds--))
   done
}

# 获取当前的日期和时间,格式为YYYY_MM_DD_HH_MM
NOW=$(date +"%Y_%m_%d_%H_%M")

# 记录容器名称,并按顺序编号
echo "正在列出所有Docker容器(跳过含有 '1Panel' 的容器):"
declare -a CONTAINERS
i=1
for name in $(docker ps -a --format "{{.Names}}" | grep -v "1Panel")
do
    echo "[$i] $name"
    CONTAINERS[$i]=$name
    i=$((i+1))
done

echo "请输入容器编号进行选择备份,用空格或者逗号隔开,或直接回车备份所有容器。输入 'exit' 退出。您有10秒时间输入:"
countdown 10

read -r -t 10 index
if [ -z "$index" ]
then
    array=($(seq 1 ${#CONTAINERS[@]}))
    echo "没有输入或输入超时,备份所有容器..."
elif [ "$index" == "exit" ]
then
    echo "正在退出脚本..."
    exit 0
else
    IFS=', ' read -r -a array <<< "$index"
    if [ ${#array[@]} -eq 0 ]
    then
        echo "未提供任何编号,正在退出脚本..."
        exit 1
    fi
fi

echo "待备份的容器目录如下:"
for element in "${array[@]}"
do
    CONTAINER_NAME="${CONTAINERS[$element]}"
    echo "===== 容器:$CONTAINER_NAME ====="
    docker inspect --format '{{ range .Mounts }}{{ println .Source }}{{ end }}' "${CONTAINERS[$element]}"
done

echo -e "\n按回车键继续执行备份,其他任意键退出脚本。倒计时10秒后将自动继续。"
countdown 10

read -r -n1 -t 10 pre_input
if [ "$pre_input" != "" ]; then
    echo -e "\n用户已选择退出脚本..."
    exit 0
fi
echo -e "\n开始备份...\n"

BACKUP_ROOT_DIR="/backup/docker_data"
BACKUP_DIR="$BACKUP_ROOT_DIR/docker_data_backup_$NOW"
mkdir -p "$BACKUP_DIR"

LOG_FILE="$BACKUP_DIR/docker_data_backup_$NOW.log"
touch "$LOG_FILE"

log_backup() {
  local message="$1"
  echo "${message}" | tee -a "$LOG_FILE"
}

# 备份过程
for element in "${array[@]}"
do
    CONTAINER_NAME="${CONTAINERS[$element]}"
    log_backup "正在备份 ${CONTAINER_NAME}:"
    
    MOUNT_INDEX=1
    VOLUMES=$(docker inspect --format '{{ range .Mounts }}{{ println .Source }}{{ end }}' "${CONTAINERS[$element]}")
    
    for volume in $VOLUMES; do
        if [[ "$volume" == "/var/run/docker.sock" ]]; then
            continue
        fi
        
        CONTAINER_BACKUP_SUBDIR="${CONTAINER_NAME}_${MOUNT_INDEX}"
        CONTAINER_BACKUP_DIR="${BACKUP_DIR}/${CONTAINER_BACKUP_SUBDIR}"
        mkdir -p "$CONTAINER_BACKUP_DIR"

        if [ -d "$volume" ] || [ -f "$volume" ]; then
            echo "正在备份:$volume"
            cp -a "$volume/" "$CONTAINER_BACKUP_DIR/" && \
            log_backup "${volume} 已备份到 ${CONTAINER_BACKUP_SUBDIR}"
        else
            log_backup "WARNING: $volume 不存在,将被跳过。"
        fi

        ((MOUNT_INDEX++))
    done
    log_backup "容器 ${CONTAINER_NAME} 备份完成。"
    echo -e "\n" | tee -a "$LOG_FILE"
done

echo "日志记录完成,正在创建备份的tar文件..."
FINAL_BACKUP_FILE="$BACKUP_ROOT_DIR/docker_data_backup_$NOW.tar"
tar -czf "$FINAL_BACKUP_FILE" -C "$BACKUP_ROOT_DIR" "docker_data_backup_$NOW" && \
echo "备份文件已打包为:$FINAL_BACKUP_FILE"

# 删除原始备份目录
if [ -f "$FINAL_BACKUP_FILE" ]; then
    echo "正在删除原始备份文件夹..."
    rm -rf "$BACKUP_DIR"
    log_backup "原始备份目录 ${BACKUP_DIR} 已删除。"
    echo "原始备份文件夹已删除。"
else
    log_backup "备份tar文件创建失败,未删除原始备份目录。"
    echo "备份tar文件创建失败,未删除原始备份目录。"
fi

# 保留最新两个备份文件并删除其他旧文件
echo "检查备份文件夹内的备份文件数量..."
cd "$BACKUP_ROOT_DIR"
BACKUP_FILES=$(ls docker_data_backup_*.tar)
TOTAL_BACKUPS=$(echo "$BACKUP_FILES" | wc -l)

if [ $TOTAL_BACKUPS -gt 2 ]; then
    echo "已超过设定的备份文件数量,正在删除旧文件..."
    # 删除最老的备份文件,只保留最新的两个
    ls -1tr docker_data_backup_*.tar | head -n -2 | xargs rm -f
    echo "旧备份文件已删除。"
fi

echo -e "所有选定容器的备份流程现已完成!"

这个脚本的主要用途是备份Docker容器相关的数据。以下是脚本执行的每一步详细解释:

  1. 设置倒计时函数(countdown):脚本定义了一个countdown函数,用于为用户提供一个简单的倒计时功能,在某些操作前给予用户时间作出反应。
  2. 获取当前时间和日期:脚本使用date命令获取当前的时间,格式为年_月_日_时_分(YYYY_MM_DD_HH_MM),这将用于创建具有时间戳的备份目录和备份文件。
  3. 列出所有Docker容器:脚本列出当前所有运行的Docker容器名称,但跳过所有名称中包含”1Panel”字符串的容器。之后,它提供一个接口让用户输入他们想要备份的特定容器编号。如果在10秒内没有响应或用户回车,脚本将备份所有容器。
  4. 接收用户输入:此步骤中,脚本等待用户指定要备份的Docker容器编号。用户输入的编号用于选定具体备份哪些容器。
  5. 决定备份哪些容器:根据用户的选择,脚本决定备份所有的容器或是只备份指定编号的容器。
  6. 创建备份目录和日志文件:脚本创建一个根备份目录以及一个特定的备份目录,路径包含了之前获取的时间戳。此外还创建了一个日志文件用于记录备份过程中的事件。
  7. 执行备份操作:针对每个选中备份的容器,脚本会获取其挂载点的数据,并将所有数据复制到之前创建的备份目录下。备份过程中的详细信息被写入日志文件。
  8. 创建备份tar包:脚本打包刚才创建的备份目录为一个.tar文件,这样备份文件会更方便地被存储和传输。
  9. 删除原始备份目录:一旦.tar备份文件创建完成,脚本会删除原来的备份目录来释放空间,只留下打包后的tar文件。
  10. 清理旧的备份文件:脚本检查备份根目录下所有现有的tar备份文件的数量。如果备份文件超过了设定的数量(在本例中为2个),脚本会删除最旧的备份文件,只保留两个最新的备份文件。
  11. 完成备份:脚本在备份完成后给出提示,显示所有选定的容器已经备份完成。此时,根据用户的备份意图,重要数据已被安全存储。

在使用这个脚本之前,请确保您对脚本有正确的理解,并已在安全的环境中进行了测试,特别是对于删除文件和目录的操作。错误的命令或者无意的使用可能会导致数据丢失。

留下评论