Docker容器的数据管理

这两天开始学习docker,发现docker确实很强大,让网站部署和维护的效率大大提高。遂准备将手头维护的几个小站,全部docker化。整理的过程中感觉到,docker可以以功能或者进程为单位进行部署和维护,不用再花时间在繁琐的配置上面,但是docker和宿主之间的数据共享以及docker间的数据共享仍然是让人头疼和操心的地方。正好翻到官方文档,看到相关内容,遂决定翻译一下,水平有限,欢迎吐槽。

几个基本概念:
docker: 一种容器管理技术,这里也指既有的开发工具链。
container: 容器
image: 镜像
volum:卷 [译者:卷可以理解成计算机中的文件路径]

原文链接:Manage data in containers

翻译正文:

之前我们介绍了docker基本概念,学习了docker 镜像如何工作以及docker之间的网络和联系。这章节我们将继续讨论怎么在docker内和docker之间管理数据。

我们将着重讨论两种你所能管理docker数据的方式

  • 数据卷
  • 数据卷容器

数据卷

数据卷是一种特殊的存在于一个或者多个docker内部的不同于Union File System的目录。数据卷提供多种有用的特性用来持久化和共享数据:

  • 数据卷在docker初始化时创建。如果容器的镜像包含外挂的数据,外挂的数据将在卷初始化时被拷贝到新的本地卷。
  • 数据卷可以被共享和在多个docker间复用。
  • 可以对数据卷直接修改。
  • 更新镜像时数据卷并不受影响。
  • 即使镜像被删除,数据卷也仍然会持久化到本地。

数据卷被设计用来持久化存储数据,独立于容器的生命周期。当你删除容器时,docker并不会自动删除数据卷,不使用的数据卷,也不会替你“垃圾回收”。

增加一个数据卷

你可以通过 -v 标示在 docker create 和 docker run 命令中给容器增加一个数据卷。你可以多次使用 -v 增加多个数据卷。让我们给我们的web应用容器挂载一个单独的数据卷。

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

这条指令将会在容器内部的 /webapp 路径下创建一个新卷。

注意:你也可以使用 VOLUME 指令在 Dockerfile 文件中添加一个或者多个卷到容器中。
docker中的卷默认是读写权限,但你也可以设置为只读。

$ 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’表示的是宿主路径,‘Destination’表示的是容器路径。 RW 用来标示这个卷的读写属性。

将宿主目录挂载为数据卷

通过 -v 标示你可以挂载一个宿主目录到容器中。

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

这条命令将宿主的 /src/webapp 挂载到容器的 /opt/webapp 卷上。如果 /opt/webapp 已经存在,/src/webapp 将会覆盖但是不会移除已有的文件。当挂载的卷移除后,原先被覆盖的内容将可以再次使用。这个和mount的行为是一致的。

container-dir 必须是绝对路径,比如 /src/docs。host-dir可以是绝对路径,也可以是一个已经声明过的数据卷。如果你指定host-dir为绝对路径,docker将会按你指定的路径挂载,如果你提供的是一个声明的卷,docker将会按照name 指定的名称创建一个声明的卷。

一个 name 声明的卷必须以字母开头,后面跟随z-z0-9,_,. 或者 -。绝对路径都以 / 开始。

例如,你可以用/foo 或者 foo 作为一个 host-dir.如果你使用 /foo ,docker 创建一个挂载点。如果你使用 foo, docker 创建一个声明的卷。

如果你在 Mac 或者 Windows 上使用 docker,你的docker后台只能拥有有限的权限。docker尝试着自动分享你的 /User 或者 C:\Users 目录,所以在OS X上挂载如下:

docker run -v /Users/<path>:/<container path> ...

windows上如下:

docker run -v /c/Users/<path>:/<container path> ...

其他来自虚拟机的目录,比如你想共享virtualbox 中的某些目录,你需要做些额外的工作。在 virtualbox下,你先要使宿主的目录变成共享,然后才能使用 -v 来挂载。

挂载宿主的目录对测试来说很有用。比如你可以在容器中挂载源码,然后修改代码,实时看修改后的执行效果。宿主的路径必须是绝对路径,如果路径不存在,docker就会在本地创建它。这种 auto-creation 将会被移除。

这里我们仍然挂载 /src/webapp 目录,但是增加了ro选项来声明挂载的目录是只读的。

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

注意:宿主目录是主机相关的,所以你不能在Dockerfile中挂载一个宿主目录,因为镜像要可移植,而不同的主机有不同的目录结构。

卷标识

Labeling 系统例如 SELinux 要求数据卷被容器挂载时要标示。如果没有标示,安全系统可能会阻止容器内的进程访问卷的内容。默认情况下,docker不会改变操作系统的卷标识。

要在容器的上下文中修改卷标识,你需要在挂载时增加 :z 或者 :Z 标识。z 表示多个容器共享卷内容,所以docke用共享标识来标示卷内容,共享的标识允许多个容器读写内容。Z 表示docker用私有的标识来标示卷内容,所以只有当前的容器能访问这个私有的卷内容。

挂载宿主的文件作为卷

-v 也可以用来挂载一个单独的文件而不仅仅是目录:

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

这个指令将会让你进入容器的命令行,能获取宿主的历史纪录,并且退出时,宿主的历史记录能纪录容器的指令操作。

注意:许多编辑工具包括 vi ,sed --in-place 等可能会导致inode数据结构变更。自从docker1.1.0后,此类操作会导致" sed:cannot rename ./sedKdj9Dy:Device or resource busy"。[In the case where you want to edit the mounted file, it is often easiest to instead mount the parent directory.][译者:这句不是很明白什么意思,望看到的网友指点]

创建和挂载一个数据卷容器

如果你有一些希望分享给多个容器的持久化数据,或者想从 non-persistent 容器中使用这些数据,最好的办法是创建一个命名的数据卷容器,然后从从容器中挂载使用数据。

让我们创建一个共享数据卷的容器。这个容器不运行应用,它提供 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 的目录,将会隐藏这个目录,只有挂载的可见。

你可以多次使用 --volumes-from 从多个容器中挂载多个卷。

你也可以用 db1 db2 来引用 dbdata。

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

如果你删除挂载了卷的容器,包括初始的dbdata 或者子序列的 db1 db2等,这些卷将不会被删除。如果你要删除卷,你需要明确的调用 docker rm -v,这个操作可以让你去在容器之间更新,或者高效的迁移数据。

注意:当你删除一个容器不带 -v 时,docker不会警告提醒你。当你不带 -v 删除容器时,将会出现‘挂起’的数据卷,这些卷不再被容器引用。‘挂起’的卷很难避免,并且占用空间。我们在尽力提升卷管理功能,参见pull request #14214

备份,恢复,迁移数据卷

数据卷的另外一个有用的功能是用来备份,恢复和迁移数据。我们使用 --volumes-from 来创建一个新容器并挂载数据卷:

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

示例中我们启动了一个新容器并且从dbdata 容器挂载了数据卷。我们把宿主本地路径挂载成 /backup.最后,我们用tar命令将dbdata数据卷备份进backup.tar,并存入 /backup目录。当命令结束时,我们将在本地目录下得到一个dbdata的备份。

你可以用它来恢复到之前备份的容器里,或者其他的容器:

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

然后 un-tar 备份的文件到新的容器数据卷中:

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

你可以使用上面的方法来自动化地备份迁移和恢复数据。

使用共享卷的注意点

多个容器可以共享一个或者多个数据卷,但是同时写入的时候会发生冲突。

数据卷在宿主里面是可以直接操作的。你可以使用普通的linux工具操作它们。但是建议你不要这样直接做,因为容器和应用并不知道你的操作,这可能会导致数据操作冲突。

下一步

我们学习了很多关于怎么使用docker,接下来我们将看到怎么将docker和Docker Hub上的服务例如自动编译,创建私有仓库等结合起来使用。

Go to Working with Docker Hub

完!

译者:本文中的部分命令参数是过时或者错误的,但这些可以忽略,并不影响我们理解指令的正确用法。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注