Docker 匿名卷、命名卷与绑定挂载

1 容器删除,容器内数据丢失

如果仅仅将数据存储在容器中,那么就会遇到一个问题:一旦容器被删除,所存储的数据将全部丢失。

2 卷 (由 Docker 管理)

容器具有独立的文件系统,一般情况下与本机没有任何关联。但是要实现数据的持久保存,就需要用到卷.
Docker 使用卷实现容器数据的持久化保存。

Volumes are folders on your host machine hard drive, which are mounted(“made available”, mapped) into containers. 卷是主机文件夹,被映射到容器中。

Docker 匿名卷、命名卷与绑定挂载_第1张图片
这两个文件夹中的任意一个发生变化,都会反映到另一个文件夹。容器既可以写入数据到卷,也可以从卷中读出数据。

卷由 Docker 管理,分为匿名卷和命名卷。

2.1 匿名卷 Anonymous Volumes

在 Dockerfile 中,匿名卷的指令格式: VOLUME ["folder_name"]folder_name 为容器内的文件夹( / 路径)。
以下是一个 node app 的 Dockerfile:

FROM node

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .  

EXPOSE 80

VOLUME ["/app/feedback"]

CMD [ "node", "server.js" ]

匿名卷被 docker 隐藏存放在本地的未知路径,由docker管理。

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

要删除匿名卷,使用命令 docker volume rm 或者 prune
除了可以在Dockerfile 中指定创建匿名卷意外,也可以在创建容器时指定匿名卷。

2.2 命名卷 Named Volumes

与匿名卷相反,如果容器被删除,命名卷不会被自动删除。 同匿名卷,被 docker 隐藏存放在本地的未知路径,不易寻找,不应访问或者编辑命名卷。
命名卷相反不会被附加到具体容器上,即使容器被删除,命名卷也会保留。

不能在 Dockerfile 内创建命名卷,只能在运行容器时指定,使用-v 选项,v 即 volume,语法格式:
-v 任意名称:容器内路径
例如:
docker container run -d -p 3000:80 --rm --name feedback-app -v feedback:/app/feedback feedback-node:volumes

2.3 与卷相关的命令

docker volume --help 列举与卷相关的命令:

Commands:
  create      Create a volume
  inspect     Display detailed information on one or more volumes
  ls          List volumes
  prune       Remove all unused local volumes
  rm          Remove one or more volumes

docker volume ls 列出所有卷

3 绑定挂载 (由开发者管理,可用于代码调试)

绑定挂载也是映射本地文件夹到运行的容器,与卷不同的是,可以直接修改本地文件夹,修改直接反映到容器。应用可以是,将app 的源代码文件夹进行绑定挂载,编辑源代码,容器中的 image 自动被修改。
从而避免 1. 修改代码 -> 2. stop 容器 -> 3. 重新 build image ->4. 重新启动容器 这一重复的开发过程。

Docker 匿名卷、命名卷与绑定挂载_第2张图片
绑定挂载同样在运行容器时指定,同命名卷,不能在 Dockerfile 中指定。命令格式:

-v 绝对路径:容器内文件夹

对于如下的 Dockerfile,源代码被copy到 WORKDIR /app

FROM node

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

EXPOSE 80

CMD [ "node", "server.js" ]

因此冒号右边是 /app,对于 windows 系统,命令如下:

docker container run -p 3000:80 --name feedback-app --rm -v feedback:/app/feedback -v ${PWD}:/app feedback-node:volumes

也可以不用 ${PWD}而是 copy 整个路径,例如 vs code explorer 里,右键"copy path",

-v D:\temp\data-volumes-01-starting-setup:/app
使用 -v "D:\temp\data-volumes-01-starting-setup:/app" 即加双引号更好,以防止特殊字符使命令不起作用。

如果本地文件夹没有 node_modules, 此命令将无法启动容器,原因是,本地文件夹没有 node_modules,绑定挂载使得本地的文件完全覆盖了 /app 里的文件,app 缺少依赖包,无法运行。
解决方法是,使用匿名卷。
使用绑定挂载,本地文件夹将覆盖容器文件夹,而不是反过来,毕竟,容器删除或修改本地源代码是不能容忍的。
为了防止容器内的 node_modules 因为本地没有而被删除,使用匿名卷,上述命令修改如下:

docker container run -d -p 3000:80 --name feedback-app --rm -v feedback:/app/feedback -v ${PWD}:/app -v /app/node_modules feedback-node:volumes

此命令中使用了:

  1. 命名卷:容器删除后保存数据, -v feedback:/app/feedback
  2. 绑定挂载:代码调试, -v ${PWD}:/app,还需要步骤3的匿名卷
  3. 因为步骤2,使用绑定挂载,本地文件夹覆盖了容器内的文件夹,容器内新生成的node_modules被删除,因此增加匿名卷 -v /app/node_modules

当卷存在冲突时,docker指定路径越长越具体优先级越高,/app/node_modules路径比/app长,因此,匿名卷的由 npm install 生成的文件夹 node_modules 将覆盖绑定挂载的空的 app/node_modules

将绑定挂载设定为容器只能读取,无法写入,在路径后加 :ro,同时为了保证源代码中的 temp 文件夹允许容器写入,增加匿名卷 -v /app/temp

docker container run -d -p 3000:80 --rm --name feedback-app -v feedback:/app/feedback -v ${PWD}:/app:ro -v /app/temp -v /app/node_modules feedback-node:volumes

容器正在运行,此时VS code 修改代码,例如 MySite 改成,MySite!!!!!,保存,然后浏览器页面刷新,就可以看到对应的界面已经被修改:
Docker 匿名卷、命名卷与绑定挂载_第3张图片

4 比较

Docker 匿名卷、命名卷与绑定挂载_第4张图片

匿名卷,不管是在 Dockerfile 中指定,还是在命令行指定,可以用于锁定容器中已经存在的某些数据。

你可能感兴趣的:(docker)