Docker 101 - Volume

在 Docker 实践中,程序在运行时会产生大量的数据和文件资源,这些文件是需要被持久保存和共享的。Docker 中提供了 Volumn 来完成这种操作,事实上 Volume 的应用很频繁,但官方的介绍却很含糊,于是多作了下深入了解。

docker

Layer & UFS

Docker的文件系统比较特别,Image 是由多个文件系统(只读层 Layer)叠加而成。

当容器初次启动时,Docker 先是读取镜像层,然后在其上添加一个新的读写层。如果一个已存在文件在 Container 中被修改,该文件将会 Image Layer 中复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏。当删除 Docker 容器,并通过该镜像重新启动时,之前的更改将会丢失。在 Docker 中,只读层及在顶部的读写层的组合被称为 Union File System(联合文件系统)。

为了能够保存(持久化)数据以及共享容器间的数据,Docker 提出了 Volume 的概念。简单来说,Volume 就是目录或者文件,它可以绕过默认的联合文件系统,通过挂载的方式加入 Container,而以正常的文件或者目录的形式存在于宿主机上。

Container 的一个误区就是不能持久化,Stop 之后所有的数据就不在了,真实情况是,除非手动 docker rm 删除 Container,否则这个 Container 会一直存在,可以重新启动,运行。所以 Volume 的存在也不是为了数据持久化。通过 Volume 可以将容器本身和容器所产生的数据进行分离。

挂载用法

有两种方式来声明 Volume:

  • 在 Dockerfile 中指定 VOLUME /data
  • 执行 docker run -v /data 命令来指定

这两种方式本质是一样的,在 Host 上创建一个目录,然后挂载到 Contrainer 中指定的目录下,这样,Container 在被删除时,Volumn 也会一直存在。但这这两种方式有着细微的差别。

Dockerfile

1
2
3
4
FROM ubuntu:14.04
MAINTAINER Lanvige Jiang <lanvige(@)gmail.com>
VOLUME /data

build、run 起来后,使用 inspect 查看 container 中 volume 的配置:

1
2
3
4
5
6
"Volumes": {
"/data": "/var/lib/docker/vfs/dir/fe3e925bb4ca5d6c04b51d1908c27e7386f5555f71d28752371c6ae91fb39966"
},
"VolumesRW": {
"/data": true
}
  • Volumes 中 /data 被映射到了 var/lib/docker/vfs/dir/fe3e9... 的目录中。
  • VolumesRW 中则是配置 /data 读写权限。

docker run

除了 Dockerfile 外,最常见的用法就是在 docker run 时通过参数 -v / --volume 进行配置:

1
$ sudo docker run -ti -v /data /ubuntu:14.04 /bin/bash

通过 inspect 查看容器 Volume 的配置,和上面 Dockerfile 中一致。

docker run 的方式,可以指定 Host 目录,将 Host 中指定路径挂载到 Contrainer 中,如果指定 Host 中的路径不存在,Docker 会自动创建。

1
$ sudo docker run -ti --volume=/opt/data:/data /ubuntu:14.04 /bin/bash

通过 docker inspect 查看:

1
2
3
$ sudo docker inspect -f {{.Volumes}} gitlab711
map[/data:/opt/data]

Volume 内无法配置指向路径进行挂载,因为 Image 不能提前知道自己的 Host 是什么服务器,Windows? Ubuntu? Mac?

Hybrid

如果在 Dockfile 中配置了多个 Volume,并 docker run 时也进行了部分映射,docker inspect 时会看到:

1
2
3
4
5
6
7
8
"Volumes": {
"/data": "/opt/data",
"/var/log/docker": "/var/lib/docker/vfs/dir/7c039ca15a0767d75a9f84fb963e506121bf4d4bfbd64e778fd32bd3f0bc61c0"
},
"VolumesRW": {
"/home/git/data": true,
"/var/log/gitlab": true
},

数据共享

除了挂载之外,Volume 的另一个用途是容器间的数据共享。

比如一个容器需要访问另一个容器来获取数据,就可以借助于 docker run --volumes-from 来实现。

1
2
$ docker run -it --volumes-from volume_container ubuntu /bin/bash
# 启动一个 ubuntu:leatest 的容器,并将 volume_container 中所有的 Volume 挂载进来。

无论 volume_container 是否处于启动状态,该 Volume 总是能被访问到。

数据容器

软件 = 运行环境 + 代码

如果以这样来看的话,环境是一个不常更新的部分,像 Java 基础环境、数据库。如果将环境和代码分离,可以减小更新包的大小。

当然,也可以将数据,配置文件使用于此场景。

当然,这种场景也是有很大缺陷的,如果一个 Container 中只有数据,会徒增 Image 大小,浪费资源。

REF::