はじめに

下記のようなことを実現しようとして少し苦労しました。

  • Dockerイメージに最初にから入っていて欲しいファイルをDockerfile内で作成
  • それをbindマウントでホストと共有したい

すごくシンプルな例として/usr/local/work/hello.txtを共有してみます。

失敗例

当初はDockerfileの中に必要な処理をすベて書いていました。

$ cat Dockerfile
FROM centos:7.6.1810
RUN mkdir /usr/local/work
RUN echo "hello" > /usr/local/work/hello.txt

Dockerイメージをビルドします。

$ docker build -t test:1.0 .

/usr/local/workをbindマウントでdocker runしてみます。

$ docker run --rm -v $PWD/data:/usr/local/work test:1.0

dataフォルダが存在しなければ勝手に作られます。ただし、フォルダは作成されますが中身は空っぽです。

$ ls data

コンテナに入ってdfコマンドを叩いてみると、単純にホスト側のディレクトリがマウントされていました。
この方法でコンテナのファイルを共有するのは無理だということが分りました。何事も経験です。

# df -h
Filesystem      Size  Used Avail Use% Mounted on
overlay          59G   12G   44G  22% /
tmpfs            64M     0   64M   0% /dev
tmpfs          1000M     0 1000M   0% /sys/fs/cgroup
/dev/sda1        59G   12G   44G  22% /etc/hosts
shm              64M  8.0K   64M   1% /dev/shm
osxfs           466G  136G  326G  30% /usr/local/work
tmpfs          1000M  8.3M  992M   1% /run

解決方法

今度はフォルダやファイルの作成をDockerfile内では直接やらずENTRYPOINTから起動したシェルの中でやるようにします。

$ cat Dockerfile
FROM centos:7.6.1810
COPY entrypoint.sh /tmp/
ENTRYPOINT sh -x /tmp/entrypoint.sh

entrypoint.shではファイルを作成します。ちなみに/usr/local/workフォルダはdocker run -vで勝手に作成されるので明示的に作成しませんでした。この動きも最初はよく分かっていなくて大分混乱しました。

#!/bin/sh
echo "hello" > /usr/local/work/hello.txt

docker runしてみるとdataフォルダが作成され中身もちゃんと共有され、やりたいことが出来ました。

$ docker run --rm -v $PWD/data:/usr/local/work test:1.0
$ ls data
data/hello.txt

$ cat data/hello.txt
hello

Entrypointでファイルを書き込む手法はPostgreSQLやMySQLのコンテナでも行われてましたので一般的なやり方なんでしょう。

–mountオプション

だいぶ前からですが、-vより--mountの方が推奨されているそうです。どっちがsourceでどっちがdestinationなのかも分りやすいかな。ちなみにdataフォルダは事前に存在しないとエラーになるので注意が必要ですがホストのファイルをマウントすることを案に示しているようで良いと思いました。

$ mkdir data
$ docker run --rm --mount type=bind,src=$PWD/data,dst=/usr/local/work test:1.0

おわりに

Dockerのバインドマウントは他にもパーミッションの問題があったり色々難しいですね。通常はVOLUMEマウントして必要なファイルをdocker cpで取得するのが良いかなというのが現時点での私の結論です。