【问题标题】:How to mount a single file in a volume如何在卷中挂载单个文件
【发布时间】:2017-07-04 01:03:38
【问题描述】:

我正在尝试 dockerize 一个 PHP 应用程序。在 dockerfile 中,我下载存档、解压缩等。

一切正常。但是,如果发布了新版本并更新了 dockerfile,我必须重新安装应用程序,因为 config.php 会被覆盖。

所以我想我可以像处理数据库一样将文件挂载为卷。

我尝试了两种方式,卷和直接路径。

码头工人撰写:

version: '2'
services:
  app:
    build: src
    ports:
      - "8080:80"
    depends_on:
      - mysql
    volumes:
      -  app-conf:/var/www/html/upload
      -  app-conf:/var/www/html/config.php
    environment:
      DB_TYPE: mysql
      DB_MANAGER: MysqlManager

  mysql:
    image: mysql:5.6
    container_name: mysql
    volumes:
      - mysqldata:/var/lib/mysql
    ports:
      - 3306:3306
    environment:
      MYSQL_ROOT_PASSWORD:
      MYSQL_DATABASE:
      MYSQL_USER:
      MYSQL_PASSWORD:

volumes:
  mysqldata:
  app-conf:

这会导致错误:

我用给定的路径尝试了它,作为一个挂载的卷。

/src/docker/myapp/upload:/var/www/html/upload
/src/docker/myapp/upload:/var/www/html/config.php

但是,这两种方法都行不通。使用已安装的卷,我看到上传已创建。

然后它失败了:

/var/www/html/config.php\"导致\"不是目录\"""

如果我尝试一下

/src/docker/myapp/upload/config.php:/var/www/html/config.php

Docker 创建上传文件夹,然后创建一个 config.php 文件夹。不是文件。

或者还有其他方法可以持久化配置吗?

【问题讨论】:

  • 在我的例子中,我简单地在创建容器/卷之前“触摸”一个空文件。如果文件不存在,则创建一个目录。

标签: docker docker-compose


【解决方案1】:

TL;DR/注意:

如果您遇到一个目录被创建来代替您尝试挂载的文件,则您可能未能提供 有效绝对 路径。这是无声且令人困惑的故障模式的常见错误。

文件卷在docker中是这样完成的(绝对路径示例(可以使用env变量),需要提及文件名):

    volumes:
      - /src/docker/myapp/upload:/var/www/html/upload
      - /src/docker/myapp/upload/config.php:/var/www/html/config.php

你也可以这样做:

    volumes:
      - ${PWD}/upload:/var/www/html/upload
      - ${PWD}/upload/config.php:/var/www/html/config.php

如果您从 /src/docker/myapp 文件夹中触发 docker-compose

【讨论】:

  • 就像我说的,如果我尝试 /src/docker/myapp/upload/config.php:/var/www/html/config.php Docker 创建上传文件夹,然后创建一个 config.php 文件夹.不是文件。
  • 在你的帖子中你写的不同。如果文件在那里,它应该作为文件挂载。
  • 为了让它工作,我必须提供一个绝对路径。使用 PWD 的提示在这里帮助很大,谢谢!
  • 对我来说是创建一个文件夹而不是文件,但我认为因为该文件是稍后在该路径中复制的,所以无论如何都要推迟绑定?
  • 如果 /src/docker/myapp/upload/config.php 在主机中不存在 docker 将创建一个文件夹...我正在做的是创建一个创建空文件的脚本在运行 docker run 之前 - 如果我可以通过 cmd line args 指示 docker 为我执行此操作,那将是理想的。
【解决方案2】:

我也遇到过类似的问题。我试图将我的配置文件导入到我的容器中,这样我就可以在每次需要时修复它,而无需重新构建映像。

我的意思是我认为下面的命令会将 $(pwd)/config.py 从 Docker 主机映射到 /root/app/config.py 作为文件到容器中。

docker run -v $(pwd)/config.py:/root/app/config.py my_docker_image

但是,它总是创建一个名为 config.py 的目录,而不是一个文件。

在寻找线索的过程中,我找到了原因(来自here

如果你使用 -v 或 --volume 绑定挂载一个文件或目录 Docker 主机上尚不存在,-v 将为您创建端点。 它总是被创建为一个目录

因此,它总是被创建为一个目录,因为我的 docker 主机没有$(pwd)/config.py

即使我在 docker 主机中创建 config.py。 $(pwd)/config.py 只是覆盖/root/app/config.py 不导出/root/app/config.py

【讨论】:

  • 我刚刚在Dockerfile中添加了一个touch path/to/file,这样当我挂载它时它仍然是一个文件(不是创建的目录)
  • : 的左侧必须是完整路径。它是这样工作的
  • touch 是答案,在 Dockerfile 中执行此操作,然后您的 docker-compose 卷挂载将正常工作而无需创建目录
  • 别忘了mkdir -p /path/to/filetouch /path/to/file 之前采取良好措施
  • 谢谢!这是我的情况的问题。我使用 './docker/uploads.ini:/usr/...' 但 docker-compose 文件位于 docker 文件夹中。所以我改成 './uploads.ini:/usr/...' 就可以了。
【解决方案3】:

使用 mount (--mount) 代替 volume (-v)

更多信息:https://docs.docker.com/storage/bind-mounts/

示例:

确保 /tmp/a.txt 存在于 docker 主机上

docker run -it --mount type=bind,source=/tmp/a.txt,target=/root/a.txt alpine sh

【讨论】:

  • 谢谢 Subbu,@Jakub Juszczak 这应该是公认的答案,它是唯一能回答这个问题的答案,它对我有用
  • 这并不能真正回答问题。 volumes 也可以(因为问题出在绝对路径中)。 --mount-v 之间的唯一区别是卷的主机部分尚不存在时的行为。根据您的链接: >如果您使用 -v 或 --volume 绑定挂载 Docker 主机上尚不存在的文件或目录, -v 会为您创建端点。它总是被创建为一个目录。
  • 我不知道如何将其翻译成 docker-compose。 '一点帮助?
  • @msanford 我在答案中添加了一个 docker-compose 示例。
【解决方案4】:

对我有用的方法是使用 bind 挂载

  version: "3.7"    
  services:
  app:
    image: app:latest
    volumes:
      - type: bind
        source: ./sourceFile.yaml
        target: /location/targetFile.yaml

感谢麦克品种的回答:Mount single file from volume using docker-compose

您需要使用“长语法”来表达bind 挂载,使用volumes 键:https://docs.docker.com/compose/compose-file/compose-file-v3/#long-syntax-3

【讨论】:

  • 这个答案比上面的更简洁,并且完全符合我的需要。谢谢。
【解决方案5】:

从 docker-compose 文件版本 3.2 开始,您可以指定类型为“bind”的卷挂载(而不是默认类型“卷”),它允许您将单个文件挂载到容器。在 docker-compose 卷文档中搜索“绑定挂载”: https://docs.docker.com/compose/compose-file/#volumes

就我而言,我试图将单个“.secrets”文件挂载到我的应用程序中,其中包含仅用于本地开发和测试的机密。在生产中,我的应用程序改为从 AWS 获取这些机密。

如果我使用速记语法将此文件作为卷安装:

volumes:
 - ./.secrets:/data/app/.secrets

Docker 会在容器内创建一个“.secrets”目录,而不是映射到容器外的文件。然后我的代码会引发类似“IsADirectoryError: [Errno 21] Is a directory: '.secrets'”的错误。

我改用长手语法解决了这个问题,使用只读“绑定”卷挂载指定我的秘密文件:

volumes:
 - type: bind
   source: ./.secrets
   target: /data/app/.secrets
   read_only: true

现在 Docker 正确地将我的 .secrets 文件挂载到容器中,在容器内创建一个文件而不是目录。

【讨论】:

    【解决方案6】:

    对于像我这样使用 Windows 容器的任何人,请知道您不能使用 Windows 容器绑定或装载单个文件。

    以下示例在使用基于 Windows 的容器时将失败,因为容器内卷或绑定挂载的目标必须是以下之一:不存在或空目录;或 C: 以外的驱动器。此外,绑定挂载的来源必须是本地目录,而不是文件

    net use z: \\remotemachine\share

    docker run -v z:\foo:c:\dest ...

    docker run -v \\uncpath\to\directory:c:\dest ...

    docker run -v c:\foo\somefile.txt:c:\dest ...

    docker run -v c:\foo:c: ...

    docker run -v c:\foo:c:\existing-directory-with-contents ...

    很难发现但it's there

    Link to the Github issue regarding mapping files into Windows container

    【讨论】:

    • 你知道2020年7月还是这样吗?
    • @OvidiuBuligan 是的。根据 docker 开发人员对此问题的评论,最好还是挂载目录,即使在 linux 上也是如此。他们还表示,如果不进行大规模重写,他们就不能允许这种行为。所以我真的怀疑我们会在中短期内看到这一点。
    【解决方案7】:

    以上所有答案都是正确的。

    但我发现真正有用的一件事是挂载的文件应该预先存在于 docker 主机中,否则 docker 将创建一个目录。

    例如:

    /a/file/inside/host/hostFile.txt:/a/file/inside/container/containerFile.txt
    

    hostFile.txt 应该提前存在。 否则你会收到这个错误:containerFile.txt is a directory

    【讨论】:

      【解决方案8】:

      您也可以像这样在 docker-compose.yml 文件中使用相对路径(在 Windows 主机、Linux 容器上测试):

      volumes:
          - ./test.conf:/fluentd/etc/test.conf
      

      【讨论】:

      • 我在 Mac 上使用docker-compose.yml 文件中指定的version: '3.7' 测试了这个,docker-compose 版本为 1.24.1,构建 4667896b,它工作正常。
      • 我仍然得到一个目录而不是一个文件。
      • @chovy 看看这个答案stackoverflow.com/a/66327349/10539792
      【解决方案9】:

      您可以挂载文件或目录/文件夹,这一切都取决于源文件或目录。而且您还需要提供完整路径,或者如果您不确定是否可以使用 PWD。这是一个简单的工作示例。

      在这个例子中,我挂载了已经存在于我的工作目录中的 env-commands 文件

      $ docker run  --rm -it -v ${PWD}/env-commands:/env-commands aravindgv/eosdt:1.0.5 /bin/bash -c "cat /env-commands"
      

      【讨论】:

        【解决方案10】:

        我遇到了同样的问题,docker-compose 正在创建一个目录而不是一个文件,然后在中途崩溃。

        我做了什么:

        1. 在不映射文件的情况下运行容器

        2. 将配置文件复制到主机位置:

          docker cp 容器名称:/var/www/html/config.php ./config.php

        3. 移除容器(docker-compose down)

        4. 放回映射并重新挂载容器

        docker compose 将找到配置文件,并将映射该文件而不是尝试创建目录。

        【讨论】:

          【解决方案11】:

          对我来说,问题是我试图挂载到容器中的文件的符号链接损坏

          【讨论】:

            【解决方案12】:

            我在 Windows 上遇到了同样的问题,Docker 18.06.1-ce-win73 (19507)

            通过 Docker 设置面板删除并重新添加共享驱动器,一切都恢复正常了。

            【讨论】:

              【解决方案13】:

              在窗口中, 如果您需要 docker-compose.yml 中的 ${PWD} 环境变量,您可以在与 docker-compose.yml 文件相同的目录中创建一个 .env 文件,然后手动插入文件夹的位置。

              CMD (pwd_var.bat):

              echo PWD=%cd% >> .env
              

              Powershell (pwd_var.ps1):

              $PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'; echo "PWD=$(get-location).path" >> .env
              

              对于 docker-compose .env 变量有更多好的功能: https://docs.docker.com/compose/reference/envvars/ 尤其是 COMPOSE_CONVERT_WINDOWS_PATHS 环境变量,它允许 docker compose 接受带有 baskslash "\" 的 windows 路径。

              当你想在windows上共享文件时,文件必须在与容器共享之前存在。

              【讨论】:

              • 您不能将 PowerShell 与 docker 一起使用,它会造成供应商锁定并损害社区...
              【解决方案14】:

              也许这对某人有帮助。

              我遇到了这个问题并尝试了所有方法。卷绑定看起来不错,即使我挂载了目录(不是文件),我在挂载目录中的文件名也正确但挂载为目录。

              我尝试重新启用共享驱动器,但 Docker 抱怨防火墙处于活动状态。

              禁用防火墙后一切正常。

              【讨论】:

              • 谢谢!你的帖子让我意识到我的 selinux 被设置为强制执行,这导致了我所有的麻烦。不知道为什么你被否决了¯_(ツ)_/¯
              【解决方案15】:

              我的 Windows 8.1 也有同样的问题

              原来是因为路径区分大小写。 我从目录cd /c/users/alex/ 中调用了docker-compose up,并且在容器内将一个文件变成了目录。

              但是当我使用cd /c/Users/alex/(不是用户大写)并从那里调用docker-compose up 时,它起作用了。

              在我的系统中,Users dir 和 Alex dir 都是大写的,但似乎只有 Users dir 重要。

              【讨论】:

                猜你喜欢
                • 2017-11-11
                • 2016-08-01
                • 2016-03-12
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-06-02
                • 2020-07-10
                相关资源
                最近更新 更多