BuildKit でイメージ構築

Docker Build は Docker Engine で最も使われる機能の1つです。利用者、開発チーム、リリースチームに至る利用者の全てが Docker Build を使います。

リリース 18.09 では、強く求められていた構築方式が見直され、 Docker Build の拡張が導入されました。利用者は BuildKit(ビルドキット) との統合により、性能、ストレージ管理、際立つ機能性、セキュリティの改善を目の当たりにするでしょう。

  • BuildKit が作成した Docker イメージは、これまでの構築で作成した Docker イメージ同様、 Docker Hub に送信可能

  • Dockerfile の書式は、従来の構築用だけでなく、 BuitKit による構築でも動作

  • Dockerfile で新しいイメージの構築時、新しい --secret コマンドライン オプションを使えば、 機微情報(secret) (シークレット)を(コマンドライン上で)渡せるようになる

build オプションに関する詳しい情報は、リファレンスガイド上の コマンドライン build オプションDockerfile リファレンス ページ をご覧ください。

動作条件

  • Docker の最新版(18.09 以上)

  • 任意のフロントエンドからイメージをダウンロードするには、ネットワーク接続が必要

制限事項

  • Linux コンテナの構築のみサポート

BuildKit での構築を有効化するには

Docker の新規インストールが最も簡単で、 docker build コマンドの実行時に DOCKER_BUILDKIT 環境変数を次のように指定します。

$ DOCKER_BUILDKIT=1 docker build .

デフォルトで docker BuildKit を有効化するには、 /etc/docker/daemon.json にあるデーモンの設定で、(buildkit の)機能を true にしてから、デーモンを再起動します。

{ "features": { "buildkit": true } }

新しい Docker Build コマンドラインによる構築の出力

新しい docker build 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

新しい docker build BuildKit による、そのままの出力。

$ 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

デフォルトのフロントエンドを上書き

# syntax=<frontend image>, e.g. # syntax=docker/dockerfile:1.2

このページの例では、 docker/dockerfile バージョン 1.2.0 以上で利用可能な機能を使います。ですが、私たちは docker/dockerfile:1 の利用を推奨します。こちらであれば、常にバージョン1構文の最新リリースを示すからです。BuildKit は構築前に構文の更新を自動的に確認し、最新の安定バージョンを使っているかどうかを常に確認します。 syntax 命令について学ぶには、 Dockerfile リファレンス をご覧ください。

Docker Build の新しいシークレット情報

docker build の新しい --secret フラグは、利用者が Dockerfile で シークレット(secret) 情報(機微情報)を渡す必要があるときに、docker イメージを安全に構築するための方法であり、最終イメージに機微情報を保存しません。

id とは、 docker build --secret で(シークレットを)渡すための 識別子(identifier) です。この識別子は Dockerfile 中で使う RUN --mount 識別子と関連付けられます。これは、 Dockerfile の外で持つシークレットがどこにあるのかは、ファイル名が機微情報になり得るため、Docker ではファイル名を(直接)扱いません。

Dockerfile 内の RUN コマンドの使用時に、シークレット ファイルを dest で指定するファイルに名称変更します。

たとえば、機微情報の一部をテキストファイル中に保存します。

$ echo 'WARMACHINEROX' > mysecret.txt

そして、Dockerfile 側では、Buildkit フロントエンド docker/dockerfile:1.2 を使う指定をすると、 RUN 命令の処理時にシークレットを利用できます。

# syntax = docker/dockerfile:1.2

FROM alpine

# デフォルトのシークレットの場所から、シークレットを表示
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret

# 任意のシークレットの場所から、シークレットを表示
RUN --mount=type=secret,id=mysecret,dst=/foobar cat /foobar

シークレットを使うには、構築時に --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 で 内部データ(private date) にアクセス

参考

Build secrets and SSH forwarding in Docker 18.09 に詳しい情報と例がありますのでご覧ください。

Dockerfile 内のコマンドによっては、プライベート リポジトリをクローンするような、 SSH 認証の指定が必要となる場合があります。秘密鍵をイメージにコピーしてしまうと、一般公開してしまう危険性があります。コピーするのではなく、 docker build でイメージの構築時に、ホストシステム上の ssh へのアクセスする方法があります。

この手順には、3つの過程があります。

1番目は、 ssh-add を実行し、認証エージェントに対して秘密鍵(identity)を追加します。もしも複数の SSH 鍵があり、デフォルトの id_rsa でリソースにアクセスできるかどうか疑わしい場合は、 ssh-add ~/.ssh/<他の何らかの鍵> で鍵のパスを追加する必要があります(SSH エージェントの詳しい情報は、 OpenSSH の man ページ をご覧ください。)。

2番目は、 docker build コマンドの実行時、 --ssh オプションを使い、既存の SSH エージェントへ接続するソケットを指定します。たとえば、 --ssh default=$SSH_AUTH_SOCK や、同等の省略形の --ssh default です。

3番目は、その SSH アクセスを Dockerfile 内の RUN 命令で使えるようにするため、 ssh タイプとしてマウントを定義します。これにより、 docker build 時にホスト上で提供された値が SSH_AUTH_SOCK 環境変数に指定され、結果として RUN 命令内のあらゆるプログラムが、SSH で自動的にそのソケットを使うよう依存します。type=ssh マウントの定義があれば、 SSH アクセスを明示的に要求する Dockerfile 内のコマンドのみが、SSH エージェントへ接続できます。他のコマンドは、どのような SSH エージェントが利用可能かどうかを一切知りません。

こちらはコンテナ内で SSH を使う Dockerfile の例です:

# syntax=docker/dockerfile:1
FROM alpine

# ssh クライアントと git をインストール
RUN apk add --no-cache openssh-client git

# github.com のための公開鍵をダウンロード
RUN mkdir -p -m 0600 ~/.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:未知の認証局によって書名された証明書

(自己書名した証明書を使う) 安全ではない レジストリ(insecure registry) からイメージを取得しようとすると、あるいはレジストリをミラーとして使おうとすると、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 証明書は無料で取得できます。 レジストリ・サーバのデプロイ をご覧ください。

Sonatype Nexus バージョン 3.15 未満の上でプライベート・レジストリを実行中に、イメージが見つからない

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 以上にアップグレードします。