虽然我们可以在docker容器中保存写入的数据,但还是有这样几个不足:

  1. 1.容器中的数据会随着容器的停止运行而消失,而且当其他的进程需要这些
  2. 数据时,很难将这些数据从容器中提取出来;
  3. 2.容器的数据写入层是紧密地对应着他的宿主操作系统的,数据不能容易
  4. 的被迁移到其他地方;
  5. 3.要将数据写入到容器的数据写入层,需要一个特定的存储驱动,利用
  6. linux内核构建一个统一的文件系统,来管理宿主和容器的文件系统。这
  7. 层额外的虚拟化显然会降低性能。

为了避免性能下降,docker使用data volumes的方式,直接对宿主文件系统进行写操作。

Docker提供了三种不同的方式用于将宿主的数据挂载到容器中:

  • volumes
  • bind mounts
  • tmpfs volumes

当你不知道该选择哪种方式时,记住,volumes总是正确的选择

下面将进一步介绍每种挂载方式的工作机制:
选择正确的挂载类型无论选择哪种挂载方式,在容器内部看来,数据就是数据,并没有什么不同。主机的数据在容器的文件系统中总被显示为目录或文件。如何简单的理解这三种挂载方式的不同之处呢,我们可以理解为在这三种方式下,容器内的数据在宿主机中存放的位置不同,见下图:
挂载方式 - 图1

一、挂载目录介绍

Volumes方式下

容器内的数据被存放到宿主机(linux)一个特定的目录下(/var/lib/docker/volumes/)。这个目录只有Docker可以管理,其他进程不能修改如果想持久保存容器的应用数据,Volumes是Docker推荐的挂载方式。挂载命令:

$ docker run -d -p 5000:5000 --mount type=volume,source="registry",target="/var/lib/registry" registry:2.4.1

其中,
source是相对目录,基于/var/lib/docker/volumes/
target是容器中的目录

Bind mounts方式下

容器内的数据被存放到宿主机文件系统的任意位置,甚至存放到一些重要的系统目录或文件中。除了Docker之外的进程也可以任意对他们进行修改; 挂载命令:

$ docker run -d -p 5000:4000 -v /opt/data/registry:/tmp/registry registry

tmpfs方式下

容器的数据只会存放到宿主机的内存中,不会被写到宿主机的文件系统中,因此不能持久保存容器的应用数据

二、详细介绍三种挂载方式

Volumes

  • 由Docker进程创建和管理,可以通过命令docker volume create来创建一个指定的卷,也可以由Docker进程在创建容器或服务的过程中来创建;
  • 当你为一个容器创建一个volume时,这个volume将会被存储到宿主机的一个目录下。当你挂载这个volume到一个容器中时,就是在挂载这个目录到容器中。这和bind mount的工作机制很相似,当然,除了volume是被Docker所管理并与宿主机其他核心功能隔离之外。
  • 一个volume可以被同时挂载到多个容器中。当没有任何容器在使用这个volume的时候,这个volume也仍然可以被Docker进程所使用,而不是自动被删除。当然,你可以使用命令手动删除volume:docker volume prune
  • 当你挂载一个volume时,可以选择为他命名(named),也可以不命名(anonymous). 如果不命名的话,当这个volume首次被挂载到一个容器中时,Docker进程会分配给它一个随机的名字,以保证在宿主机操作系统中这个volume的名字唯一。除了名字之外,命名和不命名的volume没有区别。
  • Volume也支持使用volume drivers,以帮助你将数据保存到远程主机或云上。

Bind mounts

  • 在docker的早期版本中就存在的功能,与volumes相比,他的功能比较局限。当使用bind mounts时,宿主机的目录或文件被挂载到容器中。容器将按照挂载目录或文件的绝对路径来使用或修改宿主机的中的数据。宿主机中的目录或文件不需要预先存在,在需要的时候会自动创建。使用Bind mounts在性能上是非常好的,但这依赖于宿主机有一个目录妥善结构化的文件系统。如果你要创建一个新的Docker应用,我们仍推荐使用named volume的方式,因为你无法通过Docker CLI来管理bind mounts。(警告:bind mounts是一把双刃剑,因为使用bind mounts的容器可以在通过容器内部的进程对主机文件系统进行修改,包括创建,修改和删除重要的系统文件和目录,这个功能虽然很强大,但显然也会造成安全方面的影响,包括影响到宿主机上Docker以外的进程)

tmpfs mounts

  • 在这种挂载方式下,容器内的应用数据将不会被持久的保存到硬盘上,其中的数据只能在某个容器的生存周期内被使用,或者用于保存一些不需要持久存储,或一些敏感的数据信息。比如,在docker内部,swarm服务使用tmpfs方式来将secrets挂载到服务的容器中。(关于secrets,参考https://docs.docker.com/engine/swarm/secrets/)
    Bind mounts和volumes都可以通过使用标志-v或—volume来挂载到容器中,只是格式有些许不同。tmpfs可以使用标志—tmpfs进行挂载。然而,在Docker17.06及其以上版本中,我们推荐使用—mount来对容器或服务进行这三种方式的挂载,因为这种格式更加清晰。

三、使用场景

Volume的适用场景

  • 多个容器间需要共享数据。如果volume没有手动被创建,它将会在首次挂载到某个容器之前被自动创建,当容器被停止或删除时,这个volume不会随之被删除。多个容器可以同时以rw或ro的方式挂载这个volume。只有手动指定删除volume,它才会被删除。
  • 当宿主机并没有专用于Docker的文件系统结构时。使用volume可以使宿主机的配置与容器的运行解耦。
  • 当你希望将数据保存到远程主机或云上。
  • 当你希望在不同的宿主机直接备份/恢复/迁移数据时,volume是一个很好的选择。你可以停止运行使用volume的容器,然后直接备份volume所在的目录即可,如/var/lib/docker/volumes/

bind mounts的适用场景

  • 将宿主机的系统配置文件共享给容器,这是Docker为容器提供DNS配置的默认方式,即通过bind mounts的方式将宿主的的/etc/resolv.conf文件挂载到容器中。
  • 将宿主机开发环境中的源代码或实验结果共享给容器。例如:你在宿主机上进行一个项目Maven的测试,每次你在宿主机上对Maven项目进行了更改后,容器就可以直接获取更改后的结果
  • 当你可以确定宿主机的文件系统结构应该与容器内部完全一致时。

tmpfs的适用场景

  • 当你出于安全原因,或者容器性能优化的原因(如需要写入大量的不持久的状态数据时),不需要容器的数据长久保存时可以使用这种方式。