如何从主机编辑Docker容器文件?

57

现在我找到了一种向容器映射主机文件的方法(-v选项),我想做相反的操作:

如何使用主机编辑器编辑正在运行的容器中的文件?

sshfs可能可以完成这项工作,但由于正在运行的容器已经是某种主机目录,因此我想知道是否有一种可移植的方法(适用于aufs、btrfs和device mapper)来实现这一点?

10个回答

49

最好的方法是:

  $ docker cp CONTAINER:FILEPATH LOCALFILEPATH
  $ vi LOCALFILEPATH
  $ docker cp LOCALFILEPATH CONTAINER:FILEPATH

$ docker exec存在局限性:它只能附加到正在运行的容器。

$ docker run存在局限性:它将创建一个新的容器。


9
这只是复制那些文件,然后再将它们复制回来。 - smatthewenglish

29

虽然有可能,且其他答案已经解释了如何操作,但如果可以的话,应该避免编辑Union文件系统中的文件。

你对卷的定义不完全正确——更多是绕过Union文件系统而不是在主机上暴露文件。例如,如果我执行以下操作:

$ docker run --name="test" -v /volume-test debian echo "test"

容器内的目录/volume-test不会成为联合文件系统的一部分,而是存在于主机的某个位置。我没有指定它在主机上的位置,因为我可能不在意 - 我不暴露主机文件,只是创建了一个可在容器和主机之间共享的目录。您可以通过以下命令确定它在主机上的确切位置:

$ docker inspect -f "{{.Volumes}}" test
map[/volume_test:/var/lib/docker/vfs/dir/b7fff1922e25f0df949e650dfa885dbc304d9d213f703250cf5857446d104895]

如果您只是需要快速编辑文件来测试某些内容,可以使用docker exec在容器中获取shell并直接进行编辑,或使用docker cp将文件复制出来,在主机上进行编辑,然后再次复制回去。


docker inspect 命令似乎只适用于 docker 1.7 版本。如需了解新版本上的映射检查方法,请参阅 https://www.adelton.com/docs/docker/docker-inspect-volumes-mounts。 - Dirk Vollmar
你能将这个答案推广到其他容器吗?在OSX上是否可能实现? - smatthewenglish
2
@s.matthew.english,MacOS 没有容器这个概念。在 MacOS 上,Docker 内部运行一个 xhyve Linux 虚拟机来托管 Docker 容器。卷路径只存在于此虚拟机中(对用户而言几乎是隐藏的)。 - Adrian Mouat
-v /volume-test 语法实现了 卷挂载,但我认为使用 绑定挂载 更为合适。 - MatrixManAtYrService

6

我们可以使用另一种方法来编辑工作容器中的文件(如果容器停止,则此方法将无法使用)。

逻辑如下:
-)从容器中复制文件到主机
-)使用主机编辑器在主机上编辑文件
-)将文件重新复制回容器

我们可以手动执行所有这些步骤,但我编写了一个简单的bash脚本,通过一次调用使其更加容易。

/bin/dmcedit:

#!/bin/sh
set -e

CONTAINER=$1
FILEPATH=$2
BASE=$(basename $FILEPATH)
DIR=$(dirname $FILEPATH)
TMPDIR=/tmp/m_docker_$(date +%s)/

mkdir $TMPDIR
cd $TMPDIR
docker cp $CONTAINER:$FILEPATH ./$DIR
mcedit ./$FILEPATH
docker cp ./$FILEPATH $CONTAINER:$FILEPATH
rm -rf $TMPDIR

echo 'END'
exit 1;

使用示例:

dmcedit CONTAINERNAME /path/to/file/in/container

该脚本非常简单,但对我来说运行良好。

欢迎提出任何建议。


@JensKohl,你能提供更详细的信息吗? - Mikl
1
你应该尝试使用 docker cp $FILEPATH $CONTAINER:...等 命令将文件复制回去。 - Joaquín M
@JoaquínM 谢谢!我已经编辑了我的答案。很高兴Docker改进了它的功能,并为我们提供了这个新功能。 - Mikl
在Windows上,我最终使用MINGW64控制台中的docker cp。这对我有用。因此,首先从容器复制到主机,进行编辑,然后将相同的文件复制回容器。 - Mustafa Temiz

4

将文件挂载到容器中有两种方式。看起来你需要使用绑定挂载

绑定挂载

该方式会直接将本地文件挂载到容器的文件系统中,容器端和主机端的路径都指向同一个文件,在任何一侧所做的修改都会在另一侧显示出来。

  • 挂载文件:
echo foo > ./foo
❯ docker run --mount type=bind,source=$(pwd)/foo,target=/foo -it debian:latest
# cat /foo
foo # local file shows up in container
  • 在另一个终端窗口中,编辑该文件:
❯ echo 'bar' > ./foo # make a hostside change
  • 回到容器中:
# cat /foo
bar # the hostside change shows up
# echo baz > /foo # make a containerside change
# exitcat foo
baz # the containerside change shows up

挂载卷

  • 挂载卷
❯ docker run --mount type=volume,source=foovolume,target=/foo  -it debian:latest
root@containerB# echo 'this is in a volume' > /foo/data
  • 本地文件系统不变
  • Docker 视为一个新的卷:
❯ docker volume ls
DRIVER    VOLUME NAME
local     foovolume
  • 创建一个具有相同卷的新容器
❯ docker run --mount type=volume,source=foovolume,target=/foo  -it debian:latest
root@containerC:/# cat /foo/data
this is in a volume # data is still available

语法: -v vs --mount

这两个选项的作用相同,-v 更加简练,--mount 则更加明确。

绑定挂载

-v /hostside/path:/containerside/path
--mount type=bind,source=/hostside/path,target=/containerside/path

卷挂载

-v /containerside/path
-v volumename:/containerside/path
--mount type=volume,source=volumename,target=/containerside/path

(如果未指定卷名,则会选择一个随机的卷名。)

文档试图说服您使用某些东西而不是仅仅告诉您它的工作方式,这很令人困惑。


1
这是我使用的脚本:

#!/bin/bash
IFS=$'\n\t'
set -euox pipefail


CNAME="$1"
FILE_PATH="$2"

TMPFILE="$(mktemp)"
docker exec "$CNAME" cat "$FILE_PATH" > "$TMPFILE"
$EDITOR "$TMPFILE"
cat "$TMPFILE" | docker exec -i "$CNAME" sh -c 'cat > '"$FILE_PATH"
rm "$TMPFILE"

当我修复问题但忘记更新此答案时的要点:

https://gist.github.com/dmohs/b50ea4302b62ebfc4f308a20d3de4213


我刚刚通过保留文件扩展名来改进了脚本,以便让编辑器突出显示文件内容。https://gist.github.com/mostafar/116a0cb79005955476b24c0960b72d5a - MostafaR

1
如果您认为您的卷是一个“网络驱动器”,那么它会更容易。要编辑位于此驱动器中的文件,您只需要打开另一台机器并连接到此网络驱动器,然后像正常情况下一样编辑文件。
如何仅使用docker来完成这个过程(不需要FTP/SSH等)?
1. 运行一个带有编辑器(VI,Emacs)的容器。在Docker Hub上搜索“alpine vim”。
示例:
docker run -d --name shared_vim_editor \
 -v <your_volume>:/home/developer/workspace \
jare/vim-bundle:latest
  1. 运行交互式命令:

docker exec -it -u root shared_vim_editor /bin/bash

希望这能帮到你。


0

我使用IDE的sftp插件。

  1. 为您的容器安装ssh服务器并允许root访问。
  2. 使用-p localport:22运行您的docker容器
  3. 从您的IDE安装sftp插件

使用sublime sftp插件的示例: https://www.youtube.com/watch?v=HMfjt_YMru0


你能提供一整套关于如何做这件事的指令吗?请包括安装SSH服务器所需的命令(例如apt-get或类似命令),以及设置SSH服务器以运行SFTP服务器所需的命令。同时,如果能提供至少一个编辑器同步设置的指令(例如sublime),那就更好了。回答应避免依赖外部链接,特别是YouTube链接。 - Micah Zoltu

0

我的做法是使用安装了docker包的Emacs。我建议使用Spacemacs版本的Emacs。请按照以下步骤进行:

1) 安装Emacs (说明)并安装Spacemacs(说明)

2) 在您的.spacemacs文件中添加docker

3) 启动Emacs

4) 找到文件(SPC+f+f)并键入/docker:<container-id>:/<path of dir/file in the container>

5) 现在您的Emacs将使用容器环境来编辑文件


似乎比在容器中直接运行emacs更糟糕...但是对我来说,emacs已经老旧了,而docker是新的。这个容器也可以运行shell吗? - nroose

-3
docker run -it -name YOUR_NAME IMAGE_ID /bin/bash

$>vi path_to_file

8
这将适用于一些容器,但很多容器只包含最小的环境,因此不会包括vi。 - mkirk

-4
以下方法适用于我:
docker run -it IMAGE_NAME /bin/bash
例如,我的镜像名为ipython/notebook
docker run -it ipython/notebook /bin/bash

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