我最近也有这个问题,我没有完整的答案,但这是我所知道的,或者至少是相信的:
设置和测试
设置的魔力 - 每次重新启动系统都需要一次,就是这样:
# start root's docker (not via any `-rootless` scripts, obviously)
sudo systemctl start docker
# setup QEMU static executables formats
sudo docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
# test
docker run --rm -t arm64v8/ubuntu uname -m
# shoudl expect:
# >> aarch64
# optional: shutdown root's docker
sudo systemctl stop docker
请注意,测试示例假设您正在运行您自己的“rootless-”docker,因此是作为您自己,而不是作为 root(也不是通过 sudo)运行,而且它运行得很好。
血腥细节
...如果您想了解这是如何/为什么起作用的,这很重要。
此信息的主要来源:
完成这项工作的基本技巧是将新的“魔术”字符串安装到内核进程空间中,以便当 (ARM) 可执行文件在 docker 映像中运行时,它可以识别 bin-fmt 并使用 QEMU 解释器(从multiarch/* docker 镜像)来执行它。在我们设置 bin 格式之前,内容如下所示:
root@odysseus # mount | grep binfmt_misc
systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=35,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=45170)
binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,relatime)
root@odysseus # ls /proc/sys/fs/binfmt_misc/
jar llvm-6.0-runtime.binfmt python2.7 python3.6 python3.7 python3.8 register sbcl status
在我们启动(root 的)dockerd 并设置格式之后:
root@odysseus # systemctl start docker
root@odysseus # docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
Setting /usr/bin/qemu-alpha-static as binfmt interpreter for alpha
Setting /usr/bin/qemu-arm-static as binfmt interpreter for arm
[...]
root@odysseus # ls /proc/sys/fs/binfmt_misc/
jar python3.8 qemu-armeb qemu-microblazeel qemu-mipsn32 qemu-ppc64le qemu-sh4eb qemu-xtensaeb
llvm-6.0-runtime.binfmt qemu-aarch64 qemu-hexagon qemu-mips qemu-mipsn32el qemu-riscv32 qemu-sparc register
python2.7 qemu-aarch64_be qemu-hppa qemu-mips64 qemu-or1k qemu-riscv64 qemu-sparc32plus sbcl
python3.6 qemu-alpha qemu-m68k qemu-mips64el qemu-ppc qemu-s390x qemu-sparc64 status
python3.7 qemu-arm qemu-microblaze qemu-mipsel qemu-ppc64 qemu-sh4 qemu-xtensa
现在我们可以运行 ARM 版本的 ubuntu:
root@odysseus # docker run --rm -t arm64v8/ubuntu uname -m
WARNING: The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested
aarch64
由于主机CPU是AMD,因此可以预料到警告,可以通过将平台指定给docker来摆脱:
root@odysseus # docker run --rm --platform linux/arm64 -t arm64v8/ubuntu uname -m
aarch64
这究竟是如何工作的?
QEMU 的基础就是插入一个 DBM(动态二进制修改)解释器来将一个系统的指令集翻译成底层平台的指令集。
我们唯一要做的就是告诉底层系统在哪里可以找到这些解释器。这就是qemu-user-static 图像在注册二进制格式魔术字符串/解释器时所做的。那么,那些binfmts 里有什么?
root@odysseus # cat /proc/sys/fs/binfmt_misc/qemu-aarch64
enabled
interpreter /usr/bin/qemu-aarch64-static
flags: F
offset 0
magic 7f454c460201010000000000000000000200b700
mask ffffffffffffff00fffffffffffffffffeffffff
嗯——这很有趣,尤其是因为在主机系统上没有/usr/bin/qemu-aarch64-static,而且它也不在目标图像中,那么这个东西在哪里?它在qemu-user-static 图像本身中,带有适当的表单标记:<HOST-ARCH>-<GUEST-ARCH>,如multiarch/qemu-user-static:x86_64-aarch64。
# Not on the local system
odysseus % ls /usr/bin/qemu*
ls: cannot access '/usr/bin/qemu*': No such file or directory
# Not in the target image
odysseus % docker run --rm --platform linux/arm64 -t arm64v8/ubuntu bash -c 'ls /usr/bin/qemu*'
/usr/bin/ls: cannot access '/usr/bin/qemu*': No such file or directory
# where is it?
odysseus % docker run --rm multiarch/qemu-user-static:x86_64-aarch64 sh -c 'ls /usr/bin/qemu*'
docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "sh": executable file not found in $PATH: unknown.
# Hmm, no `sh` in that image - let's try directly...
odysseus % docker run --rm multiarch/qemu-user-static:x86_64-aarch64 /usr/bin/qemu-aarch64-static --version
qemu-aarch64 version 7.0.0 (Debian 1:7.0+dfsg-7)
Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers
# AHA - there it is.
这才是真正的魔法,我还不太明白。我相信,docker 不知何故使用该图像来启动 QEMU 解释器,然后将您要运行的实际图像/容器中的代码提供给它,就像前面的 uname 示例一样。一些网络搜索让我对这种魔法是如何实现的感到不满意,但我猜如果我继续关注这里的链接,我可能会找到这种轻微手的真正来源。