我一直在用这个Docker-image教程/wordpress演示一个wordpress网站。最近我发现映像使用卷作为mysql数据。
所以问题是这样的:如果我想备份和恢复容器,我可以尝试提交一个映像,然后删除该容器,并从已提交的映像创建一个新容器。但如果我这样做,卷就会被删除,所有的数据都消失了。
一定有一些简单的方法来备份我的容器及其卷数据,但我到处都找不到它。
我一直在用这个Docker-image教程/wordpress演示一个wordpress网站。最近我发现映像使用卷作为mysql数据。
所以问题是这样的:如果我想备份和恢复容器,我可以尝试提交一个映像,然后删除该容器,并从已提交的映像创建一个新容器。但如果我这样做,卷就会被删除,所有的数据都消失了。
一定有一些简单的方法来备份我的容器及其卷数据,但我到处都找不到它。
当前回答
假设您的卷名为data_volume。您可以使用以下命令将卷备份到名为data_image的docker镜像,并从镜像中恢复卷:
备份:
docker run --rm --mount source=data_volume,destination=/data alpine tar -c -f- data | docker run -i --name data_container alpine tar -x -f-
docker container commit data_container data_image
docker rm data_container
恢复:
docker run --rm data_image tar -c -f- data | docker run -i --rm --mount source=data_volume,destination=/data alpine tar -x -f-
其他回答
更新2
原始单卷备份bash脚本:
#!/bin/bash
# This script allows you to backup a single volume from a container
# Data in given volume is saved in the current directory in a tar archive.
CONTAINER_NAME=$1
VOLUME_PATH=$2
usage() {
echo "Usage: $0 [container name] [volume path]"
exit 1
}
if [ -z $CONTAINER_NAME ]
then
echo "Error: missing container name parameter."
usage
fi
if [ -z $VOLUME_PATH ]
then
echo "Error: missing volume path parameter."
usage
fi
sudo docker run --rm --volumes-from $CONTAINER_NAME -v $(pwd):/backup busybox tar cvf /backup/backup.tar $VOLUME_PATH
原始单卷恢复bash脚本:
#!/bin/bash
# This script allows you to restore a single volume from a container
# Data in restored in volume with same backupped path
NEW_CONTAINER_NAME=$1
usage() {
echo "Usage: $0 [container name]"
exit 1
}
if [ -z $NEW_CONTAINER_NAME ]
then
echo "Error: missing container name parameter."
usage
fi
sudo docker run --rm --volumes-from $NEW_CONTAINER_NAME -v $(pwd):/backup busybox tar xvf /backup/backup.tar
用法可以这样:
$ volume_backup.sh old_container /srv/www
$ sudo docker stop old_container && sudo docker rm old_container
$ sudo docker run -d --name new_container myrepo/new_container
$ volume_restore.sh new_container
假设:备份文件名为backup.tar,与备份恢复脚本在同一目录下,容器之间的卷名称相同。
更新
在我看来,从容器备份卷与从数据容器备份卷没有什么不同。
卷就是链接到容器的路径,所以过程是一样的。
我不知道docker-backup是否也适用于相同的容器卷,但你可以使用:
sudo docker run --rm --volumes-from yourcontainer -v $(pwd):/backup busybox tar cvf /backup/backup.tar /data
and:
sudo docker run --rm --volumes-from yournewcontainer -v $(pwd):/backup busybox tar xvf /backup/backup.tar
最后更新
有一个很好的工具可以让你备份和恢复docker卷容器:
https://github.com/discordianfish/docker-backup
如果你有一个像这样链接到一些容器卷的容器:
$ docker run --volumes-from=my-data-container --name my-server ...
您可以像这样备份所有卷:
$ docker-backup store my-server-backup.tar my-server
然后像这样还原:
$ docker-backup restore my-server-backup.tar
或者你可以按照官方的方式:
如何将只有数据的卷从一个主机移植到另一个主机?
如果我想恢复容器,我可以尝试提交一个图像,然后删除容器,并从提交的图像创建一个新容器。但如果我这样做,卷就会被删除,所有的数据都消失了
正如docker用户指南解释的那样,数据卷意味着将数据持久化在容器文件系统之外。这也简化了多个容器之间的数据共享。
虽然Docker永远不会删除卷中的数据(除非你用Docker rm -v删除关联的容器),但没有被任何Docker容器引用的卷被称为悬挂卷。这些悬空的书卷很难清除,也很难进入。
这意味着,一旦删除了使用卷的最后一个容器,数据卷就会悬空,其内容就难以访问。
为了防止这些悬空卷,诀窍是使用您想要持久化的数据卷创建一个额外的docker容器,以便始终至少有该docker容器引用该卷。通过这种方式,你可以删除运行wordpress应用程序的docker容器,而不会失去对数据量内容的访问。
这样的容器称为数据卷容器。
一定有一些简单的方法来备份我的容器和卷数据,但我到处都找不到它。
备份docker映像
要备份docker映像,请使用docker save命令,该命令将生成一个tar归档文件,稍后可以使用docker load命令创建一个新的docker映像。
备份docker容器
您可以通过不同的方式备份docker容器
通过使用docker commit命令根据docker容器当前状态提交一个新的docker映像 使用docker export命令将docker容器文件系统导出为tar归档文件。稍后,您可以使用docker import命令从tar归档文件创建一个新的docker映像。
注意,这些命令只备份docker容器分层文件系统。这里不包括数据卷。
备份docker数据卷
要备份数据卷,您可以使用想要备份的卷运行一个新容器,并执行tar命令以生成docker用户指南中描述的卷内容的存档。
在您的特定情况下,数据卷用于存储MySQL服务器的数据。因此,如果您想为这个卷导出一个tar存档,首先需要停止MySQL服务器。要做到这一点,你必须停止wordpress容器。
备份MySQL数据
另一种方法是远程连接到MySQL服务器,使用mysqldump命令生成数据库转储。然而,为了实现这一点,你的MySQL服务器必须配置为接受远程连接,并有一个允许远程连接的用户。这可能不是你正在使用的wordpress docker图像的情况。
Edit
Docker最近引入了Docker卷插件,允许将卷的处理委托给供应商实现的插件。
docker run命令对-v选项有一个新的行为。现在可以给它传递一个卷名。以这种方式创建的卷被命名,以后很容易引用,从而缓解了悬空卷的问题。
编辑2
Docker引入了Docker volume prune命令,可以轻松删除所有悬挂卷。
我们可以使用映像备份所有卷。我写了一个脚本来帮助备份和恢复。此外,我将数据保存到tar文件压缩,以将所有数据保存在本地磁盘上。我使用这个脚本将我的Postgres和Cassandra卷数据库保存在同一个映像上。例如,如果Postgres有pg_data, Cassandra数据库有cassandra_data,我们可以调用下面的脚本两次,一次带有pg_data参数,然后cassandra_data参数
备份脚本:
#! /bin/bash
GENERATE_IMAGE="data_image"
TEMPRORY_CONTAINER_NAME="data_container"
VOLUME_TO_BACKUP=${1}
RANDOM=$(head -200 /dev/urandom | cksum | cut -f1 -d " ")
if docker images | grep -q ${GENERATE_IMAGE}; then
docker run --rm --mount source=${VOLUME_TO_BACKUP},destination=/${VOLUME_TO_BACKUP} ${GENERATE_IMAGE} tar -c -f- ${VOLUME_TO_BACKUP} | docker run -i --name ${TEMPRORY_CONTAINER_NAME} ${GENERATE_IMAGE} tar -x -f-
else
docker run --rm --mount source=${VOLUME_TO_BACKUP},destination=/${VOLUME_TO_BACKUP} alpine tar -c -f- ${VOLUME_TO_BACKUP} | docker run -i --name ${TEMPRORY_CONTAINER_NAME} alpine tar -x -f-
fi
docker container commit ${TEMPRORY_CONTAINER_NAME} ${GENERATE_IMAGE}
docker rm ${TEMPRORY_CONTAINER_NAME}
if [ -f "$(pwd)/backup/${VOLUME_TO_BACKUP}.tar" ]; then
docker run --rm -v $(pwd)/backup:/backup ${GENERATE_IMAGE} tar cvf /backup/${VOLUME_TO_BACKUP}_${RANDOM}.tar /${VOLUME_TO_BACKUP}
else
docker run --rm -v $(pwd)/backup:/backup ${GENERATE_IMAGE} tar cvf /backup/${VOLUME_TO_BACKUP}.tar /${VOLUME_TO_BACKUP}
fi
例子:
。/ backup.sh cassandra_data 。/ backup.sh pg_data
恢复脚本:
#! /bin/bash
GENERATE_IMAGE="data_image"
TEMPRORY_CONTAINER_NAME="data_container"
VOLUME_TO_RESTORE=${1}
docker run --rm ${GENERATE_IMAGE} tar -c -f- ${VOLUME_TO_RESTORE} | docker run -i --rm --mount source=${VOLUME_TO_RESTORE},destination=/${VOLUME_TO_RESTORE} alpine tar -x -f-
例子:
。/ restore.sh cassandra_data 。/ restore.sh pg_data
docker container run --rm --volumes-from your_db_container -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /your_named_volume
Run创建新的容器
——rm选项在执行tar CVF /backup/backup.tar /dbdata命令后删除容器
——volumes-from从你在your_db_container中创建的卷中创建一个命名的卷(your_named_volume)
-v $(pwd):/backup在当前主机目录($(pwd))和新容器中的/backup目录之间创建一个绑定挂载
Tar CVF /backup/backup. Tar /your_named_volume创建归档文件
源:备份卷
我一直在使用这个批处理脚本备份我所有的卷。该脚本将容器名称作为唯一参数,并自动查找其所有已挂载的卷。
然后它为每个卷创建一个tar存档。
#! /bin/bash
container=$1
dirname="backup-$container-$(date +"%FT%H%M%z")"
mkdir $dirname
cd $dirname
volume_paths=( $(docker inspect $container | jq '.[] | .Mounts[].Name, .Mounts[].Source') )
volume_count=$(( ${#volume_paths[@]} / 2 ))
for i in $(seq $volume_count); do
volume_name=${volume_paths[i-1]}
volume_name=$(echo $volume_name | tr -d '"')
volume_path=${volume_paths[(i-1)+volume_count]}
volume_path=$(echo $volume_path | tr -d '"')
echo "$volume_name : $volume_path"
# create an archive with volume name
tar -zcvf "$volume_name.tar" $volume_path
done
该代码可在Github上获得。