运维 M1 芯片构建容器镜像的跨平台问题

lanzhiheng · 2023年01月03日 · 最后由 daqing 回复于 2023年03月19日 · 1966 次阅读

这个问题是笔者尝试把项目部署到 k8s 集群上的时候遇到的,简单记录一下。估计使用 M1 芯片的 Macbook 构建镜像的开发者多少都会遇到类似的问题。原文链接: https://step-by-step.tech/posts/m1-build-image-issue

L1002263.jpg

能在本地运行却无法在 k8s 集群上运行的镜像

当要把某个项目部署到 k8s 集群的时候,必先要对这个项目进行容器化。也就是需要编写对应的Dockerfile文件。项目运行所需要依赖的环境都将在Dockerfile文件中指定。不过这里面也会有些坑。

笔者遇到的问题是构建出来的镜像可以在本地运行,但是在 k8s 集群上就是运行失败。要说明的是,笔者的本地机器是 M1 芯片的 Macbook。

比如lanzhiheng/stone这个镜像,在本地docker run本地好好的

> docker run -it -p 4000:3000 lanzhiheng/stone
...
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop

但是在 k8s 运行不了(这里是先把镜像推送到 Docker Hub,然后 k8s 从上面去拉取)

> kubectl run blog-on-k8s --image=lanzhiheng/stone

> kubectl get pods  blog-on-k8s
NAME          READY   STATUS             RESTARTS      AGE
blog-on-k8s   0/1     CrashLoopBackOff   3 (24s ago)   88s

而且出问题的时候,是没办法看到更详细的日志的。该服务目前的状态就是 Pod 还在,但是里面的 Container(容器) 启动失败,所以没有办法进入容器内部实施调试。

笔者初出茅庐,一直在猜想会不会是 k8s 有专门的镜像制作方式,会不会 Docker 制作的镜像需要某种特殊处理才能在 k8s 上使用?然而上网搜了一下始终没有找到相关的镜像处理器,看来正常情况下 Docker 打包的镜像应该是可以直接用在 k8s 上了。为了简化这个问题,笔者弄了个更简单的镜像来测试。

简化问题

为了测试 Docker 的镜像是不是真的能在 k8s 上面使用,直接在 k8s 跑一下 nginx 的 Docker 官方镜像就知道了。

> kubectl run  nginx --image=nginx

> kubectl get pods nginx
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          65s

还是能跑起来的。这么说 Docker 的镜像还是能用在 k8s 上的,那就是笔者构建的镜像有问题了,我再尝试往官方镜像套一层试试看,Dockerfile文件尽可能简单

FROM nginx
> docker build -t lanzhiheng/nginx  .
> docker push lanzhiheng/nginx

本地用 Docker 服务针对该镜像创建容器是没啥问题的

> docker run -it lanzhiheng/nginx
...
2022/12/31 03:48:34 [notice] 1#1: signal 28 (SIGWINCH) received
2022/12/31 03:48:34 [notice] 1#1: signal 28 (SIGWINCH) received

然而推送到 Docker Hub,然后 k8s 拉下来跑就有问题了

> kubectl run my-nginx --image=lanzhiheng/nginx

> kubectl get pods  my-nginx
NAME       READY   STATUS             RESTARTS     AGE
my-nginx   0/1     CrashLoopBackOff   1 (8s ago)   74s

官方的镜像nginx跟笔者自己包了一层的镜像lanzhiheng/nginx本质上并无太大区别。然而笔者的镜像在自己的 Macbook 上用 Docker 跑没啥问题,然后到了线上的 k8s 环境就有问题了。

现在想想最可能的原因就是笔者的 M1 芯片 Macbook 打包的镜像无法在基于 Linux 的 k8s 服务上直接使用。后来笔者跑去一台 Linux 服务器上构建一摸一样的镜像(同一个 Dockerfile)

> kubectl run my-nginx-from-linux --image=lanzhiheng/nginx-from-linux

> kubectl get pods  my-nginx-from-linux
NAME                  READY   STATUS    RESTARTS   AGE
my-nginx-from-linux   1/1     Running   0          55s

这次能运行成功,这么看来问题就出在 M1 芯片的 Macbook 上了。看来系统不同构建的镜像还是不能通用的。

解决方案

从前面的现象可知,M1 芯片 Macbook 在默认情况下构建的镜像在 Macbook 上使用没啥太大问题,然后到了 Linux 的机器上可能就有问题了。不管是用基于 Linux 的 Docker 来跑还是 k8s 来跑都是不行的

> docker run lanzhiheng/nginx

WARNING: The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested
exec /docker-entrypoint.sh: exec format error

类似的提示信息,在 k8s 环境下还不一定能看得到,所以打包镜像的时候一定要注意跨平台的问题。其实解决方案也很简单,我们只需要在 M1 芯片的 Mac 上构建镜像的时候加上参数--platform linux/amd64,那么打包出来的镜像就可以在 Linux 的机器上使用了。

> docker build -t lanzhiheng/nginx-for-linux --platform linux/amd64  .
> docker push lanzhiheng/nginx-for-linux

简单点我就不登录 Linux 的机器了,直接用 k8s 来跑了(我的 k8s 连接了远端的集群,M1 的 Macbook 有点跑不起 k8s 服务)。

> kubectl run nginx-from-macbook-image --image=lanzhiheng/nginx-for-linux
> kubectl get pods  nginx-from-macbook-image
NAME                       READY   STATUS    RESTARTS   AGE
nginx-from-macbook-image   1/1     Running   0          55s

这里只是以nginx的镜像作个例子,Rails 项目的解决方案也是类似,只需要加上参数--platform linux/amd64就能在 M1 的 Macbook 上构建出可以在 Linux 平台上使用的镜像了。这里就不赘述了,反正笔者亲测可用。

总结

今天这个问题算是项目容器化之路中耽误我最长时间的问题了,一个系统兼容性的问题,但这种问题对于新手来说真的很难排查。笔者调试这个问题花了大概 2 天的时间,有点绝望。后面尝试把问题简化(换别的官方镜像来做试验),一步步排除问题,起码确认了不是Dockerfile的问题,最终才定位到是镜像构建的系统不兼容所导致的。

其实笔者只要长个心眼,去 Linux 的机器上用该环境的 Docker 服务运行一下 Macbook 构建出来的镜像就能从提示信息中更快地定位出问题了,而不用一直去折腾 k8s 的配置,说到底还是经验不足啊。

我们升级 k8s 也遇到过,从 MacBook 下载的镜像升级 k8s 时候一直报错,最后也是看了好几天,才发现是镜像的问题。之后在 linux 下载的镜像才完成了 k8s 升级。

在生产环境中编译不行吗

我是在 github actions 里面 build 的。

amonlei #1 回复

当然可以啊。

Rei #2 回复

后期有这个打算了。不过现在感觉上容器还有点早。

M1 芯片是基于 arm 的,在 x86 机器上所以不行

M 系 Apple 本地构建其实不好弄的 build_opts="--platform linux/amd64" # building at M1 mac for linux 判断一下加这句

哈哈,写的两个问题,时区、platform 都遇到过了…… 😀

今天打包 docker 也遇到了这个问题,想起来你发过贴,果然解决了。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号