Docker Buildxでマルチアーキテクチャ対応のイメージを構築する

はじめに

最近まで、GoogleのクラウドプロジェクトにKubernetesクラスタを作成していましたが、有料のため金銭的なコストをかけずにマルチノードクラスタを実践できる環境を構成したいと思い、自宅にあった4台の開発ボードordroid-xu4を活用してみることにしました。

エラー発生

Dockerをセットする過程で問題が発生しました。Dockerをインストールしてテストするため、簡単にビルドしたイメージをPullで受け取りコンテナを実行したところ、以下のように失敗しました。

jaeeunyoo@skynet-201:~$ docker pull frontiersofme/py-app
Using default tag: latest
latest: Pulling from frontiersofme/py-app
...
Status: Image is up to date for frontiersofme/py-app:latest
docker.io/frontiersofme/py-app:latest
jaeeunyoo@skynet-201:~$ docker run -it -p 8081:8081 frontiersofme/py-app
standard_init_linux.go:211: exec user process caused "exec format error"

 

なぜダメなのか?

表示されるエラーメッセージを検索したところ、私の状況ではビルドされたイメージの実行環境(CPUアーキテクチャ)が異なるためでした。(xu4はarm32v7)

hello-worldはなぜうまくいくのだろう?

jaeeunyoo@skynet-201:~$ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.
...

ところが、hello-worldのサンプルは次のように正常に実行されました。
GitHubに公開されているhello-worldプロジェクトを見ると、Dockerfileにメジャー(major)したアーキテクチャに対して、クロスコンパイル(cross-compile)することを確認しました。

# explicitly use Debian for maximum cross-architecture compatibility
FROM debian:buster-slim

RUN set -eux; \
    apt-get update; \
    apt-get install -y --no-install-recommends \
        ca-certificates \
        gnupg dirmngr \
        wget \
        \
        gcc \
        libc6-dev \
        make \
        \
        libc6-dev-arm64-cross \
        libc6-dev-armel-cross \
        libc6-dev-armhf-cross \
        libc6-dev-i386-cross \
        libc6-dev-mips64el-cross \
        libc6-dev-ppc64el-cross \
        libc6-dev-s390x-cross \
        \
        gcc-aarch64-linux-gnu \
        gcc-arm-linux-gnueabi \
        gcc-arm-linux-gnueabihf \
        gcc-i686-linux-gnu \
        gcc-mips64el-linux-gnuabi64 \
        gcc-powerpc64le-linux-gnu \
        gcc-s390x-linux-gnu \
        \
        file \
    ; \
    rm -rf /var/lib/apt/lists/*
    ...

他にも異なるデバイスで実行させるビルド方法はいくつかありますが、組込みSW開発者でない限り、あまり興味をひく内容ではなく、簡単にビルドや配布を行うためにDockerを使用しようとする立場では、かなり面倒なように感じました。

Docker Buildxの紹介

諦めようとしたとき、たまたまDocker Buildxを見つけました。
Buildxは、さまざまなプラットフォームに向けてビルドする機能などを含むCLIの拡張プラグインで、19.03バージョンから使用できます。現在は試験的な機能(experimental feature)として提供されているため、使用するにはその機能を手動で有効にする必要があります。

使用方法

MacOS基準で作成しました。

Docker バージョンを確認

NHNEntui-MacBook-Pro-45:~ nhnent$ docker --version
Docker version 19.03.12, build 48a66213fe

19.03から適用されるため、それより前のバージョンでは使用できません。

Docker CLI (試験的な機能)の有効化

MacOS

# 有効前
NHNEntui-MacBook-Pro-45:~ nhnent$ docker buildx
docker: 'buildx' is not a docker command.
See 'docker --help'

# 有効後
NHNEntui-MacBook-Pro-45:~ nhnent$ 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.

ビルダーインスタンスを確認

NHNEntui-MacBook-Pro-45:~ nhnent$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS  PLATFORMS
default * docker
  default default         running linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6

inux/amd64、linux/arm64、linux/ppc64le、linux/s390x、linux/386、linux/arm/v7、linux/arm/v6に対してビルドが提供されます。

ビルダー作成および使用設定

docker buildx create –name [builder instance名] –driver [ドライバ名] –use

# マルチアーキテクチャービルダーとして使用設定
NHNEntui-MacBook-Pro-45:~ nhnent$ docker buildx create --name multi-arch-builder --driver docker-container --use
multi-arch-builder

# 現在のビルダーインスタンス情報
NHNEntui-MacBook-Pro-45:~ nhnent$ docker buildx inspect --bootstrap
[+] Building 6.0s (1/1) FINISHED
 => [internal] booting buildkit                                                                                                                                                                                                                                            6.0s
 => => pulling image moby/buildkit:buildx-stable-1                                                                                                                                                                                                                         5.0s
 => => creating container buildx_buildkit_multi-arch-builder0                                                                                                                                                                                                              1.0s
Name:   multi-arch-builder
Driver: docker-container

Nodes:
Name:      multi-arch-builder0
Endpoint:  unix:///var/run/docker.sock
Status:    running
Platforms: linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6

Buildxでイメージをビルド、およびPush

NHNEntui-MacBook-Pro-45:multiarch nhnent$ docker buildx build --platform linux/arm/v7 -t frontiersofme/multiarch-py-app --push .
[+] Building 105.2s (8/8) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                                                                                                                       0.0s
 => => transferring dockerfile: 125B                                                                                                                                                                                                                                       0.0s
 => [internal] load .dockerignore                                                                                                                                                                                                                                          0.0s
 => => transferring context: 2B                                                                                                                                                                                                                                            0.0s
 => [internal] load metadata for docker.io/library/python:3.8                                                                                                                                                                                                             12.7s
 => [internal] load build context                                                                                                                                                                                                                                          0.0s
 => => transferring context: 28B                                                                                                                                                                                                                                           0.0s
 => [1/3] FROM docker.io/library/python:3.8@sha256:2c1045587e4452d49544c6dce92efe21c3b4b33864cfb56fdee66a2c8585c769                                                                                                                                                       39.4s
 => => resolve docker.io/library/python:3.8@sha256:2c1045587e4452d49544c6dce92efe21c3b4b33864cfb56fdee66a2c8585c769                                                                         ...

対象のボードにイメージを Pull、その後コンテナを実行

# イメージを Pull
jaeeunyoo@skynet-201:~$ docker pull frontiersofme/multiarch-py-app
Using default tag: latest
latest: Pulling from frontiersofme/multiarch-py-app
e7cf402ee4b1: Already exists
cff6bd4a89fc: Already exists
e48f5852a753: Already exists
e58a93b33dd4: Already exists
a3c3db37cc57: Already exists
2c8297f1a358: Already exists
6d14eca818f3: Already exists
704121b65f11: Already exists
6cdf5a94358e: Already exists
9f9eec9ff6d5: Pull complete
a54ba2fcc3f8: Pull complete
Digest: sha256:3b46e7ef76d3512d31ddef962b07a5d46f999a9e10b7367d3548112b34af2cd7
Status: Downloaded newer image for frontiersofme/multiarch-py-app:latest
docker.io/frontiersofme/multiarch-py-app:latest

# コンテナ実行
jaeeunyoo@skynet-201:~$ docker run -it -p 10001:10001 frontiersofme/multiarch-py-app
 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:10001/ (Press CTRL+C to quit)
192.168.0.3 - - [27/Aug/2020 23:47:25] "GET / HTTP/1.1" 200 -
192.168.0.3 - - [27/Aug/2020 23:47:25] "GET / HTTP/1.1" 200 -
192.168.0.3 - - [27/Aug/2020 23:47:25] "GET /favicon.ico HTTP/1.1" 404 -
..

 

まとめ

現在は試験的な機能として提供されていますが、当該機能に対する需要は非常に高く、実際に低消費電力、高性能ARM 64bit基盤のインスタンスを提供するサービスも増えています。早く正式機能になることを期待しています。

TOAST Meetup 編集部

TOASTの技術ナレッジやお得なイベント情報を発信していきます
pagetop