Dockerのビルドコンテキスト(build context)について確認したときのメモ

[Best practices for writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)

上記のビルドコンテキストについての内容を確認したときのメモ

日本語としては以下があり

[Dockerfileを書くためのベストプラクティス【参考訳】v18.09](https://qiita.com/zembutsu/items/a96b68277d699f79418d)

# TL:DR

+ `docker build`を行う時に Docker デーモンへビルドコンテキストのファイルを送信している。ビルドコンテキストは PATH 形式を指定した場合、PATH のディレクトリ
+ 例えば送信不要な大きなファイルがビルドコンテキストとして送信されるとビルドに時間が掛かる(送信に時間が掛かるため）
+ 対応方法としては「.dockerignoreでビルドコンテキストから不要ファイルを除外する」、「Dockerfile を標準入力から受け取ってビルドコンテキストを送信しない」がある
+ .dockerignore を利用する事でビルドコンテキストとして送信しないファイルを指定できる
+ `docker build`の際に`-` を指定することで Dockerfile を標準入力から受け取ることが出来る。これを使ってヒアドキュメントなどで Dockerfile を書くことでビルドコンテキストの送信をしないように制御できる
+ Dockerfile において ADD/COPY がある場合、ビルドコンテキストとしてファイルが送信されている必要があるのでビルドコンテキスト


# docker build の指定について

[docker build](https://docs.docker.com/engine/reference/commandline/build/)

>docker build [OPTIONS] PATH | URL | -

上記のように 「PATH」or「URL」or「-」を指定する必要がある。
ビルドコンテキストはファイルlocationのセットであり、PATHもしくはURLで指定する。
URLでは Git や tarboll、plain text などを選べる。
公式ドキュメントとしては以下辺り

>The docker build command builds Docker images from a Dockerfile and a “context”. A build’s context is the set of files located in the specified PATH or URL. The build process can refer to any of the files in the context. For example, your build can use a COPY instruction to reference a file in the context.
>The URL parameter can refer to three kinds of resources: Git repositories, pre-packaged tarball contexts and plain text files.

# Understand build context

[Understand build context](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#understand-build-context)

上記に書いてあるように `docker build`をした際に  current working directory がビルドコンテキストとなる。
実施には PATH に指定したディレクトリがビルドコンテキストとなり、この例の場合、PATHでは「.」を指定しているため、カレントディレクトリがビルドコンテキストとなる模様。

```bash
mkdir myproject && cd myproject
echo "hello" > hello
echo -e "FROM busybox\nCOPY /hello /\nRUN cat /hello" > Dockerfile
 docker build -t helloapp:v1 .
Sending build context to Docker daemon  3.072kB
Step 1/3 : FROM busybox
latest: Pulling from library/busybox
53071b97a884: Pull complete
Digest: sha256:4b6ad3a68d34da29bf7c8ccb5d355ba8b4babcad1f99798204e7abb43e54ee3d
Status: Downloaded newer image for busybox:latest
 ---> 64f5d945efcc
Step 2/3 : COPY /hello /
 ---> f272f46ea1ce
Step 3/3 : RUN cat /hello
 ---> Running in b14805f05dd4
hello
Removing intermediate container b14805f05dd4
 ---> 2a4bfc48b972
Successfully built 2a4bfc48b972
Successfully tagged helloapp:v1

```

上記場合、「Sending build context to Docker daemon  3.072kB」の内容が Docker deaemon へビルドコンテキストへファイルを送っているというサイズであり、上記場合には 3.072kB の内容を送信している。

これによってどのような事が起きるかというと例えば大きなファイルがあったりするとその分、デーモンへ送信するまでの時間が掛かって、ビルドに余計な時間が掛かる。
以下では実際には dummy.file を配置し、デーモンへの送信サイズ数が増えているようだ。

[.dockerignore アンチパターン](https://qiita.com/munisystem/items/b0f08b28e8cc26132212)

あとでも出てくるが `.dockerignore`ファイルを作れば対象ファイルのデーモンへの送信を除外できるのでこれが対応方法の一つとなる。

なお、PATH 「.」を指定した場合、ビルドコンテキストはカレントディレクトリになり、「PATH で「context」などを指定した場合、PATH で指定したディレクトリ(この例の場合、context)がビルドコンテキストとなった。

```bash
# 大きいファイルを context 配下に配置
 mv ~/Downloads/dummy.zip context

# ビルドコンテキストのサイズが増えた
 docker build --no-cache -t helloapp:v2 -f dockerfiles/Dockerfile context
Sending build context to Docker daemon  67.65MB
Step 1/3 : FROM busybox
 ---> 64f5d945efcc
Step 2/3 : COPY /hello /
 ---> 59f1224b5937
Step 3/3 : RUN cat /hello
 ---> Running in d16daf4550ac
hello
Removing intermediate container d16daf4550ac
 ---> dd658cd50739
Successfully built dd658cd50739
Successfully tagged helloapp:v2

# docker build コマンドを実行するディレクトリは変更せず、 PATH を変える。この場合、ビルドコンテキストが代わり、送信内容は減る。ただし、Dockerfile の COPY で指定されたファイルがビルドコンテキストに存在しないのでエラーとなる
 docker build --no-cache -t helloapp:v2 -f dockerfiles/Dockerfile dockerfiles
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM busybox
 ---> 64f5d945efcc
Step 2/3 : COPY /hello /
COPY failed: stat /var/lib/docker/tmp/docker-builder387958989/hello: no such file or directory
```

# Build an image using a Dockerfile from stdin, without sending build context

`docker build`では標準入力として Dockerfile を受け取れる。
そしてこれを利用すればビルドコンテキストの送信無しでビルドできる。
その為、COPY/ADD などが必要ない場合、有効。

```bash
docker build -t myimage:latest -<<EOF
FROM busybox
RUN echo "hello world"
EOF

Sending build context to Docker daemon  3.072kB
Step 1/2 : FROM busybox
 ---> 64f5d945efcc
Step 2/2 : RUN echo "hello world"
 ---> Running in b26cc8dd6f4d
hello world
Removing intermediate container b26cc8dd6f4d
 ---> f0a66bf2d32d
Successfully built f0a66bf2d32d
Successfully tagged myimage:latest
```

COPY/ADD が必要な場合、失敗するので注意。