5.1.1. 前言
默认情况下,在容器内创建的所有文件都存储在容器的 可写层 中,这意味着:
- 当容器不存在时,数据将不会自动持久化保存(即消失)。并且,如果另一个进程需要容器的数据将很难从容器中读取。
- 容器的可写层与容器的运行主机机器紧密的耦合在一起,很难将数据进行迁移。
- 写入容器的可写层需要存储驱动程序来管理文件系统。存储驱动提供了一个联合文件系统,它使用 linux 内核系统。与使用数据
volumes
相比,这种额外的 抽象扩展降低的主机的性能。它直接写入主机的文件系统。
Docker 有两种方式在宿主机器中存储数据、文件,这两种方式在容器停止数据也依然会存储在宿主机器中,不会进行删除。分别是 volumes 和 bind mounts。
另外,如果是在 Linux 中,你同时可以使用 tmpfs mount。
5.1.2. 挂载类型选择
无论使用哪种安装类型,数据在容器中看起来是一样的。它作为目录或容器文件系统中的单个文件公开。
通过下图可以看到 volume
、bind mounts
和 tmpfs mounts
之间在宿主机器中存储的位置可以比较差异。
volumes 存储在主机文件系统的一部分中。完全由 docker 进行管理,在 Linux 中的目录为:
/var/lib/docker/volumes
。非 docker 进程应 禁止操作者部分数据,在 Docker 中,volumes
是持久化数据的最好方式。bind mounts 可以将数据存储在宿主机器中的任意位置。甚至可以是重要的系统或目录。Docker 主机或 Docker 容器上的非 Docker 进程可以随时修 改他们。
tmpfs mounts 是唯一一个将数据存储在内存中的,永远不会将数据持久化到硬盘。
5.1.3. 挂载类型详解
- volumes 由 docker 创建、管理。可以使用
docker volume create
命令进行显示的创建卷(volumes
)。或者也可以在 容器或服务创建期间创建卷(volumes
)。
当创建 volume 时,它存储在 docker 主机上的目录中。当将 volume 装入容器时,此目录是装入容器的目录。除了 volume 是由 docker 管理并与主机
的核心功能隔离外,这类似 bind mounts
的工作方式。
一个 volume 可以同时安装到多个容器中。当没有正在运行的容器使用 volume 时,该卷(volume
)仍可供 docker 使用,并且不会自动删除。不过,你可
以使用 docker volume rm prune
命令进行删除所有本地不使用的 volume。
当装载 volume 时,可以显示的命名也可以匿名。匿名 volume 首次装载时没有明确的名称。因此,docker 会给定一个随机的名称,保证在给定的 docker 主 机中是唯一的。除名称外,匿名和显示命名的行为是相同的。
volume 还支持使用 卷驱动 程序,这些驱动程序允许将数据存储在远程主机或云提供程序上,以及其他可能存储的任何位置。
- bind mounts 是 docker 早期就提供的数据挂载方式。
与 volume 相比,绑定挂载 具有有限的功能。当使用 bind mounts
,主机上的一个文件或目录就会挂载到一个容器中。文件或目录由主机上的
完整路径引用。该文件或目录不需要在主机中已经存在,如果之前不存在则会按需进行自动进行创建。绑定挂载非常高效,但它们依赖于具有特定目录结构的主机文
件系统。如果你部署一个新的 docker 应用,考虑使用命名 volume 进行代替。你不能直接使用 docker CLI 命令进行管理绑定挂载。
[warning] 绑定挂载允许访问敏感文件
使用绑定装载的一个副作用,不论结果好坏。是你可以通过容器中运行的进程更改主机文件系统。包括创建、修改或删除重要的系统文件或目录。这是一个强大的 功能,可能会产生安全隐患,包括影响主机系统上的非 docker 进程。
- tmpfs mounts 不会在磁盘上持久化
tmpfs mount
的数据,而是将数据存储在内存中。它可以在容器的生命周期中使用, 以存储非持久化状态或敏感数据信息。
bind mount
和 volume
都可以使用 --mount
、--volume
命令选项进行挂载到容器中,不过每种语法有些不同。对于 tmpfs mount
,你可以
使用 --tmpfs
命令选项。在 docker v17.06
以及更高版本,推荐在容器和服务中使用 --mount
选项。
5.1.4. volume 实现数据存储优势
volumes 是容器和服务中持久化数据的最佳方式。以下是使用 volume 的使用优点:
在多个容器之间共享数据。如果没有明确创建它,在第一次将 volume 装入容器时会按需创建 volume。当容器停止或者删除,volume 依然会存在。多个容器 可以装载同一个 volume。可以是读写,也可以是只读。只有在你明确指定移除时 volume 才会被删除。删除命令可以是用
docker volume rm <volume-name>
。当 Docker 主机不能保证具有给定的目录或文件结构时。volumes 可帮助配置 docker 主机与容器运行时进行分离。
当你想将容器产生的数据存储在远程主机或云上而不是存储在本地时可以考虑使用 volume。
当需要将 docker 数据备份、迁移或恢复到另一台 docker 主机上,volumes 是最好的选择。你可以使用 volume 停止容器,然后备份 volume 的目录。( 如:
/var/lib/docker/volumes/<volume-name>
)
5.1.5. bind mounts 实现数据存储优势
注意,volumes 是推荐使用的数据持久化方式。不过,在以下场景下可以考虑使用 bind mounts
:
在宿主机器与 docker 容器之间分享配置文件。这就是 docker 默认为容器提供 DNS 解析的方式,通过安装
/etc/resolv.conf
重宿主机到每个容器中。在 docker 主机上的开发环境和容器之间共享源代码或构建工件。你可以将 Maven
/target
目录挂载到容器中,每次在 docker 主机上构建 maven 项 目时,容器都可以访问重建的工件。如果以这种方式使用 Docker 进行开发,你的生产环境 Dockerfile 会将生产就绪工件直接复制到镜像中,而不是依赖bind mounts
。当 docker 主机的文件或目录结构保证与容器所需的绑定安装一致时可以使用绑定挂载。
5.1.6. tmpfs mounts 实现数据存储优势
当你不想将数据存储在宿主机器上或者容器中时,tmpfs mount
是最好的方式。当应用程序需要编写大量非持久化数据时,可能出于安全原因还要保护性能。
5.1.7. volumes 和 bind mounts 小提示
如果需要使用 bind mount 和 volumes 时,需要记住以下几点:
如果将 空volume 安装到容器中存在文件或目录的目录中,则会将这些文件或目录传播(复制)到 volume 中。同样,如果你启动一个容器并且指定一个 不存在的 空volume,一个 空volume 会自动创建。这是预先填充另一个容器所需数据的好方法。
如果安装一个 bind mount 或 非空 volume 到容器的一个目录中。这些文件或目录将会被模糊安装,就像 linux 主机上将文件保存到
/mnt
, 然后将 USB 驱动器安装到/mnt
中一样。在卸载 USB 驱动之前,/mnt
的内容会被 USB 驱动器的内容遮挡。隐藏的文件不会被删除或更改,但在安装 绑定装载或卷 时无法访问。