Docker 数据卷 VS 挂载主机目录

121

我们可以在Docker中拥有数据卷:

$ docker run -v /path/to/data/in/container --name test_container debian
$ docker inspect test_container
...
Mounts": [
    {
        "Name": "fac362...80535",
        "Source": "/var/lib/docker/volumes/fac362...80535/_data",
        "Destination": "/path/to/data/in/container",
        "Driver": "local",
        "Mode": "",
        "RW": true
    }
]
...

但如果数据位于/var/lib/docker/volumes/fac362...80535/_data,那么它与使用-v /path/to/data/in/container:/home/user/a_good_place_to_have_data挂载的文件夹中的数据有什么不同吗?

4个回答

141
虽然使用卷和绑定挂载感觉相同(唯一的变化是目录的位置),但行为上存在差异。
卷与绑定挂载的区别:
- 绑定挂载将主机机器上的文件或目录挂载到容器中。文件或目录通过主机机器上的完整路径或相对路径引用。 - 卷在Docker的存储目录中创建一个新目录,并由Docker管理该目录的内容。
卷相对于绑定挂载的优势:
- 卷比绑定挂载更容易备份或迁移。 - 您可以使用Docker CLI命令或Docker API来管理卷。 - 卷适用于Linux和Windows容器。 - 卷可以更安全地在多个容器之间共享。 - 卷驱动程序允许您将卷存储在远程主机或云提供商上,加密卷的内容,或添加其他功能。 - 新卷的内容可以由容器预填充。
根据@Sebi2020的评论,绑定挂载更容易备份。Docker没有提供任何备份卷的命令。您必须使用带有绑定挂载的临时容器来创建备份。 由Docker创建和管理。您可以使用docker volume create命令显式地创建卷,或者在容器或服务创建过程中,Docker可以自动创建卷。
当您创建一个卷时,它将存储在Docker主机上的一个目录中。当您将卷挂载到容器中时,这个目录就会被挂载到容器中。这类似于绑定挂载的工作方式,不同之处在于卷由Docker管理,并且与主机机器的核心功能隔离开来。
一个给定的卷可以同时挂载到多个容器中。当没有运行的容器使用卷时,该卷仍然可供Docker使用,并且不会自动删除。您可以使用docker volume prune命令删除未使用的卷。
当您挂载一个卷时,它可以是具名的或匿名的。匿名卷在首次挂载到容器时不会被赋予明确的名称,因此Docker会为它们分配一个随机名称,该名称在给定的Docker主机中保证是唯一的。除了名称外,具名卷和匿名卷的行为方式相同。
卷还支持使用卷驱动程序,允许您将数据存储在远程主机或云提供商等位置。

Volumes on the Docker host

绑定挂载

自 Docker 早期以来就可用。与卷相比,绑定挂载的功能有限。当您使用绑定挂载时,主机上的文件或目录被挂载到容器中。文件或目录通过其在主机上的完整路径引用。文件或目录不需要已经存在于 Docker 主机上。如果尚不存在,则会按需创建。绑定挂载具有非常高的性能,但它们依赖于主机机器的文件系统具备特定的目录结构。如果您正在开发新的 Docker 应用程序,请考虑使用命名卷。无法使用 Docker CLI 命令直接管理绑定挂载。

Bind mounts on the Docker host

还有tmpfs挂载

tmpfs挂载

tmpfs挂载不会持久保存在磁盘上,无论是在Docker主机还是容器内部。它可以在容器的生命周期中被容器使用,用于存储非持久状态或敏感信息。例如,swarm服务在服务的容器中使用tmpfs挂载来挂载密钥。
Docker主机上的tmpfs

Reference:
https://docs.docker.com/storage/


35
绑定挂载的备份更容易。遗憾的是,Docker没有提供任何用于备份卷的命令。您需要使用临时容器和绑定挂载来创建备份。 - Sebi2020
5
我正想问为什么备份卷更容易,但是@Sebi2020先问了。我认为修改回答是公平的。 - Bora M. Alper
2
我支持@Sebi2020的观点。当我在寻找备份卷的方法时,发现它们备份起来非常复杂(与我最初的直觉不同)。绑定挂载要容易得多。 - Dojo
1
我也是来寻找同样问题答案的。我认为卷挂载的一个主要缺点是,对于较旧版本的Docker,当我想要升级它时,必须先删除当前版本。这也会删除所有Docker数据(镜像、容器、卷)。我不确定在升级时是否会再次发生这种情况。我同意绑定挂载更容易备份。如果Docker文档能详细说明所谓的卷优于绑定挂载的优势,那就太好了。 - Tallandtree
就Windows Docker卷而言:在Windows Docker主机上,关于卷有很多不起作用的地方。因此,这也不是一个优势。 - Tallandtree
10
我觉得这个答案有很大的偏见,只讲述了卷的缺点,所以让我来补充一些信息。我猜想每个人都已经说过绑定挂载更容易备份了。除此之外,卷是使用网络协议栈,而绑定挂载是使用Linux内核,这使得卷要慢得多,特别是在执行原子操作时,比如fseek,在卷上要比绑定挂载慢800倍(经过基准测试)。此外,卷无法利用Linux文件缓存,而绑定挂载可以(虽然它们从技术上讲也可以,但是要经过网络协议栈的开销)。 - HubertNNN

66

那么使用-v /path/to/data/in/container:/home/user/a_good_place_to_have_data挂载容器中的文件夹是否与之前提到的不同呢?

这是因为,如“将主机目录作为数据卷挂载”文档中所述:

由于主机目录依赖于主机环境,因此不能在Dockerfile中挂载主机目录,因为镜像应该是可移植的。主机目录在所有潜在的主机上都不可用。

如果您有一些持久数据想要在容器之间共享,或者希望从非持久性容器中使用它,则最好创建一个命名的Data Volume容器,并从其中挂载数据。

你可以将两种方法结合使用:

 docker run --volumes-from dbdata -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata

在这里,我们启动了一个新的容器,并挂载了来自 dbdata 容器的卷。
然后,我们将本地主机目录挂载为/backup
最后,我们传递了一个命令,使用tardbdata 卷的内容备份到位于我们的/backup目录中的backup.tar 文件中。当命令完成并容器停止时,我们将得到我们的dbdata 卷的备份。


3
你能澄清一下Docker文档中所说的主机目录是“主机相关”的含义吗?文件权限是否意味着当您将容器和挂载目录移动到另一个主机时,在某些情况下很难复制?此外,命名卷和挂载目录之间有什么区别?我知道在多个容器之间共享持久数据是数据卷容器的一个用例。但是对于仅与单个容器相关的持久数据,我有点困惑 :-)。 - PermaFrost
4
"host-dependent" 的意思是,你不能在 Dockerfile 中写入从主机传递的卷路径,因为该 Dockerfile 可以在任何主机上构建,每个主机都具有其自己的特点:在一个主机上有效的路径可能在另一个主机上不可用。这就是为什么挂载主机文件夹是一种运行时(docker run)操作,而不是编译时(docker build)的操作。 - VonC
3
@PermaFrost,所谓的命名卷是独立于主机的,并且是持久的。即使只有一个容器,这意味着我可以在任何主机上导出和恢复命名卷。请参见https://madcoda.com/2016/03/docker-named-volume-explained/。 - VonC
1
@user1050619 逻辑卷管理器(LVM https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux))是特定于Linux内核而不是Docker的。因此,在那个问题的背景下,我无法澄清区别,因为答案与LVM没有直接关系:它适用于Docker卷,您可以拥有许多不同的卷驱动程序,其中一些与LVM无关。有些是相关的:https://medium.com/@kalahari/docker-data-volume-snapshots-and-encryption-with-lvm-and-luks-ce80e0555225 - VonC
1
如何在 docker-compose.yml 中使用相同的功能? --volume-from 在 docker-compose 中是否得到支持? - Tara Prasad Gurung
显示剩余9条评论

32

是的,从几个角度来看,这与其他情况有很大不同。正如你在问题标题中所写的那样,它关于理解为什么我们需要数据卷而不是绑定到主机上。

第一部分 - 基本场景和示例

让我们列举两个场景。

情况1:Web服务器
我们想要为我们的Web服务器提供一个配置文件,该文件可能会经常更改。
例如:根据当前环境暴露端口。
我们可以每次重新构建具有相关设置的映像,或者为每个环境创建2个不同的映像。这两种解决方案都不太有效。

通过绑定挂载点,Docker将给定源目录挂载到容器内部的位置。
(联合文件系统中只读层中的原始目录/文件将被覆盖)。

例如-将动态端口绑定到nginx:

version: "3.7"
services:
  web:
    image: nginx:alpine
    volumes:
     - type: bind #<-----Notice the type
       source: ./mysite.template
       target: /etc/nginx/conf.d/mysite.template
    ports:
     - "9090:8080"
    environment:
     - PORT=8080
    command: /bin/sh -c "envsubst < /etc/nginx/conf.d/mysite.template > 
        /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"

(*) 请注意,本例也可以使用卷来解决。

案例二:数据库
Docker 容器不会存储持久数据-任何写入容器联合文件系统可写层的数据都将在容器停止运行时丢失。

但如果我们在容器上运行数据库,当容器停止时,这意味着所有数据都将丢失?

来挽救。
这些是由 Docker 为我们管理的命名文件系统树。

例如-持续保存 Postgres SQL 数据:

services:    
  db:
    image: postgres:latest
    volumes:
      - "dbdata:/var/lib/postgresql/data"
    volumes:
     - type: volume #<-----Notice the type
       source: dbdata
       target: /var/lib/postgresql/data
volumes:
  dbdata:

请注意,在这种情况下,对于命名的卷,源是卷的名称(对于匿名卷,该字段被省略)。

第二部分 - 比较

主机上管理和隔离的差异

绑定挂载点存在于主机文件系统上并由主机维护者管理。 Docker之外的应用程序/进程也可以修改它。

也可以在主机上实现,但Docker会为我们管理它们,并且无法在Docker之外访问。

卷是一个更广泛的解决方案

虽然这两个解决方案都帮助我们将数据生命周期与容器分离, 但使用可以使您在系统上获得更多的功能和灵活性。

使用,我们可以通过将数据存储在专用远程位置(例如云中)并将其与备份,监视,加密和硬件管理等外部服务集成来有效地设计数据并将其与系统的其他部分解耦。


4
感谢您提供使用示例,特别是关于docker-compose ymls的。 - Robino
这两个是一样的吗? volumes: - "dbdata:/var/lib/postgresql/data" volumes: - type: volume #<-----注意类型 source: dbdata target: /var/lib/postgresql/data 来自你的1.2示例。 - Eugen Konkov

9
主机目录和数据卷的区别在于Docker通过将数据卷放置到$DOCKER-DATA-DIR/volumes目录并附加引用(名称或随机生成的ID)来管理后者。这样你就会得到一些方便。主机目录和数据卷都是主机上的目录,两者都依赖于主机。你无法在Dockerfile中引用它们中的任何一个;每次启动新容器时,VOLUME指令都会创建一个新的无名称(带有随机生成的ID)的卷,并且无法引用现有卷。* 这里的$DOCKER-DATA-DIR/var/lib/docker,除非你更改了默认值。

在我的情况下,它看起来像这样:overlay 38G 19G 18G 51% /var/lib/docker/overlay2/b94...c973/merged - Eugen Konkov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接