随笔 - 5  文章 - 0  评论 - 2 

Docker container中mount文件内容无法同步的问题解决

  这篇博文主要讲述了我遇到的一个Docker使用mount volume中的一个“坑”,即在宿主机中修改了某个已经mount进container的文件,但在container中发现却没有改变。我们尝试找解决方法,也顺便尝试知道是什么原因。

1. 问题描述

在Linux(Oracle Linux Server release 7.7)本地创建一个文件"test.txt"(默认权限664),然后再用如下命令mount进一个container:

docker run -v "/home/ec2-user/deleme_me/test.txt:/test.txt" --rm -it ubuntu:18.04 /bin/bash

在container中看到了"/test.txt"的内容与宿主机相同。

然后在宿主机上用vim(7.4.1099)修改文件内容,增加一行内容,再到container中,发现/test.txt并没有随之变化。

很显然,这不是我们所期望的,按照我们的设想,在这样的mount之后,宿主机和container中文件的变更应该是双向同步的,即在容器中改变文件内容,宿主机中应当即时得到更新,反之亦然,因为mount本身就表示同一个文件,只是借由docker使其其出现在了不同的 Mount namespaces 中。

那究竟是什么原因导致这样与设想大相径庭的结果呢?

2. 问题分析

我们知道,在Docker中,mount volume的原理是借用了Linux Namespace中的 Mount NameSpace,隔离系统中不同进程的挂载点视图,实际文件是没有变化的,比如上面的例子,在container中,/bin/bash实际就是一个运行在宿主机上的进程,被Docker用Linux分别隔离了Mount Namespace、UTS Namespace、IPC Namespace、PID Namespace、Network Namespace和User Namespace,使得它看上去好像运行在了一个独立的、相对隔离的系统上,但实际它的一切资源都是宿主机在不同Namespace中的一个投影,文件也不例外。

所谓文件没有变化,我们如何证明呢?在Linux中,证明文件是否相同的根本途径是,判断其inode,如果两个文件的inode相同,两个文件必定为同一文件,从而两个文件的内容也必然相同。

那我们来看看宿主机和container中看到的text.txt是不是一样:

// container中
root@e365010a98f6:/# stat test.txt File: test.txt Size: 7 Blocks: 8 IO Block: 4096 regular file Device: ca01h/51713d Inode: 4510751 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1000/ UNKNOWN) Gid: ( 1000/ UNKNOWN) Access: 2020-04-26 14:03:12.898485003 +0000 Modify: 2020-04-26 14:03:12.898485003 +0000 Change: 2020-04-27 01:35:38.779485729 +0000 Birth: -

 

// 宿主机中
$ stat test.txt File: 'test.txt' Size: 7 Blocks: 8 IO Block: 4096 regular file Device: ca01h/51713d Inode: 4510751 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1000/ec2-user) Gid: ( 1000/ec2-user) Access: 2020-04-26 14:03:12.898485003 +0000 Modify: 2020-04-26 14:03:12.898485003 +0000 Change: 2020-04-27 01:35:38.779485729 +0000 Birth: -

以上两个代码块中,上图是container中的,下图时候宿主机中的。我们看到在容器中和宿主机中的test.txt的inode号都是4510745,即可认为是同一文件。

这时,回到我们刚开始遇到的那个问题,我们在宿主机中修改test.txt文件,用Vim打开文件,追加一行,然后保存退出,然后打开container中的文件,发现出现了怪事,container中的文件没有更新,也就是重现了我们一开始描述的问题。

那我们再来看看他们的文件信息:

// container中
root@e365010a98f6:/# stat test.txt File: test.txt Size: 7 Blocks: 8 IO Block: 4096 regular file Device: ca01h/51713d Inode: 4510751 Links: 0 Access: (0664/-rw-rw-r--) Uid: ( 1000/ UNKNOWN) Gid: ( 1000/ UNKNOWN) Access: 2020-04-26 14:03:12.898485003 +0000 Modify: 2020-04-26 14:03:12.898485003 +0000 Change: 2020-04-27 01:42:25.252402227 +0000 Birth: -

 

// 宿主机中
$ stat test.txt File: 'test.txt' Size: 14 Blocks: 8 IO Block: 4096 regular file Device: ca01h/51713d Inode: 6553165 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1000/ec2-user) Gid: ( 1000/ec2-user) Access: 2020-04-27 01:42:25.252402227 +0000 Modify: 2020-04-27 01:42:25.252402227 +0000 Change: 2020-04-27 01:42:25.252402227 +0000 Birth: -

由上面的输出可以看到,container中的inode没有变化,但宿主机中的已经变化了,也就是说,从文件系统层面来看,此时container中和宿主机中它们已经不是同一个文件了。既然不是同一个文件,那又有什么同步可言?

那好了,那问题的初步原因明白了,就是用vim在宿主机编辑了被mount进container的文件后,宿主机上的文件inode变化了,导致两个文件内容无法同步。

那问题就变成vim为何有什么神奇的逻辑呢?

原来,Linux默认情况下,vim为了防止在你修改文件的过程中,由于磁盘或者系统出现问题而导致当前被修改的文件的损坏,它做了类似如下逻辑:

  • 复制出一个需要修改文件的副本,命名为在原来文件的基础上增加".swp"后缀以及"."前缀
  • 修改内容保存到有".swp"后缀的文件,并flush到磁盘
  • 交换原文件和swp文件的名称
  • 删除swp文件

如此一来,在宿主机看来,原来inode就被释放掉了,可是在container看来,他还是需要留着这个inode。

3. 解决方法

提到解决,实际上无解的,因为以上出现的每一方站在其自身的角度看,都是合理的,这些逻辑也是by design的。于是乎,我们就寻找workaround的角度来尝试绕过我们的问题吧。

以下几种方法均可以成功绕过该问题:

  • 用echo等代替vim文件修改

既然这个修改文件的逻辑是vim引入的,那么我们替换vim就可以解决问题了,增加文件内容的方法很多,比如我们可以echo代替:

root@2c8d3dfc7d57:/# echo hello >> test.txt
  • 修改vim配置

打开vim,输入

:scriptnames

找到vimrc的路径,例如是"/etc/vimrc",再打开"/etc/vimrc",添加如下两行:

set backup
set backupcopy=yes

这样可以解决问题,不过也有一个很大的副作用,那就是每次用vim编辑文件保存之后,vim会生成一个类似该被修改文件,但末尾增加了一个"~"后缀,用以保存修改之前的文件内容。

  • 修改文件权限

我们发现,当文件的权限修改为"其他user有写权限"后,以上说的vim逻辑就变了,修改保存后,原文件的inode不再变化,这就使得我们workaround变得非常简单:

$ chmod 666 test.txt

这个应该是目前为止,最为轻量的workaround了。

4. 参考

1. https://forums.docker.com/t/modify-a-file-which-mount-as-a-data-volume-but-it-didnt-change-in-container/2813

2. https://unix.stackexchange.com/questions/36467/why-inode-value-changes-when-we-edit-in-vi-editor

 

 

 

posted on 2020-04-27 15:03  青舟生  阅读(361)  评论(0编辑  收藏