qemu-user-static on Kubernetes
仕事柄、Raspberry Pi のようなマイコン向けの組み込み Linux システムのユーザーランドの CI/CD をよくやっています。
その際、ターゲットのマイコンはとても非力でビルドが遅いことから (特にストレージ I/O がしんどい)、 代わりに AMD64 サーバで Linux カーネルの binfmt_misc と qemu-user-static を使い、 GitLab Runner の Docker executor で armhf などのターゲットシステムのコンテナを動かして CI/CD するということをしてきました。
エミュレーションがかかっているとはいえ AMD64 サーバの性能は圧倒的なので、 Raspberry Pi に比べるとだいぶ高速なビルドができます。
このような Docker ホスト向けの binfmt_misc/qemu-user-static の設定については
multiarch/qemu-user-static というイメージがあり、
docker run
で一度実行するだけで必要なセットアップができるので便利です。
ただし、これはコンテナの中からホストの /proc
などをいじりますので、権限つきで実行する必要があります。
最近、このエミュレーション CI/CD 環境を見直す機会があり、これまでは単なる VM の Docker ホストで GitLab Runner を動かしていましたが、 今度は AKS や GKE のようなクラウドベンダのマネージドな Kubernetes クラスタでも同じことができたら便利だと思ったので、いろいろ試してみました。
結論からいうと binfmt_misc/qemu-user-static の設定は各ノードで 1 回だけやればいいのだから、 DaemonSet が使えるんじゃないかな?と思って YAML を書いてみたら、あっさりできてしまいました。
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: qemu-user-static
namespace: kube-system
spec:
selector:
matchLabels:
name: qemu-user-static
template:
metadata:
labels:
name: qemu-user-static
spec:
containers:
- name: qemu-user-static
image: multiarch/qemu-user-static
command: ["/bin/sh", "-c", "/register --reset -p yes && sleep infinity"]
securityContext:
privileged: true
multiarch/qemu-user-static イメージは binfmt_misc を設定したら終了するのですが、
DaemonSet のコンテナとしてこれを動かすとすぐに CrashLoopBackOff 状態になって都合が悪いため、
Dockerfile
の ENTRYPOINT
をオーバーライドして /register
を実行したら永遠に寝るような工夫をしました。
また特権つきのコンテナとして実行するために securityContext
で privileged: true
を追加しています。
この DaemonSet を導入すると、 既存のノードに加えてスケールアウトして増えたノードについても自動的 multiarch/qemu-user-static が走るので、 すぐにターゲットアーキテクチャのコンテナが動かせるようになります。
$ kubectl create -f daemonset-qemu-user-static.yaml
$ az aks scale -n gk-tmp -g gk-tmp -c 2
$ kubectl -n kube-system get pod -l name=qemu-user-static -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
qemu-user-static-dmjd2 1/1 Running 0 11m 10.244.0.62 aks-nodepool1-91423968-0 <none> <none>
qemu-user-static-gtlhr 1/1 Running 0 5m26s 10.244.1.2 aks-nodepool1-91423968-1 <none> <none>
$ kubectl -n kube-system logs qemu-user-static-gtlhr
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
$ kubectl run --rm -it --image arm32v6/busybox busybox
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.
/ # uname -a
Linux busybox-fc6475bf5-f2fdj 4.15.0-1055-azure #60-Ubuntu SMP Thu Aug 8 18:29:07 UTC 2019 armv7l GNU/Linux
ただ、使わないコンテナがいつまでもスリープして残っているのはやはり無駄なので、 DaemonSet ではなく各ノードで 1 回だけ実行される Job みたいなリソースがあればと思いました。 実際にそういう提案もなされているので、 今後に期待したいと思います。