Bind Mount 与 Data Volume、共享数据、Volume 生命周期管理
第 6 章 Docker 存储
6.1 Docker 的两类存储资源
Docker 为容器提供了两种存放数据的资源:
- 由 Storage Driver 管理的镜像层和容器层
- Data Volume
6.1.1 Storage Driver
先来回顾一下 Docker 镜像的分层结构:
容器由最上面一个可写的容器层,以及若干只读的镜像层组成,容器的数据就存放在这些层中。这样的分层结构最大的特性是 Copy-on-Write:
- 新数据会直接存放在最上面的容器层
- 修改现有数据会先从镜像层将数据复制到容器层,修改后的数据直接保存在容器层中,镜像层保持不变
- 如果多个层中有命名相同的文件,用户只能看到最上面那层中的文件
分层结构使镜像和容器的创建、共享以及分发变得非常高效,而这些都要归功于 Docker Storage Driver。正是 storage driver 实现了多层数据的堆叠并为用户提供一个单一的合并之后的统一视图。
Docker 会默认优先使用 Linux 发行版默认的 Storage Driver。
Docker 安装时会根据当前系统的配置选择默认的 driver。默认 driver 具有最好的稳定性,因为默认 driver 在发行版上经过了严格的测试。
运行docker info
可查看 Storage Driver 的相关信息:
[root@localhost ~]# docker info
Containers: 3
Running: 0
Paused: 0
Stopped: 3
Images: 4
Server Version: 18.09.2
Storage Driver: overlay2
Backing Filesystem: xfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 9754871865f7fe2f4e74d43e2fc7ccd237edcbce
runc version: 09c8266bf2fcf9519a651b04ae54c967b9ab86ec
init version: fec3683
Security Options:
seccomp
Profile: default
Kernel Version: 3.10.0-957.el7.x86_64
Operating System: CentOS Linux 7 (Core)
OSType: linux
Architecture: x86_64
CPUs: 6
Total Memory: 7.487GiB
Name: localhost.localdomain
ID: ZUUO:D7MX:3YFL:D67V:Y3DR:B57B:JAVB:IN7B:ZMWO:Q2SN:YNP4:TXP5
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
Product License: Community Engine
6.1.2 Data Volume
Data Volume 本质上是 Docker Host 文件系统中的目录或文件,能够直接被 mount 到容器的文件系统中。
Data Volume 有以下特点:
- Data Volume 是目录或文件,而非没有格式化的磁盘(块设备)
- 容器可以读写 volume 中的数据
- volume 数据可以被永久的保存,即使使用它的容器已经被销毁
在具体使用中应遵循这样的原则:
- 无状态的数据存放在数据层中,作为镜像一部分;
- 有状态的数据存放在 Data Volume 中,因为这是需要持久化的数据,并且应该与镜像分开存放。
在具体使用上,Docker 提供了两种类型的 volume:bind mount 和 Docker managed volume。
bind mount
bing mount 是将 host 上已存在的目录或文件 mount 到容器。
例如 Docker host 上有目录$HOME/htdocs
:
通过-v
将其 mount 到 httpd 容器:
参数-v
的格式为<host_path>:<container_path>
,原有的同名目录会被隐藏起来,这与 Linux 系统中的mount
命令的行为是一致的。
bind mount 可以让 host 与容器共享数据,这在管理上是非常方便的。
另外,使用 bind mount 时还可以指定数据的读写权限,默认是可读可写,可指定为只读:
ro
设置了只读权限。在容器中是无法对 bind mount 数据进行修改的,只有 host 有权修改数据,提高了安全性。
除了 bind mount 目录,还可以单独指定一个文件:
使用 bind mount 单个文件的场景是:只需要向容器添加文件,不希望覆盖整个目录。
使用单一文件有一点要注意:host 中的源文件必须要存在,不然会当作一个新目录 bind mount 给容器。
bind mount 的使用直观高效,易于理解,但它也有不足的地方:bind mount 需要指定 host 文件系统的特定路径,这就限制了容器的可移植性,当需要将容器迁移到其他 host,而该 host 没有要 mount 的数据或者数据不在相同的路径时,操作会失败。
移植性更好的方式是 docker managed volume。
docker managed volume
docker managed volume 与 bind mount 在使用上的最大区别是不需要指定 mount 源,指明 mount point 就行了。
以 httpd 容器为例:
[root@localhost ~]# docker run -d -p 80:80 -v /usr/local/apache2/htdocs httpd
2e973f7246b72d60c42e0a124d8bcb4e8ba053c4eb946de6c6d2ec5d2fb29cdd
上述命令通过-v
告诉 Docker 需要一个 data volume,并将其 mount 到/usr/local/apache2/htdocs
。
执行docker inspect
命令:
[root@localhost ~]# docker inspect 2e97
...
"Mounts": [
{
"Type": "volume",
"Name": "bb76558c26ec96b8f2fd9aced58105495b610119520dbe84c3f83bc347bc2209",
"Source": "/var/lib/docker/volumes/bb76558c26ec96b8f2fd9aced58105495b610119520dbe84c3f83bc347bc2209/_data",
"Destination": "/usr/local/apache2/htdocs",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
...
可以看到Source
就是该 Volume 在 host 上的目录。每当容器申请一个 docker managed volume 时,Docker 都会在/var/lib/docker/volumes
下生成一个目录,这个目录就是 mount 源。查看该 volume 下的文件内容:
[root@localhost ~]# ls /var/lib/docker/volumes/bb76558c26ec96b8f2fd9aced58105495b610119520dbe84c3f83bc347bc2209/_data
index.html
总结一下 docker managed volume 的创建过程:
- 容器启动时,简单的告诉 docker “我需要一个 volume 存放数据,帮我 mount 到目录
/abc
“。 - docker 在
/var/lib/docker/volumes
中生成一个随机目录作为 mount 源 - 如果
/abc
已经存在,则将数据复制到 mount 源 - 将 volume mount 到
/abc
还可以通过docker volume
命令查看 docker managed volume,不过看不到 bing mount,而且也无法知道 volume 对应的容器:
[root@localhost ~]# docker volume ls
DRIVER VOLUME NAME
local bb76558c26ec96b8f2fd9aced58105495b610119520dbe84c3f83bc347bc2209
bing mount 和 docker managed volume 对比
相同点:都是 host 文件系统中的某个路径
不同点:
bind mount | docker managed volume | |
---|---|---|
volume 位置 | 可任意指定 | /var/lib/docker/volumes/… |
对已有 mount point 影响 | 隐藏并替换为 volume | 原有数据复制到 volume |
是否支持单个文件 | 支持 | 不支持,只能是目录 |
权限控制 | 可设置为只读,默认为读写权限 | 无控制,均为读写权限 |
移植性 | 移植性弱,与 host path 绑定 | 移植性强,无需指定 host 目录 |
6.2 共享数据
6.2.1 容器与 host 共享数据
bing mount 与 docker managed volume 均可实现在容器与 host 之间共享数据。
用 bind mount 来共享数据非常简单:直接将要共享的目录 mount 到容器。
而对与 docker managed volume,由于 volume 位于 host 中的目录,是在容器启动时才生成,所以需要使用docker cp
将共享数据拷贝到 volume 中:
6.2.2 容器之间共享数据
第一种方法是将共享数据放在 bind mount 中,然后将其 mount 到多个容器。
另一种在容器之间共享数据的方式是使用 volume container。
6.2.3 使用 volume container 共享数据
volume container 是专门为其他容器提供 volume 的容器。它提供的卷可以是 bind mount,也可以是 docker managed volume。下面我们创建一个 volume container:
将容器命名为vc_data
。这里执行的是docker create
命令,这是因为 volume container 的作用只是提供数据,它本身不需要处于运行状态。容器 mount 了两个 volume:
- bing mount,存放 web server 的静态文件
- docker managed volume,存放一些实用工具
其他容器可以通过--volumes-from
使用vc_data
这个 volume container:
volume container 的特点如下:
- 与 bind mount 相比,不必为每一个容器指定 host path,所有的 path 都在 volume container 中定义好了,容器只需要与 volume container 关联,这样就实现了容器与 host 的解耦
- 使用 volume container 的容器其 mount point 是一致的,有利于配置的规范和标准化,但也带来一定的局限,使用时需要综合考虑。
6.2.4 data-packed volume container
之前的例子中 volume container 的数据还是在 host 上,其实还可以将数据打包到镜像中,然后通过 docker managed volume 共享。通常我们称这种容器为 data-packed volume container。
首先使用下面的 Dockerfile 构建镜像:
FROM busybox:latest
ADD htdocs /usr/local/apache2/htdocs
VOLUME /usr/local/apache2/htdocs
之后构建新镜像 datapacked:
用新镜像创建 data-packed volume container:
因为在 Dockerfile 中已经使用了VOLUME
指令,这里就不需要指定 volume 的 mount point 了。启动 httpd 容器并使用 data-packed volume container:
容器能够正确读取 volume 中的数据。data-packed volume container 是自包含的,不依赖 host 提供数据,具有很强的移植性,非常适合只使用静态数据的场景,比如应用的配置信息、web server 的静态文件等。
6.3 volume 生命周期管理
6.3.1 volume 备份
因为 volume 实际上是 host 文件系统中的目录和文件,所以 volume 的备份实际上是对文件系统的备份。
所有的本地镜像都存在 host 的/myregistry
目录中,我们要做的就是定期备份这个目录。
6.3.2 volume 恢复
volume 的恢复也很简单,如果数据损坏了,直接用之前备份的数据拷贝到/myregistry
就可以了。
6.3.3 volume 迁移
如果想使用更新版本的 Registry,就涉及到数据迁移:
docker stop
当前 Registry 容器- 启动新版本容器并 mount 原有 volume:
docker run -d -p 5000:5000 -v /myregistry:/var/lib/registry registry:latest
在启用新容器前要确认新版本的默认数据路径是否发生变化。
6.3.4 volume 销毁
Docker 不会销毁 bind mount。删除 bind mount 数据的工作只能由 host 负责。
对于 docker managed volume,在执行docker rm
删除容器时可以加上-v
参数,Docker 会将容器使用到的 volume 一并删除,但前提是没有容器 mount 该 volume。
如果在删除容器时没有带-v
,就会产生孤儿 volume:
可以使用docker volome rm
删除这些孤儿 volume:
如果想批量删除孤儿 volume,可以使用以下命令:
docker volume rm $(docker volume ls -q)
6.4 小结
本章包括以下内容:
- Docker 为容器提供了两种存储资源:数据层和 data volume
- 数据层包括镜像层和容器层,由 storage driver 管理
- Data Volume 有两种类型:bind mount 和 docker managed volume
- bing mount 可以实现容器与 host 之间、容器与容器之间共享数据
- volume container 是一种具有更好移植性的容器间数据共享方案,特别是 data-packed volume container
- volume 生命周期管理包括备份、恢复、迁移和销毁 Data Volume