linuxでマルチアーキテクチャに対応したdockerイメージを作ろうの巻(buildx)
はじめに
現在使われているコンピューターには殆どの場合CPUが存在します。
CPUにも種類があり数年前までは、x86というインテルとAMDのCPUが一般的なコンピュータの主流でしたが、スマートフォンの普及によりARMというCPUも紛れ込むようになりました。
ARMを利用したコンピューターを名指しするとラズベリーパイですね、はい。
で、最近流行りのdockerですが、有名所のイメージ以外はこのARMに対応していない場合がありますし、自分が作るイメージもARMのイメージ作りたかったらARM機でイメージを作る必要がりました。
開発機x86じゃん?一緒にARMのイメージも作りたいじゃん?
それできるよ。そうbuildxならね
仕組み
MACだと意識せずできるっぽいので置いておいて、linuxの話をします。
そもそもx86でARMのイメージ作りたければ、
x86でARMが動作する状態でなけれないけません。
x86でARMを動かすには、ARMのエミュレーターを動かす必要があります。
これをlinuxカーネルとエミュレーターのqemuがいい感じにやってくれるのがbinfmt_miscというものです。
その上でdockerのbuildxを実行した環境で実行可能なCPUアーキテクチャのイメージが作成できるという感じです。
前提として以下のコマンドが実行できなければ、カーネルが仕組みを利用できないので、注意してください。
sudo /sbin/modprobe binfmt_misc
x86でarmを動かそう
実際にx86でarmのバイナリを動かしてみます。最終的にはこれが実行できるようになります。
まずは動かないことを確認
まずはx86であることを確認します。
~# uname -m
x86_64
今回の実験台としてbusybox君に登場してもらいます。x86とarmをダウンロードして実行してみます。
~# wget https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64
~# wget https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-armv8l
~# chmod +x busybox-*
x86はちゃんと動きますね。
~# ./busybox-x86_64
BusyBox v1.31.0 (2019-06-10 15:54:54 CEST) multi-call binary.
BusyBox is copyrighted by many authors between 1998-2015.
Licensed under GPLv2. See source distribution for detailed
copyright notices.
armはというと、「このバイナリ実行できないよ」と言われています。
実行できないというよりは、実行方法が登録されていないという意味合いです。
~# ./busybox-armv8l
bash: ./busybox-armv8l: cannot execute binary file: Exec format error
もちろんarm版ubuntuイメージも動作しません
~# docker run --rm -t arm64v8/ubuntu uname -m
Unable to find image 'arm64v8/ubuntu:latest' locally
latest: Pulling from arm64v8/ubuntu
a25fe3630538: Pull complete
326fa3abf061: Pull complete
ff1b87601e0a: Pull complete
Digest: sha256:b6c4e3af24ef7a0ff26b56188fc15f6a868dacca523050fb30fc0267bc51b861
Status: Downloaded newer image for arm64v8/ubuntu:latest
standard_init_linux.go:211: exec user process caused "exec format error"
qemuのインストール
まずはエミュレーターがなければどうにもできないのでこれをインストールします。
~# sudo apt update
~# sudo apt install qemu-user qemu-user-static
そうするとCPUのエミュレータが使えるようになりました。
~# qemu-
qemu-aarch64 qemu-armeb qemu-i386-static qemu-mips-static qemu-mipsn32-static qemu-ppc-static qemu-riscv32-static qemu-sh4eb-static qemu-tilegx-static
qemu-aarch64-static qemu-armeb-static qemu-m68k qemu-mips64 qemu-mipsn32el qemu-ppc64 qemu-riscv64 qemu-sparc qemu-x86_64
qemu-aarch64_be qemu-cris qemu-m68k-static qemu-mips64-static qemu-mipsn32el-static qemu-ppc64-static qemu-riscv64-static qemu-sparc-static qemu-x86_64-static
qemu-aarch64_be-static qemu-cris-static qemu-microblaze qemu-mips64el qemu-nios2 qemu-ppc64abi32 qemu-s390x qemu-sparc32plus qemu-xtensa
qemu-alpha qemu-debootstrap qemu-microblaze-static qemu-mips64el-static qemu-nios2-static qemu-ppc64abi32-static qemu-s390x-static qemu-sparc32plus-static qemu-xtensa-static
qemu-alpha-static qemu-hppa qemu-microblazeel qemu-mipsel qemu-or1k qemu-ppc64le qemu-sh4 qemu-sparc64 qemu-xtensaeb
qemu-arm qemu-hppa-static qemu-microblazeel-static qemu-mipsel-static qemu-or1k-static qemu-ppc64le-static qemu-sh4-static qemu-sparc64-static qemu-xtensaeb-static
qemu-arm-static qemu-i386 qemu-mips qemu-mipsn32 qemu-ppc qemu-riscv32 qemu-sh4eb qemu-tilegx
大丈夫だと思いますが、qemu-*-staticが「/usr/bin/」にあることを確認してください。下記で紹介するスクリプトがここにコマンドがある事を前提に動作します。
~# which qemu-aarch64-static
/usr/bin/qemu-aarch64-static
エミュレータをカーネルに登録
このままだと実行できる環境があるだけで、実際にこれを利用するようにカーネルに登録しないといけません。
これは起動時に毎回行う必要があるので、systemdに登録でもしましょう。
まずは必要なものを集めます。
~# wget https://raw.githubusercontent.com/qemu/qemu/master/scripts/qemu-binfmt-conf.sh
~# chmod +x qemu-binfmt-conf.sh
~# sudo mv qemu-binfmt-conf.sh /usr/local/bin/qemu-binfmt-conf.sh
cat <<EOF | sudo tee /usr/local/bin/register.sh
#!/bin/sh
QEMU_BIN_DIR=${QEMU_BIN_DIR:-/usr/bin}
if [ ! -d /proc/sys/fs/binfmt_misc ]; then
echo "No binfmt support in the kernel."
echo " Try: '/sbin/modprobe binfmt_misc' from the host"
exit 1
fi
if [ ! -f /proc/sys/fs/binfmt_misc/register ]; then
mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
fi
if [ "${1}" = "--reset" ]; then
shift
find /proc/sys/fs/binfmt_misc -type f -name 'qemu-*' -exec sh -c 'echo -1 > {}' \;
fi
exec /usr/local/bin/qemu-binfmt-conf.sh --qemu-suffix "-static" --qemu-path "${QEMU_BIN_DIR}" $@
EOF
sudo chmod +x /usr/local/bin/register.sh
cat <<EOF | sudo tee /etc/systemd/system/register.service
[Unit]
Description= register cpu emulator
[Service]
ExecStart = /usr/local/bin/register.sh
Restart = no
Type = simple
RemainAfterExit=yes
[Install]
WantedBy = multi-user.target
EOF
登録を行います。
sudo systemctl daemon-reload
sudo systemctl enable register.service
sudo systemctl start register.service
以下のようになれば登録されました。
sudo systemctl status register.service
● register.service - register cpu emulator
Loaded: loaded (/etc/systemd/system/register.service; enabled; vendor preset: enabled)
Active: active (exited) since Tue 2020-10-20 14:03:03 JST; 10s ago
Process: 2592756 ExecStart=/usr/local/bin/register.sh (code=exited, status=0/SUCCESS)
Main PID: 2592756 (code=exited, status=0/SUCCESS)
10月 20 14:03:03 niilab register.sh[2592756]: Setting /qemu-riscv64-static as binfmt interpreter for riscv64
10月 20 14:03:03 niilab register.sh[2592756]: /usr/local/bin/qemu-binfmt-conf.sh: 269: echo: echo: I/O error
10月 20 14:03:03 niilab register.sh[2592756]: Setting /qemu-xtensa-static as binfmt interpreter for xtensa
10月 20 14:03:03 niilab register.sh[2592756]: /usr/local/bin/qemu-binfmt-conf.sh: 269: echo: echo: I/O error
10月 20 14:03:03 niilab register.sh[2592756]: Setting /qemu-xtensaeb-static as binfmt interpreter for xtensaeb
10月 20 14:03:03 niilab register.sh[2592756]: /usr/local/bin/qemu-binfmt-conf.sh: 269: echo: echo: I/O error
10月 20 14:03:03 niilab register.sh[2592756]: Setting /qemu-microblaze-static as binfmt interpreter for microblaze
10月 20 14:03:03 niilab register.sh[2592756]: /usr/local/bin/qemu-binfmt-conf.sh: 269: echo: echo: I/O error
10月 20 14:03:03 niilab register.sh[2592756]: Setting /qemu-microblazeel-static as binfmt interpreter for microblazeel
10月 20 14:03:03 niilab register.sh[2592756]: Setting /qemu-or1k-static as binfmt interpreter for or1k
実際にarm版のbusyboxを動かすと動作します。
./busybox-armv8l
BusyBox v1.31.0 (2019-06-10 15:54:51 CEST) multi-call binary.
BusyBox is copyrighted by many authors between 1998-2015.
Licensed under GPLv2. See source distribution for detailed
copyright notices.
arm版のイメージも動くことがわかります。
docker run --rm -t arm64v8/ubuntu uname -m
aarch64
マルチアーキテクチャイメージを作ろう
ここまでできればあとはイメージ作成の設定を行うだけでできます。
ただしこの機能は実験的なものであるため、デフォルトでは利用できません。
dockerの設定
以下のように実験的機能を有効にします。
mkdir ~/.docker/
cat <<EOF | tee ~/.docker/config.json
{
"experimental": "enabled"
}
EOF
そうすると*がついたものが表示されるようになります。
docker help
Usage: docker [OPTIONS] COMMAND
A self-sufficient runtime for containers
Options:
--config string Location of client config files (default "/home/niilab/.docker")
-c, --context string Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with "docker context use")
-D, --debug Enable debug mode
-H, --host list Daemon socket(s) to connect to
-l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info")
--tls Use TLS; implied by --tlsverify
--tlscacert string Trust certs signed only by this CA (default "/home/niilab/.docker/ca.pem")
--tlscert string Path to TLS certificate file (default "/home/niilab/.docker/cert.pem")
--tlskey string Path to TLS key file (default "/home/niilab/.docker/key.pem")
--tlsverify Use TLS and verify the remote
-v, --version Print version information and quit
Management Commands:
app* Docker Application (Docker Inc., v0.8.0)
builder Manage builds
buildx* Build with BuildKit (Docker Inc., v0.4.2-tp-docker)
config Manage Docker configs
container Manage containers
context Manage contexts
engine Manage the docker engine
image Manage images
manifest Manage Docker image manifests and manifest lists
network Manage networks
node Manage Swarm nodes
plugin Manage plugins
secret Manage Docker secrets
service Manage services
stack Manage Docker stacks
swarm Manage Swarm
system Manage Docker
trust Manage trust on Docker images
volume Manage volumes
現在利用しているイメージ作成エンジンはマルチアーキテクチャに対応していないので、新しいビルドプロファイルを作成します。
この際、このプロファイルでビルド可能な候補を登録します。
docker buildx create --name multi-arch-builder --platform linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6
そうすると新しく追加されます。
docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
multi-arch-builder docker-container
multi-arch-builder0 unix:///var/run/docker.sock inactive linux/amd64*, linux/386*, linux/arm64*, linux/arm/v7*, linux/arm/v6*
default * docker
default default running linux/amd64, linux/386
これを利用してイメージを作成する指定を行います。
docker buildx use multi-arch-builder
最後に出力先にdockerhubを用いるのでログインします。
docker login
Authenticating with existing credentials...
WARNING! Your password will be stored unencrypted in /home/server/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
マルチアーキテクチャのイメージを作る
ここまで来たらあとはビルドするだけです。
以下のようなdockerfileを用意しました。
FROM python:3.8-alpine
COPY ./test.py /test.py
RUN python3 /test.py
CMD python3 /test.py
import platform↲
print(platform.uname())
ビルドしたいアーキテクチャを指定して実行します。この際useridとなっている場所は上記でログインしたアカウント名です。
docker buildx build --platform linux/amd64,linux/arm64 -t userid/python-test --push .
buildができるとdockerhubに登録されます。
まとめ
以上がマルチアーキテクチャに対応したdockerイメージを作ろうの巻でした
ちゃんちゃん
ディスカッション
コメント一覧
まだ、コメントがありません