如何向现有的Docker容器添加卷?

539

我有一个Docker容器,我是通过在Ubuntu上安装Docker并执行以下命令来创建的:

sudo docker run -i -t ubuntu /bin/bash

我立刻开始安装Java和其他一些工具,花了一些时间进行操作,并通过

停止了容器。
exit

然后我想添加一个卷,但发现这并不像我想象的那么简单。如果我使用sudo docker -v /somedir run ...,那么我最终会得到一个全新的容器,因此我必须安装Java并执行之前已经完成的操作,才能获得一个挂载了卷的容器。
所有有关从主机挂载文件夹的文档似乎都暗示着在创建容器时可以执行挂载卷的操作。因此,为了避免重新配置一个全新的容器,我唯一的选择就是将现有的容器提交到仓库,并将其用作新容器的基础,同时挂载卷。
这确实是向现有容器添加卷的唯一方法吗?

不确定是否有帮助,但当我从相同的位置执行 docker exec -it d1a2cc990208 bash 时,我进入了挂载卷的容器。 - Samos
10个回答

680

您可以提交现有的容器(即从容器更改创建新镜像),然后使用新的挂载运行它。

例如:

$ docker ps  -a

CONTAINER ID        IMAGE                 COMMAND                  CREATED              STATUS                          PORTS               NAMES
5a8f89adeead        ubuntu:14.04          "/bin/bash"              About a minute ago   Exited (0) About a minute ago                       agitated_newton

$ docker commit 5a8f89adeead newimagename
$ docker run -ti -v "$PWD/somedir":/somedir newimagename /bin/bash

如果一切正常,请停止您的旧容器并使用这个新容器。

您也可以使用容器名称提交容器,例如:

docker commit agitated_newton newimagename

就是这样 :)


50
如果你因为某些原因需要让新容器使用旧名称,可以在删除旧容器后使用docker rename - Dirk
17
我想指出你提到 newnameofcontainer 的地方应该改为 new_image_name,因为 docker commit 在你的系统上创建一个新的 镜像。接下来,在进行 docker run 时,你实际上使用要从中运行新容器的 镜像 名称。上面的方法可以工作,但我只是想澄清一下,对其他人来说,上面的 newnameofcontainer 实际上是一个新镜像的名称占位符。谢谢!回答非常好。噢,你可以使用 docker image ls 命令查看第一个 docker commit 命令创建的新镜像。 - FireDragon
6
实际上,如果你想从一个镜像开始启动,你不需要创建新的容器。 只需运行命令 docker run -v /srv/a:/tmp ubuntu:14.04 即可。 - chris
6
这不会保留在原始容器中运行的进程。 - gbajson
4
提交容器不会保留卷绑定,有什么解决办法吗? - mecaf
显示剩余7条评论

179

我们没有任何方法可以在运行的容器中增加容量,但为了实现这个目标,您可以使用以下命令:

在容器和本地文件系统之间复制文件/文件夹:

docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH

docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH

参考:

https://docs.docker.com/engine/reference/commandline/cp/


106
挂载卷和在容器之间复制文件之间存在巨大的区别... - Jules
72
无论如何,它帮了我。我不知道“docker cp”命令,并且正试图实现这个目标——从正在运行的容器复制文件到主机。 - Ivan
7
这不是一个挂载点,但它很方便,可以在容器和本地主机之间传输文件。 - linehrr
9
我不明白为什么有这么多赞,这个回答与问题所问完全相反,是十分错误的。 - João Matos
22
虽然这不完全符合原帖作者的情况,但我来到这里是为了寻找一种将卷附加到正在运行的容器的方法。我的目标是在容器运行时即时地获取/插入其中的一些内容,假设我无法控制容器创建并且没有停止它的选项。这个答案实际上指出了唯一实现这一目标的方法。因此,点赞并感谢! - AlexanderF
显示剩余4条评论

113

我已经成功将主机上的/home/<user-name>文件夹挂载到现有(未运行)容器的/mnt文件夹中。您可以按照以下方式操作:

  1. 打开相应已停止容器的配置文件,可以在/var/lib/docker/containers/99d...1fb/config.v2.json找到(对于早期版本的docker可能是config.json)。

  2. 找到MountPoints部分,在我的情况下为空:"MountPoints":{}。然后将其内容替换为类似以下内容(您可以从具有正确设置的另一个容器复制正确的内容):

"MountPoints":{"/mnt":{"Source":"/home/<user-name>","Destination":"/mnt","RW":true,"Name":"","Driver":"","Type":"bind","Propagation":"rprivate","Spec":{"Type":"bind","Source":"/home/<user-name>","Target":"/mnt"},"SkipMountpointCreation":false}}

或者相同(格式化):

  "MountPoints": {
    "/mnt": {
      "Source": "/home/<user-name>",
      "Destination": "/mnt",
      "RW": true,
      "Name": "",
      "Driver": "",
      "Type": "bind",
      "Propagation": "rprivate",
      "Spec": {
        "Type": "bind",
        "Source": "/home/<user-name>",
        "Target": "/mnt"
      },
      "SkipMountpointCreation": false
    }
  }
  1. 重新启动docker服务: service docker restart

我在Ubuntu 18.04.1和Docker 18.09.0上尝试过,此方法对我有效。


8
谢谢您的回答。第三步非常重要。我还要补充一点,最好在进行写操作之前先停止Docker容器。 - buzypi
16
这是最佳答案,因为它完全保留了现有的容器。 以下是我的操作步骤:
  1. 停止 Docker 引擎:systemctl stop docker.service
  2. 编辑 config.v2.json 文件:vim <(jq . /var/lib/docker/containers/<container-ID>/config.v2.json)
  3. 将更新保存到文件中::w config.v2.json
  4. 退出 vim::q!
  5. 更新现有文件:jq -c . config.v2.json > /var/lib/docker/containers/<container-ID>/config.v2.json
  6. 启动 Docker 引擎:systemctl start docker.service
  7. 如有必要,启动容器:docker start <container-name/ID>
  8. 愉快使用 :-)
- Android Control
12
一个关键步骤是service docker restart。我尝试过docker restart <container>,但新配置没有被采用,并且被旧配置覆盖了。 - KFL
1
另外,jq 将帮助美化打印 JSON,以便更方便人类编辑:cat config.v2.json | jq . > config.json - KFL
2
在 Docker 版本 20.10.7 中,我似乎无法通过容器 ID 找到 config.v2.json - yode
显示剩余6条评论

28
Jérôme Petazzoni 在他的博客文章中介绍了一个非常有趣的主题,即如何在容器运行时将卷附加到容器。这并不是 Docker 的内置功能,但是可以实现。
正如他所指出的那样,

这在不基于块设备的文件系统上无法工作。

只有在 /proc/mounts 正确列出块设备节点时才能工作(正如我们上面所看到的,并不一定是真实情况)。

此外,我只在我的本地环境中进行了测试;我甚至没有尝试在云实例或其他环境中测试。


14
很遗憾,挂载卷的开关选项只能在run命令中找到。 docker run --help -v, --volume list Bind mount a volume (default []) 然而,有一种方法可以绕过这个问题,这样你就不必重新安装已经在容器中设置好的应用程序了。
  1. 导出容器 docker container export -o ./myimage.docker mycontainer
  2. 作为镜像导入 docker import ./myimage.docker myimage
  3. 然后运行docker run -i -t -v /somedir --name mycontainer myimage /bin/bash

1
FYI - docker container不是1.11.2上的有效命令(这是Synology目前支持的最新版本)。我找不到任何文档说明它是何时添加的。在这种情况下,第一个命令是docker export -o ./myimage.docker mycontainer - Chris R. Donnelly
1
使用 docker commit 更加简单,参见上面的答案 https://dev59.com/-14c5IYBdhLWcg3wHHGY#33956387 - Franklin Piat
1
这个方案虽然不会保留历史记录,但对于空间有限的情况仍是一个不错的解决方案。 - Spartan

8
在我为这个问题搜索了很长时间后,以下是有关在Docker Windows容器中使用注意事项:
条件: - Windows 10 - Docker Desktop(最新版本) - 使用Docker Windows容器镜像microsoft/mssql-server-windows-developer 问题: - 我想将主机文件夹挂载到Windows容器中。
解决方案如下: 1. 创建Docker容器: ``` docker run -d -p 1433:1433 -e sa_password= -e ACCEPT_EULA=Y microsoft/mssql-server-windows-developer ```
2. 进入容器的命令行: ``` docker exec -it cmd.exe ```
3. 创建目录: ``` mkdir DirForMount ```
4. 停止容器: ``` docker container stop ```
5. 提交容器变更: ``` docker commit ```
6. 删除旧容器: ``` docker container rm ```
7. 使用新镜像创建新容器并进行卷挂载: ``` docker run -d -p 1433:1433 -e sa_password= -e ACCEPT_EULA=Y -v C:\DirToMount:C:\DirForMount ```
通过以上步骤,我解决了在Docker Windows容器中挂载文件夹的问题。

0
您可以在启动脚本中停止和删除容器,附加现有卷,并从镜像重新启动。如果已经存在的分区保留数据,则不应该遇到任何信息丢失。这也适用于Dockerfile和Docker composer。
例如(solr镜像)。 (初始脚本)
#!/bin/sh
docker pull solr:8.5
docker stop my_solr
docker rm solr:8.5

docker create \
  --name my_solr \
  -v "/XXXX/docker/solr/solrdata":/var/solr \
  -p 8983:8983 \
  --restart unless-stopped \
  --user 1000:1000 \
  -e SOLR_HEAP=1g \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  solr:8.5
docker cp /home/XXXX/docker/solr/XXXXXXXX.jar my_solr:/opt/solr/contrib/dataimporthandler-extras/lib
docker start my_solr

第二卷的文件

#!/bin/sh
docker pull solr:8.5
docker stop my_solr
docker rm solr:8.5

docker create \
  --name my_solr \
  -v "/XXXX/docker/solr/solrdata":/var/solr \
  -v "/XXXX/backups/solr_snapshot_folder":/var/solr_snapshots \
  -p 8983:8983 \
  --restart unless-stopped \
  --user 1000:1000 \
  -e SOLR_HEAP=1g \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  solr:8.5
docker cp /home/XXXX/docker/solr/XXXXXXXX.jar my_solr:/opt/solr/contrib/dataimporthandler-extras/lib
docker start my_solr

-1

使用符号链接到已经挂载的驱动器:

ln -s 源路径 目标路径(已在运行的Docker上挂载)


-1

我的答案会略有不同。您可以停止容器,添加卷并重新启动它。如何操作,请按照以下步骤进行。

docker volume create ubuntu-volume
docker stop <container-name>
sudo docker run -i -t --mount source=ubuntu-volume,target=<target-path-in-container> ubuntu /bin/bash 

7
docker run 命令用于从镜像创建一个 新的 容器(参考链接:https://docs.docker.com/engine/reference/commandline/run/)。OP 正在询问如何向一个 已经存在的 容器添加卷。 - wxgeorge

-6

最好的方法是通过以下方式将本地文件系统中目录内的所有文件和文件夹复制到容器内:docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH

SRC_PATH 在容器内,DEST_PATH 在本地主机上。

然后执行 docker-compose down 命令,将卷附加到相同的 DEST_PATH 上,并使用 docker-compose up -d 命令运行 Docker 容器。

docker-compose.yml 文件中添加卷,如下所示:

volumes:
 - DEST_PATH:SRC_PATH

有其他更好的选项。 - MrR
确实,有更好的选择,而复制文件并不是挂载它们。此外,对于docker-compose选项给出的“volumes”定义是相反的: volumes:
  • HOST_PATH:CONTAINER_PATH
- Guillaume S.

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