Dockerfiles で複数のビルド コンテキストがサポートされるようになりました

Dockerfile 1.4 および Buildx v0.8+ の新しいリリースには、複数のビルド コンテキストを定義する機能が付属しています。つまり、ビルドの一部として異なるローカルディレクトリのファイルを使用できます。 なぜそれが役立つのか、そしてビルドパイプラインでどのように活用できるのかを見てみましょう。

コマンドを呼び出す docker build と、ビルド コンテキストへのパスまたは URL である 1 つの位置引数が取ります。 最も一般的には docker build . 、 現在の作業ディレクトリをビルドコンテキストにする。

内部 Dockerfile では、および ADD コマンドを使用して、ビルドコンテキストからファイルをコピーし、ビルドステップで使用できるようにすることができます COPY 。BuildKit では、ビルドコンテキストファイルをコピーせずに直接アクセスできるビルドマウント RUN --mount も追加され、パフォーマンスが向上しました。

複雑なビルドの征服

しかし、ビルドがより複雑になるにつれて、1つの場所からのみファイルにアクセスする機能は非常に制限されました。 そのため、フラグを追加して --from 別の Dockerfile ステージまたはリモートイメージの名前を指定することで、の他の Dockerfile 部分からファイルをコピーできるマルチステージビルドを追加しました。

新しい名前付きビルド コンテキスト機能は、このパターンの拡張機能です。 build コマンドの実行時に追加のビルドコンテキストを定義し、名前を付けて、以前のビルドステージと同じ方法でアクセス Dockerfile できるようになりました。

追加のビルド コンテキストは、新しい --build-context [name]=[value] フラグを使用して定義できます。 キー コンポーネントはビルド コンテキストの名前を定義し、値は次のようになります。

  • ローカルディレクトリ – 例: --build-context project2=../path/to/project2/src
  • Git リポジトリ – 例えば --build-context qemu-src=https://github.com/qemu/qemu.git
  • タールボールへの HTTP URL – 例えば --build-context src=https://example.org/releases/src.tar
  • Docker イメージ – プレフィックスを使用して docker-image:// 定義します。 --build-context alpine=docker-image://alpine:3.15

 

Dockerfile 側面では、"from" パラメーターを受け入れるすべてのコマンドでビルド コンテキストを参照できます。これがどのように見えるかです:

# syntax=docker/dockerfile:1.4
FROM [name]
COPY --from=[name] ...
RUN --mount=from=[name] …

 

の値は [name] 、次の優先順位と一致します。

  • で定義された名前付きビルド コンテキスト --build-context [name]=..
  • 内部で AS [name] 定義されたステージ Dockerfile
  • コンテナー レジストリ内のリモート イメージ [name]

 

フラグが設定されていない場合 --from 、ファイルはメインのビルドコンテキストから読み込まれます。

例 #1: 画像の固定

ビルド コンテキストを使用して、で使用される Dockerfile イメージを特定のバージョンにピン留めする方法の例から始めましょう。

これは、さまざまな場合に役立ちます。 たとえば、 新しいビルド情報機能 すべてのビルド ソースをキャプチャし、イメージ タグが更新されている場合でも、以前のビルドと同じ依存関係でビルドを実行します。

docker buildx imagetools inspect --format '{{json .BuildInfo}}' moby/buildkit

"sources": [
      {
        "type": "docker-image",
        "ref": "docker.io/library/alpine:3.15",
        "pin": "sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300"
      },

 

docker buildx build --build-context alpine:3.15=docker-image://alpine:3.15@sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300 .


あなたが Dockerfile 使用する alpine:3.15とき、 レジストリ内の新しいバージョンで更新された場合でも、新しいビルドでは、以前のビルドとまったく同じイメージが使用されます。

別の例として、イメージのデバッグまたは開発のために、別のイメージまたは別のバージョンを試すことができます。 一般的なパターンは、イメージをまだリリースしておらず、テスト レジストリまたはステージング環境にのみ存在する場合です。 アプリをビルドしてステージング リポジトリにプッシュしたが、通常はリリース イメージを使用する他のビルドで使用したいとします。

 

docker buildx build --build-context myorg/myapp=docker-image://staging.myorg.com/registry/myapp .


前の例は、イメージのエイリアスを作成する方法と考えることもできます。

例 #2: 複数のプロジェクト

おそらく、名前付きコンテキスト機能で最も要求されるユースケースは、複数のローカルソースディレクトリを使用する可能性です。

プロジェクトに一緒にビルドする必要がある複数のコンポーネントが含まれている場合、すべてを 1 つのディレクトリに含める必要がある 1 つのビルド コンテキストでそれらを読み込むのは難しい場合があります。 さまざまな問題があります:すべてのコンポーネントはフルパスでアクセスする必要があり、1つだけ .dockerignore 持つことができます ファイル、または各コンポーネントに独自の Dockerfile.

プロジェクトのレイアウトが次の場合:

プロジェクト
├── アプリ1
│ ├── .dockerignore│ ├── 出典
├── アプリ2
│ ├── .dockerignore│ ├── 出典
├── ドッカーファイル

...このドッカーファイルで:

#syntax=docker/dockerfile:1.4
FROM … AS build1
COPY –from=app1 . /src

FROM … AS build2
COPY –from=app2 . /src

FROM …
COPY –from=build1 /out/app1 /bin/
COPY –from=build2 /out/app2 /bin/

...を使用してビルド docker buildx build –build-context app1=app1/src –build-context app2=app2/src .を呼び出すことができます。両方のソース ディレクトリは個別に Dockerfile に公開され、それぞれの名前でアクセスできます。

これにより、メインプロジェクトのソースコードの外部にあるファイルにアクセスすることもできます。 通常、 の中にいる Dockerfileときは、セキュリティ上の理由から、 ../ 親セレクターを使用してビルドコンテキスト外のファイルにアクセスすることはできません。 ただし、すべてのビルド コンテキストはクライアントから直接渡されるため、この制限を回避するために使用できます --build-context othersource=../../path/to/other/project

例 #3: リモート依存関係をローカル依存関係でオーバーライドする

複数のソース コンテキストをビルドに公開する場合、前の例のように、プロジェクトが常に複数のローカル ディレクトリに依存する場合があります。 ただし、依存関係をデフォルトでリモートソースからロードし、追加のデバッグを行う場合はローカルソースに置き換えるオプションを残したい場合もあります。

例として、アプリがマルチステージ ビルドを使用してソース コードからビルドする別のプロジェクトに依存する一般的なパターンを見てみましょう。

何かのようなもの:

FROM golang AS helper
RUN apk add git
WORKDIR /src
ARG HELPERAPP_VERSION=1.0
RUN git clone https://github.com/someorg/helperapp.git && cd helperapp && git checkout $HELPERAPP_VERSION
WORKDIR /src/helperapp
RUN go build -o /out/helperapp .

FROM alpine
COPY –link –from=helper /out/helperapp /bin
COPY –link –from=build /out/myapp /bin

 

これは非常にうまく機能します。 ビルドを実行すると、 helperapp はソース リポジトリから直接ビルドされ、アプリのバイナリの横にコピーされます。 別のバージョンを使用する必要がある場合は、build 引数を使用して HELPERAPP_VERSION 別の値を指定できます。

しかし、アプリケーションを開発していて、バグを発見したとしましょう。 バグがアプリケーションコードにあるのか、ヘルパーアプリにあるのかはよくわかりません。 コードにローカルな変更を加えて、 helperapp 何が起こっているのかを分析する必要があります。 問題は、現在のコードでは、最初に Dockerfile変更をGithubにプッシュして、. コードを変更するたびにこれを行うのは非常に苦痛です。

代わりに、前のコードを次のように変更するかどうかを検討してください。

FROM alpine AS helper-clone
RUN apk add git
WORKDIR /src
ARG HELPERAPP_VERSION=1.0
RUN git clone https://github.com/someorg/helperapp.git && cd helperapp && git checkout $HELPERAPP_VERSION

FROM scratch AS helper-src
COPY –from=helper-clone /src/helperapp /

FROM golang:alpine AS helper
WORKDIR helperapp
RUN –mount=target=.,from=helper-src go build -o /out/helperapp .

FROM alpine
COPY –link –from=helper /out/helperapp /bin
COPY –link –from=build /out/myapp /bin

 

デフォルトでは、これは Dockerfile 前のものとまったく同じように動作し、GitHubからクローンを作成してソースコードを取得します。 しかし、 の helperapp ソースコードを含む別のステージ helper-srcを追加したため、必要に応じて、新しい名前付きコンテキスト機能を使用してローカルソースディレクトリでオーバーライドできます。

docker buildx build –build-context helper-src=../パス/宛先/マイ/ローカル/ヘルパー/チェックアウト。

164336539 676b3ad0 959c 4668 9e8f 07ce211991cd

これで、個別のDockerfileを使用せずに、またはすべてのソースコードを同じディレクトリに移動することなく、すべてのローカルパッチをテストできます。

buildx ベイクの名前付きコンテキスト

'build' コマンドに加えて、'docker buildx' には 'bake' というコマンドもあります。 Bake は、ビルド コマンドのフラグの長いリストを毎回入力する代わりに、ビルド構成をファイルに定義できる高レベルのビルド コマンドです。

さらに、多くのビルドを一緒に実行したり、変数を定義したり、個別のビルド構成間で定義を共有したりすることができます。 JSON、HCL、および Docker Compose YAML ファイルのビルド構成を受け入れます。 あなたはそれについてもっと読むことができます ビルド x のドキュメント.

また、名前付きコンテキストのサポートを bakeに追加しました。 これは、複数のビルドコンテキストに依存する を記述する Dockerfile と、ビルドコマンドを呼び出すたびにこれらの値を flag で --build-context 渡す必要があることを忘れる可能性があるため、便利です。

bake を使用すると、ターゲット定義を定義できます。 例えば:

hcl
target “binary” {
  contexts = {
    app1 = “app1/src”
    app2 = “app2/src”
  }
}

 

毎回正しいパスでフラグを使用すること --build-context を覚えている代わりに、呼び出す docker buildx bake binary だけで、ビルドが正しい構成で実行されます。 もちろん、より複雑なケースでは、これらのフィールドでBake変数などを使用することもできます。

このパターンを使用して、ステージング リポジトリ内のイメージをデバッグまたはテストする目的で特別なベイク ターゲットを作成することもできます。

hcl
target “myapp” {
 …
}

target “myapp-stage” {
  inherits = [“myapp”]
  contexts = {
    helperapp = “docker-image://staging.myorg.com/registry/myapp”
  }
}

このような Bake ファイルを使用すると、ターゲットに対して myapp 定義された正確な構成でアプリをビルドするために呼び出す docker buildx bake myapp-stage ことができますが、ビルドでイメージを使用している場合は helperapp 、 に書き込まれ Dockerfileるリリースリポジトリではなく、ステージングリポジトリから読み込まれます。

ベイクターゲットのリンクによるビルドパイプラインの作成

画像、Git、URL、およびローカルディレクトリに加えて、Bake ファイルは名前付きコンテキストとして使用できる別の定義もサポートしています。 名前付きコンテキストのソースを、Bake ファイル内の別のビルドターゲットを指すように設定できます。 このようにして、相互に依存する複数の Dockerfile からビルドをチェーンし、1 回のコマンド呼び出しでビルドできます。

2つのドッカーファイルがあるとします。

# base.Dockerfile
FROM alpine
…
# Dockerfile
FROM baseapp
...

 

通常は、最初にビルド base.Dockerfileしてからレジストリにプッシュするか、Docker イメージ ストアに残します。 次に、名前でイメージを読み込む 2 番目の Dockerfile ビルドを行います。

このアプローチの問題は、Docker イメージ ストアを使用する場合、現在マルチプラットフォームのローカル イメージをサポートしていないことです。 外部レジストリを使用することも必ずしも便利であるとは限らず、どちらの場合も、外部の変更によって 2 つのビルドの間に基本イメージが更新され、2 番目のビルドで間違ったイメージが使用される可能性があります。 また、ビルド コマンドを 2 回実行し、手動で同期する必要があります。

代わりに、プレフィックスで target: 定義されたビルドコンテキストを使用して Bake ファイルを定義できます。

target “base” {
  dockerfile = “base.Dockerfile”
  platforms = [“linux/amd64”, “linux/arm64”]
}

target “myapp” {
  contexts = {
    baseapp = “target:base”
  }
  platforms = [“linux/amd64”, “linux/arm64”]
}

 

これで、実行 docker buildx bake myapp するだけでアプリをビルドして両方の Dockerfile をビルドし、必要に応じてリンクできます。 基本イメージとアプリの両方を一緒にビルドする場合は docker buildx bake myapp base、. これらのターゲットは両方ともマルチプラットフォームとして定義されており、Buildxは対応する単一プラットフォーム subimages を相互にリンクします。

164336677 CA22A85A 276A 4927 AAEF 500204BC2E35 2

これらの条件では、常に最初にパラメーターを使用して --target マルチステージビルドを使用することを検討する必要があることに注意してください。 自己完結型の Dockerfiles を使用すると、ビルドで追加のパラメーターを渡す必要がないため、より簡単なソリューションです。 このパターンは、Dockerfile を組み合わせることができず、それらを分離しておく必要がある場合に使用する必要があります。

で新しいビルドコンテキスト機能を確認してください Docker Buildx v0.8 リリース、最新の Docker デスクトップに含まれています。