简体   繁体   English

如何在卷中挂载单个文件

[英]How to mount a single file in a volume

I am trying to dockerize a PHP application.我正在尝试对 PHP 应用程序进行 docker 化。 In the dockerfile, I download the archive, extract it, etc.在 dockerfile 中,我下载了压缩包,解压缩,等等。

Everything works fine.一切正常。 However, if a new version gets released and I update the dockerfile, I have to reinstall the application, because the config.php gets overwritten.但是,如果发布了新版本并且我更新了 dockerfile,我必须重新安装应用程序,因为 config.php 被覆盖了。

So I thought I can mount the file as a volume, like I do with the database.所以我想我可以将文件挂载为一个卷,就像我对数据库所做的那样。

I tried it two ways, with a volume and a direct path.我尝试了两种方式,使用卷和直接路径。

docker-compose: 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:

Which results in the error:导致错误:

And I tried it with a given path, as a mounted volume.我用给定的路径尝试了它,作为一个安装的卷。

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

However, both ways are not working.但是,这两种方式都不起作用。 With the mounted volume, I see that upload gets created.使用已安装的卷,我看到已创建上传。

But then it fails with:但随后它失败了:

/var/www/html/config.php\" caused \"not a directory\""" /var/www/html/config.php\" 导致\"不是目录\"""

If I try it with如果我尝试

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

Docker creates the upload folder and then a config.php folder. Docker 创建上传文件夹,然后创建 config.php 文件夹。 Not a file.不是文件。

Or is there another way to persist the config?还是有另一种方法来持久化配置?

TL;DR/Notice: TL;DR/注意:

If you experience a directory being created in place of the file you are trying to mount, you have probably failed to supply a valid and absolute path.如果您遇到一个目录被创建来代替您尝试挂载的文件,那么您可能无法提供有效绝对路径。 This is a common mistake with a silent and confusing failure mode.这是静默且令人困惑的故障模式的常见错误。

File volumes are done this way in docker (absolute path example (can use env variables), and you need to mention the file name) :文件卷在 docker 中以这种方式完成(绝对路径示例(可以使用 env 变量),您需要提及文件名):

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

You can also do:你也可以这样做:

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

If you fire the docker-compose from /src/docker/myapp folder如果您从/src/docker/myapp文件夹中触发 docker-compose

I had been suffering from a similar issue.我一直在遭受类似的问题。 I was trying to import my config file to my container so that I can fix it every time I need without re-building the image.我试图将我的配置文件导入到我的容器中,以便每次需要时都可以修复它,而无需重新构建映像。

I mean I thought the below command would map $(pwd)/config.py from Docker host to /root/app/config.py into the container as a file.我的意思是我认为下面的命令会将$(pwd)/config.py从 Docker 主机映射到/root/app/config.py作为文件到容器中。

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

However, it always created a directory named config.py , not a file.但是,它总是创建一个名为config.py的目录,而不是一个文件。

while looking for clue, I found the reason(from here )在寻找线索时,我找到了原因(从这里

If you use -v or --volume to bind-mount a file or directory that does not yet exist on the Docker host, -v will create the endpoint for you.如果您使用 -v 或 --volume 绑定挂载 Docker 主机上尚不存在的文件或目录,-v 将为您创建端点。 It is always created as a directory .它总是被创建为一个目录

Therefore, it is always created as a directory because my docker host does not have $(pwd)/config.py .因此,它总是被创建为一个目录,因为我的 docker 主机没有$(pwd)/config.py

Even if I create config.py in docker host.即使我在 docker 主机中创建 config.py。 $(pwd)/config.py just overwirte /root/app/config.py not exporting /root/app/config.py . $(pwd)/config.py只是覆盖/root/app/config.py而不是导出/root/app/config.py

The way that worked for me is to use a bind mount对我有用的方法是使用bind安装

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

Thanks mike breed for the answer over at: Mount single file from volume using docker-compose感谢 mikebreed 的回答: Mount single file from volume using docker-compose

You need to use the "long syntax" to express a bind mount using the volumes key: https://docs.docker.com/compose/compose-file/compose-file-v3/#long-syntax-3您需要使用“长语法”来使用volumes键表示bind安装: https ://docs.docker.com/compose/compose-file/compose-file-v3/#long-syntax-3

Use mount ( --mount ) instead volume ( -v )使用挂载--mount )而不是-v

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

Example:例子:

Ensure /tmp/a.txt exists on docker host确保 /tmp/a.txt 存在于 docker 主机上

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

As of docker-compose file version 3.2, you can specify a volume mount of type "bind" (instead of the default type "volume") that allows you to mount a single file into the container.从 docker-compose 文件版本 3.2 开始,您可以指定类型为“bind”(而不是默认类型“volume”)的卷挂载,允许您将单个文件挂载到容器中。 Search for "bind mount" in the docker-compose volume docs: https://docs.docker.com/compose/compose-file/#volumes在 docker-compose 卷文档中搜索“绑定挂载”: https ://docs.docker.com/compose/compose-file/#volumes

In my case, I was trying to mount a single ".secrets" file into my application that contained secrets for local development and testing only.就我而言,我试图将单个“.secrets”文件挂载到我的应用程序中,其中包含仅用于本地开发和测试的机密。 In production, my application fetches these secrets from AWS instead.在生产中,我的应用程序改为从 AWS 获取这些机密。

If I mounted this file as a volume using the shorthand syntax:如果我使用速记语法将此文件作为卷安装:

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

Docker would create a ".secrets" directory inside the container instead of mapping to the file outside of the container. Docker 会在容器内创建一个“.secrets”目录,而不是映射到容器外的文件。 My code would then raise an error like "IsADirectoryError: [Errno 21] Is a directory: '.secrets'".然后我的代码会引发类似“IsADirectoryError: [Errno 21] Is a directory: '.secrets'”的错误。

I fixed this by using the long-hand syntax instead, specifying my secrets file using a read-only "bind" volume mount:我通过使用长手语法解决了这个问题,使用只读“绑定”卷安装指定我的秘密文件:

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

Now Docker correctly mounts my .secrets file into the container, creating a file inside the container instead of a directory.现在 Docker 正确地将我的 .secrets 文件挂载到容器中,在容器内创建一个文件而不是目录。

For anyone using Windows container like me, know that you CANNOT bind or mount single files using windows container.对于像我这样使用 Windows 容器的任何人,请知道您不能使用 Windows 容器绑定或挂载单个文件。

The following examples will fail when using Windows-based containers, as the destination of a volume or bind mount inside the container must be one of: a non-existing or empty directory;以下示例在使用基于 Windows 的容器时将失败,因为容器内的卷或绑定挂载的目标必须是以下之一:不存在或空目录; or a drive other than C:.或 C: 以外的驱动器。 Further, the source of a bind mount must be a local directory, not a file .此外,绑定挂载的源必须是本地目录,而不是文件

net use z: \\remotemachine\share

docker run -vz:\foo:c:\dest ...

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

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

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

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

It's hard to spot but it's there很难发现,但它就在那里

Link to the Github issue regarding mapping files into Windows container链接到有关将文件映射到 Windows 容器的 Github 问题

All above answers are Correct.以上所有答案都是正确的。

but one thing that I found really helpful is that mounted file should exist inside docker host in advance otherwise docker will create a directory instead.但我发现真正有用的一件事是挂载的文件应该预先存在于 docker 主机中,否则 docker 将创建一个目录。

for example:例如:

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

hostFile.txt should exist in advance. hostFile.txt应该预先存在。 otherwise you will receive this error: containerFile.txt is a directory否则你会收到这个错误: containerFile.txt is a directory

You can also use a relative path in your docker-compose.yml file like this (tested on Windows host, Linux container):您还可以像这样在docker-compose.yml文件中使用相对路径(在 Windows 主机、Linux 容器上测试):

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

You can mount files or directories/folders it all depends on Source file or directory.您可以挂载文件或目录/文件夹,这一切都取决于源文件或目录。 And also you need to provide full path or if you are not sure you can use PWD.而且您还需要提供完整路径,或者如果您不确定是否可以使用 PWD。 Here is a simple working example.这是一个简单的工作示例。

In this example, I am mounting env-commands file which already exists in my working directory在这个例子中,我正在挂载已经存在于我的工作目录中的 env-commands 文件

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

In compose I am using a relative path and it works:在撰写中,我使用的是相对路径,它可以工作:

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

Using docker run command to bind mount a file produces:使用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'.

Apparently the only way to do this is to specify an absolute mount path like this:显然,这样做的唯一方法是指定一个绝对安装路径,如下所示:

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

also using "%cd%" for Windows Users or "$(pwd)" for Linux Users is a way to deal with absolute paths.Windows 用户使用"%cd%"或对Linux 用户使用"$(pwd)"也是一种处理绝对路径的方法。

see storage bind mounts请参阅存储绑定安装

For Visual Studio Code Users make sure you are running the %cd% command in a Command Prompt Terminal, not PowerShell.对于 Visual Studio Code 用户,请确保您在命令提示符终端而不是 PowerShell 中运行 %cd% 命令。

I had the same issue, docker-compose was creating a directory instead of a file, then crashing mid-way.我有同样的问题,docker-compose 正在创建一个目录而不是一个文件,然后在中途崩溃。

what i did :我做了什么 :

  1. run the container without mapping the file运行容器而不映射文件

  2. copy the config file to the host location :将配置文件复制到主机位置:

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

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

  4. put the mapping back and remount up the container放回映射并重新安装容器

docker compose will find the config file, and will map that instead of trying to create a directory. docker compose 将找到配置文件,并将映射该文件而不是尝试创建目录。

2022 answer, on Mac with Minikube/Hyperkit docker and Docker Compose 2022 年答案,在 Mac 上使用 Minikube/Hyperkit docker 和 Docker Compose

Since I'm not using Docker Desktop any longer, I've experienced numerous issues similar to "docker in docker (dind)" paradigm with minikube...由于我不再使用 Docker Desktop,因此我遇到了许多类似于 minikube 的“docker in docker (dind)”范式的问题......

  1. mount minikube安装 minikube
  2. use absolute path使用绝对路径

eg, easiest way was to mount the exact home path...例如,最简单的方法是安装确切的主路径......

minikube mount $HOME:/Users/<you>

... keeps running...

docker-compose.yaml docker-compose.yaml

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

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

I had the same issue on Windows, Docker 18.06.1-ce-win73 (19507) .我在 Windows Docker 18.06.1-ce-win73 (19507)上遇到了同样的问题。

Removing and re-adding the shared drive via the Docker settings panel and everything worked again.通过 Docker 设置面板删除和重新添加共享驱动器,一切都恢复正常了。

In windows, if you need the a ${PWD} env variable in your docker-compose.yml you can creat a .env file in the same directory as your docker-compose.yml file then insert manualy the location of your folder.在 Windows 中,如果您需要 docker-compose.yml 中的 ${PWD} 环境变量,您可以在与 docker-compose.yml 文件相同的目录中创建一个 .env 文件,然后手动插入文件夹的位置。

CMD (pwd_var.bat) : CMD(pwd_var.bat):

echo PWD=%cd% >> .env

Powershell (pwd_var.ps1) :电源外壳(pwd_var.ps1):

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

There is more good features hear for docker-compose .env variables: https://docs.docker.com/compose/reference/envvars/ especially for the COMPOSE_CONVERT_WINDOWS_PATHS env variable that allow docker compose to accept windows path with baskslash "\" . docker-compose .env 变量有更多好的功能: https ://docs.docker.com/compose/reference/envvars/ 特别是COMPOSE_CONVERT_WINDOWS_PATHS变量,它允许 docker compose 接受带有 baskslash "\"的 windows 路径。

When you want to share a file on windows, the file must exist before sharing it with the container.当您要在 Windows 上共享文件时,该文件必须在与容器共享之前存在。

Maybe this helps someone.也许这对某人有帮助。

I had this problem and tried everything.我遇到了这个问题并尝试了一切。 Volume bindings looked well and even if I mounted directory (not files), I had the file names in the mounted directory correctly but mounted as dirs.卷绑定看起来不错,即使我挂载了目录(不是文件),我在挂载目录中的文件名也正确,但作为目录挂载。

I tried to re-enable shared drives and Docker complained the firewall is active.我试图重新启用共享驱动器,但 Docker 抱怨防火墙处于活动状态。

After disabling the firewall all was working fine.禁用防火墙后一切正常。

For myself, the issue was that I specified the incorrect source file.对我自己来说,问题是我指定了不正确的源文件。

Here's an example:这是一个例子:

 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 - It could be your spelling as it was for me. TL;DR - 这可能是你的拼写,因为它对我来说。

This also seems to happen if you forget to actually include the file you want to mount in the source dir.如果您忘记将要挂载的文件实际包含在源目录中,这似乎也会发生。 I learned the hard way.我学会了艰难的方式。 No error message such as 'file not present', instead creates a directory in the destination.没有诸如“文件不存在”之类的错误消息,而是在目标中创建一个目录。

Be carefull if you use docker context to operate with docker on remote virtual machine.如果您使用 docker 上下文在远程虚拟机上使用 docker 操作,请小心。 In this case the file you trying to mount will certanly not exist on target VM.在这种情况下,您尝试挂载的文件肯定不会存在于目标 VM 上。 Thus you not existed file will be mount as folder.因此,您不存在的文件将被挂载为文件夹。

TL&DR: Docker defaults all mount points to directories. TL&DR: Docker 默认所有挂载点到目录。 If you want to make sure your file is mounted into your docker instance, the file must already exist prior to docker-compose up and running.如果您想确保您的文件已安装到您的 docker 实例中,该文件必须在 docker-compose 启动并运行之前已经存在。

Explanation:解释:

Traditionally, the purpose of mounting a volume has always meant mounting a storage location to a directory.传统上,挂载卷的目的总是意味着将存储位置挂载到目录。 However, that is now changing with the ability to now just share files.但是,随着现在仅共享文件的能力,这种情况正在发生变化。 From a programmer's perspective, its hard to know whether a "/tmp/something" is a file or a directory.从程序员的角度来看,很难知道“/tmp/something”是文件还是目录。 Docker, therefore, assumes all mount points are first a directory unless the file already exists.因此,Docker 假设所有挂载点首先是一个目录,除非该文件已经存在。

In your example if you want a file to be mounted, make sure it already exists on the host system.在你的例子中,如果你想挂载一个文件,请确保它已经存在于主机系统上。 When Docker sees that this entry already exists it will mount the file into the docker environment.当 Docker 看到这个条目已经存在时,它会将文件挂载到 docker 环境中。

I have same issue on my Windows 8.1我的 Windows 8.1 也有同样的问题

It turned out that it was due to case-sensitivity of path.事实证明,这是由于路径对大小写敏感。 I called docker-compose up from directory cd /c/users/alex/ and inside container a file was turned into directory.我从目录cd /c/users/alex/调用docker-compose up ,并且在容器内将一个文件变成了目录。

But when I did cd /c/Users/alex/ (not Users capitalized) and called docker-compose up from there, it worked.但是当我执行cd /c/Users/alex/ (不是用户大写)并从那里调用docker-compose up ,它​​起作用了。

In my system both Users dir and Alex dir are capitalized, though it seems like only Users dir matter.在我的系统中,用户目录和亚历克斯目录都是大写的,尽管似乎只有用户目录很重要。

I am trying to dockerize a PHP application.我正在尝试将PHP应用程序码头化。 In the dockerfile, I download the archive, extract it, etc.在dockerfile中,我下载了存档,将其解压缩,等等。

Everything works fine.一切正常。 However, if a new version gets released and I update the dockerfile, I have to reinstall the application, because the config.php gets overwritten.但是,如果发布了新版本并更新了dockerfile,则必须重新安装该应用程序,因为config.php被覆盖了。

So I thought I can mount the file as a volume, like I do with the database.因此,我以为可以像将数据库一样将文件挂载为卷。

I tried it two ways, with a volume and a direct path.我尝试了两种方式,分别是体积和直接路径。

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:

Which results in the error:导致错误:

And I tried it with a given path, as a mounted volume.我尝试使用给定的路径(已安装的卷)进行尝试。

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

However, both ways are not working.但是,这两种方式均无效。 With the mounted volume, I see that upload gets created.使用已装载的卷,我看到已创建上传。

But then it fails with:但随后失败:

/var/www/html/config.php\\" caused \\"not a directory\\""" /var/www/html/config.php \\“导致\\”不是目录\\“”“

If I try it with如果我尝试

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

Docker creates the upload folder and then a config.php folder. Docker创建上载文件夹,然后创建config.php文件夹。 Not a file.不是文件。

Or is there another way to persist the config?还是有另一种方法来保留配置?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM