Bind Mount 与 Data Volume、共享数据、Volume 生命周期管理

《每天5分钟玩转Docker容器技术》
《每天5分钟玩转Docker容器技术》

第 6 章 Docker 存储

6.1 Docker 的两类存储资源

Docker 为容器提供了两种存放数据的资源

  1. 由 Storage Driver 管理的镜像层容器层
  2. Data Volume

6.1.1 Storage Driver

先来回顾一下 Docker 镜像的分层结构

容器由最上面一个可写的容器层,以及若干只读的镜像层组成,容器的数据就存放在这些层中。这样的分层结构最大的特性是 Copy-on-Write

  1. 新数据会直接存放在最上面的容器层
  2. 修改现有数据先从镜像层将数据复制到容器层,修改后的数据直接保存在容器层中,镜像层保持不变
  3. 如果多个层中有命名相同的文件,用户只能看到最上面那层中的文件

分层结构使镜像和容器的创建、共享以及分发变得非常高效,而这些都要归功于 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 有以下特点:

  1. Data Volume 是目录或文件,而非没有格式化的磁盘(块设备)
  2. 容器可以读写 volume 中的数据
  3. volume 数据可以被永久的保存,即使使用它的容器已经被销毁

在具体使用中应遵循这样的原则:

  1. 无状态的数据存放在数据层中,作为镜像一部分
  2. 有状态的数据存放在 Data Volume 中,因为这是需要持久化的数据,并且应该与镜像分开存放

在具体使用上,Docker 提供了两种类型的 volumebind mountDocker 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 的创建过程

  1. 容器启动时,简单的告诉 docker “我需要一个 volume 存放数据,帮我 mount 到目录 /abc“。
  2. docker 在/var/lib/docker/volumes中生成一个随机目录作为 mount 源
  3. 如果/abc已经存在,则将数据复制到 mount 源
  4. 将 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 mountdocker 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:

  1. bing mount,存放 web server 的静态文件
  2. docker managed volume,存放一些实用工具

其他容器可以通过--volumes-from使用vc_data这个 volume container:

volume container 的特点如下:

  1. 与 bind mount 相比,不必为每一个容器指定 host path,所有的 path 都在 volume container 中定义好了,容器只需要与 volume container 关联,这样就实现了容器与 host 的解耦
  2. 使用 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,就涉及到数据迁移

  1. docker stop当前 Registry 容器
  2. 启动新版本容器并 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 小结

本章包括以下内容:

  1. Docker 为容器提供了两种存储资源:数据层和 data volume
  2. 数据层包括镜像层容器层,由 storage driver 管理
  3. Data Volume 有两种类型:bind mountdocker managed volume
  4. bing mount 可以实现容器与 host 之间、容器与容器之间共享数据
  5. volume container 是一种具有更好移植性的容器间数据共享方案,特别是 data-packed volume container
  6. volume 生命周期管理包括备份、恢复、迁移和销毁 Data Volume