doridoridoriand’s diary

主に技術的なことを書いていく予定(たぶん)

Docker Buildxを利用してマルチアーキテクチャなdockerコンテナを作成する

classmethod 若槻さんの記事 Dockerのマルチアーキテクチャイメージについて調べてみた を読んでマルチアーキテクチャなdockerコンテナを作ってみたくなったので、忘備録として記事を書きます。

ビルドの方法が分からなかったので、dockerの公式サイトを覗くと記載がありました。 Leverage multi-CPU architecture support

Docker Official Imagesの githubリポジトリ を覗くと、対応しているアーキテクチャは2020年4月12日時点で

  • ARMv5 32bit (arm32v5) ※非公式
  • ARMv6 32bit (arm32v6)
  • ARMv7 32bit (arm32v7)
  • ARMv8 64bit (arm32v8)
  • Linux86-64 (amd64)
  • Windows x86-64 (windows-amd64)
  • IBM POWER8 (ppc64le) ※非公式
  • IBM z Systems (s390x) ※非公式
  • x86/i686 (i386) ※非公式

とのようです。(i386Linuxのみかな)

AWSが提供しているa1インスタンスのcpuinfoを見てみると

$ cat /proc/cpuinfo
processor   : 0
BogoMIPS    : 166.66
Features    : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x0
CPU part    : 0xd08
CPU revision    : 3

と表示されました。ARMのドキュメントを検索すると次のページがヒットしました
ARM Information Center

このページ書かれている内容から、

CPU implementer  : 0x41 => ARM Limited.
CPU part    : 0xd08 => Cortex-A72 processor.

ということが分かりました。Cortex-A72はARMv8アーキテクチャを採用しているCPUなので、問題なくマルチアーキテクチャビルドのコンテナが動くはずです。検証環境が整うことがわかったので今回は、

  1. マルチアーキテクチャのdockerコンテナをビルド
  2. docker hubにpush
  3. 手元のPCとARMインスタンスのEC2にpullしてきて動かす

を実施してみようと思います。

前準備

exprimental featureの有効化

マルチアーキテクチャのコンテナビルドのためには experimental feature(試験的な機能)を有効にする必要があります。Docker Desktopでは、

f:id:doridoridoriand:20200412203715p:plain
Docker Desktop

のように config.json を記載するエリアがあるので、 Use the Docker command line | Docker Documentation に記載されているJSONを入力します。

もしくは、ホームディレクトリ直下の .docker ディレクトリに config.json があるので、同様の内容を記載すればおkです(Linuxでも同じです)。

buildKitのインストール

今回使用する buildx というプラグインMoby BuildKit の拡張であるため、BuildKitのインストールが必要になります。 Macであれば

$ brew install buildkit

でもいいですし、公式に則った形でバイナリを /usr/local/bin 配下に設置してもいいでしょう。 buildctl コマンドが通ればインストール完了です。

$ buildctl
NAME:
   buildctl - build utility

USAGE:
   buildctl [global options] command [command options] [arguments...]

VERSION:
   v0.7.0

COMMANDS:
   du        disk usage
   prune     clean up build cache
   build, b  build
   debug     debug utilities
   help, h   Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --debug                enable debug output in logs
   --addr value           buildkitd address (default: "unix:///run/buildkit/buildkitd.sock")
   --tlsservername value  buildkitd server name for certificate validation
   --tlscacert value      CA certificate for validation
   --tlscert value        client certificate
   --tlskey value         client key
   --tlsdir value         directory containing CA certificate, client certificate, and client key
   --timeout value        timeout backend connection after value seconds (default: 5)
   --help, -h             show help
   --version, -v          print the version

buildxプラグインの追加

次に buildx というプラグインが必要になるので、 リポジトリ から最新のバイナリをダウンロードします。 ホームディレクトリにある .docker ディレクトリに cli-plugins というディレクトリを作成し、先程ダウンロードしたバイナリ docker-buildx という名前で保存します。

更に実行権限を付与します。

$ chmod a+x ~/.docker/cli-plugins/docker-buildx

以上が終わって、docker buildx コマンドが通れば利用可能です。

$ docker buildx

Usage:  docker buildx COMMAND

Build with BuildKit

Management Commands:
  imagetools  Commands to work on images in registry

Commands:
  bake        Build from a file
  build       Start a build
  create      Create a new builder instance
  inspect     Inspect current builder instance
  ls          List builder instances
  rm          Remove a builder instance
  stop        Stop builder instance
  use         Set the current builder instance
  version     Show buildx version information

Run 'docker buildx COMMAND --help' for more information on a command.

ビルド

今回は、rubyで実行したら実行マシンの現在時間を出力するコンテナを利用します。Dockerfileは以下の内容を利用します。

FROM ruby:2.6.3

CMD ruby -e "puts Time.now"

普通にビルド&実行をしてみると

$ docker build -t multi-arch-norm -f multi_arch_Dockerfile .
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM ruby:2.6.3
 ---> d529acb9f124
Step 2/2 : CMD ruby -e "puts Time.now"
 ---> Running in f6ca4f8d0f5b
Removing intermediate container f6ca4f8d0f5b
 ---> 7a130a088630
Successfully built 7a130a088630
Successfully tagged multi-arch-norm:latest

$ docker run -it --rm multi-arch-norm
2020-04-12 12:15:34 +0000

と問題なく実行出来ること分かりました。

ではマルチアーキテクチャモードでビルドを実行してみます。

docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t doridoridoriand/multi-arch:latest . 
multiple platforms feature is currently not supported for docker driver. Please switch to a different driver (eg. "docker buildx create --use")

どうやら現在の docker driver ではマルチアーキテクチャのコンテナをビルドできないようです。指示に書いてあるとおり、 docker buildx create --use を実行します。

$ docker buildx create --use
$ ecstatic_roentgen

ランダムな名前でビルド用のコンテナが作成されました。

$ docker ps
CONTAINER ID        IMAGE                           COMMAND             CREATED             STATUS              PORTS               NAMES
8ce5fa2759e8        moby/buildkit:buildx-stable-1   "buildkitd"         6 minutes ago       Up 6 minutes                            buildx_buildkit_ecstatic_roentgen0

確かにbuildkitのコンテナが起動していることが分かります。

再度ビルドコマンドを実行します。

$ docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t doridoridoriand/multi-arch:latest .
WARN[0000] No output specified for docker-container driver. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load
[+] Building 133.1s (9/9) FINISHED
 => [internal] booting buildkit                                                                                                                                                                                                          7.3s
 => => pulling image moby/buildkit:buildx-stable-1                                                                                                                                                                                       6.5s
 => => creating container buildx_buildkit_ecstatic_roentgen0                                                                                                                                                                             0.7s
 => [internal] load .dockerignore                                                                                                                                                                                                        0.0s
 => => transferring context: 2B                                                                                                                                                                                                          0.0s
 => [internal] load build definition from Dockerfile

出力が長いので省略

無事ビルドができたようです。ただ、WARNには、

No output specified for docker-container driver. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load

と書かれていることから、コンテナのビルド結果はキャッシュにしか残ってないようです。利用するには --push オプションでdocker hubにpushするか、--load オプションでローカルに持ってくるかが必要になるようです。今回は後で別マシンでpullしたいので、docker hubにpushすることにしました。

pushオプションを付けて再度実行したところ、無事にpushされ、単一のイメージタグで、複数のCPUアーキテクチャが利用出来るようになっています。

f:id:doridoridoriand:20200412223844p:plain
無事マルチアーキテクチャになっている

以下が実際にpushしたdocker imageになります。 multi-arch | Tags

pull&実行

実際に動くか試します。まずはx86-64(amd64)PCで実施します。cpuinfoは

processor       : 5
vendor_id       : GenuineIntel
cpu family      : 6
model           : 158
model name      : Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz

長いので省略

となっています。コンテナをpullして実行してみます。

$ docker pull doridoridoriand/multi-arch:latest
$ docker run -it --rm doridoridoriand/multi-arch:latest
2020-04-12 13:54:31 +0000

無事動きました。

次にARM(arm32v8)マシンで実施します。cpuinfoは

$ cat /proc/cpuinfo
processor       : 0
BogoMIPS        : 166.66
Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant     : 0x0
CPU part        : 0xd08
CPU revision    : 3

となっています。コンテナをpullして実行してみます。

$ sudo docker pull doridoridoriand/multi-arch:latest
$ sudo docker run -it --rm doridoridoriand/multi-arch:latest
2020-04-12 14:06:47 +0000

無事実行できました。

まとめ

Dockerがマルチアーキテクチャをサポートしたことで、IntelAMDのCPU以外でもDockerコンテナを動かす敷居が低くなりました。AWSで言えば、c系インスタンスとa系インスタンスのハイブリット構成でAPIサーバーを構築することも不可能ではなくなったので、実際に採用するかは別として、アーキテクティングの幅が広がったかと思います。

機会があれば、ミドルウェア等で試してみようと思います。