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 を用いたイメージビルドにあたり、機密情報を受け渡すことができるようになりました。 機密情報は、最終的にイメージ内に保存されることはないので、安全に取り扱うことができます。

iddocker 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=mainRUN --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