【问题标题】:How volume mounting works in docker-compose卷挂载在 docker-compose 中的工作原理
【发布时间】:2020-11-12 18:26:06
【问题描述】:

我有一个非常简单的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 目录。这意味着:

  1. 我当前目录中的任何文件或目录都将反映在容器目录中,对容器目录所做的任何更改也将反映回主机目录。
  2. 这意味着在 docker 构建期间安装在容器的 test-app 目录中的 node_modules 也被覆盖了。

volumes 部分的下一步是named volumes。这意味着:

  1. 它应该将容器内test-app/node_modules 中的所有内容复制到test_app_node_modules 卷。但是test-app/node_modules 是空的,因为第 1 步覆盖了它。
  2. 这意味着我们创建了一个空卷并将其安装到容器中。

如果是这样,它应该会导致缺少依赖项错误,但我的应用程序运行正常。我不确定我从哪里得到 node_modules。

另外,我在主机目录中看到一个空的 node_modules 文件夹。我认为这背后的原因是"test_app_node_modules:/test-app/node_modules" 在容器中查找node_modules,但它不存在,因此它创建了一个,因此它被反射回主机目录。

我无法理解卷安装的概念。这里的流程是什么?没有 node_modules 时如何开始将其存储到卷中?

【问题讨论】:

    标签: node.js docker docker-compose docker-volume


    【解决方案1】:

    在您的 docker 文件中,您首先在其中创建了 WORKDIR /test-app,您添加了一个 package.json 文件并安装了依赖项 RUN npm i,所以现在 docker 映像本身中已经存在 node_module

    在使用 COPY . ./ 之后,您将向 docker 映像添加额外的文件,如 index 和其他所有文件。

    如果您将删除整个 volume 部分,那么它也将工作,因为您的 docker 映像包含代码及其依赖项。

    version: '3.2'
    
    services:
      test-app:
        build: .
        ports:
          - "3000:3000"
    

    【讨论】:

    • 但我绑定安装当前目录,它会覆盖容器的test-app 目录中的所有内容。不,我的应用已经在运行了。
    • 我认为这将帮助您更好地理解立场:medium.com/devops-dudes/…
    • 谢谢,但这并不能真正回答我的问题。
    【解决方案2】:

    我不太确定您为什么要以这种方式设置 docker 容器,但它不起作用的原因是对卷和绑定挂载的工作方向的误解。你说:

    它应该将容器内的 test-app/node_modules 中的所有内容复制到 test_app_node_modules 卷。但是 test-app/node_modules 是空的,因为第 1 步覆盖了它。

    这是从前到后。当您使用卷时,volume 会被复制到 target。这就是卷的全部意义——它们旨在让您即使重建容器也能保留数据。如果您使用绑定安装的卷,那么the host directory is copied into the target in the docker container。所以你在主机上的test_app_node_modules 目录被复制到容器中的/test-app/node_modules 中。大概test_app_node_modules 包含您所有的节点模块,因此您不会收到有关缺少模块的错误。

    只有在您的容器实际运行时,容器中运行的代码才能更新/删除卷中的数据 - 而不是在您构建容器时。

    【讨论】:

    • 我的代码运行良好。只是我对它的工作方式有些困惑。是的,卷被复制到目标中,但是当您第一次启动容器并且没有卷时,容器中的数据将被复制到卷中。这就是我想说的。 test_app_node_modules 不是目录而是卷名。问题是这个test_app_node_modules 卷是如何包含所有节点模块的?
    【解决方案3】:

    在纯粹的机械层面上,这里发生了几件事。

    Docker 对卷挂载进行排序。它知道/test-app/node_modules/test-app 的子目录。所以Docker会

    1. 创建从宿主目录到容器目录的绑定挂载
    2. 如果需要作为挂载点,请创建一个空的 node_modules 目录
    3. 在该挂载点上挂载 Docker 卷

    这是主机上空的node_modules 目录的来源:Docker 在完成绑定挂载后将其创建为挂载点,因此那里的更改会反映在主机内容中。

    当命名卷挂载到容器中时,当且仅当命名卷为空时,Docker will copy the content from the image into the volume;这个特定步骤忽略了容器中已经安装了一个卷。 (如果卷是匿名卷,这也适用,您可以在其他 Node 示例中看到;它不适用于主机绑定挂载,如果旧内容在卷中或在 Kubernetes 上。)

    这就是为什么这显然有效。正如这个问题的其他答案,这并不是 Docker 的一种特别有效的使用方式。如果您需要实时开发设置,我还建议您删除这些 volumes:,并直接在主机上运行 node index.js

    【讨论】:

    • 是“docker会将内容从镜像复制到卷中”还是“从容器中复制到卷中”?
    • 这绝对是“来自图像”,即使链接的文档到处都说“容器”。
    • 所以 docker 会首先查看容器,如果我们尝试挂载的数据在那里,它将从中复制,否则它将查看图像并从那里复制?
    • 如果您尝试挂载一个空的命名卷,它会将数据从映像中的挂载点复制到卷中。然后它将卷挂载到容器中,隐藏容器文件系统中之前的所有内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-05
    • 1970-01-01
    • 1970-01-01
    • 2017-06-06
    • 2019-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多