Docker - 无法在卷内共享容器之间的数据(docker-compose 3)

13

现在我有一些容器用于 web 应用程序 (包括 nginx、gunicorn、postgres 和 node 用于从源文件构建静态文件和 React 服务器端渲染)。在一个用于 node 容器的 Dockerfile 中,我有两个步骤:构建和运行 (Dockerfile.node)。它最终会在容器中生成两个目录:bundle_client - 是 nginx 的静态文件,bundle_server - 在 node 容器内部用于启动 express 服务器。

然后,我需要将已构建的静态文件夹 (bundle_client) 与 nginx 容器共享。根据 docker-compose 参考,在我的 docker-compose.yml 文件中,我有以下服务(完整内容请参见docker-compose.yml):

node:
  volumes:
    - vnode:/usr/src

nginx:
  volumes:
    - vnode:/var/www/tg/static
  depends_on:
    - node

并且卷:

volumes:
  vnode:

运行docker-compose build没有错误。 运行docker-compose up一切正常,我可以打开localhost:80,这里有nginx,gunicorn和node express SSR全部工作良好,我可以看到一个网页,但是所有静态文件都返回404未找到错误。

如果使用docker volume ls检查卷,我可以看到两个新创建的卷,分别命名为tg_vnode(在此处考虑)和tg_vdata(请参见完整的docker-compose.yml)

如果我使用docker run -ti -v /tmp:/tmp tg_node /bin/bash进入nginx容器,我无法看到映射我的静态文件的www/tg/static文件夹。 此外,我尝试使用nginx容器Dockerfile.nginx创建一个空的/var/www/tg/static文件夹,但它保持为空。

如果我将主机机器上的bundle_client文件夹映射到docker-compose.yml中的nginx.volumes部分中,如- ./client/bundle_client:/var/www/tg/static,则它可以正常工作,并且我可以在浏览器中看到所有已提供给nginx的静态文件。

我做错了什么以及如何使我的容器与nginx容器共享构建的静态内容?

PS:我阅读了所有文档,所有GitHub问题和stackoverflow Q&As,并且我理解它应该起作用,没有信息可以告诉我它不起作用时该怎么办。

UPD:docker volume inspect vnode的结果:

[
{
    "CreatedAt": "2018-05-18T12:24:38Z",
    "Driver": "local",
    "Labels": {
        "com.docker.compose.project": "tg",
        "com.docker.compose.version": "1.21.1",
        "com.docker.compose.volume": "vnode"
    },
    "Mountpoint": "/var/lib/docker/volumes/tg_vnode/_data",
    "Name": "tg_vnode",
    "Options": null,
    "Scope": "local"
}
]

文件: Dockerfile.node, docker-compose.yml

Nginx dockerfile: Dockerfile.nginx


更新:我创建了一个简化的存储库以重现问题:repo (在npm install上有一些警告,请不要担心它可以正常安装和构建)。最终当我们打开localhost:80时,我们会看到一个空白页面和Chrome开发工具中静态文件(vendor.jsapp.js)的404信息,但是应该有一个由React脚本生成的消息React app: static loaded


2
使用相同的容器卷跨多个服务的问题在于,首先加载的容器将复制其自己的数据到卷中,其余容器都会看到相同的数据。如果要避免这种情况,您需要使用nocopy 选项。我在您的compose文件中看到您已经在多个地方使用了它,只有一个地方会将其内容放入卷中并获胜。如果构建发生在挂载之后,则会在运行时放置文件。很难想象您的compose文件,但请看看这些提示是否有所帮助。 - Tarun Lalwani
请问您能否添加您的Dockerfile.nginx文件?我正在尝试重现您的错误。我有些线索,但我需要检查nginx容器。 - Alejandro Galera
@AlexGalera 当然,我已经更新了一个问题。 - Max
是的,因为很难对这么长的东西进行评论。 - Tarun Lalwani
请问您能否给这个问题添加 docker-volume 标签呢?非常感谢。 - Alejandro Galera
显示剩余11条评论
2个回答

12

您需要进行两个更改。在您的节点服务中添加卷,例如:

volumes:
  - vnode:/usr/src/bundle_client

由于您想共享/usr/src/bundle_client,所以不应使用/usr/src/,因为这将共享整个文件夹和其结构。

然后在您的nginx服务中添加卷:

volumes:
  - type: volume
    source: vnode
    target: /var/www/test/static
    volume:
      nocopy: true
nocopy: true的作用是明确声明在容器的初始映射中不应该复制映射文件夹的内容。默认情况下,第一个映射到卷的容器将获得映射文件夹的内容。在您的情况下,您希望这是node容器。在测试之前,请确保运行以下命令以杀死缓存的卷:
docker-compose down -v

在我的测试中,您可以看到容器中有这些文件:

Nginx has files


3

逐步解释发生的事情

Dockerfile.node

此文件是Docker容器的构建文件,其中包含了构建一个node.js应用程序所需的指令。通过使用该文件,可以轻松地创建一个包含node.js环境的容器来运行应用程序。
    ...
    COPY ./client /usr/src
    ...

docker-compose.yml

services:
  ...
  node:
    ...
    volumes:
      - ./server/nginx.conf:/etc/nginx/nginx.conf:ro
      - vnode:/usr/src
  ...
volumes:
  vnode:
  1. docker-compose up 使用 Dockerfile.node 和 docker-compose 部分创建一个命名卷,其中数据保存在 /usr/src 中。

Dockerfile.nginx

FROM nginx:latest

COPY ./server/nginx.conf /etc/nginx/nginx.conf

RUN mkdir -p /var/www/tg/static

EXPOSE 80
EXPOSE 443

CMD ["nginx", "-g", "daemon off;"]
  1. 这将导致使用docker-compose创建的nginx容器具有空的/var/www/tg/static/目录。

docker-compose.yml

 ...
 nginx:
    build:
      context: .
      dockerfile: ./Dockerfile.nginx
    container_name: tg_nginx
    restart: always
    volumes:
      - ./server/nginx.conf:/etc/nginx/nginx.conf:ro
      - vnode:/var/www/tg/static
    ports:
      - "80:80"
      - "443:443"
    depends_on:
      - node
      - gunicorn
    networks:
      - nw_web_tg

 volumes:
   vdata:
   vnode:
  1. docker-compose up将创建名为vnode的命名卷,并将数据从/var/www/tg/static(现在为空)填充到现有的vnode。

因此,此时, - nginx容器中的/var/www/tg/static为空,因为它是空的(请参见Dockerfile.nginx中的mkdir) - node容器具有客户端文件的/usr/src目录(请参见Dockerfile.node中的复制操作) - vnode具有来自node的/usr/src和来自nginx的/var/www/tg/static的内容。

Definitively, to pass data from /usr/src from your node container to /var/www/tg/static in nginx container you need to do something that is not very pretty because Docker hasn't developed another way yet: You need to combine named volume in source folder with bind volume in destination:

 nginx:
     build:
       context: .
       dockerfile: ./Dockerfile.nginx
     container_name: tg_nginx
     restart: always
     volumes:
       - ./server/nginx.conf:/etc/nginx/nginx.conf:ro
       - /var/lib/docker/volumes/vnode/_data:/var/www/tg/static
     ports:
       - "80:80"
       - "443:443"
     depends_on:
       - node
       - gunicorn
     networks:
       - nw_web_tg

只需在docker-compose文件中将 - vnode:/var/www/tg/static 修改为 - /var/lib/docker/volumes/vnode/_data:/var/www/tg/static


明确地使用 RUN rm -rf /var/www/tg/static 命令并不能起到移除文件的作用。 - Max
好的,我误解了。请讲解一下,我们继续。我不太明白你想做什么。也许你对命名卷有所误解,希望我能稍微澄清一下。 - Alejandro Galera
我会在这里简要解释: - Max
我有一个节点容器,当运行 docker-compose up 时,应该运行 npm install && npm build 并创建一个包含编译静态文件的构建文件夹,并将其放在某个地方以便传递给 nginx 容器(据我所知,命名卷是唯一的方法)。接下来,我理解当我首次提到卷(这里是节点部分),它将填充给定数据,然后(在 nginx - 第二次提到)将使用此数据。我通过 depends_on.node 确保节点排在第一位,nginx 排在第二位。我错过了什么? - Max
你对命名卷的理解是正确的。 npm install 在哪里创建这些信息?在 node 容器中的 /usr/src 目录下吗? 我认为你可能忽略了一个问题,就是不应该在 nginx 容器中创建另一个命名卷,因为 nginx 是从 node 获取数据的目标,而不是数据源(因为它是空的)。 所以,你需要从命名卷创建一个绑定到 nginx 目录。 - Alejandro Galera
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接