如何在Docker Machine中挂载本地卷

90

我正在尝试使用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:没有那个文件或目录

本地卷未挂载到远程机器。建议使用什么策略来挂载带有Web应用程序代码的本地卷?


项目结构和docker-compose.yml文件与此教程类似:http://www.syncano.com/configuring-running-django-celery-docker-containers-pt-1/ - jdcaballerov
1
这应该在docker-compose文档中作为有用的提示,供那些刚开始使用本地compose的人参考。如果我早知道这个提示,就不用花几个小时去搞明白为什么我的文件路径错了或找不到了。现在我只感到有点傻。 - timbrown
12个回答

95

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

我不知道Docker 1.6,但在1.8中,您可以向docker-machine添加附加挂载。

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

命令行:(仅在停止机器时有效)

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中挂载(第二部分)

手动在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. Edit/create (as root) /mnt/sda1/var/lib/boot2docker/bootlocal.sh, sda1 may be different for you...
  2. Add

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

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


旧方法:不太推荐,但作为另一种选择留下。

  • Edit (as root) /mnt/sda1/var/lib/boot2docker/profile, sda1 may be different for you...
  • Add

    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 #or your appropriate version
  • Edit rootfs/etc/rc.d/automount-shares
  • Add try_mount_share <local_dir> <mount_name> line right before fi at the end. For example

    try_mount_share /e e
    

    Just be sure not to set the to anything the os needs, like /bin, etc...

  • docker build -t boot2docker . #This will take about an hour the first time :(
  • docker run --rm boot2docker > boot2docker.iso
  • Backup the old boot2docker.iso and copy your new one in its place, in ~/.docker/machine/machines/

这是可行的,只是有点长而且复杂。

docker版本为1.8.1,docker-machine版本为0.4.0。


对于那些遇到问题的人,我相信我必须使本地路径与docker-machine中的路径匹配。此外,docker-compose似乎成功挂载了卷,而常规的docker没有——不确定为什么。 - spieden
3
根据此处提到的解决方案创建了一个脚本。适用于最新版本的docker 1.10和docker-machine 0.6.0。链接:gist.github.com/cristobal/fcb0987871d7e1f7449e - cristobal
不同的资源讨论了使用/mnt/sda1/var/lib/boot2docker/profile,你能解释一下为什么你改用/mnt/sda1/var/lib/boot2docker/bootlocal.sh吗?此外,划掉这么多文字并没有提高你回答的可读性。;-) - Forage
1
@Forage 我注意到了你对我的格式的建议:)。我不记得为什么我以前建议使用bootlocal.sh方法了。我只能说,像我在bootlocal.sh中所做的那样使用挂载命令看起来更清晰,而不是在配置文件中使用。此外,通常情况下,我认为可以多次运行profile,而只需要运行一次挂载,因此这更有意义。但两者都可以工作。 - Andy
太棒了!谢谢你! - Qorbani
显示剩余2条评论

28

我也遇到了这个问题,看起来在使用docker-machine时本地卷没有被挂载。一种hack的解决方案是:

  1. 获取docker-machine实例的当前工作目录:docker-machine ssh <name> pwd

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

rsync -avzhe ssh --progress <name_of_folder> username@remote_ip:<result _of_pwd_from_1>.
默认密码是/root,因此上面的命令将是rsync -avzhe ssh --progress <name_of_folder> username@remote_ip:/root
注意:您需要为远程系统提供密码。您可以通过ssh连接到远程系统并创建一个密码来快速创建密码。
在您的docker-compose.yml文件中更改卷挂载点从.:/app/root/<name_of_folder>:/app
运行docker-compose up -d
当本地进行更改时,请不要忘记重新运行rsync以将更改推送到远程系统。
这并不完美,但它起作用。正在进行一个问题https://github.com/docker/machine/issues/179 其他试图解决此问题的项目包括docker-rsync

似乎需要在远程系统上安装rsyncsh:rsync:未找到 rsync:连接意外关闭(迄今为止收到0字节)[sender] rsync错误:在/SourceCache/rsync/rsync-45/rsync/io.c(453)[sender=2.6.9]处未找到远程命令(代码127) 你是如何让它工作的? - krinker
1
rsync 需要安装在您的本地系统上。 - gbozee
使用这些步骤会完全锁定我的DigitalOcean主机。文件传输正常,但当我尝试使用docker-machine重新连接主机时,会出现“退出状态255”的错误,并且必须完全重新创建该机器。 - dsifford
1
创建了一个脚本来解决此处提到的问题。适用于最新的 docker 1.10docker-machine 0.6.0 https://gist.github.com/cristobal/fcb0987871d7e1f7449e - cristobal
@cristobal 看起来你编写了挂载解决方案,而不是rsync解决方案? - Andy
显示剩余3条评论

14

目前我无法看到在机器上挂载卷的任何方法,因此现在的方法是将需要的文件复制或同步到机器中。

关于如何解决这个问题,在docker-machine的github存储库上有一些讨论。有人在拉取请求中实现了scp在docker-machine中,它已经合并到主分支,所以很可能下一个发布版本将包含它。

由于它还没有发布,所以现在我建议如果你的代码托管在github上,在运行应用程序之前先克隆你的仓库。

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

更新:进一步查看后,我发现该功能已经在最新的二进制文件中提供,当你获得它们后,你可以使用以下命令复制你的本地项目:

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

这是一般形式:

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

所以您可以在各台设备之间复制文件。

干杯!1


docker-machine scp 的文档:https://docs.docker.com/machine/reference/scp/ - Anthony Dahanne
2
这个方法非常慢 :( - Sergej Brazdeikis

5

1
令人难以置信的是,从Docker Machine文档(您提供的链接)中,我们无法确定命令中的顺序是...:<guest-path> <host-path>(而不是相反的顺序)。像这样简单而关键的事情在文档中却没有被注意到...这真的很遗憾! - Dan Nissenbaum
我猜这不是很明确,你是对的。它必须从命令列表中猜测。 - Jorge
它能起到作用,但是方法不同。它允许在本地机器上挂载docker-machine目录。不幸的是,它不允许其他方式 :( - ravenwing

4
如果您选择使用docker-machine的rsync选项,可以按如下方式与docker-machine ssh <machinename>命令结合使用: rsync -rvz --rsh='docker-machine ssh <machinename>' --progress <local_directory_to_sync_to> :<host_directory_to_sync_to> 它使用了rsync命令的此格式,将HOST留空: rsync [OPTION]... SRC [SRC]... [USER@]HOST:DESThttp://linuxcommand.org/man_pages/rsync1.html

2
最终找到了如何将Windows Docker Toolbox升级到v1.12.5并保持我的数据卷正常工作的方法,那就是在Oracle VM VirtualBox管理器中添加一个共享文件夹,并禁用路径转换。如果你使用的是Windows 10+,那么最好使用新版本的Docker for Windows。
1. 升级痛苦:
- 首先卸载VirtualBox。 - 安装新版Docker Toolbox。
2. Redis数据库示例:
``` redis: image: redis:alpine container_name: redis ports: - "6379" volumes: - "/var/db/redis:/data:rw" ```
在Docker Quickstart终端中执行以下命令:
- 运行 `docker-machine stop default` 命令,确保VM已经停机。
在Oracle VM VirtualBox Manager中:
- 通过CLI或者命令行向default VM添加共享文件夹,例如:`D:\Projects\MyProject\db => /var/db`。
在docker-compose.yml中:
- 将redis容器映射到`"/var/db/redis:/data:rw"`。
在Docker Quickstart终端中:
- 设置`COMPOSE_CONVERT_WINDOWS_PATHS=0`(对于Toolbox版本>=1.9.0)。 - 运行`docker-machine start default`以重新启动虚拟机。 - 执行`cd D:\Projects\MyProject\`和`docker-compose up`应该可以正常工作了。
现在,在`D:\Projects\MyProject\db\redis\dump.rdb`中创建redis数据库。
为什么要避免使用相对主机路径?
我在Windows Toolbox中“避免使用相对主机路径”,因为它们可能会引入无效的'\'字符。这不像使用相对于docker-compose.yml的路径那样方便,但至少我的同事可以轻松地进行操作,即使他们的项目文件夹在其他地方也可以如此而无需修改docker-compose.yml文件(这对SCM来说很糟糕)。
原问题:
FYI...以下是我在使用适用于旧版本的可靠相对路径时收到的原始错误。我的卷映射过去曾经只是`"./db/redis:/data:rw"`。

错误:对于redis服务,无法创建容器:无效的绑定挂载规范"D:\\Projects\\MyProject\\db\\redis:/data:rw":无效的卷规范:'D:\Projects\MyProject\db\redis:/data'

这个错误有两个原因:

  1. 它无法访问 D: 磁盘
  2. 卷路径不能包含 \ 字符
    • docker-compose 会添加它们,然后责怪你!!
    • 使用 COMPOSE_CONVERT_WINDOWS_PATHS=0 来停止这种无聊的事情。

我建议在你的 docker-compose.yml 文件中记录你的额外的虚拟机共享文件夹映射,因为你可能需要再次卸载 VirtualBox 并重置共享文件夹,无论如何,你的同事们都会感谢你的。


你先生,是一个优秀的人。 - AaronHS

1
所有其他答案在当时都很好,但现在(Docker Toolbox v18.09.3)一切都可以直接使用。您只需要将共享文件夹添加到VirtualBox VM中即可。
Docker Toolbox会自动将C:\Users添加为共享文件夹/c/Users,并在虚拟Linux机器下使用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的虚拟机的设置,打开共享文件夹部分并添加新的共享文件夹:

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

    按两次OK并关闭Virtual Box GUI。

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

就这些。主机下所有路径在D:\dev下的路径现在应该在docker-compose.yml中的挂载中起作用了。


1

可以通过三种工具的组合来实现:docker-machine mountrsyncinotifywait

TL;DR

基于下面所有内容的脚本在这里

假设您的docker-compose.ymlrun_web.sh位于/home/jdcaballerov/web

  1. Mount directory on machine which has same path as you have it on your host docker-machine machine:/home/jdcaballerov/web /tmp/some_random_dir
  2. Synchronize mounted directory with dir on your host rsync -r /home/jdcaballerov/web /tmp/some_random_dir
  3. Synchronize on every change of files in your directory:

    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 计算机上。


0

总结了这里的帖子后,附上更新的脚本,以创建额外的主机挂载点,并在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'

0

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

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


感谢您的回答。它在同一个目录中。然而,我注意到卷没有被挂载。文件不可用,这就是问题所在。如何添加它们。结构类似于http://www.syncano.com/configuring-running-django-celery-docker-containers-pt-1/。 - jdcaballerov
请确保您拥有最新版本的Docker和Docker Compose。 - Thomasleveil
docker:Docker版本1.6.0,构建4749651,docker-machine版本0.2.0(8b9eaf2),docker-compose 1.2.0 - jdcaballerov
你是否对来自http://www.syncano.com/configuring-running-django-celery-docker-containers-pt-1/的Dockerfile进行了任何修改? - Thomasleveil
1
是的,我确实添加了代码并创建了目录。问题在于当docker-compose执行时,它会覆盖卷:volumes: - .:/app 并留下一个空目录。我在compose中注释了volumes,这样就可以正常工作了。 - jdcaballerov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接