Dockerイメージのビルド時にシンボリックリンクの実体を含める

タイトルがわかりにくいですが...

事の発端

以下のような構成のディレクトリがあったとします。

  • /foo (共有したいファイル)
  • /bar/foo (/fooへのシンボリックリンク)
  • /bar/Dockerfile
  • /baz/foo (/fooへのシンボリックリンク)
  • /baz/Dockerfile

そして、{bar,baz}/DockerfileCOPY foo ...のような記述があったとします。

そのとき、fooとbarの各ディレクトリにおいてdocker build .のようなコマンドで(そのディレクトリをコンテキストとして)dockerイメージをビルドしようとするとエラーが出ます。具体的にはCOPY failed: Forbidden path outside the build contextと言われます。

「それはそう」という感じなのですが、このような場面において「ファイルを追加する際、それがシンボリックリンクならばそれをたどって実体をイメージに追加する」といったことができても便利なのではないかと思ったりすることがありました。

解決策

ここでなるほどと思う解決策を見つけました。

docker build .の代わりにtar -czh . | docker build -ようにすればよい」というものです。

具体的にはこのような感じのことが起こっています。

  • tar -czh .で、カレントディレクトリをgzipにアーカイブする。その際、シンボリックリンクが存在した場合はそれをたどり、辿った先のファイルの内容でそのリンクを置き換える(-hオプション)。
  • それをコンテキストとしてdocker buildにわたす。

なるほどなあという感じですね。

実際にやってみると、このようにうまくいきます。

$ echo 'foo' > foo
$ mkdir bar
$ cd bar
$ ln -s ../foo foo
$ cat <<EOF >Dockerfile
FROM busybox
WORKDIR /work
COPY foo .
EOF
$ tar -czh . | docker build -
Sending build context to Docker daemon  10.24kB
Step 1/3 : FROM busybox
 ---> e4db68de4ff2
Step 2/3 : WORKDIR /work
 ---> Using cache
 ---> 2daef8292bd0
Step 3/3 : COPY foo .
 ---> Using cache
 ---> 1ec7cbed7c90
Successfully built 1ec7cbed7c90
$docker run -it 1ec7cbed7c90
/work # cat foo
foo

tar -czh . | docker build -docker build .におきかえるとエラーがでることも確認できます。


ちなみに執筆時点でのdockerのバージョンは以下の通りでした。

$ docker --version
Docker version 18.09.2, build 6247962