管理容器里的数据

到目前为止,我们已经介绍了一些docker基本概念,也明白怎样使用Docker的镜像,而且学习有关容器之间的网络和链接。在这个章节里,我们将讨论你应该如何管理容器内部和容器之间共享的数据。

接下来,我们将介绍docker内部数据管理两个主要方法。

数据卷

数据卷容器

数据卷

数据卷是指在存在于一个或多个容器中的特定目录。它可以绕开联合文件系统(Union File System),为数据的持续化或共享提供一些有用的特性:

  • 在容器被创建的时候,数据也被初始化。如果容器的基础镜像包含一个指定的数据挂载点,在数据卷初始化的时候,挂载点的数据会被复制到新的数据卷中。
  • 数据卷可以在容器之间共享和重用。
  • 数据卷中的更改可以直接生效。
  • 数据卷丢失掉你在更新镜像期间的数据。
  • 数据卷持久化,甚至与之关联的容器被删除后。

数据卷设计的初衷就是持久化数据,与容器的生命周期是独立关系。因此,当你删除一个容器的时候,Docker不会自动删除与之关联的数据卷,就算这个数据卷已经没有任何容器与之关联,“垃圾回收”也不会回收它(删除)。

添加一个数据卷

docker create 和 docker run 命令时,你可以使用 -v 标志为即将创建的容器添加一个数据卷。你可以多次使用 -v

$ docker run -d -P --name web -v /webapp training/webapp python app.py

这会在容器内部创建一个新的卷 /webapp。

注意:你也可以在Dockerfile 文件里使用 VOLUME 指令,为即将创建的容器添加一个或多个新的数据卷。

Docker数据卷的默认挂载模式是“可读写”(read-write),但是,你也可以设置它为“仅可读”(read-only)模式。

$ docker run -d -P --name web -v /opt/webapp:ro training/webapp python app.py

查找数据卷路径

通过 docker inspect 命令,你可以查找到数据卷在宿主机上的路径。

$ docker inspect web

这段输出提供了容器的配置(包括数据卷)的详细说明。类似如下信息:

...
Mounts": [
    {
        "Name": "fac362...80535",
        "Source": "/var/lib/docker/volumes/fac362...80535/_data",
        "Destination": "/webapp",
        "Driver": "local",
        "Mode": "",
        "RW": true
    }
]
...

Source “Source”指定一个宿主机的路劲,Desination 指定容器内部的路径。RW 用来设置数据卷的读写模式。

挂载一个主机目录作为卷

使用 -v ,除了可以创建一个数据卷,还可以挂载本地主机目录到容器中:

注意: 如果你在Mac或Windows上使用Docker Machine,Docker会被限制访问操作系统的文件系统。Docker Machine尝试自动共享你的/Users (OS X) 或 C:\Users (Windows) 文件夹,所以,你可以使用docker run -v /Users/<path>:/<container path> ... (OS X) 或 docker run -v /c/Users/<path>:/<container path ...

$ docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py

这将会把本地目录/src/webapp,挂载到容器的/opt/webapp目录。

注意:如果路径 /opt/webapp 在容器的镜像中已经存在,它的内容会被宿主机的 /src/webappGit bash 使用Boot2Docker 时,这里可能会出现一个源目录名称解析的问题。你可以在源目录的开始添加一个双斜杠来修复这个问题。具体的解释在问题#12751里。

这在做测试时是非常有用的,例如我们可以挂载宿主机的源代码到容器内部,这样我们就可以看到改变源代码时的应用时如何工作的。宿主机上的目录必须是绝对路径,如果目录不存在docker会自动创建它。

注意:为了构建镜像的移植性和分享的目的,在Dockerfile 中不能采用这用方法。因为这种方式依赖了宿主机的特定目录,部署的环境千差万别,不能保证Dockerfile 在所有的环境下都能正常运行。

Docker数据卷的默认挂载模式是“可读写”(read-write),但是,你也可以设置它为“仅可读”(read-only)模式。

$ docker run -d -P --name web -v /src/webapp:/opt/webapp:ro training/webapp python app.py

/src/webapp 目录,只是添加了 ro选项来指定挂载模式为只读。 

挂载一个宿主机的文件作为数据卷

-v

$ docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash

上述命令会在容器中运行一个 bash shell ,即使退出容器后,在主机上也能够看到容器中 bash shell 的命令历史。

注意: 很多文件编辑工具如 vi 和 sed --in-place 会导致inode 改变。Docker v1.1.0之后的版本,会产生一个错误:“sed cannot rename ./sedKdJ9Dy: Device or resource busy”。这种情况下如果想要更改挂载的文件,最好是直接挂载它的父目录。

创建、挂载数据卷容器

如果你想要容器之间数据共享,或者从非持久化容器中使用一些持久化数据,最好创建一个指定名称的数据卷容器,然后用它来挂载数据。

training/postgres

$ docker create -v /dbdata --name dbdata training/postgres /bin/true

--volumes-from 标志挂载 /dbdata

$ docker run -d --volumes-from dbdata --name db1 training/postgres

另外容器:

$ docker run -d --volumes-from dbdata --name db2 training/postgres

如果 postgres 镜像包含一个名为 /dbdata 的目录,则 dbdata挂载的数据会隐藏来自镜像的 /dbdata 文件,只能看到 dbdata 容器书卷中的文件。--volumes-fromdb1 或db2

$ docker run -d --name db3 --volumes-from db1 training/postgres

dbdata 或  db1 和db2,数据卷一个不会被删除的。假如你想要删除数据卷,必须在删除最后一个与之关联的容器时,显示的执行 docker rm -v

注意:当你在删除一个容器且没有指定删除它的数据卷选项-v 时,Docker不会有任何的警告提示。如果你删除容器时没有使用-v 选项,你可能会得到一个“悬空”的数据卷;这个数据卷没有任何容器与之关联。悬空的数据卷的产生很难避免且占用大量的硬盘空间。我们已经在努力的改进数据卷的管理,你可以在#14214里查看这项工作的最新进展。

备份、恢复或者迁移数据卷

数据卷还有另外一个有用的功能,我们可以用它来执行备份、恢复或迁移数据。为此我们使用/--volumes-from来创建一个挂载数据卷的容器,像这样:

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

这里我们启动了一个挂载 dbdata卷的新容器,并且挂载了一个本地目录作为/backup卷。最后,我们通过使用 tar 命令将 dbdata 数据卷的内容备份到容器中的 /backup 目录下的 backup.tar 文件中。当命令完成或者容器停止,我们将会得到dbdata 数据卷的的备份。

你可以在同一容器或其他地方的容器中恢复此数据。创建一个新的容器。

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

然后,把刚刚备份的数据卷数据解压到新创建的容器的数据卷中。

$ docker run --volumes-from dbdata2 -v $(pwd):/backup ubuntu cd /dbdata && tar xvf /backup/backup.tar

你可以使用上述的方法结合你偏爱的工具,测试数据卷的自动化备份、迁移、恢复。