Dockerでファイルをbind mountしたら同期されなかった話
何が起きたのか
僕の体験です。
ある日のこと
彡(゚)(゚)「自作アプリのコンテナ立てるやで」
彡(-)(-)「自作アプリの設定ファイルがあるんだけど運用中にちょくちょく変更が必要そうだし、その度にコンテナを再起動するのは面倒やな。せっかくアプリには設定ファイルを勝手にリロードする機能があるんだし…」
彡(゚)(゚)「せや!」
version: '3.9'
services:
app:
image: my-app
volumes:
- type: bind
source: ./my-config/config.yaml
target: /etc/my-config/config.yaml
mode: normal
彡(^)(^)「bind mountを使えばホスト側で設定ファイルを書き換えるだけでコンテナ内に反映されるで」
彡(゚)(^)「dockerのmountは便利やなぁ」
次の日
彡(゚)(゚)「早速設定ファイルを変更することになったわ」
彡(゚)(^)「こんなこともあろうかとbind mountしておいたからホスト側で設定ファイルを書き換えるだけで反映できるやで」
彡(^)(^)「vimで設定ファイルを開いてチョチョイのチョイっとw」
$ vim ./my-config/config.yaml
$ cat ./my-config/config.yaml
mode: busy
彡(^)(^)「これでオッケー!w」
更に次の日
彡(。)(;)「あれ、アプリが設定通りに動いてない!」
彡(。)(;)「なんでや!昨日確かに設定ファイルは書き換えたはず!」
$ cat ./my-config/config.yaml
mode: busy
彡(゚)(゚)「せやろ?一応コンテナの中のも見とくか」
# コンテナ内
$ cat /etc/my-config/config.yaml
mode: normal
彡(●)(●) 「は?」
なぜこうなったのか
I bet I know what's happening here...
If you are using some editor like vim, when you save the file it does not save the file directly, rather it creates a new file and copies it into place.
This breaks the bind-mount, which is based on inode. Since saving the file effectively changes the inode, changes will not propagate into the container.
When the container is restarted the new inode.
If you edit the file in place you should see changes propagate.
This is a known limitation of file-mounts and is not fixable.
Does this accurately describe the issue?
https://github.com/moby/moby/issues/15793#issuecomment-135411504
どうやらvimなどのエディタではファイルの編集時にファイルを直接更新するのではなく、「新しいファイルを作って」「元のファイルを新しいファイルで置き換える」ことがあるらしいです。そのためファイルのinodeが変わり、同期が切れてしまうと。
どうすれば良かったのか
ファイル単体ではなくディレクトリをbind mountする
ディレクトリごとbind mountすることでファイルが置き換えられても同期されるようになります。
version: '3.9'
services:
app:
image: my-app
volumes:
- type: bind
- source: ./my-config/config.yaml
+ source: ./my-config
- target: /etc/my-config/config.yaml
+ target: /etc/my-config
エディタの設定を変えて直接ファイルが更新されるようにする
File mount does not update with changes from host · Issue #15793 · moby/moby のissueにいくつかコメントがあるように、エディタの設定を変えることでファイルを直接更新するようになるようです。
僕は前述の「ディレクトリをbind mountする」ことで解決したので、この方法で解決するかは試していないのですが、どうしてもファイル単体でbind mountしないといけない場合はこちらの解決方法をする必要がありそうです。
まとめ
- dockerでファイル単体をbind mountすると意図せずinodeが変わって同期が切れることがある
- 「ディレクトリをbind mountする」「エディタの設定を変えて直接ファイルが変更されるようにする」ことで解決が可能( 後者は未検証 )
- 安全側に倒すなら「常にディレクトリをbind mountする」のが良いと思う。
- 他の人のエディタの設定を指定するのは難しい
- エディタが原因とは気付きにくい
- でもディレクトリをbind mountしても、ディレクトリのinodeが変わったら結局同じかもしれない?
Discussion