1. 概述

在使用 Docker 容器进行开发时,最佳实践是将重要数据存储在 Docker Volume 中。由于 Volume 独立于容器生命周期存在,因此可以保证数据的持久化。为了提升开发体验,我们也可以将容器及其数据卷一起备份。但需要注意的是,包含数据卷的备份并不是一个直接的过程。

本文将以 MySQL 容器为例,演示如何完整地备份和恢复一个 Docker 容器及其数据卷。

2. 容器与数据卷的准备

在开始之前,我们需要先创建一个容器和对应的 Volume。

2.1 创建带数据卷的容器

我们以 MySQL 容器为例,创建并启动一个容器:

$ docker run --name mysql_container -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=wordpress -v mysql_data:/var/lib/mysql -d mysql:5.7

命令说明:

  • --name mysql_container:容器名称为 mysql_container
  • -e MYSQL_ROOT_PASSWORD=root:设置 root 用户密码为 root
  • -e MYSQL_DATABASE=wordpress:创建名为 wordpress 的数据库
  • -v mysql_data:/var/lib/mysql:挂载名为 mysql_data 的 Volume 到容器的 /var/lib/mysql 目录
  • -d mysql:5.7:以后台模式运行 mysql:5.7 镜像

MySQL 容器的数据将保存在名为 mysql_data 的 Volume 中。

2.2 查看容器使用的 Volume

我们可以使用 docker inspect 命令查看容器挂载的 Volume:

$ docker inspect mysql_container | grep Mounts -A 10
        "Mounts": [
            {
                "Type": "volume",
                "Name": "mysql_data",
                "Source": "/var/lib/docker/volumes/mysql_data/_data",
                "Destination": "/var/lib/mysql",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }

可以看到,容器 mysql_container 使用了名为 mysql_data 的 Volume。

3. 容器与数据卷的备份

本节将演示如何对容器及其数据卷进行备份。

3.1 备份镜像

使用 docker save 命令将容器使用的镜像打包保存:

$ docker save -o mysql_image_backup.tar mysql:5.7
  • -o mysql_image_backup.tar:输出文件名
  • mysql:5.7:要备份的镜像名称

执行后,当前目录下将生成 mysql_image_backup.tar 文件。

3.2 备份 Volume 数据

使用 tar 命令将 Volume 中的数据打包备份:

$ docker run --rm -v mysql_data:/data -v $(pwd):/backup busybox tar cvf /backup/mysql_data_backup.tar /data

这个命令会:

  • 启动一个临时容器,挂载 mysql_data/data
  • 将当前目录挂载为 /backup
  • 使用 tar 打包 /data 内容,并保存为当前目录下的 mysql_data_backup.tar

3.3 备份容器元数据

使用 docker inspect 命令保存容器的元信息:

$ docker inspect mysql_container > mysql_metadata.json

该命令将容器的配置信息保存到 mysql_metadata.json 文件中,便于后续恢复使用。

3.4 自动化备份脚本

为了简化备份流程,我们可以编写一个 Bash 脚本:

#!/bin/bash

# 设置变量
CONTAINER_NAME="mysql_container"
IMAGE_NAME="mysql:5.7"
VOLUME_NAME="mysql_data"

# 备份文件名
IMAGE_BACKUP="mysql_image_backup.tar"
VOLUME_BACKUP="mysql_data_backup.tar"
METADATA_BACKUP="mysql_metadata.json"

echo "Starting backup process..."

# 备份镜像
echo "Saving Docker image..."
docker save -o "$IMAGE_BACKUP" "$IMAGE_NAME"

# 备份 Volume
echo "Saving volume data..."
docker run --rm -v "$VOLUME_NAME":/data -v "$(pwd)":/backup busybox tar cvf "/backup/$VOLUME_BACKUP" /data

# 备份元数据
echo "Saving container metadata..."
docker inspect "$CONTAINER_NAME" > "$METADATA_BACKUP"

echo "Backup completed successfully!"

赋予执行权限并运行:

$ chmod +x backup_script.sh
$ ./backup_script.sh

这样我们就可以一键完成镜像、数据卷和元数据的备份。

4. 容器与数据卷的恢复

在恢复之前,我们需要先删除已有的容器、Volume 和镜像,以确保恢复过程是基于备份进行的。

4.1 删除现有容器、Volume 和镜像

$ docker stop mysql_container && docker rm mysql_container
$ docker volume rm mysql_data
$ docker rmi mysql:5.7

4.2 加载镜像

使用 docker load 命令加载之前备份的镜像:

$ docker load -i mysql_image_backup.tar

确认镜像是否已加载成功:

$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
mysql        5.7       5107333e08a8   15 months ago   501MB

4.3 提取元数据信息

我们可以使用 jq 工具从 mysql_metadata.json 中提取信息:

CONTAINER_NAME=$(jq -r '.[0].Name' mysql_metadata.json)
IMAGE_NAME=$(jq -r '.[0].Config.Image' mysql_metadata.json)
VOLUME_MAPPINGS=$(jq -r '.[0].Mounts | map("-v \(.Name):\(.Destination)") | join(" ")' mysql_metadata.json)
ENV_VARS=$(jq -r '.[0].Config.Env | map("-e " + .) | join(" ")' mysql_metadata.json)

这些变量将用于重建容器。

4.4 重建 Volume

$ docker volume create mysql_data

4.5 恢复 Volume 数据

使用 tar 命令将备份的数据恢复到新 Volume 中:

$ docker run --rm -v mysql_data:/data -v $(pwd):/backup busybox tar xvf /backup/mysql_data_backup.tar -C /

4.6 恢复容器配置

使用提取的元数据重建容器:

RESTORE_CMD="docker run --name $CONTAINER_NAME $ENV_VARS $VOLUME_MAPPINGS -d $IMAGE_NAME"
eval "$RESTORE_CMD"

4.7 重启容器

$ docker restart mysql_container

至此,我们完成了容器及其数据卷的完整恢复。

4.8 自动化恢复脚本

为了简化恢复流程,也可以编写一个恢复脚本:

#!/bin/bash

# 备份文件名
IMAGE_BACKUP="mysql_image_backup.tar"
VOLUME_BACKUP="mysql_data_backup.tar"
METADATA_BACKUP="mysql_metadata.json"

echo "Starting restoration process..."

# 提取元数据
CONTAINER_NAME=$(jq -r '.[0].Name' "$METADATA_BACKUP")
IMAGE_NAME=$(jq -r '.[0].Config.Image' "$METADATA_BACKUP")
VOLUME_MAPPINGS=$(jq -r '.[0].Mounts | map("-v \(.Name):\(.Destination)") | join(" ")' "$METADATA_BACKUP")
ENV_VARS=$(jq -r '.[0].Config.Env | map("-e " + .) | join(" ")' "$METADATA_BACKUP")

# 删除已有容器
if docker ps -a --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then
    echo "Stopping and removing existing container..."
    docker stop "$CONTAINER_NAME"
    docker rm "$CONTAINER_NAME"
fi

# 删除已有 Volume
for VOL in $(docker volume ls --format '{{.Name}}'); do
    if echo "$VOLUME_MAPPINGS" | grep -q "$VOL"; then
        echo "Removing existing volume: $VOL"
        docker volume rm "$VOL"
    fi
done

# 删除已有镜像
if docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "^$IMAGE_NAME$"; then
    echo "Removing existing image..."
    docker rmi "$IMAGE_NAME"
fi

# 加载镜像
echo "Loading Docker image..."
docker load -i "$IMAGE_BACKUP"

# 创建 Volume
for VOL in $VOLUME_MAPPINGS; do
    VOL_NAME=$(echo "$VOL" | cut -d ':' -f1 | sed 's/-v //')
    echo "Recreating volume: $VOL_NAME"
    docker volume create "$VOL_NAME"
done

# 恢复 Volume 数据
echo "Restoring volume data..."
docker run --rm $VOLUME_MAPPINGS -v "$(pwd)":/backup busybox tar xvf "/backup/$VOLUME_BACKUP" -C /

# 构建运行命令并启动容器
RESTORE_CMD="docker run --name $CONTAINER_NAME $ENV_VARS $VOLUME_MAPPINGS -d $IMAGE_NAME"
echo "Recreating container..."
eval "$RESTORE_CMD"

# 重启容器
echo "Restarting container..."
docker restart "$CONTAINER_NAME"

echo "Restoration completed successfully!"

赋予执行权限并运行:

$ chmod +x restoration_script.sh
$ ./restoration_script.sh

这个脚本可以自动完成资源清理、镜像加载、Volume 创建、数据恢复和容器重建等操作。

5. 总结

本文演示了如何完整地备份和恢复一个 Docker 容器及其数据卷。我们首先创建了一个 MySQL 容器,并挂载了一个 Volume 用于持久化数据;然后分别备份了镜像、Volume 数据和容器元信息;最后通过自动化脚本实现了恢复流程。

这种备份和恢复机制对于应对系统故障或误删操作非常有用,能有效保障数据安全并提升容器管理的可靠性。


原始标题:Backup Docker Container With Its Data Volumes