【问题标题】:How to mount local volumes in docker machine如何在 docker 机器上挂载本地卷
【发布时间】:2015-07-14 11:06:59
【问题描述】:

我正在尝试将 docker-machine 与 docker-compose 一起使用。 docker-compose.yml 文件定义如下:

web:
  build: .
  command: ./run_web.sh
  volumes:
    - .:/app
  ports:
    - "8000:8000"
  links:
    - db:db
    - rabbitmq:rabbit
    - redis:redis

运行docker-compose up -d时一切顺利,直到尝试执行命令并产生错误:

无法启动容器 b58e2dfa503b696417c1c3f49e2714086d4e9999bd71915a53502cb6ef43936d:[8] 系统错误:exec:“./run_web.sh”:stat ./run_web.sh:没有这样的文件或目录

本地卷未安装到远程计算机。使用 webapps 代码挂载本地卷的推荐策略是什么?

【问题讨论】:

  • 项目结构与docker-compose.yml类似本教程syncano.com/…
  • 这应该在 docker-compose 文档中,作为对那些可能开始在本地使用 compose 的人的有用提示。本来可以为我节省数小时的 WTF 时刻,试图弄清楚为什么我的文件路径错误或找不到。不,我只是觉得很傻。

标签: docker dockerfile docker-compose


【解决方案1】:

Docker-machine 自动挂载用户目录...但有时这还不够。

我不知道 docker 1.6,但在 1.8 中,您可以向 docker-machine 添加一个额外的挂载

添加虚拟机挂载点(第 1 部分)

CLI:(仅在机器停止时有效)

VBoxManage sharedfolder add <machine name/id> --name <mount_name> --hostpath <host_dir> --automount

所以windows中的一个例子是

/c/Program\ Files/Oracle/VirtualBox/VBoxManage.exe sharedfolder add default --name e --hostpath 'e:\' --automount

GUI:(不需要停止机器)

  1. 启动“Oracle VM VirtualBox Manager”
  2. 右键单击<machine name>(默认)
  3. 设置...
  4. 共享文件夹
  5. 右侧的文件夹+ 图标(添加共享)
  6. 文件夹路径:<host dir> (e:)
  7. 文件夹名称:<mount name> (e)
  8. 选中“自动挂载”和“永久设置”(如果需要,请只读...)(自动挂载目前毫无意义...)

在 boot2docker 中安装(第 2 部分)

在boot2docker中手动挂载

  1. 有多种登录方式,使用“Oracle VM VirtualBox Manager”中的“Show”,或通过IP地址docker-machine ip default ssh/putty进入docker等...
  2. sudo mkdir -p <local_dir>
  3. sudo mount -t vboxsf -o defaults,uid=`id -u docker`,gid=`id -g docker` <mount_name> <local_dir>

但这只有在你重新启动机器之前是好的,然后挂载丢失了......

向 boot2docker 添加自动挂载

登录机器时

  1. 编辑/创建(以 root 身份)/mnt/sda1/var/lib/boot2docker/bootlocal.sh,您的 sda1 可能不同...
  2. 添加

    mkdir -p <local_dir>
    mount -t vboxsf -o defaults,uid=`id -u docker`,gid=`id -g docker` <mount_name> <local_dir>
    

通过这些更改,您应该有一个新的挂载点。这是我能找到的少数几个在启动时调用并且是持久的文件之一。在有更好的解决方案之前,这应该可行。


旧方法:不太推荐,但留作备用

  • 编辑(以 root 身份)/mnt/sda1/var/lib/boot2docker/profile,您的 sda1 可能不同...
  • 添加

    add_mount() {
      if ! grep -q "try_mount_share $1 $2" /etc/rc.d/automount-shares ; then
        echo "try_mount_share $1 $2" >> /etc/rc.d/automount-shares
      fi
    }
    
    add_mount <local dir> <mount name>
    

作为最后的手段,您可以采用稍微繁琐的替代方案,并且只需修改启动映像。

  • git -c core.autocrlf=false clone https://github.com/boot2docker/boot2docker.git
  • cd boot2docker
  • git -c core.autocrlf=false checkout v1.8.1 #或您的适当版本
  • 编辑rootfs/etc/rc.d/automount-shares
  • 在最后的 fi 之前添加 try_mount_share &lt;local_dir&gt; &lt;mount_name&gt; 行。例如

    try_mount_share /e e
    

    请确保不要将 设置为操作系统需要的任何内容,例如 /bin 等...

  • docker build -t boot2docker . #第一次大概需要一个小时 :(
  • docker run --rm boot2docker &gt; boot2docker.iso
  • 备份旧的 bo​​ot2docker.iso 并将新的 boot2docker.iso 复制到 ~/.docker/machine/machines/ 中的位置

这确实有效,只是冗长而复杂

docker 版本 1.8.1,docker-machine 版本 0.4.0

【讨论】:

  • 对于任何对此有疑问的人,我很确定我必须使本地路径与 docker-machine 中的路径匹配。此外,docker-compose 似乎成功地安装了卷,而普通 docker 没有——不知道为什么。
  • 为这里提到的解决方案创建了一个脚本。适用于最新的 docker 1.10 和 docker-machine 0.6.0 gist.github.com/cristobal/fcb0987871d7e1f7449e
  • 不同资源谈论使用/mnt/sda1/var/lib/boot2docker/profile,你能解释一下你为什么改用/mnt/sda1/var/lib/boot2docker/bootlocal.sh吗?此外,删除这么多文本并不会提高您答案的可读性;-)
  • @Forage Point 了解我的格式:)。我不记得为什么我不再建议使用bootlocal.sh 方法了。我只能说,像我在bootlocal.sh 中那样使用挂载命令看起来比在配置文件中更干净。另外,通常情况下,我相信profile 可以运行多次,而一个挂载只需要运行一次,因此更有意义。但两者都可以工作。
  • 爱它!谢谢!
【解决方案2】:

也遇到了这个问题,使用 docker-machine 时似乎没有安装本地卷。一个黑客解决方案是

  1. 获取docker-machine实例的当前工作目录docker-machine ssh &lt;name&gt; pwd

  2. 使用rsync 之类的命令行工具将文件夹复制到远程系统

    rsync -avzhe ssh --progress <name_of_folder> username@remote_ip:<result _of_pwd_from_1>.
    

默认密码是/root,所以上面的命令是rsync -avzhe ssh --progress &lt;name_of_folder&gt; username@remote_ip:/root

注意:您需要提供远程系统的密码。您可以通过 ssh 进入远程系统并创建密码来快速创建一个。

  1. docker-compose.yml 文件中的卷挂载点从.:/app 更改为/root/&lt;name_of_folder&gt;:/app

  2. 运行docker-compose up -d

注意在本地进行更改时,不要忘记重新运行 rsync 以将更改推送到远程系统。

它并不完美,但它确实有效。存在问题https://github.com/docker/machine/issues/179

其他试图解决这个问题的项目包括docker-rsync

【讨论】:

  • rsync 似乎需要安装在远程系统上` sh:rsync:未找到 rsync:连接意外关闭(到目前为止已收到 0 个字节) [sender] rsync 错误:找不到远程命令(代码 127 ) at /SourceCache/rsync/rsync-45/rsync/io.c(453) [sender=2.6.9] `你是如何让它工作的?
  • rsync 需要安装在本地系统上
  • 使用这些步骤完全锁定了我的 digitalocean 主机。文件传输正常,但是当我尝试使用 docker-machine 重新连接到主机时,我得到 exit status 255 并且必须完全重新创建机器。
  • 为这里提到的解决方案创建了一个脚本。适用于最新的 docker 1.10docker-machine 0.6.0 gist.github.com/cristobal/fcb0987871d7e1f7449e
  • @cristobal 看起来您编写了 mount 解决方案,而不是 rsync 解决方案?
【解决方案3】:

目前我真的看不到任何在机器上挂载卷的方法,所以现在的方法是以某种方式将您需要的文件复制或同步到机器中。

在 docker-machine 的 github repo 上有 conversations 说明如何解决这个问题。有人在 docker-machine 上做了一个 pull request 实现 scp 并且它已经合并到 master 上,所以很可能下一个版本会包含它。

由于它还没有发布,现在我建议如果您将代码托管在 github 上,只需在运行应用程序之前克隆您的 repo

web:
  build: .
  command: git clone https://github.com/my/repo.git; ./repo/run_web.sh
  volumes:
    - .:/app
  ports:
    - "8000:8000"
  links:
    - db:db
    - rabbitmq:rabbit
    - redis:redis

更新:进一步看,我发现 latest binaries 中已经提供了该功能,当您获得它们时,您将能够复制您的本地项目,运行如下命令:

docker-machine scp -r . dev:/home/docker/project

这是一般形式:

docker-machine scp [machine:][path] [machine:][path]

因此您可以在机器之间复制文件。

干杯!1

【讨论】:

【解决方案4】:

自 2017 年 10 月以来,docker-machine 有一个新命令可以解决问题,但在执行之前确保目录中没有任何内容,否则可能会丢失:

docker-machine mount &lt;machine-name&gt;:&lt;guest-path&gt; &lt;host-path&gt;

查看文档了解更多信息:https://docs.docker.com/machine/reference/mount/

公关更改:https://github.com/docker/machine/pull/4018

【讨论】:

  • 令人难以置信的是,从 Docker Machine 文档(您链接的)中,从字面上看,不可能知道命令中的顺序是 ...:&lt;guest-path&gt; &lt;host-path&gt;(而不是相反)。像在文档中要注意的那样简单和关键的东西......不是!
  • 我想这不是很明确,你是对的。必须从命令列表中猜出来
  • 它可以解决问题,但以其他方式。它允许在本地机器上挂载 docker-machine 目录。不幸的是,它不允许其他方式:(
【解决方案5】:

如果你在 docker-machine 中选择 rsync 选项,你可以将它与docker-machine ssh &lt;machinename&gt; 命令结合起来,如下所示:

rsync -rvz --rsh='docker-machine ssh &lt;machinename&gt;' --progress &lt;local_directory_to_sync_to&gt; :&lt;host_directory_to_sync_to&gt;

它使用rsync这种命令格式,将HOST留空:

rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST

(http://linuxcommand.org/man_pages/rsync1.html)

【讨论】:

    【解决方案6】:

    终于弄清楚了如何将 Windows Docker Toolbox 升级到 v1.12.5 并通过在 Oracle VM VirtualBox 管理器中添加一个共享文件夹并禁用路径转换来保持我的卷正常工作。如果您有 Windows 10+,那么您最好使用更新的 Docker for Windows。

    第一次升级痛点:

    1. 首先卸载 VirtualBox。
      • 是的,这可能会破坏其他工具(如 Android Studio)中的内容。谢谢 Docker :(
    2. 安装新版本的 Docker Toolbox。

    Redis 数据库示例: redis: image: redis:alpine container_name: redis ports: - "6379" volumes: - "/var/db/redis:/data:rw"

    在 Docker 快速入门终端 ....

    1. 运行 docker-machine stop default - 确保虚拟机被拖运

    在 Oracle VM VirtualBox 管理器中 ...

    1. 通过或命令行在default VM 中添加了一个共享文件夹
      • D:\Projects\MyProject\db => /var/db

    docker-compose.yml...

    1. 映射redis卷为:"/var/db/redis:/data:rw"

    在 Docker 快速入门终端 ....

    1. 设置COMPOSE_CONVERT_WINDOWS_PATHS=0(工具箱版本>= 1.9.0)
    2. 运行docker-machine start default 重启虚拟机。
    3. cd D:\Projects\MyProject\
    4. docker-compose up 现在应该可以工作了。

    现在在D:\Projects\MyProject\db\redis\dump.rdb创建redis数据库

    为什么要避免相对主机路径?

    避免了 Windows 工具箱的相对主机路径,因为它们可能会引入无效的“\”字符。它不如使用相对于docker-compose.yml 的路径好,但至少我的开发人员同事可以轻松地做到这一点,即使他们的项目文件夹在其他地方,而不必破解docker-compose.yml 文件(对SCM 不利)。

    原始问题

    仅供参考...这是我使用干净的相对路径时得到的原始错误,这些路径过去在旧版本中工作得很好。我的卷映射过去只是"./db/redis:/data:rw"

    ERROR: for redis Cannot create container for service redis: Invalid bind mount spec "D:\\Projects\\MyProject\\db\\redis:/data:rw": Invalid volume specification: 'D:\Projects\MyProject\db\redis:/data

    这有两个原因..

    1. 无法访问D:驱动器
    2. 卷路径不能包含\ 字符
      • docker-compose 添加它们然后责怪你!
      • 使用COMPOSE_CONVERT_WINDOWS_PATHS=0 停止这种废话。

    我建议在您的 docker-compose.yml 文件中记录您的额外 VM 共享文件夹映射,因为您可能需要再次卸载 VirtualBox 并重置共享文件夹,无论如何您的开发人员都会喜欢您。

    【讨论】:

    • 你先生,是个好人
    【解决方案7】:

    当时所有其他答案都很好,但现在(Docker Toolbox v18.09.3)都可以开箱即用。您只需在 VirtualBox VM 中添加一个共享文件夹即可。

    Docker Toolbox 自动将C:\Users 添加为虚拟Linux 机器下的共享文件夹/c/Users(使用Virtual Box 共享文件夹功能),因此如果您的docker-compose.yml 文件位于此路径下的某个位置并且您仅将主机的目录挂载在这条路径 - 一切都应该开箱即用。

    例如:

    C:\Users\username\my-project\docker-compose.yml:

    ...
      volumes:
        - .:/app
    ...
    

    . 路径将自动转换为绝对路径C:\Users\username\my-project,然后再转换为/c/Users/username/my-project。这正是从 linux 虚拟机的角度来看这条路径的方式(你可以检查它:docker-machine ssh 然后ls /c/Users/username/my-project)。所以,最终的挂载将是/c/Users/username/my-project:/app

    一切都为您透明地工作。

    但是如果您的主机挂载路径不在C:\Users 路径下,这将不起作用。例如,如果您将相同的docker-compose.yml 放在D:\dev\my-project 下。

    这很容易解决。

    1. 停止虚拟机 (docker-machine stop)。
    2. 打开Virtual Box GUI,打开名为default的虚拟机设置,打开Shared Folders部分并添加新的共享文件夹:

      • 文件夹路径:D:\dev
      • 文件夹名称:d/dev

      按两次OK 并关闭 Virtual Box GUI。

    3. 启动虚拟机 (docker-machine start)。

    就是这样。 D:\dev 下主机的所有路径现在应该可以在 docker-compose.yml 挂载。

    【讨论】:

      【解决方案8】:

      可以通过三种工具的组合来完成: docker-machine mount, rsync, inotifywait

      TL;DR

      脚本基于以下所有is here

      假设您在/home/jdcaballerov/web 中有docker-compose.ymlrun_web.sh

      1. 挂载目录在机器上,它与主机上的相同路径 docker-machine machine:/home/jdcaballerov/web /tmp/some_random_dir
      2. 将挂载目录与主机上的目录同步rsync -r /home/jdcaballerov/web /tmp/some_random_dir
      3. 同步目录中文件的每次更改:

        inotifywait -r -m -e close_write --format '%w%f' /home/jdcaballerov/web | while read CHANGED_FILE
        do
            rsync /home/jdcaballerov/web /tmp/some_random_dir
        done
        

      注意 - 有两个目录具有相同的路径 - 一个在本地(主机)机器上,第二个在 docker 机器上。

      【讨论】:

        【解决方案9】:

        我假设run_web.sh 文件与您的docker-compose.yml 文件位于同一目录中。那么命令应该是command: /app/run_web.sh

        除非Dockerfile(您没有透露)负责将run_web.sh 文件放入Docker 映像中。

        【讨论】:

        • 感谢您的回答。它位于同一目录中。但是我注意到没有安装卷。文件不可用,这就是问题所在。如何添加它们。结构类似于syncano.com/…
        • 确保您拥有最新版本的 docker 和 dicker-compose。
        • docker:Docker 版本 1.6.0,构建 4749651,docker-machine 版本 0.2.0 (8b9eaf2),docker-compose 1.2.0
        • 您是否以任何方式修改了来自syncano.com/… 的Dockerfile?
        • 是的,我确实添加了代码,创建了目录。问题在于,当 docker-compose 执行时,它会覆盖 volume: volumes: - .:/app 并留下一个空目录。我在 compose 中评论了卷,它起作用了。
        【解决方案10】:

        在这里总结帖子后,附上更新的脚本,以创建额外的主机挂载点并在 Virtualbox 重新启动时自动挂载。工作环境简介如下: - Windows 7的 - docker-machine.exe 版本 0.7.0 - VirtualBox 5.0.22

            #!env bash
        
            : ${NAME:=default}
            : ${SHARE:=c/Proj}
            : ${MOUNT:=/c/Proj}
            : ${VBOXMGR:=C:\Program Files\Oracle\VirtualBox\VBoxManage.exe}
            SCRIPT=/mnt/sda1/var/lib/boot2docker/bootlocal.sh
        
            ## set -x
            docker-machine stop $NAME
            "$VBOXMGR" sharedfolder add $NAME --name c/Proj --hostpath 'c:\' --automount 2>/dev/null || :
            docker-machine start $NAME
            docker-machine env $NAME
        
            docker-machine ssh $NAME 'echo "mkdir -p $MOUNT" | sudo tee $SCRIPT'
            docker-machine ssh $NAME 'echo "sudo mount -t vboxsf -o rw,user $SHARE $MOUNT" |  sudo tee -a $SCRIPT'
            docker-machine ssh $NAME 'sudo chmod +x /mnt/sda1/var/lib/boot2docker/bootlocal.sh'
            docker-machine ssh $NAME 'sudo /mnt/sda1/var/lib/boot2docker/bootlocal.sh'
            #docker-machine ssh $NAME 'ls $MOUNT'
        

        【讨论】:

          【解决方案11】:

          我在本地机器上使用带有 virtualbox 驱动器的 docker-machine 0.12.2。我发现有一个目录/hosthome/$(user name) 可以从中访问本地文件。

          【讨论】:

            【解决方案12】:

            只是想我会提到我一直在 Windows 10 上使用 18.03.1-ce-win65 (17513),我注意到如果您以前共享过驱动器并缓存了凭据,一旦您更改了密码 docker将开始将容器中的卷挂载为空白。

            它没有表明实际发生的情况是它现在无法使用旧的缓存凭据访问共享。 这种情况下的解决方案是通过 UI(设置->共享驱动器)重置凭据,或者禁用然后重新启用驱动器共享并输入新密码。

            如果 docker-compose 在这些情况下出错会很有用。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2021-11-16
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-01-01
              • 2021-10-30
              • 2017-11-05
              相关资源
              最近更新 更多