BuildKit によるイメージ構築
読む時間の目安: 6 分
Docker によるビルドは、Docker Engine においてもっとも利用されている機能と言えます。 開発者、ビルドチーム、リリースチームなど幅広いユーザーがこの Docker ビルドを行います。
Docker 18.09 のリリースにおいて行われたビルド機能の拡張は、ビルドアーキテクチャーの総見直しにより必要となる機能を導入しています。 BuildKit を統合したことによって、処理性能、ストレージ管理、ツール機能、セキュリティのどれをとっても改善が見られるはずです。
- BuildKit を用いて生成された Docker イメージは、従来の Docker イメージと同じように Docker Hub にプッシュすることができます。
- 従来のビルドに対して動作している Dockerfile の記述は、BuildKit を用いてビルドを行っても同様に動作します。
- 新しくコマンドラインオプション
--secret
が導入され、Dockerfile を用いたイメージビルドにあたり、機密情報を受け渡すことができるようになりました。
ビルド時のオプションに関しては コマンドライン build オプション および Dockerfile リファレンス を参照してください。
システム要件
- Docker 最新版 (18.09 または それ以降)
- 独自のフロントエンドイメージをダウンロードするにはネットワーク接続が必要です。
制約条件
- Linux コンテナーのビルドにのみ対応しています。
BuildKit によるビルドの有効化
一番簡単な方法としては docker を起動し始める際に環境変数DOCKER_BUILDKIT=1
を設定した上でdocker build
コマンドを起動します。
$ DOCKER_BUILDKIT=1 docker build .
docker BuildKit をデフォルトで有効にするには、/etc/docker/daemon.json
にあるデーモン設定の features を true にしデーモンを再起動します。
{ "features": { "buildkit": true } }
新たな Docker ビルドのコマンドライン出力
新たな Docker BuildKit の TTY 出力は(デフォルトで)以下のとおりです。
$ docker build .
[+] Building 70.9s (34/59)
=> [runc 1/4] COPY hack/dockerfile/install/install.sh ./install.sh 14.0s
=> [frozen-images 3/4] RUN /download-frozen-image-v2.sh /build buildpa 24.9s
=> [containerd 4/5] RUN PREFIX=/build/ ./install.sh containerd 37.1s
=> [tini 2/5] COPY hack/dockerfile/install/install.sh ./install.sh 4.9s
=> [vndr 2/4] COPY hack/dockerfile/install/vndr.installer ./ 1.6s
=> [dockercli 2/4] COPY hack/dockerfile/install/dockercli.installer ./ 5.9s
=> [proxy 2/4] COPY hack/dockerfile/install/proxy.installer ./ 15.7s
=> [tomlv 2/4] COPY hack/dockerfile/install/tomlv.installer ./ 12.4s
=> [gometalinter 2/4] COPY hack/dockerfile/install/gometalinter.install 25.5s
=> [vndr 3/4] RUN PREFIX=/build/ ./install.sh vndr 33.2s
=> [tini 3/5] COPY hack/dockerfile/install/tini.installer ./ 6.1s
=> [dockercli 3/4] RUN PREFIX=/build/ ./install.sh dockercli 18.0s
=> [runc 2/4] COPY hack/dockerfile/install/runc.installer ./ 2.4s
=> [tini 4/5] RUN PREFIX=/build/ ./install.sh tini 11.6s
=> [runc 3/4] RUN PREFIX=/build/ ./install.sh runc 23.4s
=> [tomlv 3/4] RUN PREFIX=/build/ ./install.sh tomlv 9.7s
=> [proxy 3/4] RUN PREFIX=/build/ ./install.sh proxy 14.6s
=> [dev 2/23] RUN useradd --create-home --gid docker unprivilegeduser 5.1s
=> [gometalinter 3/4] RUN PREFIX=/build/ ./install.sh gometalinter 9.4s
=> [dev 3/23] RUN ln -sfv /go/src/github.com/docker/docker/.bashrc ~/.ba 4.3s
=> [dev 4/23] RUN echo source /usr/share/bash-completion/bash_completion 2.5s
=> [dev 5/23] RUN ln -s /usr/local/completion/bash/docker /etc/bash_comp 2.1s
新たな BuildKit の plain な出力は以下のとおりです。
$ docker build --progress=plain .
#1 [internal] load .dockerignore
#1 digest: sha256:d0b5f1b2d994bfdacee98198b07119b61cf2442e548a41cf4cd6d0471a627414
#1 name: "[internal] load .dockerignore"
#1 started: 2018-08-31 19:07:09.246319297 +0000 UTC
#1 completed: 2018-08-31 19:07:09.246386115 +0000 UTC
#1 duration: 66.818µs
#1 started: 2018-08-31 19:07:09.246547272 +0000 UTC
#1 completed: 2018-08-31 19:07:09.260979324 +0000 UTC
#1 duration: 14.432052ms
#1 transferring context: 142B done
#2 [internal] load Dockerfile
#2 digest: sha256:2f10ef7338b6eebaf1b072752d0d936c3d38c4383476a3985824ff70398569fa
#2 name: "[internal] load Dockerfile"
#2 started: 2018-08-31 19:07:09.246331352 +0000 UTC
#2 completed: 2018-08-31 19:07:09.246386021 +0000 UTC
#2 duration: 54.669µs
#2 started: 2018-08-31 19:07:09.246720773 +0000 UTC
#2 completed: 2018-08-31 19:07:09.270231987 +0000 UTC
#2 duration: 23.511214ms
#2 transferring dockerfile: 9.26kB done
デフォルトフロントエンドの上書き設定
Dockerfile
における新たな文法機能として、デフォルトのフロントエンドを上書き設定することが可能になりました。
これを行うにはDockerfile
の先頭行に、コメントとして特定のフロントエンドイメージを指定します。
# syntax=<frontend image>, e.g. # syntax=docker/dockerfile:1.2
本ページに示す利用例においては docker/dockerfile
バージョン 1.2.0 およびそれ以降で利用可能となる機能を用いています。
これに対してはdocker/dockerfile:1
と指定することを推奨します。
これはバージョン 1 の最新リリースを常に指し示すものです。
BuildKit ではビルド前に文法が更新されているかどうか、利用するバージョンが最新であるかどうかが自動的にチェックされます。
syntax
ディレクティブについては Dockerfile リファレンス を参照してください。
新たな Docker ビルドにおける機密情報の扱い
Docker ビルドにおいては新たに--secret
フラグが導入され、Dockerfile を用いたイメージビルドにあたり、機密情報を受け渡すことができるようになりました。
機密情報は、最終的にイメージ内に保存されることはないので、安全に取り扱うことができます。
id
はdocker build --secret
において受け渡される識別子です。
これは Dockerfile 内において用いられるRUN --mount
識別子に関連づいています。
Docker では Dockerfile の外に保持されている機密情報のファイル名は用いません。
これが重要な情報となることもあるからです。
dst
は Dockerfile 内にて用いられるRUN
コマンドにおいての機密情報ファイルを、所定ファイル名に名称変更します。
たとえば以下のようなテキストファイル内に、一部の機密情報が含まれているとします。
$ echo 'WARMACHINEROX' > mysecret.txt
BuildKit フロントエンドdocker/dockerfile:1.2
を利用する Dockerfile では、Secret は以下のようにRUN
を実行する際にアクセスすることができます。
# syntax=docker/dockerfile:1.2
FROM alpine
# shows secret from default secret location:
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
# shows secret from custom secret location:
RUN --mount=type=secret,id=mysecret,dst=/foobar cat /foobar
secret はビルドの際に--secret
フラグを使って受け渡すことが必要です。
この Dockerfile は、単に機密情報にアクセスできる例を示したにすぎません。
機密情報はビルド時の出力に表示されます。
最終的にビルドされるイメージに、この機密情報ファイルは含まれません。
$ docker build --no-cache --progress=plain --secret id=mysecret,src=mysecret.txt .
...
#8 [2/3] RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
#8 digest: sha256:5d8cbaeb66183993700828632bfbde246cae8feded11aad40e524f54ce7438d6
#8 name: "[2/3] RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret"
#8 started: 2018-08-31 21:03:30.703550864 +0000 UTC
#8 1.081 WARMACHINEROX
#8 completed: 2018-08-31 21:03:32.051053831 +0000 UTC
#8 duration: 1.347502967s
#9 [3/3] RUN --mount=type=secret,id=mysecret,dst=/foobar cat /foobar
#9 digest: sha256:6c7ebda4599ec6acb40358017e51ccb4c5471dc434573b9b7188143757459efa
#9 name: "[3/3] RUN --mount=type=secret,id=mysecret,dst=/foobar cat /foobar"
#9 started: 2018-08-31 21:03:32.052880985 +0000 UTC
#9 1.216 WARMACHINEROX
#9 completed: 2018-08-31 21:03:33.523282118 +0000 UTC
#9 duration: 1.470401133s
...
SSH を用いたビルド内個人情報へのアクセス
感謝
詳しい情報と利用例については Build secrets and SSH forwarding in Docker 18.09 (Docker 18.09 における機密情報生成と SSH フォワーディング)を参照してください。
Dockerfile
内のコマンドにおいて SSH 認証を必要とするものがあります。
たとえばプライベートリポジトリを生成する場合です。
秘密鍵をイメージ内にコピーすると、機密情報を公開する危険にさらされます。
そうではなくdocker build
を使えば、イメージビルドの際に、ホストシステムの ssh アクセスが利用できます。
その作業を進めるには以下の 3 つの手順を行います。
まずはssh-add
を実行して秘密鍵の追加を行い、認証エージェントが認識できるようにします。
SSH 鍵を複数持っていて、デフォルトのid_rsa
が、これから行うリソースアクセス用ではなかった場合には、鍵へのパスを指定して、ssh-add ~/.ssh/<some other key>
のように実行する必要があります。
(SSH エージェントに関する詳細は OpenSSH man ページ を参照してください。)
次にdocker build
の実行にあたっては--ssh
オプションを使って、SSH エージェントの既存の接続ソケットへのパスを指定します。
たとえば--ssh default=$SSH_AUTH_SOCK
とするか、同じことを短縮して--ssh default
とします。
最後に、Dockerfile
内のRUN
コマンドにおいて SSH アクセスが可能となるように、ssh
タイプのマウントを定義します。
これによって、docker build
の実行にあたって環境変数SSH_AUTH_SOCK
が設定されることになり、その値はホストから提供されるものとなります。
こうすれば SSH 接続を行うようなRUN
コマンドが含まれていると、自動的にそのソケットが利用されます。
Dockerfile
内にあるコマンドでは、type=ssh
としてマウント定義された SSH アクセスを明示的に要求したものだけが、SSH エージェント接続を求めることになります。
それ以外のコマンドは、どの SSH エージェントが利用可能であるのかを知ることはできません。
Docker ファイル内にてコンテナーが SSH を利用する例が以下です。
# syntax=docker/dockerfile:1
FROM alpine
# ssh クライアントと git のインストール
RUN apk add --no-cache openssh-client git
# github.com に対する公開鍵をダウンロード
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
# プライベートリポジトリのクローン
RUN --mount=type=ssh git clone git@github.com:myorg/myproject.git myproject
イメージは以下のようにしてビルドすることができます。
$ docker build --ssh default .
--mount=type=secret
と同じように、各ビルドごとに複数ソケットを利用したい場合やそれらを区別したい場合にはid
を指定します。
たとえばdocker build --ssh main=$SSH_AUTH_SOCK --ssh other=$OTHER_SSH_AUTH_SOCK
とすることもできます。
Dockerfile
内には、2 つのソケットを利用するためにRUN --mount=type=ssh,id=main
とRUN --mount=type=ssh,id=other
を記述することができます。
--mount=type=ssh
においてid
を指定しなかった場合はdefault
として扱われます。
トラブルシューティング: プライベートレジストリに関する問題
x509: certificate signed by unknown authority
Docker イメージを(自己署名証明書を使って)セキュアではないレジストリから取得しようとする場合、あるいはそのようなレジストリをミラーとして利用する場合、Docker 18.09 では既知の問題が発生します。
[+] Building 0.4s (3/3) FINISHED
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 169B
=> [internal] load .dockerignore
=> => transferring context: 2B
=> ERROR resolve image config for docker.io/docker/dockerfile:experimental
------
> resolve image config for docker.io/docker/dockerfile:experimental:
------
failed to do request: Head https://repo.mycompany.com/v2/docker/dockerfile/manifests/experimental: x509: certificate signed by unknown authority
解決方法 = レジストリを適切にセキュアなものにしてください。 無償の Let’s Encrypt から SSL 証明書を取得することができます。 Registry サーバーのデプロイ を参照してください。
Sonatype Nexus バージョン 3.15 未満、プライベートリポジトリ実行時のイメージ “not found”
Sonatype Nexus バージョン 3.15 未満を利用してプライベートリポジトリを起動させている場合、以下のようなエラーとなる場合があります。
------
> [internal] load metadata for docker.io/library/maven:3.5.3-alpine:
------
------
> [1/4] FROM docker.io/library/maven:3.5.3-alpine:
------
rpc error: code = Unknown desc = docker.io/library/maven:3.5.3-alpine not found
以下に示すバグ NEXUS-12684 に該当している可能性があります。
これを解決するには Nexus をバージョン 3.15 以上にアップグレードしてください。
build, security, engine, secret, BuildKit