如何挂载卷中的单个文件

我正在尝试dockerize一个PHP应用程序。在dockerfile中,我下载存档,提取它,等等。

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

因此,我认为我可以将该文件作为卷挂载,就像对数据库所做的那样。

我尝试了两种方法,用音量和直接路径。

docker-compose:

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创建upload文件夹,然后创建config.php文件夹。不是文件。

或者是否有另一种方法来持久化配置?

556489 次浏览

TL;博士/注意:

如果你正在尝试挂载的文件被创建了一个目录,你可能无法提供有效的绝对路径。这是沉默和令人困惑的失败模式的常见错误。

文件卷在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

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

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

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

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

在寻找线索时,我发现了原因(from 在这里)

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

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

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

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

我的Windows 8.1也有同样的问题

结果证明,这是由于路径的大小写敏感性。 我从目录cd /c/users/alex/调用docker-compose up,在容器内部,一个文件被转换到目录

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

在我的系统中,Users dir和Alex dir都是大写的,尽管看起来只有Users dir重要。

我在Windows上也有同样的问题,Docker 18.06.1-ce-win73 (19507)

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

使用 (--mount)代替体积 (-v)

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

例子:

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

docker run -it --mount type=bind,source=/tmp/a.txt,target=/root/a.txt alpine sh
< p >在windows中, 如果你需要在docker-compose中使用a ${PWD} env变量。Yml,你可以在docker-compose的同一个目录下创建一个。env文件。

. Yml文件然后手动插入你的文件夹的位置

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 env变量,它允许docker compose接受带有baskslash的windows路径"\".

当您想要在windows上共享一个文件时,该文件必须在与容器共享之前存在。

对于像我这样使用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 ...

这很难发现,但它的存在

关于将文件映射到Windows容器的Github问题的链接

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

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

也许这能帮到别人。

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

我尝试重新启用共享驱动器,Docker抱怨防火墙是活动的。

在禁用防火墙后,一切工作正常。

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

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

感谢mike breed在使用docker-compose从卷中挂载单个文件给出的答案

你需要使用“长”句法。使用volumes键来表示bind挂载

从docker-compose文件3.2版开始,你可以使用指定类型为&;bind"(而不是默认类型"volume")来将单个文件装入容器。搜索“bind mount”;在docker-compose volume docs中: https://docs.docker.com/compose/compose-file/#volumes < / p >

就我而言,我试图建立一个单一的“秘密”;文件中包含仅用于本地开发和测试的秘密。在生产环境中,我的应用程序从AWS中获取这些秘密。

如果我使用简写语法将这个文件挂载为卷:

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

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

我通过使用长手语法来解决这个问题,使用只读的“bind"卷挂载:

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

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

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

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

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

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

我做了什么:

  1. 运行容器而不映射文件

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

    Docker cp containername:/var/www/html/config.php ./config.php

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

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

Docker compose会找到配置文件,并将其映射,而不是尝试创建一个目录。

以上答案均正确。

但我发现有一件事非常有用,那就是挂载的文件应该提前在docker主机中存在,否则docker将创建一个目录。

例如:

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

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

在合成中,我使用了一个相对路径,它工作:

version: "3.7"
services:
svc1:
volumes:
# Current dir is parent of src
- "./src/file.conf:/path/in/container/file.conf

使用docker run命令绑定挂载文件会产生:

docker: Error response from daemon: invalid mount config for type "bind": invalid mount path: 'path/file.conf' mount path must be absolute.
See 'docker run --help'.

显然,这样做的唯一方法是指定一个绝对挂载路径,就像这样:

docker run -it --rm --mount type=bind,source="/path/to/file.conf",target=/file.conf alpine sh

对于Windows用户使用"%cd%",对于Linux用户使用"$(pwd)"也是一种处理绝对路径的方法。

看到存储绑定挂载

对于Visual Studio Code用户,请确保您在命令提示符终端中运行%cd%命令,而不是PowerShell。

对我来说,问题是我指定了不正确的源文件。

这里有一个例子:

 robert ❱ ~ ❱ docker run --rm -it -v "${PWD}/non-existant:/root/destination" -w /root --entrypoint /usr/bin/ls ubuntu -lad destination
drwxr-xr-x 2 root root 64 Mar 29 05:54 destination
robert ❱ ~ ❱ touch exists
robert ❱ ~ ❱ docker run --rm -it -v "${PWD}/exists:/root/destination" -w /root --entrypoint /usr/bin/ls ubuntu -lad destination
-rw-r--r-- 1 root root 0 Mar 29 05:58 destination
robert ❱ ~ ❱

TL;DR -这可能是你的拼写,就像我的拼写一样。

2022年答案,在Mac上使用Minikube/Hyperkit docker和docker Compose

由于我不再使用Docker Desktop,我遇到了许多类似于“Docker in Docker (dind)”的问题。范例与minikube…

  1. 山minikube
  2. 使用绝对路径

例如,最简单的方法是安装确切的回家路径……

minikube mount $HOME:/Users/<you>


... keeps running...

docker-compose.yaml

volumes:
- /Users/<you>/path/to/file.yaml:/somedir/file.yaml

如果您忘记在源目录中实际包含想要挂载的文件,似乎也会发生这种情况。我经历了惨痛的教训。没有诸如“文件不存在”之类的错误消息,而是在目标中创建一个目录。

在远程虚拟机上使用docker上下文操作docker时要小心。在这种情况下,您尝试挂载的文件在目标VM上肯定不存在。这样你不存在的文件将被挂载为文件夹。

TL&博士: Docker默认所有挂载点到目录。如果你想确保你的文件被挂载到docker实例中,这个文件必须在docker-compose启动和运行之前已经存在。

解释:

传统上,挂载卷的目的总是意味着将存储位置挂载到目录。然而,现在这种情况正在改变,现在可以共享文件了。从程序员的角度来看,很难知道&;/tmp/something"是文件或目录。因此,Docker假定所有挂载点首先是一个目录,除非文件已经存在。

在您的示例中,如果您希望挂载一个文件,请确保它已经存在于主机系统中。当Docker看到这个条目已经存在时,它会将这个文件挂载到Docker环境中。