在 Docker 中管理数据
默认情况下,在容器内创建的所有文件都存储在可写容器层上。这意味着:
- 当该容器不再存在时,数据不会保留,如果另一个进程需要数据,则可能很难将数据从容器中取出。
- 容器的可写层与运行容器的主机紧密耦合。您不能轻易地将数据移动到其他位置。
- 写入容器的可写层需要存储驱动程序来管理文件系统。存储驱动程序使用 Linux 内核提供联合文件系统。与使用直接写入主机文件系统的数据卷相比,这种额外的抽象会降低性能。
Docker 有两个容器选项,用于在主机上存储文件,以便即使在容器停止后文件也会保留:卷(Volumes)
和绑定挂载(Bind mounts)
。
Docker 还支持将文件存储在主机上内存中的容器。不会保留此类文件。如果您在 Linux 上运行 Docker,则 tmpfs mount 用于将文件存储在主机的系统内存中。如果您在 Windows 上运行 Docker,则命名管道用于将文件存储在主机的系统内存中。
选择正确的装载类型
无论您选择使用哪种类型的装载,从容器内看数据看起来都是一样的。它作为容器文件系统中的目录或单个文件公开。
可视化卷、绑定挂载和 tmpfs 装载之间差异的一种简单方法是考虑数据在 Docker 主机上的位置。
Volumes
卷存储在主机文件系统的一部分中,该文件系统由 Docker(Linux 上的 /var/lib/docker/volumes/
)管理。非 Docker 进程不应修改文件系统的这一部分。卷是在 Docker 中持久保存数据的最佳方式。
Bind mounts
绑定挂载可以存储在主机系统上的任何位置。它们甚至可能是重要的系统文件或目录。Docker 主机或 Docker 容器上的非 Docker 进程可以随时修改它们。
tmpfs mounts
tmpfs 挂载仅存储在主机系统的内存中,从不写入主机系统的文件系统。
有关装载类型的更多详细信息
Volumes(卷)
由 Docker 创建和管理。可以使用 docker volume create 命令显式创建卷,或者 Docker 可以在创建容器或服务期间创建卷。
创建卷时,该卷存储在 Docker 主机上的目录中。将卷装入容器时,此目录是装入容器中的目录。这类似于绑定挂载的工作方式,不同之处在于卷由 Docker 管理,并且与主机的核心功能隔离。
给定的卷可以同时装入多个容器中。当没有正在运行的容器正在使用卷时,该卷仍可供 Docker 使用,并且不会自动删除。您可以使用 docker volume prune 删除未使用的卷。
装入卷时,该卷可以是命名的,也可以是匿名的。匿名卷在首次挂载到容器中时不会给出显式名称,因此 Docker 会为它们提供一个随机名称,该名称保证在给定的 Docker 主机中是唯一的。除了名称之外,命名卷和匿名卷的行为方式相同。
卷还支持使用卷驱动程序,这允许您将数据存储在远程主机或云提供商上,以及其他可能性。
Bind mounts(绑定挂载)
从 Docker 的早期开始就可用。与卷相比,绑定挂载的功能有限。使用绑定挂载时,主机上的文件或目录将装载到容器中。文件或目录由其在主机上的完整路径引用。该文件或目录不需要已存在于 Docker 主机上。如果尚不存在,则按需创建。绑定挂载的性能非常高,但它们依赖于具有特定目录结构的主机文件系统。如果要开发新的 Docker 应用程序,请考虑改用命名卷。不能使用 Docker CLI 命令直接管理绑定挂载。
绑定挂载允许访问敏感文件
使用绑定挂载的一个副作用,无论好坏,都是可以通过容器中运行的进程来更改主机文件系统,包括创建、修改或删除重要的系统文件或目录。这是一项强大的功能,可能会产生安全隐患,包括影响主机系统上的非 Docker 进程。
tmpfs mounts(tmpfs 挂载)
tmpfs 挂载不会保留在磁盘上,无论是在 Docker 主机上还是在容器内。容器可以在容器的生存期内使用它来存储非持久性状态或敏感信息。例如,在内部,swarm 服务使用 tmpfs 装载将机密装载到服务的容器中。
named pipes(命名管道)
npipe 装载可用于 Docker 主机和容器之间的通信。常见用例是在容器内运行第三方工具,并使用命名管道连接到 Docker 引擎 API。
绑定挂载和卷都可以使用 -v 或 --volume 标志装载到容器中,但两者的语法略有不同。对于 tmpfs 装载,可以使用 --tmpfs 标志。我们建议对容器和服务、绑定挂载、卷或 tmpfs 装载使用 --mount 标志,因为语法更清晰。
案例
volumes(卷)的使用案例
卷是在 Docker 容器和服务中保存数据的首选方法。卷的一些用例包括:
- 在多个正在运行的容器之间共享数据。如果未显式创建卷,则会在首次装入容器时创建卷。当该容器停止或删除时,卷仍然存在。多个容器可以同时挂载同一卷,可以是读写卷,也可以是只读卷。仅当您显式删除卷时,才会删除它们。
- 当 Docker 主机不能保证具有给定的目录或文件结构时。卷可帮助您将 Docker 主机的配置与容器运行时分离。
- 当您想要将容器的数据存储在远程主机或云提供商上,而不是本地时。
- 当您需要将数据从一个 Docker 主机备份、还原或迁移到另一个主机时,卷是更好的选择。您可以使用卷停止容器,然后备份卷的目录(例如
/var/lib/docker/volumes/<volume-name>
)。 - 当您的应用程序需要在 Docker 桌面上实现高性能 I/O 时。卷存储在 Linux VM 而不是主机中,这意味着读取和写入具有更低的延迟和更高的吞吐量。
- 当您的应用程序需要 Docker 桌面上的完全本机文件系统行为时。例如,数据库引擎需要精确控制磁盘刷新以保证事务持久性。卷存储在 Linux VM 中,可以做出这些保证,而绑定挂载则远程到 macOS 或 Windows,其中文件系统的行为略有不同。
bind mounts(绑定挂载) 的使用案例
通常,应尽可能使用卷。绑定挂载适用于以下类型的用例:
- 将配置文件从主机共享到容器。这就是 Docker 默认向容器提供 DNS 解析的方式,方法是将
/etc/resolv.conf
从主机挂载到每个容器中。 - 在 Docker 主机上的开发环境和容器之间共享源代码或生成项目。例如,您可以将 Maven target/ 目录挂载到容器中,每次在 Docker 主机上构建 Maven 项目时,容器都可以访问重建的项目。
- 如果您以这种方式使用 Docker 进行开发,您的生产 Dockerfile 会将生产就绪的项目直接复制到映像中,而不是依赖于绑定挂载。
- 当 Docker 主机的文件或目录结构保证与容器所需的绑定挂载一致时。
tmpfs 的使用案例
tmpfs 装载最适合在不希望数据保留在主机上或容器内的情况。这可能是出于安全原因,也可能是为了在应用程序需要写入大量非持久性状态数据时保护容器的性能。
使用 bind mounts(绑定挂载)或 volumes(卷)的提示
如果使用绑定挂载或卷,请记住以下几点:
- 如果将空卷装载到容器中存在文件或目录的目录中,则这些文件或目录将传播(复制)到卷中。同样,如果启动容器并指定尚不存在的卷,则会为您创建一个空卷。这是预填充另一个容器所需的数据的好方法。
- 如果将绑定挂载或非空卷装载到容器中存在某些文件或目录的目录中,则这些文件或目录将被装载遮盖,就像将文件保存到 Linux 主机上的 /mnt 中,然后将 USB 驱动器装载到 /mnt 中一样。在卸载 USB 驱动器之前, /mnt 的内容将被 USB 驱动器的内容遮盖。遮盖的文件不会被删除或更改,但在装入绑定挂载或卷时无法访问。