如何在Docker容器中挂载主机目录

897

我正在尝试将主机目录挂载到Docker容器中,以便在主机上进行的任何更新都会反映在Docker容器中。

我在哪里做错了呢?以下是我的操作步骤:

kishore$ cat Dockerfile

FROM ubuntu:trusty
RUN apt-get update
RUN apt-get -y install git curl vim
CMD ["/bin/bash"]
WORKDIR /test_container
VOLUME ["/test_container"]

kishore$ tree
.
├── Dockerfile
└── main_folder
    ├── tfile1.txt
    ├── tfile2.txt
    ├── tfile3.txt
    └── tfile4.txt

1 directory, 5 files kishore$ pwd /Users/kishore/tdock

kishore$ docker build --tag=k3_s3:latest .

Uploading context 7.168 kB
Uploading context
Step 0 : FROM ubuntu:trusty
 ---> 99ec81b80c55
Step 1 : RUN apt-get update
 ---> Using cache
 ---> 1c7282005040
Step 2 : RUN apt-get -y install git curl vim
 ---> Using cache
 ---> aed48634e300
Step 3 : CMD ["/bin/bash"]
 ---> Running in d081b576878d
 ---> 65db8df48595
Step 4 : WORKDIR /test_container
 ---> Running in 5b8d2ccd719d
 ---> 250369b30e1f
Step 5 : VOLUME ["/test_container"]
 ---> Running in 72ca332d9809
 ---> 163deb2b1bc5
Successfully built 163deb2b1bc5
Removing intermediate container b8bfcb071441
Removing intermediate container d081b576878d
Removing intermediate container 5b8d2ccd719d
Removing intermediate container 72ca332d9809

kishore$ docker run -d -v /Users/kishore/main_folder:/test_container k3_s3:latest c9f9a7e09c54ee1c2cc966f15c963b4af320b5203b8c46689033c1ab8872a0ea

kishore$ docker run -i -t k3_s3:latest /bin/bash

root@0f17e2313a46:/test_container# ls -al
total 8
drwx------  2 root root 4096 Apr 29 05:15 .
drwxr-xr-x 66 root root 4096 Apr 29 05:15 ..

root@0f17e2313a46:/test_container# exit exit

kishore$ docker -v
Docker version 0.9.1, build 867b2a9

  • 我不知道如何检查boot2docker的版本

面临的问题和问题:

  1. 我需要将main_folder链接到docker容器内部存在的test_container文件夹中,应该怎样做?
  2. 我需要自动完成这个过程。不使用run -d -v命令,有什么方法可以实现?
  3. 如果boot2docker崩溃了会发生什么?除了Dockerfile之外,Docker文件存储在哪里?

2
FYI,根据此评论,这个问题在 Docker 中已经被解决了。我正在我的 Mac 上使用 Boot2Docker。我可以使用 -v 选项,将我的本地目录挂载到容器中。 - Marco
1
以下任何答案都不符合原帖中的第二个要求。他们想要从Dockerfile中实现,而不是使用docker run命令。我也在思考。 - Josh Albert
请不要给您的图像打上“latest”的标签!https://medium.com/@mccode/the-misunderstood-docker-tag-latest-af3babfd6375 - mteam88
27个回答

698

你可以有几种方法来实现这一点。最简单的方法是使用dockerfile中的ADD命令,如下所示:

ADD . /path/inside/docker/container

然而,在构建 Dockerfile 之后对该目录进行的任何更改都不会在容器中显示。这是因为当构建容器时,Docker 将该目录压缩为 .tar 文件,并将其上传到容器中永久保存。

第二种方法是您尝试的方法,即挂载卷。由于要尽可能便携,您无法在 Dockerfile 中将主机目录映射到 Docker 容器目录,因为主机目录可能会根据正在运行的机器而变化。要将主机目录映射到 Docker 容器目录,您需要在使用 docker run 命令时使用 -v 标志,例如:

# Run a container using the `alpine` image, mount the `/tmp`
# directory from your host into the `/container/directory`
# directory in your container, and run the `ls` command to
# show the contents of that directory.
docker run \
    -v /tmp:/container/directory \
    alpine \
    ls /container/directory

我尝试过这样做:kishore$ docker -v /Users/kishore/main_folder:/test_container Docker版本0.9.1,构建867b2a9kishore$ docker run -v /Users/kishore/main_folder:/test_container用法:docker run [OPTIONS] IMAGE [COMMAND] [ARG...]在新容器中运行命令 -P, --publish-all=false: 将所有暴露的端口发布到主机接口 -a, --attach=[]: 附加到标准输入、标准输出或标准错误。 -c, --cpu-shares=0: CPU份额(相对权重) - Kishore
3
如果没有指定主机挂载,您能否同时拥有 ADD 和 -v 卷挂载?使用默认 app_dir。 - alexzg
52
在我的Mac OS X系统上,我使用docker-machine和VirtualBox成功运行了这个命令。但我发现一个需要注意的地方是,主机目录必须是绝对路径而不是相对路径。请注意,本人是通过试错方法发现这一点的。 - jbustamovej
74
ADD 实际上并不是“挂载”,它会将文件复制到容器中,使其持久化存在。 - kev
我使用docker-compose...所以对于我有用的是在我的docker-compose.yaml文件中添加一个卷挂载规范。service_name: build: . volumes: - .:/usr/src/app links: - postgres ports: - "9999:9999" command: --db_host=postgres - eMTy
显示剩余4条评论

279

这个问题的用户正在使用 Docker version 0.9.1, build 867b2a9,我会为您提供适用于 Docker 版本 >= 17.06 的答案。

您希望在容器目录中保持本地目录同步,可以通过挂载类型为 bind 的卷来实现。这将绑定源目录(您的系统)和目标目录(Docker 容器中的目录)。它几乎与在 Linux 上挂载目录相同。

根据 Docker 文档,现在挂载的合适命令是 mount 而不是 -v。以下是它的文档:

  • --mount:由多个键值对组成,由逗号分隔。每个键/值对采用 <key>=<value> 元组的形式。 --mount 语法比 -v--volume 更冗长,但是键的顺序并不重要,并且标志的值更容易理解。

  • 挂载的 type,可以是 bindvolumetmpfs(我们将使用 bind

  • 挂载的 source。对于绑定挂载,这是 Docker 守护程序主机上文件或目录的路径。可以指定为 sourcesrc

  • 挂载的 destination 取其值作为文件或目录在容器中将被挂载的路径。可以指定为 destinationdsttarget

因此,要将当前目录(源)与 /test_container(目标)挂载起来,我们将使用以下命令:

    docker run -it --mount src="$(pwd)",target=/test_container,type=bind k3_s3

如果这些挂载参数中有空格,您必须在它们周围加上引号。当我知道它们没有时,我会使用`pwd`代替:


如果这些挂载参数中有空格,您必须在它们周围加上引号。当我知道它们没有时,我会使用`pwd`代替:
    docker run -it --mount src=`pwd`,target=/test_container,type=bind k3_s3

你还需要处理文件权限,参见这篇文章


2
我已经点赞了你的回答,因为我喜欢它的简洁性和正确性,但是鉴于我的回答(https://dev59.com/d2Ag5IYBdhLWcg3wlLqh#51829247),你可能想要更新一下它。 - rainabba
3
我猜这个回答发出后文档已经更新,现在建议使用卷而非挂载。在做任何决定前,值得仔细查看一遍最新的文档。 - Sipty
1
@Sipty 可能指的是以下内容:"如果您正在开发新的 Docker 应用程序,请考虑改用命名卷。您无法使用 Docker CLI 命令直接管理绑定挂载。" - Aluthren
2
Docker仍然推荐在开发用例中使用绑定挂载,详情请见:https://docs.docker.com/develop/dev-best-practices/ "在开发过程中,有一种情况可以使用绑定挂载,即您可能想将源目录或刚构建的二进制文件挂载到容器中。对于生产环境,请使用卷替代绑定挂载,并将其挂载到与开发期间所挂载的绑定挂载相同的位置。" - kjones
这个很好用,但是如何挂载到现有的容器上呢? - Yatin Mistry
这对我有用...请问,有人知道它是否可以放在Dockerfile中吗? - xCovelus

137
你可以在命令行界面使用-v选项,但这个功能在Dockerfile中不可用。
docker run -t -i -v <host_dir>:<container_dir>  ubuntu /bin/bash

在这里,host_dir 是你想要挂载的主机目录。 如果容器中的目录不存在,你不需要担心,Docker 会自动创建它。

如果你在主机机器上对 host_dir 进行任何更改(在 root 权限下),容器中将会看到这些更改,反之亦然。


12
这是一个很好的、干净利落的例子,用来说明主机挂载选项。它可以通过使用真实目录进行改进:docker run -t -i -v /tmp:/tmp ubuntu /bin/bash,其中主机的 /tmp 目录将被挂载到容器的 /tmp 目录上。现在,您可以从容器中 touch /tmp/hello-world,并在主机上看到文件出现了。 - Stephan Henningsen
7
运作完美。但是这种挂载方式会将目录所有者设置为root,并且还要求容器以root身份运行。当以非root身份运行容器时,我们如何将目录挂载为非root? - Arun

91

需要进行两次挂载:

第一次挂载从您的主机到系统的目录

第二次挂载从boot2docker中的新目录到容器中,如下所示:

我猜这里的许多帖子可能正在使用两个boot2docker,您看不到任何内容的原因是您正在从boot2docker而不是从主机挂载目录。

您基本上需要进行两次连续挂载:

  • 1) Mount local system on boot2docker

    sudo mount -t vboxsf hostfolder /boot2dockerfolder
    
  • 2) Mount boot2docker file on linux container

    docker run -v /boot2dockerfolder:/root/containerfolder -i -t imagename
    

当你在容器文件夹中使用ls命令时,你将会看到你的主机文件夹的内容。


1
最佳答案。我必须补充说明(在Windows下),_mount_命令中的_hostfolder_是指文件夹共享名称。这个名称可以在Windows终端(cmd)中使用net share命令查看。您需要在VirtualBox GUI中添加该共享文件夹(如果按照我的逻辑,请使用相同的名称)。如果您忘记共享它,虚拟机将会工作,但您将面临权限和刷新性能方面的问题。 - Kir Kanos
1
挂载命令如何知道 /boot2docker 文件夹所在的主机? - alfonsodev
我遇到了错误:mount: unknown filesystem type 'vboxsf',使用的是Ubuntu客户机和主机。 - nnyby
"vboxsf" 是 VirtualBox 共享文件夹 "文件系统" 驱动程序。必须通过安装 VirtualBox 扩展来将其配置到虚拟机中。boot2docker 映像必须未安装这些扩展。VirtualBox 有一个 "设备" 功能,可以将 VirtualBox 添加 ISO 映像作为虚拟卷 "插入"。通常可以使用 mount /dev/cdrom /mnt 挂载它,进入 /mnt 并运行 bash VBoxLinuxAdditions。可能需要安装 gcc、kernel 或其他依赖项。 - Andrew Wolfe
mount.vboxsf:安装失败,错误为:“没有这样的文件或目录” 挂载:将mymapfoldername挂载到/boot2dockerfolder上失败:没有这样的文件或目录。 - vee

35

对于想要将文件夹挂载到当前目录的人:

docker run -d --name some-container -v ${PWD}/folder:/var/folder ubuntu

1
正是我需要的 Docker版本20.10.14,没有多余的东西。谢谢! - Wesley Cheek

34

你能否使用boot2docker或类似工具在Mac系统上运行Docker?

我有同样的体验-命令是正确的,但是容器中没有挂载任何(合理的)内容。

事实证明-这已经在Docker文档中解释了。当你输入docker run -v /var/logs/on/host:/var/logs/in/container ...时,/var/logs/on/host实际上是从boot2docker虚拟机映像中映射而来的,而不是你的Mac。

你需要将共享文件夹通过虚拟机传输到你的实际主机(在我的情况下是Mac)。


1
谢谢!这对我解决一个相关问题很有帮助。Postgres在Docker中运行时绑定到了VBox IP,而不是本地主机,如果不是在OS X上运行的话,它会绑定到localhost。 - Peter Becich
2
那个指向 Docker 文档的 URL 已经失效了。 - digitaldavenyc
哈哈,这是最好笑的笑话了...他们真的可以给一点提示。 - user2834172
3
您的文档链接无法访问(404错误)。 - Factor Mystic
3
我很好奇看到一个将共享文件夹通过虚拟机传输到实际主机的示例。 - Derek

30

我正在尝试将我的SailsJS应用程序运行在Docker容器中,以保持我的物理机器的干净。

我使用以下命令将我的SailsJS/NodeJS应用程序挂载到/app目录下:

cd 我的源代码文件夹
docker run -it -p 1337:1337 -v $(pwd):/app 我的Docker/image_with_nodejs_etc镜像

8
在Dockerfile中如何实现这个? - orad

28

[更新] 截至2017年6月, Docker for Mac已经解决了所有烦人的部分,你不必再与VirtualBox打交道。它允许您使用/private前缀将本地主机上的基本所有内容映射到一个地方。更多信息请看这里[/更新]

所有当前答案都谈到了Boot2docker。由于现在废弃不用,推荐使用docker-machine,以下方法适用于docker-machine:

首先,ssh进入docker-machine虚拟机并创建我们将要映射到的文件夹:

docker-machine ssh $MACHINE_NAME "sudo mkdir -p \"$VOL_DIR\""

现在将文件夹分享给VirtualBox:
WORKDIR=$(basename "$VOL_DIR")
vboxmanage sharedfolder add "$MACHINE_NAME" --name "$WORKDIR" --hostpath "$VOL_DIR" --transient

最后,再次ssh进入docker-machine,挂载我们刚共享的文件夹:
docker-machine ssh $MACHINE_NAME "sudo mount -t vboxsf -o uid=\"$U\",gid=\"$G\" \"$WORKDIR\" \"$VOL_DIR\""

注意:对于UID和GID,您可以使用任何整数,只要它们尚未被使用即可。
这在OS X El Capitan上经过docker-machine 0.4.1和docker 1.8.3的测试。

1
经过几个小时的苦思冥想,你的答案起作用了。这应该是“docker-machine”文档的一部分。 - Abhijit Sarkar
为什么不使用“-v”标志呢? - igracia
2
@igracia 在OS X(或Windows)中,-v标志的工作方式与您期望的不同。Docker在虚拟机中运行,因此-v中引用的文件夹指向虚拟机中的文件夹,而不是根操作系统中的文件夹。这种技术采取了将虚拟机文件夹映射回根操作系统的下一步。 - Patrick Gunderson
1
在我的情况下(OS X),文件夹位于主机中,而不是虚拟机中,并且它可以正常工作。Docker工具负责将其导出到虚拟机,然后再导入到容器中。 - igracia
1
我提到的规则中唯一的例外是,VM会自动挂载/Users以供docker共享。这意味着如果您的工作文件在~/*中,则可以开始使用。如果您的开发环境设置为在/development或任何其他位置运行,而不是在/Users中,则会遇到问题。https://docs.docker.com/engine/userguide/containers/dockervolumes/ - Patrick Gunderson
在Osx上,vboxmanage实际上是“VBoxManage”,可以通过virtualbox扩展包进行下载。看起来很琐碎,但我刚刚浪费了30分钟尝试弄清楚如何获取我已经拥有的命令。 - Steve B.

27

使用命令行:

docker run -it --name <WHATEVER> -p <LOCAL_PORT>:<CONTAINER_PORT> -v <LOCAL_PATH>:<CONTAINER_PATH> -d <IMAGE>:<TAG>

使用 docker-compose.yaml
version: '2'
  services:
    cms:
       image: <IMAGE>:<TAG>
       ports:
       - <LOCAL_PORT>:<CONTAINER_PORT>
       volumes:
       - <LOCAL_PATH>:<CONTAINER_PATH>

假设 :

  • 镜像名称: k3_s3
  • 标签: latest
  • 本地端口: 8080
  • 容器端口: 8080
  • 本地路径: /volume-to-mount
  • 容器路径: /mnt

示例 :

  1. 首先创建 /volume-to-mount 目录。(如果存在则跳过)
$ mkdir -p /volume-to-mount
  1. docker-compose -f docker-compose.yaml up -d
version: '2'
  services:
    cms:
       image: ghost-cms:latest
       ports:
       - 8080:8080
       volumes:
       - /volume-to-mount:/mnt
  1. 验证你的容器:
docker exec -it CONTAINER_ID ls -la /mnt

25
docker run -v /host/directory:/container/directory -t IMAGE-NAME /bin/bash

docker run -v /root/shareData:/home/shareData -t kylemanna/openvpn /bin/bash

在我的系统中,我纠正了nhjk的答案,当你添加-t标志时,它可以无缝运行。


您能否在本地文件系统中创建/更新文件,并在Docker容器内看到更改? - avimehenwal

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