BuildKit によるイメージ構築

読む時間の目安: 6 分

Docker によるビルドは、Docker Engine においてもっとも利用されている機能と言えます。 開発者、ビルドチーム、リリースチームなど幅広いユーザーがこの Docker ビルドを行います。

Docker 18.09 のリリースにおいて行われたビルド機能の拡張は、ビルドアーキテクチャーの総見直しにより必要となる機能を導入しています。 BuildKit を統合したことによって、処理性能、ストレージ管理、ツール機能、セキュリティのどれをとっても改善が見られるはずです。

  • BuildKit を用いて生成された Docker イメージは、従来の Docker イメージと同じように Docker Hub にプッシュすることができます。
  • 従来のビルドに対して動作している Dockerfile の記述は、BuildKit を用いてビルドを行っても同様に動作します。
  • 新しくコマンドラインオプション --secret が導入され、Dockerfile を用いたイメージビルドにあたり、機密情報を受け渡すことができるようになりました。

ビルド時のオプションに関しては コマンドライン build オプション を参照してください。

システム要件

  • 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.0-experimental

新たな Docker ビルドにおける機密情報の扱い

Docker ビルドにおいては新たに --secret フラグが導入され、Dockerfile を用いたイメージビルドにあたり、機密情報を受け渡すことができるようになりました。 機密情報は、最終的にイメージ内に保存されることはないので、安全に取り扱うことができます。

iddocker build --secret において受け渡される識別子です。 これは Dockerfile 内において用いられる RUN --mount 識別子に関連づいています。 Docker では Dockerfile の外に保持されている機密情報のファイル名は用いません。 これが重要な情報となることもあるからです。

dst は Dockerfile 内にて用いられる RUN コマンドにおいての機密情報ファイルを、所定ファイル名に名称変更します。

たとえば以下のようなテキストファイル内に、一部の機密情報が含まれているとします。

$ echo 'WARMACHINEROX' > mysecret.txt

そして BuildKit フロントエンド docker/dockerfile:1.0-experimental を指定した Dockerfile を使えば、機密情報にアクセスすることができます。

たとえば以下のとおりです。

# syntax = docker/dockerfile:1.0-experimental
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

この 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 フォワーディング)を参照してください。

docker build には --ssh オプションがあります。 これは Docker Engine に対して SSH エージェントのフォワードによる接続を許可するものです。 SSH エージェントについては OpenSSH man ページ を参照してください。

Dockerfile 内にて SSH アクセスを要求するために明示されるコマンド記述は、マウントタイプを type=ssh とするだけです。 これが SSH エージェントによる接続を行うものとなります。 これ以外のコマンド記述からは、SSH エージェントを利用しているかどうかを知ることはできません。

Dockerfile 内の RUN コマンドにおいて SSH アクセスを指定するには、マウントタイプを ssh として定義します。 これにより環境変数 SSH_AUTH_SOCK が設定され、プログラムが SSH に基づいて自動的にソケット通信を行うようになります。

Docker ファイル内にてコンテナーが SSH を利用する例が以下です。

# syntax=docker/dockerfile:experimental
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

Dockerfile の用意ができたら、SSH エージェント接続を行う --ssh オプションを使います。

$ docker build --ssh 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/deploying/ を参照してください。

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