docker-compose 中卷挂载的工作原理
How volume mounting works in docker-compose
我有一个非常简单的 node js 应用程序,项目结构如下所示。
index.js
package.json
package-lock.json
Dockerfile
FROM node:12.18.2-alpine
WORKDIR /test-app
COPY package.json package-lock.json ./
RUN npm i
COPY . ./
EXPOSE 3000
ENTRYPOINT [ "node", "index.js" ]
docker-compose.yml
version: '3.2'
services:
test-app:
build: .
ports:
- "3000:3000"
volumes:
- .:/test-app
- "test_app_node_modules:/test-app/node_modules"
volumes:
test_app_node_modules:
driver: local
如果您查看 docker-compose.yml
文件中的 volumes
部分,首先我 bind mounting 我在主机上的当前目录到 test-app
目录容器。这意味着:
- 我当前目录中的任何文件或目录都将反映在容器目录中,对容器目录所做的任何更改也将反映回主机目录。
- 这意味着在 docker 构建期间安装在容器
test-app
目录中的 node_modules
也被覆盖。
volumes
部分的下一步是 named volumes。这意味着:
- 它应该将容器内
test-app/node_modules
的所有内容复制到 test_app_node_modules
卷。但是 test-app/node_modules
是空的,因为步骤 1 覆盖了它。
- 这意味着我们创建了一个空卷并将其安装到容器中。
如果是这样,它应该会导致缺少依赖项错误,但我的应用 运行 正常。我不确定我从哪里得到 node_modules。
此外,我在主机目录中看到一个空的 node_modules 文件夹。我假设这背后的原因是 "test_app_node_modules:/test-app/node_modules"
在容器中寻找 node_modules
但它不存在所以它创建了一个结果,它被反射回主机目录。
我无法理解批量安装的概念。这里的流量是多少?当有 none 时,node_modules 如何开始存储到卷中?
在您的 docker 文件中您首先创建了 WORKDIR /test-app
在其中您添加了一个 package.json
文件并安装了依赖项 RUN npm i
所以现在已经有 node_module
存在于 docker 图像本身中。
在使用 COPY . ./
之后,您将向 docker 图像添加额外的文件,例如 index
和其他所有文件。
如果您要删除整个 volume
部分,那么它也可以工作,因为您的 docker 图像包含代码及其依赖项。
version: '3.2'
services:
test-app:
build: .
ports:
- "3000:3000"
我不太确定您为什么要以这种方式设置 docker 容器,但它不起作用的原因是对卷和绑定安装的方向有误解工作。你说:
It should copy everything from test-app/node_modules inside container to test_app_node_modules volume. But the test-app/node_modules is empty because step 1 overwrote it.
这是从前到后。当您使用卷时,卷 被复制到目标。这就是卷的全部意义所在——它们旨在让您即使重建容器也能持久保存数据。如果您使用绑定安装的卷,则 the host directory is copied into the target in the docker container。因此,您在主机上的 test_app_node_modules
目录被复制到容器中的 /test-app/node_modules
中。据推测 test_app_node_modules
包含您所有的节点模块,因此您不会收到有关缺少模块的错误。
只有当您的容器实际上 运行 时,容器中的代码 运行 才能 update/delete 卷中的数据 - 而不是在您构建容器时。
在纯机械层面上,这里会发生一些事情。
Docker 对卷安装进行排序。它知道 /test-app/node_modules
是 /test-app
的子目录。所以Docker会
- 创建从主机目录到容器目录的绑定挂载
- 如果需要作为挂载点,请创建一个空的
node_modules
目录
- 在该安装点上安装 Docker 卷
这是主机上空 node_modules
目录的来源:Docker 将其创建为挂载点,完成绑定挂载后,因此那里的更改会反映在主机内容。
当命名卷挂载到容器中时,当且仅当命名卷为空时,Docker will copy the content from the image into the volume;这个特定的步骤忽略了容器中已经安装了一个卷。 (如果该卷是匿名卷,这也适用,如您在其他 Node 示例中看到的那样;它不适用于主机绑定挂载,如果旧内容在卷中或在 Kubernetes 上。)
这就是为什么这显然有效的原因。正如这个问题的其他答案一样,这并不是 Docker 的特别有效使用。如果您需要实时开发设置,我还建议只删除这些 volumes:
,并直接在主机上 运行 node index.js
。
我有一个非常简单的 node js 应用程序,项目结构如下所示。
index.js
package.json
package-lock.json
Dockerfile
FROM node:12.18.2-alpine
WORKDIR /test-app
COPY package.json package-lock.json ./
RUN npm i
COPY . ./
EXPOSE 3000
ENTRYPOINT [ "node", "index.js" ]
docker-compose.yml
version: '3.2'
services:
test-app:
build: .
ports:
- "3000:3000"
volumes:
- .:/test-app
- "test_app_node_modules:/test-app/node_modules"
volumes:
test_app_node_modules:
driver: local
如果您查看 docker-compose.yml
文件中的 volumes
部分,首先我 bind mounting 我在主机上的当前目录到 test-app
目录容器。这意味着:
- 我当前目录中的任何文件或目录都将反映在容器目录中,对容器目录所做的任何更改也将反映回主机目录。
- 这意味着在 docker 构建期间安装在容器
test-app
目录中的node_modules
也被覆盖。
volumes
部分的下一步是 named volumes。这意味着:
- 它应该将容器内
test-app/node_modules
的所有内容复制到test_app_node_modules
卷。但是test-app/node_modules
是空的,因为步骤 1 覆盖了它。 - 这意味着我们创建了一个空卷并将其安装到容器中。
如果是这样,它应该会导致缺少依赖项错误,但我的应用 运行 正常。我不确定我从哪里得到 node_modules。
此外,我在主机目录中看到一个空的 node_modules 文件夹。我假设这背后的原因是 "test_app_node_modules:/test-app/node_modules"
在容器中寻找 node_modules
但它不存在所以它创建了一个结果,它被反射回主机目录。
我无法理解批量安装的概念。这里的流量是多少?当有 none 时,node_modules 如何开始存储到卷中?
在您的 docker 文件中您首先创建了 WORKDIR /test-app
在其中您添加了一个 package.json
文件并安装了依赖项 RUN npm i
所以现在已经有 node_module
存在于 docker 图像本身中。
在使用 COPY . ./
之后,您将向 docker 图像添加额外的文件,例如 index
和其他所有文件。
如果您要删除整个 volume
部分,那么它也可以工作,因为您的 docker 图像包含代码及其依赖项。
version: '3.2'
services:
test-app:
build: .
ports:
- "3000:3000"
我不太确定您为什么要以这种方式设置 docker 容器,但它不起作用的原因是对卷和绑定安装的方向有误解工作。你说:
It should copy everything from test-app/node_modules inside container to test_app_node_modules volume. But the test-app/node_modules is empty because step 1 overwrote it.
这是从前到后。当您使用卷时,卷 被复制到目标。这就是卷的全部意义所在——它们旨在让您即使重建容器也能持久保存数据。如果您使用绑定安装的卷,则 the host directory is copied into the target in the docker container。因此,您在主机上的 test_app_node_modules
目录被复制到容器中的 /test-app/node_modules
中。据推测 test_app_node_modules
包含您所有的节点模块,因此您不会收到有关缺少模块的错误。
只有当您的容器实际上 运行 时,容器中的代码 运行 才能 update/delete 卷中的数据 - 而不是在您构建容器时。
在纯机械层面上,这里会发生一些事情。
Docker 对卷安装进行排序。它知道 /test-app/node_modules
是 /test-app
的子目录。所以Docker会
- 创建从主机目录到容器目录的绑定挂载
- 如果需要作为挂载点,请创建一个空的
node_modules
目录 - 在该安装点上安装 Docker 卷
这是主机上空 node_modules
目录的来源:Docker 将其创建为挂载点,完成绑定挂载后,因此那里的更改会反映在主机内容。
当命名卷挂载到容器中时,当且仅当命名卷为空时,Docker will copy the content from the image into the volume;这个特定的步骤忽略了容器中已经安装了一个卷。 (如果该卷是匿名卷,这也适用,如您在其他 Node 示例中看到的那样;它不适用于主机绑定挂载,如果旧内容在卷中或在 Kubernetes 上。)
这就是为什么这显然有效的原因。正如这个问题的其他答案一样,这并不是 Docker 的特别有效使用。如果您需要实时开发设置,我还建议只删除这些 volumes:
,并直接在主机上 运行 node index.js
。