目次
異なるオペレーティング システムやプロセッサ アーキテクチャ上でアプリケーションを実行するのが一般的であるため、異なるプラットフォーム用に個別のディストリビューションを構築するのが一般的です。アプリケーションの開発に使用するプラットフォームが、デプロイ先のプラットフォームと異なる場合、これを達成するのは簡単ではありません。たとえば、x86 アーキテクチャでアプリケーションを開発し、ARM プラットフォーム マシンに展開するには、通常、開発とコンパイルのために ARM プラットフォーム インフラストラクチャを準備する必要があります。
複数のデプロイメント用のイメージ配布を一度に構築すると、アプリケーションの配信効率が大幅に向上します。アプリケーションのクロスプラットフォーム デプロイメントが必要なシナリオでは、docker buildx を使用してクロスプラットフォーム イメージを構築することも高速で効率的なソリューションです。
前提
ほとんどのイメージ ホスティング プラットフォームはマルチプラットフォーム イメージをサポートしています。つまり、ミラー リポジトリ内の 1 つのタグに、異なるプラットフォームの複数のイメージを含めることができます。ミラー リポジトリを例に取ると、このタグには、異なるシステムとアーキテクチャの 10 個のイメージが含まれています (プラットフォーム =docker hub
システム ) + アーキテクチャ):python
3.9.6
クロスプラットフォームをサポートするイメージを渡す docker pull
か プルする場合、 現在実行中のプラットフォームに一致するイメージが自動的に選択されます。この機能の存在により、プラットフォーム間でイメージを配布する際、イメージの消費に関しては何もする必要がなく、イメージの生成、つまりクロスプラットフォームのイメージをどのように構築するかだけを考慮する必要があります。docker run
docker
ドッカービルドx
デフォルトの docker build
コマンドではクロスプラットフォームのビルド タスクを完了できないため、 コマンド ラインの機能を拡張するにはコマンド ラインのプラグインdocker
をインストールする 必要があります。Moby BuildKit によって提供されるビルド イメージの追加機能 を使用する機能 。これにより、複数のビルダー インスタンスを作成し、複数のノードでビルド タスクを並行して実行し、プラットフォーム間でビルドできます。buildx
buildx
Buildx を有効にする
macOS または Windows システム用の Docker Desktop、および Linux ディストリビューションは、 パッケージ インストール deb
によって 組み込まれており 、追加のインストールは必要ありません。rpm
docker
buildx
docker
このコマンドがない 場合は buildx
、インストール用のバイナリ パッケージをダウンロードできます。
- まず、 Docker buildxプロジェクト のリリース ページから、プラットフォームに適したバイナリ ファイルを見つけます。
- バイナリ ファイルをローカルにダウンロードし、名前を に変更し
docker-buildx
、docker プラグイン ディレクトリに移動します~/.docker/cli-plugins
。 - バイナリに実行権限を付与します。
ローカル docker
バージョンが 19.03 より高い場合は、次のコマンドを使用してローカルに直接ビルドしてインストールできます。これはより便利です。
$ export DOCKER_BUILDKIT=1
$ docker build --platform=local -o . git://github.com/docker/buildx
$ mkdir -p ~/.docker/cli-plugins
$ mv buildx ~/.docker/cli-plugins/docker-buildx
を使用して buildx
ビルドする方法は次のとおりです。
docker buildx build .
buildx
ユーザー エクスペリエンスは基本的にコマンドのエクスペリエンスと同じであり、 、など の一般的なオプション docker build
もサポートしています 。build
-t
-f
ビルダーインスタンス
docker buildx
ビルド構成とノードはビルダー インスタンス オブジェクトを通じて管理され、コマンド ラインによってビルド タスクがビルダー インスタンスに送信され、ビルダーはそれを実行条件を満たすノードに割り当てます。同じサービス プログラムに基づいて複数のビルダー インスタンスを作成し docker
、それらを異なるプロジェクトに提供して、各プロジェクトの構成を分離することもできます。また、 docker
リモート ノードのグループ用のビルダー インスタンスを作成してビルド配列を形成し、異なるノード間で迅速に切り替えることもできます。配列。
コマンドを使用して docker buildx create
ビルダー インスタンスを作成します。これにより、現在使用されている Docker サービスを使用してノードの新しいビルダー インスタンスが作成されます。DOCKER_HOST
リモート ノードを使用するには、環境変数を使用してリモート ポートを指定するか、 サンプルの作成時に 事前にリモート ノードに切り替えることができますdocker context
。次の例では、リモート ノード上に新しいビルダー インスタンスを作成し、そのドライバー、ターゲット プラットフォーム、およびインスタンス名をコマンド ライン オプションで指定します。
|
docker buildx ls
使用可能なすべてのビルダー インスタンスとその中のノードがリストされます。
|
インスタンスが作成された後、インスタンスに新しいノードを追加し続けることができます。 docker buildx create
コマンドの オプションを 使用すると、オプションで指定されたビルダー インスタンス--append <node>
にノードを追加できます 。--name <builder>
$ docker buildx create --name default --append remote-builder0
docker buildx inspect
、docker buildx stop
および docker buildx rm
コマンドは、インスタンスのライフサイクルを管理するために使用されます。
docker buildx use <builder>
指定されたビルダー インスタンスに切り替えます。
ビルドドライバー
buildx インスタンスは、異なる「ドライバー」を使用する 2 つの方法でビルド タスクを実行します。
docker
ドライバー: Docker サーバーに統合されている BuildKit ライブラリを使用してビルドを実行します。docker-container
ドライバー: BuildKit を含むコンテナーを起動し、コンテナー内でビルドを実行します。
docker
ドライバーは少数 buildx
の機能 (1 回の実行で複数のプラットフォーム イメージを同時にビルドするなど) を使用できません。また、イメージのデフォルトの出力形式にも違いがあります。ドライバーはデフォルトでビルド結果を直接出力しますdocker
。 Docker イメージ形式を docker
イメージ ディレクトリ (通常 /var/lib/overlay2
) にコピーすると、コマンドの実行後に docker images
出力ミラーを一覧表示でき、 オプションdocker container
で出力形式をミラーまたはその他の形式に指定する必要があります --output
。
複数のプラットフォーム用のイメージを一度にビルドするために、 docker container
以下ではドライバーのビルダー インスタンスを使用します。
buildx のクロスプラットフォーム ビルド戦略
さまざまなビルド ノードとターゲット プログラミング言語に応じて、buildx
次の 3 つのクロスプラットフォーム ビルド戦略がサポートされています。
- QEMU のユーザー モードを使用して軽量の仮想マシンを作成し、仮想マシン システムにミラー イメージを構築します。
- 異なるターゲット プラットフォームの複数のノードをビルダー インスタンスに追加し、ネイティブ ノードを通じて対応するプラットフォーム イメージを構築します。
- 段階的にビルドし、さまざまなターゲット アーキテクチャにクロスコンパイルします。
QEMU は通常、完全なオペレーティング システムをエミュレートするために使用されますが、ユーザー モードでも実行できます。 binfmt_misc
ホスト システムにバイナリ変換ハンドラを登録し、プログラムの実行中にバイナリ ファイルを動的に変換し、ターゲット CPU からシステム コールを転送します。必要に応じて、アーキテクチャは現在のシステムの CPU アーキテクチャに変換されます。最終的な効果は、仮想マシンでターゲット CPU アーキテクチャのバイナリを実行するようなものです。Docker Desktop には QEMU サポートが組み込まれており、実行要件を満たす他のプラットフォームは次の方法でインストールできます。
|
この方法は既存の Dockerfile を変更する必要がなく、実装コストは非常に低くなりますが、明らかに効率は高くありません。
さまざまなシステム アーキテクチャのネイティブ ノードをビルダー インスタンスに追加すると、クロスプラットフォーム コンパイルのサポートが強化され、より効率的になりますが、十分なインフラストラクチャ サポートが必要です。
プロジェクトのビルドに使用されるプログラミング言語がクロスコンパイル (C や Go など) をサポートしている場合は、Dockerfile が提供する段階的ビルド機能を使用できます。まず、ビルド ノードと同じアーキテクチャでターゲット アーキテクチャのバイナリ ファイルをコンパイルします。次に、これらのバイナリ ファイルをコンパイルし、ターゲット スキーマの別のミラーにコピーします。以下では具体的な例をGoで実装していきます。この方法は追加のハードウェアを必要とせず、より優れたパフォーマンスを実現できますが、それを実現できるのは特定のプログラミング言語のみです。
複数のアーキテクチャ Go イメージを一度に構築する練習
ソースコードとDockerfile
main.go
以下では、サンプル プログラム ファイルの内容が次のとおりであると仮定して、単純な Go プロジェクトを例として取り上げます 。
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Println("Hello world!")
fmt.Printf("Running in [%s] architecture.\n", runtime.GOARCH)
}
ビルドプロセスを定義する Dockerfile は次のとおりです。
FROM --platform=$BUILDPLATFORM golang:1.14 as builder
ARG TARGETARCH
WORKDIR /app
COPY main.go /app/main.go
RUN GOOS=linux GOARCH=$TARGETARCH go build -a -o output/main main.go
FROM alpine:latest
WORKDIR /root
COPY --from=builder /app/output/main .
CMD /root/main
ビルド プロセスは 2 つのフェーズに分かれています。
golang
1 つのステージでは、現在のビルド ノードと同じプラットフォームのイメージをプルし 、Go のクロスコンパイル機能を使用してターゲット アーキテクチャ用のバイナリにコンパイルします。- 次に、
alpine
ターゲット プラットフォームのイメージをプルし、前のステージのコンパイル結果をイメージにコピーします。
クロスプラットフォームビルドを実行する
ビルド コマンドを実行するときは、イメージ名の指定に加えて、ターゲット プラットフォームと出力形式の指定という 2 つの重要なオプションがあります。
docker buildx build
オプションで ビルドのターゲット プラットフォームを指定します--platform
。Dockerfile 内の FROM コマンドで --platform
フラグが設定されていない場合、ベース イメージはターゲット プラットフォームによってプルされ、最終的に生成されたイメージもターゲット プラットフォームに属します。 さらに、このオプションの値はBUILDPLATFORM
、 、TARGETPLATFORM
、BUILDARCH
など のパラメータを介して Dockerfile で使用できます 。TARGETARCH
ドライバーを使用する場合 docker-container
、このオプションはカンマで区切られた複数の値を入力として受け入れ、複数のターゲット プラットフォームを同時に指定できます。すべてのプラットフォームのビルド結果は出力としてイメージ リスト全体に結合されるため、直接ビルドすることはできませんローカル docker images
画像として出力します。
docker buildx build
豊富な出力動作をサポートします。--output=[PATH,-,type=TYPE[,KEY=VALUE]
オプションを通じて、ビルド結果の出力タイプとパスを指定できます。一般的に使用される出力タイプは次のとおりです。
- local: ビルド結果は、ファイルシステム形式で
dest
指定されたローカル パス に書き込まれます--output type=local,dest=./output
。 - tar: ビルド結果は、パッケージ化後に
dest
指定されたローカル パスに書き込まれます。 - oci: ビルド結果は、
dest
指定されたローカル パスに OCI 標準イメージ形式で書き込まれます。 - docker: ビルド結果は、
dest
指定されたローカル パスに書き込まれるか、docker
Docker 標準イメージ形式でイメージ ライブラリにロードされます。複数のターゲット プラットフォームを同時に指定する場合、このオプションは使用できません。 - image: ミラー イメージまたはミラー リストとして出力し、
push=true
リモート ウェアハウスに直接プッシュするオプションをサポートします。このオプションは、複数のターゲット プラットフォームを同時に指定するときに使用できます。 type=image,push=true
レジストリのコンパクトな表現:
この例では、次の docker buildx build
コマンドを実行します。
$ docker buildx build --platform linux/amd64,linux/arm64,linux/arm -t registry.cn-hangzhou.aliyuncs.com/waynerv/arch-demo -o type=registry .
linux/amd64
このコマンドは、 linux/arm64
と 3 つのプラットフォームのイメージを現在のディレクトリに同時に構築し linux/arm
、出力結果をリモートの Alibaba Cloud ミラー ウェアハウスに直接プッシュします。
構築プロセスは次のように分解できます。
docker
ビルド コンテキストをビルダー インスタンスに転送します。- ビルダーは、
--platform
基本イメージの取得やビルド ステップの実行など、コマンド ライン オプションで指定された各ターゲット プラットフォームのイメージをビルドします。 - ビルド結果がエクスポートされ、イメージ ファイル レイヤーがリモート ウェアハウスにプッシュされます。
- マニフェスト JSON ファイルを生成し、ミラー タグとしてリモート リポジトリにプッシュします。
ビルド結果を確認する
実行後、 docker buildx imagetools
リモート ウェアハウスにプッシュされたイメージを検査できます。
$ docker buildx imagetools inspect registry.cn-hangzhou.aliyuncs.com/waynerv/arch-demo:latest
Name: registry.cn-hangzhou.aliyuncs.com/waynerv/arch-demo:latest
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Digest: sha256:e2c3c5b330c19ac9d09f8aaccc40224f8673e12b88ff59cb68971c36b76e95ca
Manifests:
Name: registry.cn-hangzhou.aliyuncs.com/waynerv/arch-demo:latest@sha256:cb6a7614ee3db03c8858e3680b1585f32a6fe3de9b371e37e25cf42a83f6e0ba
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/amd64
Name: registry.cn-hangzhou.aliyuncs.com/waynerv/arch-demo:latest@sha256:034aa0077a452a6c2585f8b4969c7c85d5d2bf65f801fcc803a00d0879ce900e
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/arm64
Name: registry.cn-hangzhou.aliyuncs.com/waynerv/arch-demo:latest@sha256:db0ee3a876fb789d2e733471385eef0a056f64ee12d9e7ef94e411469d054eb5
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/arm/v7
最後に、さまざまなプラットフォームでタグを含む latest
イメージをプルして実行し、ビルド結果が正しいかどうかを確認します。Docker Desktop を使用する場合、統合された仮想化機能はさまざまなプラットフォームのイメージを実行し、 sha256
値によってイメージを直接プルできます。
$ docker run --rm registry.cn-hangzhou.aliyuncs.com/waynerv/arch-demo:latest@sha256:cb6a7614ee3db03c8858e3680b1585f32a6fe3de9b371e37e25cf42a83f6e0ba
Hello world!
Running in [amd64] architecture.
$ docker run --rm registry.cn-hangzhou.aliyuncs.com/waynerv/arch-demo:latest@sha256:034aa0077a452a6c2585f8b4969c7c85d5d2bf65f801fcc803a00d0879ce900e
Hello world!
Running in [arm64] architecture.
Golang の CGO プロジェクトをクロスコンパイルする方法
一般的なオペレーティング システムと CPU アーキテクチャへのクロスコンパイルのサポートは Golang の大きな利点ですが、上記の例のソリューションは純粋な Go コードにのみ適用できます。プロジェクトが C コードを呼び出す場合、状況はさらに複雑になります cgo
。
クロスコンパイル環境と依存関係を準備する
C コードをターゲット プラットフォームに正常にコンパイルするには、まずターゲット プラットフォームの C クロス コンパイラ (通常はベース) をコンパイル環境にインストールする必要があります。一般的に使用される Linux ディストリビューションには、ほとんどのプラットフォーム用のクロス コンパイラ インストール パッケージが用意されています gcc
。これは、パッケージ マネージャーのインストールを通じて直接インストールできます。
次に、ターゲット プラットフォームの C 標準ライブラリをインストールする必要があります (通常、標準ライブラリはクロス コンパイラのインストール依存関係として使用され、個別にインストールする必要はありません)。呼び出すコードに応じて、追加の C 依存ライブラリ (など) をインストールする必要がある場合もあります libopus-dev
。
amd64
アーキテクチャの 公式イメージをベース イメージとして使用し golang:1.14
、Linux ディストリビューション Debian を使用するコンパイルを実行します。クロスコンパイルの対象プラットフォームを とすると linux/arm64
、準備するクロスコンパイラは gcc-aarch64-linux-gnu
、C標準ライブラリは libc6-dev-arm64-cross
、インストール方法は以下のようになります。
$ apt-get update
$ apt-get install gcc-aarch64-linux-gnu
libc6-dev-arm64-cross
も同時にインストールされます。
Debian パッケージ マネージャーが提供するマルチ アーキテクチャ インストール機能のおかげで dpkg
、コードが libopus-dev
他の非標準ライブラリに依存している場合、 <library>:<architecture>
次の方法でそのアーキテクチャ arm64
のインストール パッケージをインストールできます。
$ dpkg --add-architecture arm64
$ apt-get update
$ apt-get install -y libopus-dev:arm64
クロスコンパイル CGO の例
次のサンプルコードがあるとします cgo
。
package main
/*
#include <stdlib.h>
*/
import "C"
import "fmt"
func Random() int {
return int(C.random())
}
func Seed(i int) {
C.srandom(C.uint(i))
}
func main() {
rand := Random()
fmt.Printf("Hello %d\n", rand)
}
使用するDockerfileは以下のとおりです。
FROM --platform=$BUILDPLATFORM golang:1.14 as builder
ARG TARGETARCH
RUN apt-get update && apt-get install -y gcc-aarch64-linux-gnu
WORKDIR /app
COPY . /app/
RUN if [ "$TARGETARCH" = "arm64" ]; then CC=aarch64-linux-gnu-gcc && CC_FOR_TARGET=gcc-aarch64-linux-gnu; fi && \
CGO_ENABLED=1 GOOS=linux GOARCH=$TARGETARCH CC=$CC CC_FOR_TARGET=$CC_FOR_TARGET go build -a -ldflags '-extldflags "-static"' -o /main main.go
Dockerfile は クロスコンパイラーとしてapt-get
インストールされ 、サンプル プログラムは比較的シンプルであるため、追加の依存ライブラリは必要ありません。gcc-aarch64-linux-gnu
コンパイル時に 、 使用するクロスコンパイラ と 環境変数を指定するgo build
必要があります 。CC
CC_FOR_TARGET
同じ Dockerfile に基づいて複数のターゲット プラットフォームをコンパイルするために (ターゲット アーキテクチャが amd64
/のみであると仮定しますarm64
)、一番下の RUN
命令では、Bash の条件判断構文を通じて異なるコンパイル コマンドを実行するトリックを使用します。
- ビルドタスクのターゲットプラットフォームが の場合
arm64
、 環境変数CC
にはCC_FOR_TARGET
インストールされているクロスコンパイラを指定します(値が異なることに注意してください)。 - ビルドタスクのターゲットプラットフォームが の場合
amd64
、クロスコンパイラに関連する変数を指定しないと、デフォルトの変数がgcc
コンパイラとして使用されます。
最後に、buildx を使用してビルドを実行するコマンドは次のとおりです。
$ docker buildx build --platform linux/amd64,linux/arm64 -t registry.cn-hangzhou.aliyuncs.com/waynerv/cgo-demo -o type=registry .
要約する
Buildx
プラグインを使用すると、インフラストラクチャなしでクロスプラットフォーム アプリケーション イメージを docker
簡単に。
ただし、デフォルトでは、QEMU を介してターゲット プラットフォームの命令を仮想化する方法には明らかなパフォーマンスのボトルネックがあり、アプリケーションの作成に使用される言語がクロスコンパイルをサポートしている場合は、組み合わせとクロスコンパイルによってより高い効率を達成できます buildx
。
最後に、この記事では高度なシナリオのソリューション、つまり CGO を使用して Golang プロジェクトをクロスコンパイルする方法を紹介し、 linux/arm64
プラットフォームへのコンパイルの例を示します。