クラウド上でARM用コンテナをビルドする

エッジノード候補の最右翼と菊地が考える、Raspberry Pi用のコンテナのビルドをクラウド上で高速に実行したい、ということでARMプロセッサまわりの状況について色々調べている。

ちょっと前にこの様な記事を書いた。

(恥ずかしながら)同じARMなら互換あるだろうからコンテナイメージ持ってくればいいよね、位に考えていたのだが、その後の調査でそれはちょっと甘かったことがわかった(が結果としてはできることがわかった)ので、今回はその辺のところについて書く。

ARMプロセッサの世代と各OSの対応

x86がそうであるようにARMも世代を重ねて進化していて、アーキテクチャに種類がある。

ARMv7 ( armhf | armv7l ) : 32Bit : Raspberry Pi (初代)、Rpi2, Zero、ZeroW(H)
ARMv8 (arm64 | armv8l ) : 64Bit : Raspberry Pi 3B+、現在の殆どのARMプロセッサ搭載マシン(スマホなども)

カッコ内は、unameで表示される、OSでの呼び方である(ディストリビューションによって少しづつ表記が違うのが困る)。
ほとんどのARMプロセッサは64bitアーキに移行しているのだが、Raspberry Piの古いのと、現在でも現役のZeroシリーズはARM7である。Raspberry Piのスペック情報は以下などであるが、あまりはっきり書いてないのが困りモノである。

一方でARMプロセッサに対応しているOSであるが、

32Bit OS : Raspbian OS
64Bit OS : Ubuntu, CentOSなど

となっている。Raspberry Piシリーズで現在最もメジャーなRaspberry Pi 3B+において、やはり最もメジャーなOSであるRaspbian OSが32Bit環境である。おそらくこれは、1,2など旧世代およびZeroを想定してのことだろうと思われる。

クラウド上に置いたハイパワーなARMサーバでバイナリを作りそれをRaspberry Piに持ってきて動かす、その最大の障害がここにある。現状の殆どのシステムはARM8プロセッサと64bitOSで動いている。一方Raspberry Pi(3B+)はARM8プロセッサだが32bitOSで動いている。両者の間にはバイナリ互換がない。

この時点で言いたいことを言ってしまうと「みんなRaspbian OS捨ててUbuntu(64bit)に移行しようぜ」ということになる(笑)。

バイナリ実行環境

プロセッサのアーキテクチャ(ARM7、ARM8)とOSの対応(32Bit、64Bit)がきちんと一致していれば実行バイナリの互換があるが、それらがずれている場合はそのままでは動かない。しかし動かすためのいくつかの方法が存在している。

ARM64bit環境におけるARM32bit互換モード実行
ARM8プロセッサとそれに対応した64bitOS環境下においては、OS(Linuxカーネル)の互換機能を使うことで32bitバイナリを実行させることができる。
ディストリビューションによってはOSレベルで対応している。そうでなくても、カーネルモジュールとShared Library環境(静的、動的)を用意することで32bitバイナリを実行させることが可能。

QEMUによるエミュレーション
プロセッサエミュレータ(環境)のQEMUを使うことで、ARMを飛び越えてx86環境においてARMバイナリを実行させることができる。

QEMUにはホスト環境全体をエミュレーションするシステムエミュレータモード(ホストモード)と、単一バイナリを実行するユーザランドエミュレーションモード(どちらも名称曖昧)の2種類がある。そして、やはりというか、RaspbianOS環境をエミュレーションさせている人はいた。

QEMUは実際には、binfmt_miscと組み合わせて、シームレスなバイナリ混合実行環境を作って使うケースが多いようだ。(x86なOS内で、ARMの32bitバイナリをqemuコマンドラインを意識することなく動かすことができる。)

で、ARM32bitバイナリを非Raspberry Pi環境で実行できることはわかってきたが、やりたいことはRaspberry Pi用のARM32bitのコンテナを作ることなので、引き続きバイナリ作成環境について調べる。

バイナリ作成環境(クロスコンパイル環境)

クロスコンパイル環境というとかなり大げさなイメージがあるが、実際にはすでにパッケージ化されていて、結構あっさり構築できる。

gcc環境
各OSにおいてgccのクロスコンパイル環境がパッケージ化されているので、それをインストールすることで、ターゲットのバイナリを作成することができる。

Eclipse環境(統合開発環境)
開発環境側にEclipseを入れて、プロジェクト設定によってRaspberry Pi向けのバイナリを作成する、など。

Go言語
Go言語では、(特別な作業をして環境を用意しなくても)バイナリ作成時のコマンドラインオプションで、ターゲットアーキテクチャを指定したバイナリを作成することができる。(Goで1バイナリ1コンテナって時々見かけるのはこういう訳だったのか...<多分違う)

Dockerコンテナ作成環境

上記のような状況を踏まえARM32bit向けのコンテナビルドを考えると、1. Dockerでターゲットアーキテクチャを指定した(クロスコンパイル環境を利用した)ビルド、もしくは、2. RaspbianOSが稼働する仮想ホストを作り、その中でdockerを動かしてイメージビルド(qemuによるエミュレーションを利用)、のどちらかで行けるのではと考えられた。

結論から言うと、両者を組み合わせたような素晴らしい方法がすでに確立していた。

このmultiarchプロジェクトが提供しているqemu-user-staticを利用してbinfmt_misc+QEMUによるターゲットシステムのバイナリ実行環境を用意した上で、ターゲットシステムのミニマム環境を構築したベースコンテナイメージ内でdockerビルドする、ということができることがわかった。

そのものズバリの答えがここに書いてあった。

この方法で、x86システム上でRaspberry Pi用のコンテナを作ることができる。実際にやってみた。

さくらのクラウド上でARM用コンテナをビルドする

さくらのクラウド上に(x86の)VM(CentOS 7.6)を用意し、以下のように実行してみた。

[root@fogregistry qemu]# docker run --rm --privileged multiarch/qemu-user-static:register
Unable to find image 'multiarch/qemu-user-static:register' locally
register: Pulling from multiarch/qemu-user-static
FROM multiarch/ubuntu-core:arm64-xenial
697743189b6d: Pull complete
18c6c8ea43df: Pull complete
FROM multiarch/ubuntu-core:armhf-artful
96aa3d7b9b30: Pull complete
8d669bf48302: Pull complete
Digest: sha256:be7f79d8b044f6765b4c41403a1e42466ec12969791622e3d9705f860bda4ec3
Status: Downloaded newer image for multiarch/qemu-user-static:register
Setting /usr/bin/qemu-alpha-static as binfmt interpreter for alpha
Setting /usr/bin/qemu-arm-static as binfmt interpreter for arm
Setting /usr/bin/qemu-armeb-static as binfmt interpreter for armeb
Setting /usr/bin/qemu-sparc32plus-static as binfmt interpreter for sparc32plus
Setting /usr/bin/qemu-ppc-static as binfmt interpreter for ppc
Setting /usr/bin/qemu-ppc64-static as binfmt interpreter for ppc64
Setting /usr/bin/qemu-ppc64le-static as binfmt interpreter for ppc64le
Setting /usr/bin/qemu-m68k-static as binfmt interpreter for m68k
Setting /usr/bin/qemu-mips-static as binfmt interpreter for mips
Setting /usr/bin/qemu-mipsel-static as binfmt interpreter for mipsel
Setting /usr/bin/qemu-mipsn32-static as binfmt interpreter for mipsn32
Setting /usr/bin/qemu-mipsn32el-static as binfmt interpreter for mipsn32el
Setting /usr/bin/qemu-mips64-static as binfmt interpreter for mips64
Setting /usr/bin/qemu-mips64el-static as binfmt interpreter for mips64el
Setting /usr/bin/qemu-sh4-static as binfmt interpreter for sh4
Setting /usr/bin/qemu-sh4eb-static as binfmt interpreter for sh4eb
Setting /usr/bin/qemu-s390x-static as binfmt interpreter for s390x
Setting /usr/bin/qemu-aarch64-static as binfmt interpreter for aarch64
Setting /usr/bin/qemu-aarch64_be-static as binfmt interpreter for aarch64_be
Setting /usr/bin/qemu-hppa-static as binfmt interpreter for hppa
Setting /usr/bin/qemu-riscv32-static as binfmt interpreter for riscv32
Setting /usr/bin/qemu-riscv64-static as binfmt interpreter for riscv64
Setting /usr/bin/qemu-xtensa-static as binfmt interpreter for xtensa
Setting /usr/bin/qemu-xtensaeb-static as binfmt interpreter for xtensaeb
Setting /usr/bin/qemu-microblaze-static as binfmt interpreter for microblaze
Setting /usr/bin/qemu-microblazeel-static as binfmt interpreter for microblazeel
Setting /usr/bin/qemu-or1k-static as binfmt interpreter for or1k
[root@fogregistry qemu]# uname -a
Linux fogregistry 3.10.0-957.5.1.el7.x86_64 #1 SMP Fri Feb 1 14:54:57 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
[root@fogregistry qemu]#

この時点では、まだx86システムとして見えている。
次に、以下のようなごく簡単なDockerfileを用意して、

FROM multiarch/ubuntu-core:armhf-artful

RUN apt-get update && \
    apt-get install -y vim

Dockerコンテナをビルドする。

[root@fogregistry qemu]# docker build -t armhf-artful-vim -f Dockerfile .
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM multiarch/ubuntu-core:armhf-artful
armhf-artful: Pulling from multiarch/ubuntu-core
1dcd90c0b012: Pull complete
bfe2976d4234: Pull complete
4039934b5c34: Pull complete
ecbefe68c47b: Pull complete
Digest: sha256:05818b307e33c640a238c6c07e19ff8f353c93e4dd9297e0b73ea67e22422601
Status: Downloaded newer image for multiarch/ubuntu-core:armhf-artful
 ---> 8e86381ba74d
Step 2/2 : RUN apt-get update &&     apt-get install -y vim
 ---> Running in d42baa01d860
Hit:1 http://ports.ubuntu.com/ubuntu-ports artful InRelease
Get:2 http://ports.ubuntu.com/ubuntu-ports artful-updates InRelease [88.7 kB]
Get:3 http://ports.ubuntu.com/ubuntu-ports artful-backports InRelease [74.6 kB]

<途中省略>

Setting up vim-common (2:8.0.0197-4ubuntu5) ...
Setting up vim-runtime (2:8.0.0197-4ubuntu5) ...
Setting up libmpdec2:armhf (2.4.2-1) ...
Setting up libpython3.6-stdlib:armhf (3.6.3-1ubuntu1) ...
Setting up file (1:5.32-1ubuntu0.1) ...
Setting up libpython3.6:armhf (3.6.3-1ubuntu1) ...
Setting up vim (2:8.0.0197-4ubuntu5) ...
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/vim (vim) in auto mode
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/vimdiff (vimdiff) in auto mode
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/rvim (rvim) in auto mode
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/rview (rview) in auto mode
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/vi (vi) in auto mode
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/view (view) in auto mode
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/ex (ex) in auto mode
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/editor (editor) in auto mode
Processing triggers for libc-bin (2.26-0ubuntu2.1) ...
Removing intermediate container d42baa01d860
 ---> b80263b7bd41
Successfully built b80263b7bd41
Successfully tagged armhf-artful-vim:latest
[root@fogregistry qemu]#

できたら、実行させてみると...

[root@fogregistry qemu]# docker run -it --rm armhf-artful-vim:latest bash
root@e8418d14f0e5:/# uname -a
Linux e8418d14f0e5 3.10.0-957.5.1.el7.x86_64 #1 SMP Fri Feb 1 14:54:57 UTC 2019 armv7l armv7l armv7l GNU/Linux
root@e8418d14f0e5:/# uname -m
armv7l
root@e8418d14f0e5:/# file /bin/bash
/bin/bash: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=c2fac9c07846ccd7daa2c96126fa93ce863b2ea4, stripped
root@e8418d14f0e5:/#
root@e8418d14f0e5:/# exit
exit
[root@fogregistry qemu]#

できて、動いた!
コンテナイメージをRaspberry Pi上に持っていって実行させてみた結果が以下。同様に動いている。

pi@fognode011:~ $ uname -a
Linux fognode011 4.14.34-v7+ #1110 SMP Mon Apr 16 15:18:51 BST 2018 armv7l GNU/Linux
pi@fognode011:~ $ docker run -it --rm fogregistry1.kikuzo.jp/fogadvance-example/armhf-artful-vim bash
root@ae15f965c6ab:/# uname -a
Linux ae15f965c6ab 4.14.34-v7+ #1110 SMP Mon Apr 16 15:18:51 BST 2018 armv7l armv7l armv7l GNU/Linux
root@ae15f965c6ab:/# exit
exit
pi@fognode011:~ $

本記事のまとめ

ARMやクロス環境について不勉強だったため色々調べたが、結果として、クラウド上でRaspberry Pi向けのコンテナをビルドすることができそうだとわかった。
今回は「やってみた」レベルであるので、今後、定量評価をかけていく。乞うご期待。


この記事が気に入ったらサポートをしてみませんか?