编辑 docker 容器中的文件

写在前面

为什么要这样做?

实际上我们并不需要也不建议直接编辑容器中的文件。Docker 容器是不可变的工作单元,用于运行单个、特定的进程。镜像应该在没有任何干预的情况下够建和运行。

只有在开发期间,对 Docker 容器中的文件进行编辑可能才有些用处,这让我们在无需重新够建镜像的状态下验证我们的修改是否达到了预期的效果,可以达到节省时间、提高开发效率的目的,但是在完成验证后,应该删除添加到镜像中的多于软件包,并将验证后的结果持久化到镜像中。

另外需要提醒的一点是,当我们在一个运行着的容器中编辑一个文件后需要确保所依赖这个文件的进程收到了文件编辑的通知并进行了配置更新,如果没有类似的通知机制,需要手动重启这些进程使修改生效。

本文假设你所使用的容器中没有 vi 等文本编辑工具,我们以 openjdk:11 作为演示镜像:

1
2
3
4
➜ docker run -it openjdk:11 bash
root@d0fb3a0b527c:/# vi Lol.java
bash: vi: command not found
root@d0fb3a0b527c:/#

下面介绍五种常用方法:

方法1:使用挂载

准备 Dockerfile:

1
2
FROM openjdk:11
WORKDIR "/app"

编译镜像:

1
docker build -t lol .

最后,运行带有挂载的容器:

1
docker run --rm -it --name=lol -v $PWD/app-vol:/app lol bash

如果本地 $PWD/app-vol 目录不存在,会被自动创建。此后在 $PWD/app-vol 下的文件操作会映射在容器的 /app 目录下。

方法2:安装编辑器

1
2
3
4
docker run --rm -it --name=lol lol bash

root@4b72fbabb0af:/app# apt-get update
root@4b72fbabb0af:/app# apt-get -y install vim

如果需要重复使用,更好的做法是写在 Dockerfile 中:

1
2
3
4
FROM openjdk:11
RUN ["apt-get", "update"]
RUN ["apt-get", "-y", "install", "vim"]
WORKDIR "/app"

方法3:将文件拷贝到正在运行的容器中

1
docker cp Lol.java lol:/app

另一个与之类似的方法是将 docker exec 和 cat 结合使用,下边的命令同样把 Lol.java 文件复制到了正在运行的容器中:

1
docker exec -i lol sh -c 'cat > /app/Lol.java' < Lol.java

方法4:使用 Linux 工具

虽然容器中通常没有安装编辑工具,但是其他 Linux 工具,如:sed, awk, echo, cat, cut 等是具备的,可以派上用场。比如 sed 和 awk 可以编辑文件的适当位置,还可以将 echo, cat, cut 联合起来并借助强大的重定向流创建和编辑文件。正如前文所示,这些工具可以与 docker exec 命令结合使用,从而发挥更强大的威力。

方法5:使用远程 vim(或其他编辑器)

这种方法只是为了开拓思路,并不会在实际中使用。

修改 Dockerfile:

1
2
3
4
5
6
7
8
9
10
FROM openjdk:11
RUN ["apt-get", "update"]
RUN ["apt-get", "install", "-y", "openssh-server"]
RUN mkdir /var/run/sshd
RUN echo 'root:lollol0' | chpasswd
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN ["/etc/init.d/ssh", "start"]
EXPOSE 22
WORKDIR "/app"
CMD ["/usr/sbin/sshd", "-D"]

因为我们要借助 scp 来远程进行文件编辑,所以需要安装 openssh-server 并开放其端口。

编译并运行:

1
2
docker build -t lol .
docker run --rm -p 2222:22 -d --name=lol lol

现在我们可以使用以下命令来编辑 Lol.java 文件了:

1
vim scp://root@localhost:2222//app/Lol.java

注:在 vi 中需要先执行 :set bt=acwrite 命令再去编辑文件,相关讨论见:https://github.com/vim/vim/issues/2329

编辑完成保存并退出后,可以使用下边的命令来验证文件确实被创建和保存了:

1
docker exec -it lol cat /app/Lol.java