欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

深入理解Docker:挂载绑定、匿名卷和命名卷详解

最编程 2024-08-14 22:55:04
...

容器是短暂的

记住容器是临时的、短暂的。每当容器完成执行命令时,它就完全“关闭”了。

=> docker run node ls

bin
boot
dev
etc
home
lib
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

它使用镜像node启动一个新容器 在容器内执行命令(列出文件和目录)。

该命令给出一个输出并完成。命令完成后,容器将关闭并且其所有数据都将丢失

执行 Javascript 程序

假设我们有一个包含非常基本的 Javascript 程序的文件:

helloWorld.js

greeting = (message) => {
  console.log(message)
}

greeting("Hello, world")

我们如何使用容器运行这个程序?可以尝试做类似的事情:

docker run node node helloWorld.js

得到错误:

Error: Cannot find module '/app/helloWorld.js'
...

那是因为容器是隔离的,不能共享同一个主机文件系统。

绑定挂载:在主机和容器之间同步数据

我们必须将文件helloWorld.js与容器同步。Docker 提供了一种挂载卷的方法,这意味着:

从Host 挂载一个目录或文件到 Container,这样在 Host 中所做的每一个更改都会被镜像到 Container,反之亦然。

您更改主机中的文件/目录,容器将看到更改。您更改容器中的文件/目录,主机将看到更改。

让我们与容器同步:

docker run 
  -v $(pwd)/docker-101/helloWorld.js:/app/helloWorld.js 
  node 
  node /app/helloWorld.js
Hello, world

解释:

  • pwd是指当前目录的完整路径名

  • -v {hostPath}:{containerPath} 将文件/目录挂载到容器

  • node是镜像

  • node /app/helloWorld.js使用容器内定义的path执行命令

一个真实的项目

假设我们有一个这样的项目目录:

/docker-101
  /src
    /components
    components.js
  index.js

从docker-101目录内部,执行node

docker run 
  -v $(pwd):/app
  node
  node /app/index.js

当前目录中的所有文件都将挂载到容器中的/app。

如果我们想进入容器并从那里操作文件怎么办?

docker run 
  -it
  -v $(pwd):/app
  node
  bash

.这里:

  • -it指示 Docker 保持容器终端(bash/shell)打开
  • bash在容器内打开一个新的 bash/shell

执行这个docker run命令,保持容器终端打开,在容器内部执行更多命令并创建新文件:

root@8dcbfa6d777c:/# cd /app

root@8dcbfa6d777c:/app# ls
index.js  src

root@8dcbfa6d777c:/app# touch new-file.js

root@8dcbfa6d777c:/app# ls
index.js  new-file.js src

root@8dcbfa6d777c:/app# exit
exit

现在,退出容器后,在主机执行ls,我们会发现,相应的新创建的文件都同步在host上了:

index.js    new-file.js src

这种类型的卷称为Path Volume

使用mount参数

另一种挂载卷的方法是使用选项--mount,它指定挂载类型、目标

docker run 
  --mount type=bind,source=$(pwd),target=/app
  node
  node /app/helloWorld.js

mount更明确,但在大多数情况下使用-v已经足够了。

命名卷与匿名卷

绑定加载是由程序员自己维护的,而数据卷是由 Docker 引擎维护的存储方式,使用 docker volume create 命令创建,可以利用卷驱动支持多种存储方案。其默认的驱动为 local,也就是本地卷驱动。本地驱动支持命名卷和匿名卷。

docker volume create my-volume

docker volume inspect my-volume
[    {        "CreatedAt": "2022-02-05T00:47:59Z",        "Driver": "local",        "Labels": {},        "Mountpoint": "/var/lib/docker/volumes/my-volume/_data",        "Name": "my-volume",        "Options": {},        "Scope": "local"    }]

Mountpoint指示的路径是主机中的确切路径。因此,每个使用此卷的容器都会将其数据直接同步到此挂载点。

而匿名卷就是没名字的卷,一般是 docker run -v /data 这种不指定卷名的时候所产生,或者 Dockerfile 里面的定义直接使用的。

如果容器启动时使用了 --rm 选项,容器停止时,容器被自动删除,匿名卷也会自动被删除。

反之,如果没有--rm选项,即使使用命名 docker container rm my_container 删除了容器,匿名卷不会被自动删除。每次创建新的容器,都会重新创建新的匿名卷。 重启容器则使用已有附加的匿名卷。

要删除匿名卷,使用命令 docker volume rm 或者 prune

因此,一般而言,匿名卷只存放无关紧要的临时数据,随着容器消亡,这些数据将失去存在的意义。

在 Dockerfile 中定义的挂载,是指匿名数据卷。无法在Dockerfile 内创建命名卷。

最佳实践

不应该在容器存储层内进行数据写入操作,所有写入应该使用卷。如果定制镜像的时候,就可以确定某些目录会发生频繁大量的读写操作,那么为了避免在运行时由于用户疏忽而忘记指定卷,导致容器发生存储层写入的问题,就可以在 Dockerfile 中使用 VOLUME 来指定某些目录为匿名卷。这样即使用户忘记了指定卷,也不会产生不良的后果。

比如,Dockerfile 中说 VOLUME /data,那么如果直接 docker run,其 /data 就会被挂载为匿名卷,向 /data 写入的操作不会写入到容器存储层,而是写入到了匿名卷中。但是如果运行时 docker run -v mydata:/data,这就覆盖了 /data 的挂载设置,要求将 /data 挂载到名为 mydata 的命名卷中。所以说 Dockerfile 中的 VOLUME 实际上是一层保险,确保镜像运行可以更好的遵循最佳实践,不向容器存储层内进行写入操作。