l0w.dev

qemu-user-static on Kubernetes


仕事柄、Raspberry Pi のようなマイコン向けの組み込み Linux システムのユーザーランドの CI/CD をよくやっています。

その際、ターゲットのマイコンはとても非力でビルドが遅いことから (特にストレージ I/O がしんどい)、 代わりに AMD64 サーバで Linux カーネルの binfmt_miscqemu-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 状態になって都合が悪いため、 DockerfileENTRYPOINT をオーバーライドして /register を実行したら永遠に寝るような工夫をしました。 また特権つきのコンテナとして実行するために securityContextprivileged: 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 みたいなリソースがあればと思いました。 実際にそういう提案もなされているので、 今後に期待したいと思います。