RaspberryPiでamd64のDockerイメージを起動する（夢を見た）（追記:と思ったら正夢になった！）

**注意
この記事ではRaspberryPi上のDockerでarmではないamd64のDockerイメージを起動するという通常は使わないとてもニッチなことを紹介しています。
amd64でRaspberryPiOSを起動したり、armのイメージを実行したりするわけではないのでご注意ください。**

なんとなくの思いつきで試してみたらできてしまった~~（できそうになってしまった）~~(*追記をご覧ください*)アイディアをまとめました。ただし、動作が不安定（多くのアプリケーションでsegmentation faultが起きる）で実用性はおそらくないので「へー」ぐらいで読んでいただければ幸いです。笑

## 2022/1/11追記
ありがたいことにこの記事が[こちらの記事に引用され](https://qiita.com/tangent-theta/items/3c2324733aeab13a69f2)、しかもこの方法で成功した（！）とのことだったので試しに少し調べてみました。
どうやらqemuかbinfmtとlibc-binの相性が悪かったようでいくつか他でもissueが上がっていたようです。
最新版のqemuでは解決していそうだったので前回失敗したlibc-binのインストールとmysqlのコンテナーの起動を最新のRaspberryPiOS(bullseye)でもう一度試したところ成功しました！
ということで新年早々いい正夢になりました。
もしかすると他のパッケージではうまくいかないということもあるかもしれませんが、しばらく様子を見てみようと思います。
(きっかけを与えてくださった@tangent-thetaさんありがとうございます！)


# TL;DR
```
# 通常通りDockerをインストールする
curl -sSL https://get.docker.com | sh
# rootでdockerを使うのが面倒なので(セキュリティ的にも好ましくないので)
# piをdockerグループに追加する
sudo usermod -aG docker pi

# amd64イメージを実行するためにQEMUをインストールする
sudo apt install qemu-user-static

```


# ちょっとだけ解説
## dockerのplatform属性
AppleSilicon対応の影響を受けてか、dockerにplatformオプションが追加されました。
これによりシステムのアーキテクチャと違うアーキテクチャのイメージを使うことができます。
例えばarm環境でamd64のイメージを起動するときは下記のようなコマンドを実行します。

```
docker run -it --platform=linux/amd64 ubuntu
```

しかし当然、通常ではarmでamd64のバイナリを実行することができないため、下のように`exec format`エラーが出てきます。

```
pi@raspberrypi:~ $ docker run -it --platform=linux/amd64 ubuntu
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
16ec32c2132b: Pull complete 
Digest: sha256:82becede498899ec668628e7cb0ad87b6e1c371cb8a1e597d83a47fac21d6af3
Status: Downloaded newer image for ubuntu:latest
standard_init_linux.go:228: exec user process caused: exec format error
pi@raspberrypi:~ $ 

```

しかし、もしこの時にQEMUのように違うアーキテクチャをエミュレーションするようなアプリケーションを挟むことができたら…ということで今回はやっていきます。

## QEMU
QEMUは広く使われているオープンソースのマシンエミュレータと仮想化アプリケーションです。
簡単に言えば仮想マシンを起動するためのアプリケーションです。
今回はOSごとではなくアプリケーションを実行するだけなのでユーザモードエミュレーション用の`qemu-user-static`を使います。


## binfmt_misc
今回のキモと言ってもいいかもしれません。
binfmt_miscはあらゆるファイルを実行できるようにファイルの構造を認識し、適切なアプリケーションで実行させるためのLinuxの仕組みの1つです。Linux上でWindowsのアプリケーションを実行できるようにするアプリケーションWineでも使用されているようです。
この仕組みを利用し、x86、amd64などのバイナリの実行を検知したらqemuに通すようにします。
この辺りについては`qemu-user-static`をインストールするときに一緒にインストールされる`binfmt-support`がいい感じに設定してくれます。

# 実行結果

```
pi@raspberrypi:~ $ docker run -it --platform=linux/amd64 ubuntu
root@2717779fa63a:/# uname -a
Linux 2717779fa63a 5.10.17-v7l+ #1414 SMP Fri Apr 30 13:20:47 BST 2021 x86_64 x86_64 x86_64 GNU/Linux
root@2717779fa63a:/# ls
bin   dev  home  lib32  libx32  mnt  proc  run   srv  tmp  var
boot  etc  lib   lib64  media   opt  root  sbin  sys  usr
root@2717779fa63a:/# cat /etc/os-release 
NAME="Ubuntu"
VERSION="20.04.2 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.2 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
root@2717779fa63a:/# 
```

unameの実行結果からもわかるように無事にamd64のUbuntuイメージの実行に成功しました！
…がこれには続きがあり、

（都合上上記とは違う、バージョンの古いイメージを使用しています。）

```
root@131b5d842de4:/# apt upgrade
Reading package lists... Done
Building dependency tree       
Reading state information... Done
Calculating upgrade... Done
The following packages will be upgraded:
  apt base-files bsdutils fdisk gcc-8-base gpgv gzip libapt-pkg5.0 libaudit-common libaudit1 libblkid1 libc-bin libc6
  libfdisk1 libgcc1 libgnutls30 libhogweed4 liblz4-1 libmount1 libnettle6 libp11-kit0 libpam-modules libpam-modules-bin
  libpam-runtime libpam0g libseccomp2 libsmartcols1 libstdc++6 libsystemd0 libudev1 libuuid1 libzstd1 mount perl-base tar
  ubuntu-keyring util-linux
37 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Need to get 11.8 MB of archives.
After this operation, 114 kB of additional disk space will be used.
Do you want to continue? [Y/n] 
Get:1 http://archive.ubuntu.com/ubuntu bionic-updates/main i386 gcc-8-base i386 8.4.0-1ubuntu1~18.04 [18.7 kB]
Get:2 http://archive.ubuntu.com/ubuntu bionic-updates/main i386 libgcc1 i386 1:8.4.0-1ubuntu1~18.04 [48.3 kB]
Get:3 http://archive.ubuntu.com/ubuntu bionic-updates/main i386 libstdc++6 i386 8.4.0-1ubuntu1~18.04 [432 kB]

...省略...

Unpacking util-linux (2.31.1-0.4ubuntu3.7) over (2.31.1-0.4ubuntu3.5) ...
Setting up util-linux (2.31.1-0.4ubuntu3.7) ...
(Reading database ... 4040 files and directories currently installed.)
Preparing to unpack .../libc-bin_2.27-3ubuntu1.4_i386.deb ...
Unpacking libc-bin (2.27-3ubuntu1.4) over (2.27-3ubuntu1) ...
Setting up libc-bin (2.27-3ubuntu1.4) ...
Segmentation fault (core dumped)
Segmentation fault (core dumped)
dpkg: error processing package libc-bin (--configure):
 installed libc-bin package post-installation script subprocess returned error exit status 139
Errors were encountered while processing:
 libc-bin
E: Sub-process /usr/bin/dpkg returned an error code (1)
root@131b5d842de4:/#
```

と言ったように`Segmentation fault`で正常にアプリケーションが実行できないこともあります。
（armのカーネルでamd64を動かすという無理をしているせいかもしれません…
もしこの辺り詳しい人がいらっしゃいましたらコメントいただけると嬉しいです。）
あまり安定していないので実用的ではないかなというのが個人的な感想です。

**(2022/1/11追記)上記の問題は解決したようです。[トップの追記](#2022111追記)もご覧ください**





# あとがき
もともとこの内容はRaspberryPiのカーネルをamd64の環境でビルドする際にbinfmt_miscとqemuを使うという手法を用いることを知ったのがきっかけでした。
まさかRaspberryPiでamd64のdockerイメージを実行するというところに応用できるとは思っていませんでした。
こうしてまた役に立たなそうな謎の知見を得られましたが、誰かのためになったらいいなと頭の片隅で願っています。

誤字、脱字、間違い等がありましたらご指摘いただければ幸いです。

# Reference
- https://docs.docker.com/docker-for-mac/apple-silicon/
- https://qiita.com/HyunwookPark/items/6e4854248c6d63958060
- https://papamitra.hatenadiary.org/entry/20071215/binfmt
- https://en.wikipedia.org/wiki/Binfmt_misc
- https://www.qemu.org