如果仅仅将数据存储在容器中,那么就会遇到一个问题:一旦容器被删除,所存储的数据将全部丢失。
容器具有独立的文件系统,一般情况下与本机没有任何关联。但是要实现数据的持久保存,就需要用到卷.
Docker 使用卷实现容器数据的持久化保存。
Volumes are folders on your host machine hard drive, which are mounted(“made available”, mapped) into containers. 卷是主机文件夹,被映射到容器中。
这两个文件夹中的任意一个发生变化,都会反映到另一个文件夹。容器既可以写入数据到卷,也可以从卷中读出数据。
卷由 Docker 管理,分为匿名卷和命名卷。
在 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 中指定创建匿名卷意外,也可以在创建容器时指定匿名卷。
与匿名卷相反,如果容器被删除,命名卷不会被自动删除。 同匿名卷,被 docker 隐藏存放在本地的未知路径,不易寻找,不应访问或者编辑命名卷。
命名卷相反不会被附加到具体容器上,即使容器被删除,命名卷也会保留。
不能在 Dockerfile 内创建命名卷,只能在运行容器时指定,使用-v
选项,v
即 volume,语法格式:
-v 任意名称:容器内路径
,
例如:
docker container run -d -p 3000:80 --rm --name feedback-app -v feedback:/app/feedback feedback-node:volumes
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
列出所有卷
绑定挂载也是映射本地文件夹到运行的容器,与卷不同的是,可以直接修改本地文件夹,修改直接反映到容器。应用可以是,将app 的源代码文件夹进行绑定挂载,编辑源代码,容器中的 image 自动被修改。
从而避免 1. 修改代码 -> 2. stop 容器 -> 3. 重新 build image ->4. 重新启动容器 这一重复的开发过程。
绑定挂载同样在运行容器时指定,同命名卷,不能在 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
此命令中使用了:
-v feedback:/app/feedback
-v ${PWD}:/app
,还需要步骤3的匿名卷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!!!!!
,保存,然后浏览器页面刷新,就可以看到对应的界面已经被修改:
匿名卷,不管是在 Dockerfile 中指定,还是在命令行指定,可以用于锁定容器中已经存在的某些数据。