分享一下我的docker-compose容器项目的自动备份方法

特点

使用脚本结合定时任务来按时备份docker-compose目录到指定位置,
执行过程为保证数据的统一性自动停止容器,备份完成自动启动容器,这在有数据库的项目里非常有必要。

适用条件

适用于docker-compose部署的容器项目,并且数据是以下面形式挂载:

    volumes:
      - ./data/postgres:/var/lib/postgresql/data

也就是整个数据都保存在一个目录下的情况。
其他前置条件:

  • 确保脚本执行用户对相关目录有访问和操作权限,否则压缩会失败。
  • 确保docker-compose命令是:docker compose up -d / docker compose down形式,也就是中间不带杠的,如果带横杠,需要修改脚本把-加上。

脚本需要修改的内容非常少:指定docker-compose目录,指定备份文件存放目录。其他内容都不需要动。

创建脚本

创建并编辑脚本:vim backup.sh
脚本内容:

#!/bin/bash

# 此处按需修改!!
# 定义需要备份的docker-compose项目目录(使用绝对路径)
PROJECTS=(
    "/home/cap/docker/PostgreSQL"
    "/home/cap/docker/Traefik"
	"/home/cap/docker/vaultwarden"
    #"需要备份的项目路径依次添加进来,每行一个,注意格式"
    #"如果有公共的数据库容器,一定要把数据库放到最后一个执行"
)

# 此处按需修改!!
# 定义备份存储位置(使用绝对路径)
BACKUP_DIR="/home/cap/docker_backups"

# 备份保留的次数(保留最近的 N 次备份)
BACKUP_COUNT=5

# ----------- 下面内容不要动!!---------#
TIMESTAMP=$(date  %Y%m%d_%H%M%S)
# 锁文件路径
LOCK_FILE="/tmp/backup_and_restart.lock"

# 创建备份目录(如果不存在)
mkdir -p "$BACKUP_DIR"

# 检查是否已有脚本实例在运行
if [ -e "$LOCK_FILE" ]; then
    echo "另一个脚本实例正在运行(锁文件:$LOCK_FILE),退出..."
    exit 1
fi

# 创建锁文件并设置清理机制
trap 'rm -f "$LOCK_FILE"; exit' INT TERM EXIT
touch "$LOCK_FILE"

# 记录脚本启动时间
START_TIME=$(date  %s)
echo "脚本开始执行..."

# 初始化任务结果列表
TASK_RESULTS=()

# 遍历配置的项目目录
for PROJECT_PATH in "${PROJECTS[@]}"; do
    PROJECT_NAME=$(basename "$PROJECT_PATH")  # 获取项目目录的名称
    CONTAINER_STOPPED=false

    echo "===================="
    echo "正在处理项目:$PROJECT_NAME"

    if [ -d "$PROJECT_PATH" ]; then
        # 进入项目目录
        cd "$PROJECT_PATH" || { echo "无法进入目录 $PROJECT_PATH,跳过..."; TASK_RESULTS =("$PROJECT_NAME 失败"); continue; }

        # 检查容器是否在运行
        CONTAINER_STATUS=$(docker compose ps -q)
        if [ -z "$CONTAINER_STATUS" ]; then
            echo "容器 $PROJECT_NAME 当前未在运行,直接进行备份操作..."
        else
            echo "容器 $PROJECT_NAME 已在运行,准备停止容器..."
            # 停止容器
            docker compose down || { echo "停止容器失败,跳过项目 $PROJECT_NAME..."; TASK_RESULTS =("$PROJECT_NAME 失败"); continue; }
            CONTAINER_STOPPED=true
            # 等待停止完成
            echo "等待 5 秒确保容器完全停止..."
            sleep 5
        fi

        # 备份项目目录
        echo "正在备份项目目录..."
        BACKUP_FILE="$BACKUP_DIR/${PROJECT_NAME}_${TIMESTAMP}.tar.gz"
        if ! tar -czf "$BACKUP_FILE" -C "$(dirname "$PROJECT_PATH")" "$PROJECT_NAME"; then
            echo "备份项目 $PROJECT_NAME 时发生错误,跳过该项目..."
            # 如果容器之前被停止,重新启动容器
            if [ "$CONTAINER_STOPPED" = true ]; then
                echo "重新启动容器 $PROJECT_NAME ..."
                docker compose up -d || { echo "启动容器失败,请手动检查 $PROJECT_NAME"; TASK_RESULTS =("$PROJECT_NAME 失败"); continue; }
            fi
            TASK_RESULTS =("$PROJECT_NAME 失败")
            continue
        fi
        echo "项目 $PROJECT_NAME 已备份至 $BACKUP_FILE"

        # 启动容器
        if [ "$CONTAINER_STOPPED" = true ]; then
            echo "重新启动容器 $PROJECT_NAME ..."
            docker compose up -d || { echo "启动容器失败,请手动检查 $PROJECT_NAME"; TASK_RESULTS =("$PROJECT_NAME 失败"); continue; }
            # 等待容器完全启动
            echo "等待 5 秒确保服务稳定运行..."
            sleep 5
        fi

        TASK_RESULTS =("$PROJECT_NAME 成功")
        echo "$PROJECT_NAME 成功"
    else
        echo "项目目录 $PROJECT_PATH 不存在,跳过..."
        TASK_RESULTS =("$PROJECT_NAME 失败")
    fi
done

# 删除多余的备份文件,只保留最新的 $BACKUP_COUNT 次备份
echo "保留最近的 $BACKUP_COUNT 次备份,删除其余的备份文件..."
BACKUP_FILES=$(find "$BACKUP_DIR" -type f -name "*.tar.gz" | sort -r)
EXTRA_FILES=$(echo "$BACKUP_FILES" | tail -n  $((BACKUP_COUNT 1)))
if [ -n "$EXTRA_FILES" ]; then
    echo "删除以下多余的备份文件:"
    echo "$EXTRA_FILES"
    echo "$EXTRA_FILES" | xargs rm -f
else
    echo "没有多余的备份文件需要删除。"
fi

echo "过期备份文件删除完成。"

# 删除锁文件
rm -f "$LOCK_FILE"

# 记录脚本结束时间
END_TIME=$(date  %s)
EXECUTION_TIME=$((END_TIME - START_TIME))

# 输出脚本执行总结
echo "===================="
echo "脚本执行总结"
echo "--------------------"
for result in "${TASK_RESULTS[@]}"; do
    echo "$result"
done
echo "脚本执行结束时间: $(date)"
echo "总耗时: $EXECUTION_TIME 秒"

手动测试执行

只需要把脚本内容中的备份目录和备份保存目录修改下。
然后将 backup.sh 保存到任意位置后,需要给脚本加上执行权限:

chmod  x ./backup.sh  # 使脚本具有执行权限

然后可以手动执行脚本测试其效果:

./backup.sh  # 执行脚本
定时执行

可以通过 cron 设置定时任务,确保备份操作每天自动执行。

  1. 输入命令:crontab -e,打开 cron 配置文件。
  2. 添加以下内容,将 0 2 * * * 改为你希望执行备份的时间。这里设定为每天凌晨 2 点执行。
0 2 * * * /home/cap/backup.sh >> /home/cap/log/backup_docker_compose.log 2>&1

解释:

  • 0 2 * * *:表示每天的 凌晨 2 点整 执行。
  • /home/cap/backup.sh:脚本的路径。
  • >> /home/cap/log/backup_docker_compose.log 2>&1:保存日志到这里 /home/cap/log/backup_docker_compose.log

日志目录必须存在,不存在的话先手动创建:mkdir -p /home/cap/log

备份的压缩包后续可以搭配rclone上传到网盘上,或者对象存储上。

点赞
  1. cap说道:

    这是针对不使用面板的用户,而且需要保证数据一致性的容器。如果是静态文件,没数据库可以不用脚本,直接使用rclone备份到网盘。

  2. cap说道:

    适合个人项目,例如bitwarden,就非常需要定时备份。备份需要重启容器,如果你的项目不允许暂停,不要用。备份完成后可以结合rclone再做个定时任务定时上传到对象存储或者网盘上,不然没有意义。

发表回复

电子邮件地址不会被公开。必填项已用 * 标注

×
订阅图标按钮