首先,有一个操作顺序。构建映像时,不会挂载卷,它们仅在您运行容器时才会挂载。因此,当您完成构建时,所有更改将仅存在于映像中,而不存在于任何卷中。如果您在目录上安装卷,它会覆盖该位置图像中的任何内容,从而隐藏这些内容(有一个初始化例外,见下文)。
接下来是卷语法:
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
告诉 docker-compose 从当前目录到/usr/src/app
容器内部创建一个主机卷,然后映射/usr/src/app/node_modules
到 docker 维护的匿名卷。后者将显示为一个卷中,docker volume ls
带有一个相对无用的长 uuid 字符串。
要映射/usr/src/app/node_modules
到主机上的文件夹,您需要在前面包含文件夹名称和冒号,就像上面一行一样。例如/host/dir/node_modules:/usr/src/app/node_modules
。
命名卷与主机卷有点不同,因为 docker 使用您可以在docker volume ls
. 您仅使用名称而不是路径来引用这些卷。因此node_modules:/usr/src/app/node_modules
将创建一个名为的卷node_modules
,您可以将其安装在仅具有该名称的容器中。
我分开来描述命名卷,因为它们带有一个功能,它变成了带有主机卷的陷阱。Docker 通过使用该位置的映像内容初始化命名卷来帮助您处理命名卷。所以在上面的例子中,如果命名卷node_modules
是空的(或新的),它会首先将 /usr/src/app/node_modules 中的镜像内容复制到这个卷,然后将它挂载到你的容器中。
使用主机卷,您将永远不会看到任何初始化,无论该位置的任何内容,甚至是空目录,都是您在容器中看到的所有内容。无法从该目录位置的映像中获取内容以首先复制到该位置的主机卷。这也意味着容器内所需的目录权限不会自动继承,您需要手动设置在容器内工作的主机目录的权限。
最后,还有一个适用于 windows 和 mac 的 docker 的小问题,它们在 VM 内运行,并且您的主机卷已安装到 VM。要将卷挂载到主机上,您必须配置应用程序以将主机中的文件夹共享给 VM,然后将 VM 中的卷挂载到容器中。默认情况下,在 Mac 上,包含 /Users 文件夹,但如果您使用其他目录,例如 /Projects 目录,甚至是小写的 /users(unix 和 bsd 区分大小写),您将看不到来自容器内的 Mac。
有了这些基础知识,一种可能的解决方案是重新设计您的工作流程,以将目录内容从复制到主机的图像中获取。首先,您需要将文件复制到图像中的不同位置。然后,您需要在容器启动时将文件从该保存的映像位置复制到卷安装位置。当您执行后者时,您应该注意您违背了拥有卷(持久性)的目的,并且可能需要考虑添加一些逻辑以便在运行副本时更具选择性。首先,将 entrypoint.sh 添加到您的构建中,如下所示:
#!/bin/sh
# copy from the image backup location to the volume mount
cp -a /usr/src/app_backup/node_modules/* /usr/src/app/node_modules/
# this next line runs the docker command
exec "$@"
然后更新 Dockerfile 以包含入口点和备份命令:
FROM node:6.3
# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Install app dependencies
COPY package.json /usr/src/app/
RUN npm install -g babel babel-runtime babel-register mocha nodemon
RUN npm install
# Bundle app source
COPY . /usr/src/app
RUN cp -a /usr/src/app/. /usr/src/app_backup
EXPOSE 1234
ENTRYPOINT [ "/usr/src/app/entrypoint.sh" ]
CMD [ "npm", "start" ]
然后从 docker-compose.yml 中删除额外的卷:
volumes:
- .:/usr/src/app