为啥我的 docker-compose 卷没有使用本地文件添加进行更新?

Posted

技术标签:

【中文标题】为啥我的 docker-compose 卷没有使用本地文件添加进行更新?【英文标题】:Why doesn't my docker-compose volume get updated with local file additions?为什么我的 docker-compose 卷没有使用本地文件添加进行更新? 【发布时间】:2019-10-18 04:07:36 【问题描述】:

这是我的docker-compose.yml,它包含一个网络服务,它在本地安装一个文件夹,以便在任何代码更改时watchmedo 重新启动webapp django 服务器。这是一个带有 nginx 的反向代理。

version: '3'
services:
  webapp:
    build:
      context: .
      dockerfile: ./web/Dockerfile
    volumes:
      - ./web/:/usr/src/app/
      - staticfile_volume:/usr/src/app/public
    entrypoint: >
      watchmedo auto-restart --recursive 
        --pattern="*.py" --directory="." 
          gunicorn myapp.wsgi:application -- --bind 0.0.0.0:9000
  nginx:
    build:
      context: .
      dockerfile: ./nginx/Dockerfile
    depends_on:
      - webapp
    volumes:
      - staticfile_volume:/usr/src/app/public
volumes:
  staticfile_volume:

我的本​​地文件设置如下:

$ tree -L 2
.
├── docker-compose.yml
├── nginx
│   ├── Dockerfile
│   └── nginx.conf
└── web
   ├── Dockerfile
   ├── manage.py
   ├── myapp
   ├── public
   └── static

但是当我在web/public(作为webappnginx 服务之间的共享卷安装的同一文件夹)中创建一个新文件时,我在运行的webapp 容器中看不到它。

然而,如果我在 web/ 文件夹(也作为单独的卷安装)中的其他任何位置创建一个新文件,我会从正在运行的 webapp 容器中看到更改。

这是什么原因造成的?我该如何改变这种行为?

(我需要能够从正在运行的容器中运行python manage.py collectstatic,但输出到我的本地硬盘驱动器的web/public,这样我就可以构建生产Docker 映像以进行部署。)

【问题讨论】:

这个问题有点令人困惑,就像克里斯的回答一样,我也可以看到它在工作 - 只需查看问题的最后一段,请记住您的 public 文件夹不再同步与您的主机,因为您将它安装到一个卷。我相信您正在寻找的只是一个简单的文件夹挂载(webapp 容器中的整个应用程序,并且仅在 nginx 容器中公开) 啊,是的 - 如果您在 web/public 内的主机上创建文件,它不会出现在任何一个容器卷中。这是最有可能的情况,很好的发现。随时在下面发布答案:-) 【参考方案1】:

我看不出你这里有什么问题。我复制了您的 docker-compose.yml 并仅删除了其中的 entrypoint: 部分,它对我有用。

docker-compose.yml

version: '3'
services:
  webapp:
    build:
      context: .
      dockerfile: ./web/Dockerfile
    volumes:
      - ./web/:/usr/src/app/
      - staticfile_volume:/usr/src/app/public
  nginx:
    build:
      context: .
      dockerfile: ./nginx/Dockerfile
    depends_on:
      - webapp
    volumes:
      - staticfile_volume:/usr/src/app/public
volumes:
  staticfile_volume:

网络/Dockerfile

FROM ubuntu:18.04

CMD tail -f /dev/null

nginx/Dockerfile

FROM nginx:latest

CMD tail -f /dev/null

演示:

要详细说明上面 DannyB 的评论,请确保您没有在web/public 中创建文件在主机上,因为该文件夹会在容器启动时被挂载。 docker 卷的工作方式与标准 linux 挂载类似 - docker 只会将新卷挂载到现有目录的顶部。

如果您想在 Docker 容器中运行命令来更改主机文件系统上的文件,请不要使用 docker 卷 - 而是使用 bind mount,就像您在此处所做的那样:- ./web/:/usr/src/app/

绑定挂载docker卷之间的区别在于绑定挂载会将文件从您的主机挂载到您的容器中,并且将依赖于这些文件夹/文件位于您的主机文件系统上,并且 docker 卷将完全由 docker 管理,并且只能在容器之间共享,而不能与主机共享(尽管这些文件确实存在于主机上的某个地方,但跟踪它们并尝试跟踪它们是不切实际的使用它们)。

您实际上可以从 docker-compose 文件中删除 docker 卷 并为 nginx 添加绑定挂载,然后您将开始看到您所追求的行为:

docker-compose.yml

version: '3'
services:
  webapp:
    build:
      context: .
      dockerfile: ./web/Dockerfile
    volumes:
      - ./web/:/usr/src/app/
  nginx:
    build:
      context: .
      dockerfile: ./nginx/Dockerfile
    depends_on:
      - webapp
    volumes:
      - ./web/public:/usr/src/app/public

演示:

【讨论】:

好点。正如建议的那样,我能够让它工作删除 docker 卷并有两个绑定挂载。然后在生产中为 nginx 单独的 Dockerfile 只需在构建时将web/public 复制到 nginx 映像中。这很棒!唯一的缺点似乎是将静态文件的 prod 实例上的空间加倍(没什么大不了的)。我想如果我想通过 nginx(媒体)进行文件上传和服务,我需要在 prod 实例和挂载上创建一个 docker 卷,这仍然可以。我的想法在这里正确吗? 我强烈考虑尝试以这样一种方式来构建生产,将服务用户上传到其他地方。您在这里描述的是一个有状态的实例,它限制了您可以做的事情 - 您已经看到您需要关心容器中的数据,这有点令人头疼。如果容器被更换会发生什么?如果您需要扩展并拥有多个 nginx 实例,您会怎么做?话虽如此,如果无法卸载用户上传,那么在您的主机上绑定挂载可能是最佳选择。 如果你必须使用绑定挂载,我还会考虑在容器运行时运行 collectstatic,而不是在 docker build 中运行。这意味着您的 nginx 容器可以保持原样,只需挂载一个目录来提供静态/媒体文件,而您的 Web 容器可以挂载相同的目录并将其用作collectstatic 和用户上传的输出目录。但是,如果您的生产主机因任何原因消失,您仍然会遇到问题。如果您使用的是 AWS,您可以将静态 + 媒体卸载到 S3,并完全不需要 nginx。【参考方案2】:

您告诉 Docker,staticfile_volume 包含必须在容器运行期间保留的关键应用程序数据。容器第一次启动时,并且仅是第一次,Docker 将从映像中填充它。如果稍后更新镜像,由于卷包含关键的应用程序数据,Docker 不会更改它。

最简单的短期解决方法是删除该卷。试试docker-compose down -v; docker-compose up --build。每当您更改静态内容时,您都需要这样做。

从长远来看,将后端应用程序配置为提供自己的文件会更容易一些。 Django 有一个django.contrib.staticfiles 模块来执行此操作。然后你的 nginx 代理就可以无条件地重定向到后端容器了,你就不用担心文件共享问题了。

(为了更好地看到这一点,请使用@ChrisMcKinnel 的复制配方并运行一次。然后将web/Dockerfile 从那里和COPY 文件放入/usr/src/app/public 并重新运行docker-compose up --build。你不会看到除非您docker-compose down -v,否则该文件会出现。)

【讨论】:

啊,我明白了。所以换句话说,docker 卷优先于/usr/src/app/public 的绑定挂载?这仅仅是因为 docker-compose.yml 文件本身的volumes 列表中的排序吗? Docker 按顺序对挂载进行排序,因此/usr/src/app/public 上的任何挂载将始终隐藏/usr/src/app 上任何其他挂载的内容。 这很聪明。您可以扩展静态文件的含义吗?现在在我的 nginx.conf 中,我有一个 context+directive location /static/ alias /usr/src/app/public/; ,对于我在模板中指定为 % static 'img/test.png' % 的所有文件,应该由 nginx 直接提供服务,无需 django 进行任何提升。这不是提供静态文件的“正确”方式吗?也许我没有正确理解反向代理。 为此使用 nginx 有一些小的技术优势,但是您在这个问题中显示的各种卷安装确实与您在生产环境中所做的完全不匹配,所以只对所有东西(包括静态文件)使用 Django 开发服务器不会有很大的不同。 哦。我会在生产环境中做什么?

以上是关于为啥我的 docker-compose 卷没有使用本地文件添加进行更新?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 docker-compose.yml 和 ecs-cli 将 EBS 卷附加到我的容器

转换为docker-compose后,Docker卷不会保留数据

为啥 docker-compose build 没有给我这样的文件或目录错误消息

升级镜像时 Docker-compose 卷重置

使用 docker-compose 时如何在容器内使用主机用户修改卷文件

跨多个 docker-compose 项目的共享卷 [重复]