ファイルシステムの境界

こんにちは、masm11 です。

最近、コンテナ関係でちょっとハマったことがあったので、ご紹介したいと思います。

非コンテナ

luna:t % mkdir vol1 vol2
luna:t % touch vol1/foo

ディレクトリを2つ作って、 片方のディレクトリにファイルを作ります。

luna:t % ls -li vol1/foo
1024572 -rw-r--r-- 1 masm masm 0  42 13:22 vol1/foo

ls に -i を付けると、i-node 番号を表示できます。 i-node とは、ファイルの属性を格納したディスク中領域の番号で、 同一ファイルシステムのファイルなら重複しません。 今回は 1024572 です。

luna:t % mv vol1/foo vol2/

そして、vol2 へ移動します。

luna:t % ls -li vol2/foo  
1024572 -rw-r--r-- 1 masm masm 0  42 13:22 vol2/foo

i-node 番号は変わっていません。

これは、ディレクトリの書き換えだけが行われた、ということですね。 ディレクトリには、ファイル名と i-node 番号の組が格納されていて、 この組を別のディレクトリに移動させるだけで、ファイルの移動が 完了します。高速です。今回はこの方法が使われたようです。

コンテナボリューム その1

次にコンテナ内から同じことを試してみます。

luna:t % docker pull alpine:latest

Alpine が小さくて便利ですかね。

luna:t % docker run --rm -ti --volume ./vol1:/t alpine /bin/sh 

コンテナを起動します。先程作った vol1 をコンテナ内では /t に見えるように しています。

/ # df -k
Filesystem           1K-blocks      Used Available Use% Mounted on
zroot/root/50de6be7ffbfcd66419b9730e0a5aaeaccebe153261fd0df5e7171ff59022d49
                       4192896      6528   4186368   0% /
tmpfs                    65536         0     65536   0% /dev
shm                      65536         0     65536   0% /dev/shm
zroot/home            58449664  32261248  26188416  55% /t
...

df で mount 状況を確認しました。ちゃんと mount できています。

/ # mkdir t/t1 t/t2
/ # touch t/t1/foo

ボリュームの中に、先ほどと同じようにディレクトリを2つ作って、ファイルを作ります。

/ # ls -li t/t1/foo
1024912 -rw-r--r--    1 root     root             0 Apr  2 04:40 t/t1/foo

i-node 番号を確認します。1024912 ですね。

/ # mv t/t1/foo t/t2/
/ # ls -li t/t2/foo
1024912 -rw-r--r--    1 root     root             0 Apr  2 04:40 t/t2/foo

mv した後、i-node 番号を確認しました。変化ありませんでした。

コンテナ別ボリューム

次です。

luna:t % docker run --rm -ti --volume ./vol1:/t/t1 --volume ./vol2:/t/t2 alpine /bin/sh

/t/t1/t/t2 を別のボリュームにしました。 この状態で同じことをやってみます。

/ # touch /t/t1/bar
/ # ls -li /t/t1/bar
1024698 -rw-r--r--    1 root     root             0 Apr  2 04:45 /t/t1/bar
/ # mv /t/t1/bar /t/t2/
/ # ls -li /t/t2/bar
1024585 -rw-r--r--    1 root     root             0 Apr  2 04:45 /t/t2/bar

なんと、i-node 番号が変わってしまいました。 これは、今までのようにディレクトリの書き換えだけでは済まず、 ファイルがコピーされたことを示しています。

まとめ

ボリューム間のファイルの移動は、コピーが発生しました。

考えてみれば当然です。 コンテナ内 mv コマンドから見れば、別のファイルシステムなので、 i-node を管理している場所も違うかもしれず、 i-node 番号を別ファイルシステムに持っていっても意味はないからです。

ただ、今回の場合はホスト側では同じボリュームであり、 ホスト側の都合も考慮されるのかと思っていましたが、 そんなことはありませんでした。

さて、コピーになったということは、 一瞬ですがファイルが中途半端(or 空)である、ということです。 ファイルサイズによっては一瞬じゃないかもしれません。 例えば inotify などで監視して処理する場合には要注意ですね (今回、IN_CREATE を受け取って即処理をしていたので、 ファイルの中身がまだないことがあって、ハマりました)。

おまけ

さて、上記のように mv コマンドは、同じファイルシステムであれば 移動し、別ファイルシステムであればコピーして削除します。 が、昔の Unix では、別ファイルシステムの場合はエラーでした。 移動できないからですね。

勝手にコピーして削除してくれるようになったのは便利ですが、 時々その裏側を忘れてしまって、今回のようにハマることがあります。 注意しましょう...