Docker 数据管理

57 阅读5分钟

前言

翻译一下Docker 关于数据管理方面的部分知识。

生产环境中使用Docker的过程中,往往需要对数据进行持久化,或者需要在多个容器之间进行数据共享,这必然涉及容器的数据管理操作。


Docker有两个选项供容器在主机上存储文件,这样即使容器停止后,文件也会持续存在:volumes(卷) 和 bind mounts(绑定挂载)。

不同的数据挂载方式

无论哪种类型的挂载,数据在容器内看起来都是一样的。它在容器的文件系统中以目录或单个文件的形式显示。

要直观地了解volumes、bind mounts和tmpfs mounts之间的区别,可以考虑数据在Docker主机上的存放位置。

types-of-mounts.png

  • Volumes存储在主机文件系统的一部分,由Docker管理(Linux上为/var/lib/docker/volumes/)。非Docker进程不应修改文件系统的这一部分。Volumes是在Docker中保存数据的最佳方式。

  • Bind mounts可以存储在主机系统的任何地方。它们甚至可能是重要的系统文件或目录。Docker主机或Docker容器上的非Docker进程可以在任何时候修改它们。

  • tmpfs mounts只存储在主机系统的内存中,不会被写入主机系统的文件系统。

使用 Volumes

types-of-mounts-volume.png Volumes是保存由Docker容器产生和使用的数据的首选机制。Bind mounts依赖于主机的目录结构和操作系统,而Volumes则完全由Docker管理。Volumes比Bind mounts有几个优势。

  • Volumes比Bind mounts更容易备份或迁移。

  • 你可以使用Docker CLI命令或Docker API来管理Volumes。

  • Volumes在Linux和Windows容器上都可以工作。

  • Volumes可以更安全地在多个容器之间共享。

  • Volumes的驱动可以让你在远程主机或云供应商上存储Volumes,对Volumes的内容进行加密,或添加其他功能。

  • 新的Volumes可以由容器预先填充其内容。

  • Docker桌面上的Volumes比来自Mac和Windows主机的Bind mounts的性能高得多。

此外,Volumes通常是比在容器的可写层中持久化数据更好的选择,因为Volumes不会增加使用它的容器的大小,而且Volumes的内容存在于特定容器的生命周期之外。

创建和管理Volumes

创建一个Volume

$ docker volume create my-vol

列出Volumes

$ docker volume ls

查看Volume信息

$ docker volume inspect my-vol

删除Volume

$ docker volume rm my-vol

启动一个带Volume的容器

如果你用一个尚不存在的卷来启动一个容器,Docker会为你创建这个卷。下面的例子将卷myvol2挂载到容器的/app/中。

$ docker run -d \
  --name devtest \
  --mount source=myvol2,target=/app \
  nginx:latest
# 方式一:使用 --mount

$ docker run -d \
  --name devtest \
  -v myvol2:/app \
  nginx:latest
# 方式二:使用 -v

$ docker inspect devtest
# 查看容器信息

挂载点信息:

"Mounts": [
 {
 "Type": "volume",
 "Name": "myvol2",
 "Source": "/var/lib/docker/volumes/myvol2/_data",
 "Destination": "/app",
 "Driver": "local",
 "Mode": "",
 "RW": true,
 "Propagation": ""
 }
],

这表明挂载的是一个卷,它显示了正确的源和目标,并且挂载是读写的。

停止容器并删除卷。注意卷的移除是一个单独的步骤。

$ docker container stop devtest
$ docker container rm devtest
$ docker volume rm myvol2

用卷来启动一个服务容器

当你启动一个服务并定义一个卷时,每个服务容器使用它自己的本地卷。如果你使用本地卷驱动,所有的容器都不能共享这些数据,但有些卷驱动确实支持共享存储。Docker for AWS和Docker for Azure都支持使用Cloudstor插件的持久化存储。

下面的例子启动了一个有四个副本的nginx服务,每个副本使用一个名为myvol2的本地卷。

$ docker service create -d \
  --replicas=4 \
  --name devtest-service \
  --mount source=myvol2,target=/app \
  nginx:latest

$ docker service ps devtest-service
# 查看服务容器运行起来

$ docker service rm devtest-service
# 删除该服务,这将停止其所有任务。
# 移除服务不会移除该服务所创建的任何卷。卷的移除是一个单独的步骤。

服务容器语法不同点

docker service create 命令不支持 -v 或者 --volume 标志,当挂载一个卷到一个服务类型的容器中时,你必须使用 -mount 标志。

使用一个容器填充一个卷

如果你启动了一个创建新卷的容器,如上所述,并且该容器在要挂载的目录中拥有文件或目录(比如上面的/app/),那么该目录的内容就会被复制到该卷。然后容器挂载并使用该卷,其他使用该卷的容器也可以访问预先填充的内容。

为了说明这一点,本例启动了一个nginx容器,并将容器的/usr/share/nginx/html目录的内容填充到新的卷nginx-vol中,该目录是Nginx存储默认HTML内容的地方。

$ docker run -d \
  --name=nginxtest \
  --mount source=nginx-vol,destination=/usr/share/nginx/html \
  nginx:latest
# 方式一:使用 --mount

$ docker run -d \
  --name=nginxtest \
  -v nginx-vol:/usr/share/nginx/html \
  nginx:latest
# 方式二:使用 -v

运行以下命令来清理容器和卷。注意卷的移除是一个单独的步骤。

$ docker container stop nginxtest
$ docker container rm nginxtest
$ docker volume rm nginx-vol

使用只读卷

对于一些开发应用,容器需要写进绑定挂载,以便将变化传播回Docker主机。在其他时候,容器只需要对数据进行读取访问。请记住,多个容器可以挂载同一个卷,它可以对其中一些容器进行读写挂载,而同时对其他容器进行只读挂载。

这个例子修改了上面的例子,但是把目录挂载为只读卷,方法是在容器内的挂载点后面的选项(默认为空)列表中加入ro。如果有多个选项,请用逗号将它们分开。

$ docker run -d \
  --name=nginxtest \
  --mount source=nginx-vol,destination=/usr/share/nginx/html,readonly \
  nginx:latest
# 方式一:使用 --mount

$ docker run -d \
  --name=nginxtest \
  -v nginx-vol:/usr/share/nginx/html:ro \
  nginx:latest
# 方式二:使用 -v

$ docker inspect nginxtest
# 查看容器信息

挂载点信息:

"Mounts": [
    {
        "Type": "volume",
        "Name": "nginx-vol",
        "Source": "/var/lib/docker/volumes/nginx-vol/_data",
        "Destination": "/usr/share/nginx/html",
        "Driver": "local",
        "Mode": "",
        "RW": false,
        "Propagation": ""
    }
],

运行以下命令来清理容器和卷。注意卷的移除是一个单独的步骤。

$ docker container stop nginxtest
$ docker container rm nginxtest
$ docker volume rm nginx-vol

备份、恢复或迁移数据卷

卷对于备份、恢复和迁移很有用。使用 --volumes-from 标志来创建一个新的容器,挂载该卷。

备份一个卷

例如,创建一个名为dbstore的新容器。

$ docker run -v /dbdata --name dbstore ubuntu /bin/bash

然后在下一个命令中:

  • 启动一个新的容器,并从dbstore容器中挂载该卷

  • 挂载一个本地主机目录作为/backup

  • 传递一条命令,将dbdata卷的内容剪切到我们的/backup目录下的backup.tar文件中。

$ docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata

当命令完成,容器停止时,我们就留下dbdata卷的备份了。

从备份中恢复卷

有了刚刚创建的备份,你可以把它恢复到同一个容器,或者在其他地方创建的另一个容器。

例如,创建一个名为dbstore2的新容器:

$ docker run -v /dbdata --name dbstore2 ubuntu /bin/bash

然后在新容器的数据卷中解压备份文件:

$ docker run --rm --volumes-from dbstore2 -v $(pwd):/backup ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1"

你可以使用上述技术,用你喜欢的工具自动进行备份、迁移和恢复测试。

删除卷

一个Docker数据卷在容器被删除后仍然存在。有两种类型的卷需要考虑:

  • 命名卷有一个来自容器外部的特定来源,例如awesome:/bar

  • 匿名卷没有特定的来源,所以当容器被删除时,指示Docker引擎守护程序来删除它们

移除匿名卷

要自动删除匿名卷,可以使用--rm选项。例如,这个命令创建了一个匿名的/foo卷。当容器被删除时,Docker引擎会删除/foo卷,但不会删除awesome卷。

$ docker run --rm -v /foo -v awesome:/bar busybox top

Note:

如果另一个容器用 --volumes-from 绑定卷,卷的定义会被复制,匿名卷也会在第一个容器被移除后保持不变。

删除所有卷

要删除所有未使用的卷,释放空间:

$ docker volume prune

使用bind mounts

types-of-mounts-bind.png

用绑定挂载启动一个容器

$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  nginx:latest
# 方式一:使用 --mount

$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app \
  nginx:latest
# 方式二:使用 -v

$ docker inspect nginxtest
# 查看容器信息

挂载点信息:

"Mounts": [
    {
        "Type": "bind",
        "Source": "/tmp/source/target",
        "Destination": "/app",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],

挂载到容器上一个非空的目录中

如果你绑定挂载到容器上的一个非空目录,该目录的现有内容会被绑定挂载所掩盖。这可能是有益的,例如当你想测试你的应用程序的一个新版本而不建立一个新的镜像。然而,它也可能是令人惊讶的,这种行为与docker卷的行为不同。

$ docker run -d \
  -it \
  --nme broken-container \
  --mount type=bind,source=/tmp,target=/usr \
  nginx:latest
# 方式一:使用 --mount

$ docker run -d \
  -it \
  --name broken-container \
  -v /tmp:/usr \
  nginx:latest
# 方式二:使用 -v

使用只读的绑定挂载

对于某些开发应用,容器需要写入绑定挂载,这样变化就会传播到Docker主机上。在其他时候,容器只需要读取权限。

$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app,readonly \
  nginx:latest
# 方式一:使用 --mount

$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app:ro \
  nginx:latest
# 方式二:使用 -v

$ docker inspect nginxtest
# 查看容器信息

挂载点信息:

"Mounts": [
    {
        "Type": "bind",
        "Source": "/tmp/source/target",
        "Destination": "/app",
        "Mode": "ro",
        "RW": false,
        "Propagation": "rprivate"
    }
],

总结

Docker 数据管理推荐使用volume,绑定挂载常用于应用开发环境。

Docker 数据管理非常重要。

参考链接: