如何备份docker容器的数据卷?

我一直在使用这个Docker-image tutum / wordpress来演示Wordpress网站。最近我发现映像使用卷作为mysql数据。

所以问题是这样的:如果我想备份和恢复容器,我可以尝试提交一个映像,然后删除该容器,并从已提交的映像创建一个新容器。但如果我这样做,卷就会被删除,所有的数据都消失了。

一定有一些简单的方法来备份我的容器及其卷数据,但我到处都找不到它。

370582 次浏览

如果我想恢复容器,我可以尝试提交一个图像,然后删除容器,并从提交的图像创建一个新容器。但如果我这样做,卷就会被删除,所有的数据都消失了

正如docker用户指南解释的那样,数据量意味着将数据持久化在容器文件系统之外。这也简化了多个容器之间的数据共享。

虽然Docker永远不会删除卷中的数据(除非你删除了与docker rm -v相关的容器),但没有被任何Docker容器引用的卷被称为晃来晃去的卷。那些晃来晃去的卷是很难摆脱和很难访问。

这意味着,一旦删除了使用卷的最后一个容器,数据卷就会悬空,其内容就难以访问。

为了防止这些悬空卷,诀窍是使用您想要持久化的数据卷创建一个额外的docker容器,以便始终至少有该docker容器引用该卷。通过这种方式,你可以删除运行wordpress应用程序的docker容器,而不会失去对数据量内容的访问。

这样的容器被称为数据卷容器

一定有一些简单的方法来备份我的容器和卷数据,但我到处都找不到它。

备份docker映像

要备份docker映像,可以使用码头工人保存命令,该命令将生成一个tar归档文件,稍后可以使用码头工人负载命令创建一个新的docker映像。

备份docker容器

您可以通过不同的方式备份docker容器

  • 通过使用码头工人承诺命令根据docker容器当前状态提交一个新的docker映像
  • 通过使用码头工人出口命令将docker容器文件系统导出为tar存档。稍后,您可以使用码头工人进口命令从tar归档文件创建一个新的docker映像。

注意,这些命令只备份docker容器分层文件系统。这里不包括数据卷

备份docker数据卷

要备份数据卷,您可以使用要备份的卷运行一个新容器,并执行tar命令以生成Docker用户指南中所述的卷内容的存档。

在您的特定情况下,数据卷用于存储MySQL服务器的数据。因此,如果您想为这个卷导出一个tar存档,首先需要停止MySQL服务器。要做到这一点,你必须停止wordpress容器。

备份MySQL数据

另一种方法是远程连接到MySQL服务器,使用, mysqldump命令生成数据库转储。然而,为了实现这一点,你的MySQL服务器必须配置为接受远程连接,并有一个允许远程连接的用户。这可能不是你正在使用的wordpress docker图像的情况。


编辑

Docker最近引入了Docker卷插件,允许将卷的处理委托给供应商实现的插件。

docker run命令对于-v选项有一个新的行为。现在可以向它传递卷名。以这种方式创建的卷是命名,以后很容易引用,从而缓解了晃来晃去的卷的问题。

编辑2

Docker引入了docker volume prune命令来轻松删除所有悬空卷。

更新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

和:

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

或者你可以按照官方的方式:

如何将数据卷从一个主机移植到另一个主机?< / >

如果你只需要备份挂载的卷,你可以从你的Dockerhost复制文件夹。

注意:如果你在Ubuntu上,Dockerhost是你的本地机器。如果你在Mac上,Dockerhost就是你的虚拟机。

在Ubuntu上

你可以在这里找到所有带卷的文件夹:/var/lib/docker/volumes/,这样你就可以复制它们并在任何你想要的地方存档。

在MAC

它不像Ubuntu那么简单。需要从虚拟机中拷贝文件。

下面是如何从虚拟机(Docker服务器运行的地方)复制所有带卷的文件夹到本地机器的脚本。我们假设您的docker-machine虚拟机名为default

docker-machine ssh default sudo cp -v -R /var/lib/docker/volumes/ /home/docker/volumes


docker-machine ssh default sudo chmod -R 777 /home/docker/volumes


docker-machine scp -R default:/home/docker/volumes ./backup_volumes


docker-machine ssh default sudo rm -r /home/docker/volumes

它将在当前目录中创建一个文件夹。/ backup_volumes,并将所有卷复制到该文件夹中。

下面是一个脚本,演示如何从本地目录(./backup_volumes)复制所有保存的卷到Dockerhost机器

docker-machine scp -r ./backup_volumes default:/home/docker


docker-machine ssh default sudo mv -f /home/docker/backup_volumes /home/docker/volumes


docker-machine ssh default sudo chmod -R 777 /home/docker/volumes


docker-machine ssh default sudo cp -v -R /home/docker/volumes /var/lib/docker/


docker-machine ssh default sudo rm -r /home/docker/volumes

现在你可以检查它是否工作:

docker volume ls

下面的命令将在一个装载了所有命名数据卷的容器中运行tar,并将输出重定向到一个文件中:

docker run --rm `docker volume list -q | egrep -v '^.{64}$' | awk '{print "-v " $1 ":/mnt/" $1}'` alpine tar -C /mnt -cj . > data-volumes.tar.bz2

确保测试结果存档,以防出现错误:

tar -tjf data-volumes.tar.bz2
我知道这是旧的,但我意识到没有一个很好的文档解决方案来推动一个数据容器(作为备份)到docker中心。我刚刚发布了一个简短的例子,说明如何在 https://dzone.com/articles/docker-backup-your-data-volumes-to-docker-hub < / p >

以下是底线

docker教程建议您可以在本地备份和恢复数据卷。我们将使用这种技术,添加更多的行来将这个备份推入docker中心,以便将来轻松恢复到我们想要的任何位置。那么,让我们开始吧。以下是需要遵循的步骤:

从名为data-container-to- Backup的数据容器备份数据卷

docker run --rm --volumes-from data-container-backup --name tmp-backup -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /folderToBackup

将这个tar文件展开到一个新的容器中,这样我们就可以将它作为映像的一部分提交

docker run -d -v $(pwd):/backup --name data-backup ubuntu /bin/sh -c "cd / && tar xvf /backup/backup.tar"

提交并推送带有所需标签($VERSION)的图像

docker commit data-backup repo/data-backup:$VERSION
docker push repo/data-backup:$VERSION

最后,让我们清理一下

docker rm data-backup
docker rmi $(docker images -f "dangling=true" -q)

现在,在我们的repo中有一个名为data-backup的映像,它只是一个包含备份文件和文件夹的文件系统。为了使用这个映像(又名从备份恢复),我们执行以下操作:

使用数据备份映像运行数据容器

run -v /folderToBackup --entrypoint "bin/sh" --name data-container repo/data-backup:${VERSION}

使用数据容器中的卷运行whatEver映像

docker run --volumes-from=data-container repo/whatEver

这是它。

我很惊讶没有关于这项工作的文档。我希望有人觉得这有帮助。我知道我花了很长时间来考虑这个问题。

如果你的情况和我的一样简单,你可以这样做:

  1. 创建一个Dockerfile来扩展容器的基本映像
  2. 我假设您的卷映射到您的文件系统,因此您可以使用ADD folder destination将这些文件/文件夹添加到映像中
  3. 完成了!

例如,假设你有来自你的主目录上的卷的数据,例如/home/mydata,你可以运行以下命令:

DOCKERFILE=/home/dockerfile.bk-myimage
docker build --rm --no-cache -t $IMAGENAME:$TAG -f $DOCKERFILE /home/pirate

DOCKERFILE指向这样的文件:

FROM user/myimage
MAINTAINER Danielo Rodríguez Rivero <example@gmail.com>


WORKDIR /opt/data
ADD mydata .

其余的东西是从基本映像继承的。现在,您可以将该映像推到docker云中,您的用户将直接在他们的容器上获得可用的数据

如果您喜欢从命令行输入神秘的操作符,那么您会喜欢这些手动容器备份技术。请记住,还有一种更快、更有效的备份容器的方法。我在这里写了指令:https://www.morpheusdata.com/blog/2017-03-02-how-to-create-a-docker-backup-with-morpheus

步骤1:添加Docker主机到任意云 正如Morpheus支持网站上的教程所解释的那样,您可以在几秒钟内将Docker主机添加到您所选择的云。从Morpheus主导航栏上选择Infrastructure开始。在“基础设施”窗口顶部选择“主机”,单击右上方的“+容器主机”按钮

要通过Morpheus将Docker主机备份到云,请导航到基础设施屏幕并打开“+容器主机”菜单。

在菜单中选择容器主机类型,选择一个组,然后在“名称”、“描述”、“可见性”、“选择一个云”和“输入标签”(可选)五个字段中输入数据。单击“下一步”,选择业务规划,配置主机选项。请注意,只有当您选择的计划启用了自定义选项时,Volume、Memory和CPU计数字段才可见。

在这里,您可以添加和调整卷,设置内存大小和CPU计数,并选择网络。您还可以配置操作系统的用户名和密码、域名和主机名(默认为前面输入的容器名)。单击Next,然后添加任何自动化工作流(可选)。最后,检查您的设置并单击Complete保存它们。

步骤2:将Docker注册表集成添加到公有云或私有云 Adam Hicks在Morpheus的另一个教程中描述了与私有Docker Registry集成是多么简单。(使用公共Docker API使用Morpheus为Docker的公共中心提供映像不需要添加配置。

在主导航栏的Admin选项卡下选择Integration,然后选择屏幕右侧的“+New Integration”按钮。在出现的Integration窗口中,在Type下拉菜单中选择Docker Repository,输入名称并添加私有注册中心API端点。为正在使用的注册表提供用户名和密码,然后单击Save Changes按钮。

通过Morpheus“New Integration”对话框将Docker Registry与私有云集成。

要提供您刚刚创建的集成,请在创建实例对话框的“类型”下选择Docker,在“配置”选项卡下的“Docker注册表”下拉菜单中选择注册表,然后继续提供任何Docker容器。

步骤3:管理备份 一旦您添加了Docker主机并集成了注册表,就会为您提供的每个实例自动配置和执行备份。Morpheus支持提供了查看备份、创建实例备份和创建服务器备份的说明

我已经创建了一个工具来编排和启动备份数据和mysql容器,简单地称为docker-backup。甚至还有docker集线器上的现成可用映像

它主要是用Bash编写的,因为它主要是编制。它使用duplicity作为实际的备份引擎。目前可以备份到FTP(S)和Amazon S3。

配置非常简单:在YAML中编写一个配置文件,描述要备份的内容和位置,然后就完成了!

对于数据容器,它自动挂载由容器共享的卷以备份和处理它。对于mysql容器,它链接它们,并执行一个mysqldump与你的容器捆绑,并处理结果。

我写它是因为我使用Docker- cloud,它不是最新的Docker引擎版本,因为我想拥抱Docker的方式,在我的应用程序容器中不包括任何备份过程。

如果你只是需要一个简单的存档备份,你可以试试我的小实用程序:https://github.com/loomchild/volume-backup

例子

备份:

docker run -v some_volume:/volume -v /tmp:/backup --rm loomchild/volume-backup backup archive1

将归档卷命名为some_volume/tmp/archive1.tar.bz2的归档文件

恢复:

docker run -v some_volume:/volume -v /tmp:/backup --rm loomchild/volume-backup restore archive1

并从/tmp/archive1.tar.bz2归档文件中恢复名为some_volume的卷。

更多信息:https://medium.com/@loomchild/backup-restore-docker-named-volumes-350397b8e362

假设卷名是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-

如果你想要一个完整的备份,你需要执行以下几个步骤:

  1. 将容器提交给一个映像
  2. 保存图像
  3. 通过在容器中创建卷的挂载点的tar文件来备份容器的卷。
  4. 对数据库容器也重复步骤1-3。

注意,Docker仅将容器提交到映像,并不包括附加到容器的卷(ref: Docker提交文档)。

提交操作将不包括容器内挂载的卷中包含的任何数据。

如果您的项目使用docker-compose,这里有一种备份和恢复卷的方法。

docker-compose.yml

基本上,你将db-backupdb-restore服务添加到docker-compose中。Yml文件,并将其改编为卷的名称。在本例中,我的卷名为dbdata

version: "3"


services:
db:
image: percona:5.7
volumes:
- dbdata:/var/lib/mysql


db-backup:
image: alpine
tty: false
environment:
- TARGET=dbdata
volumes:
- ./backup:/backup
- dbdata:/volume
command: sh -c "tar -cjf /backup/$${TARGET}.tar.bz2 -C /volume ./"


db-restore:
image: alpine
environment:
- SOURCE=dbdata
volumes:
- ./backup:/backup
- dbdata:/volume
command: sh -c "rm -rf /volume/* /volume/..?* /volume/.[!.]* ; tar -C /volume/ -xjf /backup/$${SOURCE}.tar.bz2"


避免腐败

为了保持数据一致性,在备份或恢复之前停止db容器

docker-compose stop db

备份

备份到默认目标(backup/dbdata.tar.bz2):

docker-compose run --rm db-backup

或者,如果你想指定另一个目标名称,请执行以下操作:

docker-compose run --rm -e TARGET=mybackup db-backup

恢复

要从backup/dbdata.tar.bz2恢复,请执行:

docker-compose run --rm db-restore

或从特定文件恢复使用:

docker-compose run --rm -e SOURCE=mybackup db-restore

我改编了https://loomchild.net/2017/03/26/backup-restore-docker-named-volumes/中的命令来创建这种方法。

卷-文件夹-备份方式 如果你有docker注册表基础,这个方法是非常有用的 这使用docker注册表轻松移动zip文件

#volume folder backup script. !/bin/bash


#common bash variables. set these variable before running scripts
REPO=harbor.otcysk.org:20443/levee
VFOLDER=/data/mariadb
TAG=mariadb1


#zip local folder for volume files
tar cvfz volume-backup.tar.gz $VFOLDER


#copy the zip file to volume-backup container.
#zip file must be in current folder.
docker run -d -v $(pwd):/temp --name volume-backup ubuntu \
bash -c "cd / && cp /temp/volume-backup.tar.gz ."




#commit for pushing into REPO
docker commit volume-backup $REPO/volume-backup:$TAG


#check gz files in this container
#docker run --rm -it --entrypoint bash --name check-volume-backup \
$REPO/volume-backup:$TAG


#push into REPO
docker push $REPO/volume-backup:$TAG

在另一个服务器上

#pull the image in another server
docker pull $REPO/volume-backup:$TAG


#restore files in another server filesystem
docker run --rm -v $VFOLDER:$VFOLDER --name volume-backup $REPO/volume-backup:$TAG \
bash -c "cd / && tar xvfz volume-backup.tar.gz"

运行使用此卷文件夹的映像 您可以轻松地创建一个映像,其中包含一个运行映像和一个卷压缩文件 但是我不推荐因为各种原因(图像大小,输入命令,..)。< / p >

这个问题:你想备份你的镜像容器中的数据卷,但这个选项不是开箱即用的,直接和平凡的方式将是复制卷路径和备份docker镜像'重新加载它,并将两者链接在一起。但是这个解决方案似乎很笨拙,而且不可持续和维护-您需要创建一个cron作业,以便每次都能进行此流程。

解决方案:使用dockup - Docker镜像备份Docker容器卷,并将其上传到s3 (Docker + backup = dockup)。dockup将使用您的AWS凭据根据环境变量创建一个具有名称的新桶,获得配置的卷,并将被压缩、gzip、时间戳并上传到S3桶。

步骤:

  1. 配置docker-compose.yml并将env.txt配置文件附加到它,数据应该上传到一个专用的安全s3桶,并准备在DRP执行时重新加载。为了验证要配置的卷路径,运行docker inspect <service-name>并找到:
< p > "量":{ “/ etc /服务示例”:{}, “/服务示例”:{} }, < / p >
  1. 编辑配置文件env.txt的内容,并将其放在项目路径上:

    AWS_ACCESS_KEY_ID=<key_here>
    AWS_SECRET_ACCESS_KEY=<secret_here>
    AWS_DEFAULT_REGION=us-east-1
    BACKUP_NAME=service-backup
    PATHS_TO_BACKUP=/etc/service-example /service-example
    S3_BUCKET_NAME=docker-backups.example.com
    RESTORE=false
    
  2. Run the dockup container

$ docker run --rm \
--env-file env.txt \
--volumes-from <service-name> \
--name dockup tutum/dockup:latest
  1. 然后验证s3桶包含相关数据

我建议使用静息胶。这是一个易于使用的备份应用程序,可以备份到各种目标,例如本地文件系统、S3兼容存储服务或静态REST目标服务器。使用标记器,你将有一个已经准备好的容器,可以用cron语法调度:https://github.com/djmaze/resticker

对于那些想要了解更多关于restic和它的用法的人,我确实写了一个关于这个主题的系列博客文章,包括它的用法示例: https://remo-hoeppli.medium.com/restic-backup-i-simple-and-beautiful-backups-bdbbc178669d < / p >

我们可以使用映像备份所有卷。我写了一个脚本来帮助备份和恢复。此外,我将数据保存到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获得。