Docker - Container 数据共享/存储

docker

在实践中,代码是一个很麻烦的地方,因为容器一旦确定很少有情况会去修改,但代码 Deploy 的频率会非常高,对于一个动辄上G的 Image,Deploy 的成本还是非常高的,如何处理这种动静分离?代码还会产生数据,数据一部分会记录在数据库,这个很好解决,另一部分是Log,它被写在Container的文件系统里,每次关闭都会被丢掉。如何解决这个问题。

Docker文件系统

Docker的文件系统AUFS,一种“增量文件系统”,用户所做修改以增量的方式保存,决定了其分层存储特性。

最顶层为读写层,初始将无内容,后续创建文件将会保存于此。若依赖层(亦即较低层)需要更新文件,那么更新后的文件将被保存在最顶层。总之,除了最顶层,所有底层都不可写。较低层总是被其它镜像共享。总之,Docker仅仅需要记录最顶层容器所有改变即可,这部分可以做到版本更迭、增量式存储,设计思路很赞!

Docker如何持久化状态

其实,我很关心Docker运行中的容器实例,如何保存状态。

运行中的Docker容器实例首先会在当前内存中持有一些状态信息,其次文件系统都会发生或多或少的改变。当前Docker能够支持哪些持久化,下面一一列出。

容器实例因意外出现退出,如何持久化这种情况,人为使用 ‘docker commit’,也是不错的方法。但有人提出了更好的方式,通过捕捉退出信号,不妨一试。

  • trapping signal from “docker stop” in bash
  • docker-exec项目

容器实例的挂起/恢复当前Docker不支持容器的挂起/恢复操作,但可参考一下原生的LXC内置命令lxc-freeze/lxc-unfreeze和CRIU(http://criu.org/Main_Page):

  • consider a docker suspend and resume
  • Can I suspend and then resume Docker container?

目前最期待Docker和CRIU的整合工作有待进展,可能会促进Docker商业化更进一步。

挂载宿主机目录 Docker支持挂载宿主机目录,支持宿主机目录和容器之间文件目录进行映射,彼此共享:

1
docker run -i -t -v /host/dir:/container/path ubuntu /bin/bash

在Dockerfile中,则可以使用’VOLUME’命令

1
VOLUME ["/var/volume1", "/var/volume2"]

如何在容器之间共享存储 容器之间文件目录(数据卷)可分享、重用,主要借助于’-volumes-from’参数实现。

1
2
COUCH1=$(sudo docker run -d -v /var/lib/couchdb shykes/couchdb:2013-05-03)
COUCH2=$(sudo docker run -d -volumes-from $COUCH1 shykes/couchdb:2013-05-03)

这个特性,可引入很大的想象空间。若,一个Container实例用于Web存储,另外两个实例负载用于处理应用请求,分分离离,降低耦合。

镜像导入/导出构建的镜像,作为一种状态存在,支持很方便的导入导出。导出:

1
docker save IMAGENAME | bzip2 -9 -c>img.tar.bz2

导入镜像:

1
bzip2 -d -c <img.tar.bz2 | docker load

Data Volume 共享文件

数据卷

数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

  • 数据卷可以在容器之间共享和重用
  • 对数据卷的修改会立马生效
  • 对数据卷的更新,不会影响镜像
  • 卷会一直存在,直到没有容器使用
    *数据卷的使用,类似于 Linux 下对目录或文件进行 mount。

创建一个数据卷

在用 docker run 命令的时候,使用 -v 标记来创建一个数据卷并挂载到容器里。在一次 run 中多次使用可以挂载多个数据卷。

下面创建一个 web 容器,并加载一个数据卷到容器的 /webapp 目录。

1
2
$ sudo docker run -d -P --name web -v /webapp training/webapp python app.py
*注意:也可以在 Dockerfile 中使用 VOLUME 来添加一个或者多个新的卷到由该镜像创建的任意容器。

挂载一个主机目录作为数据卷

使用 -v 标记也可以指定挂载一个本地主机的目录到容器中去。

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

上面的命令加载主机的 /src/webapp 目录到容器的 /opt/webapp 目录。这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作。本地目录的路径必须是绝对路径,如果目录不存在 Docker 会自动为你创建它。

*注意:Dockerfile 中不支持这种用法,这是因为 Dockerfile 是为了移植和分享用的。然而,不同操作系统的路径格式不一样,所以目前还不能支持。

Docker 挂载数据卷的默认权限是读写,用户也可以通过 :ro 指定为只读。

1
2
3
# 加了 :ro 之后,就挂载为只读了。
$ sudo docker run -d -P --name web -v /src/webapp:/opt/webapp:ro
training/webapp python app.py

挂载一个本地主机文件作为数据卷

-v 标记也可以从主机挂载单个文件到容器中

$ sudo docker run –rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash
这样就可以记录在容器输入过的命令了。

*注意:如果直接挂载一个文件,很多文件编辑工具,包括 vi 或者 sed –in-place,可能会造成文件 inode 的改变,从 Docker 1.1 .0起,这会导致报错误信息。所以最简单的办法就直接挂载文件的父目录。

小结

目前Docker对运行中容器内存状态持久化不支持,仅限于文件层面的持久化支持等。通过挂载宿主机目录,把变化部分mount过来即可,减少docker commit次数,实现动静态分离。