问题

需要在x86的mac上构建一个arm64的docker镜像,该镜像的dockerfile如下

FROM alpine:3.16.2 as fetcher

ARG NOVNC="v1.3.0"

WORKDIR /source

RUN apk --no-cache add curl unzip && \
    curl -L -o /source/novnc.zip https://github.com/novnc/noVNC/archive/refs/tags/${NOVNC}.zip && \
    unzip /source/novnc.zip && \
    mv /source/noVNC-* /source/novnc

FROM bitnami/kubectl:1.21.14 as app

WORKDIR /static

COPY --from=fetcher /source/novnc/app /static/app
COPY --from=fetcher /source/novnc/core /static/core
COPY --from=fetcher /source/novnc/vendor /static/vendor
COPY --from=fetcher /source/novnc/*.html /static/

CMD ["proxy", "--www=/static", "--accept-hosts=^.*$", "--address=[::]", "--api-prefix=/k8s", "--www-prefix="]

问题

本次交叉编译遇到的问题就是,使用下面命令可以正常完成编译

docker buildx build --platform=linux/arm64 -t virtvnc:1.0.1 .

但是该镜像在arm64环境下并不能run起来,因为最后一阶段FROM使用的基础镜像中的kubectl是x86架构的,并不是arm64架构的

TLDR

  1. 比较好理解的是,镜像中如果有编译应用,那么肯定需要把arch传到编译命令中

  2. 多阶段构建中,FROM后的镜像的行为是不一样的。最后一阶段用的是目标arch镜像,除此之外前面的镜像都用的是当前arch。

     FROM --platform=$BUILDPLATFORM alpine AS build
     # RUN <install build dependecies/compiler>
     # COPY <source> .
     ARG TARGETPLATFORM
     RUN compile --target=$TARGETPLATFORM -o /out/mybinary
  3. 实际上FROM中的镜像不需要做任何修改,docker会自动匹配

  4. 比较坑的一点:如果FROM中的镜像没有需要的arch,docker不会build失败,而只是会提示,然后用其他arch去替代。所以一定要要确认好基础镜像是否有目标arch的版本

  5. intel mac 上的docker-dektop可以运行arm的镜像(不知道是什么特性,或许是因为apple M芯片的兼容?)

排查

这种问题的排查,金标准是把镜像中的二进制拷贝出来,用file命令去看一下其真正的架构是什么

至于docker inspect之类的,都有点不够直接。

另外还有一个比较坑的点:如果在x86的mac上试图去运行arm64的镜像,且其中的二进制确实是arm64的,并不会像预想中的一样得到arch错误之类的提示,而是能正常运行,因此在intel的mac上不能直接通过能否运行去判断镜像中的二进制是什么arch。

另外,可以尝试直接pull,观察其输出,可以发现一些猫腻。比如

❯ docker pull --platform=linux/arm64 bitnami/kubectl:1.21.14
1.21.14: Pulling from bitnami/kubectl
c674143b45a3: Pull complete 
397629b35917: Pull complete 
6a123cd6595f: Pull complete 
2bfd724e7bc7: Pull complete 
3badbf53fc33: Pull complete 
4cac02c1e3cb: Pull complete 
9670d06b42e1: Pull complete 
Digest: sha256:bba32da4e7d08ce099e40c573a2a5e4bdd8b34377a1453a69bbb6977a04e8825
Status: Downloaded newer image for bitnami/kubectl:1.21.14
image with reference docker.io/bitnami/kubectl:1.21.14 was found but does not match the specified platform: wanted linux/arm64, actual: linux/amd64

一开始看最后这行提示我是有点费解的,虽然大致意思是架构不匹配,但是并不确定是谁和谁不匹配。

比如我有怀疑过这行报错有没有可能是说命令行里的--platform参数有问题,导致pull时没识别到我想要的arch,总之没有第一时间想到可能是远程仓库里没有想pull的架构。因为我默认了一个基本的逻辑:如果需要的arch不存在,那么直接失败就可以了。

直到最后我去dockerhub网页上看了一下,才发现bitnami/kubectl:1.21.14是真的只有amd64,没有arm的。坑爹的docker可能是出于别的什么逻辑考虑,当基础镜像不存在目标arch时,会用其他arch去替代

参考

Faster Multi-Platform Builds: Dockerfile Cross-Compilation Guide | Docker

文章目录