超详细的ARM架构适配介绍!
本⽂主要介绍云联壹云平台如何适配ARM,并运⾏在ARM CPU架构的机器上。
背景介绍
1、平台服务运⾏架构
云联壹云平台采⽤容器化,分布式的架构运⾏在 Kubernetes(K8s)之上。下⾯是平台服务运⾏的架构图:
在多个节点之上,我们会构建Kubernetes的集,它是⼀个容器管理的平台。
在Kubernetes的平台之上,后端服务都是容器化的,是以容器的⽅式去分布式运⾏。
通过K8s去做调度的管理,然后将服务⾃动地打散到多个节点上运⾏,总结两点是服务容器化,并依靠K8s来提供容器分布式运⾏的环境。
另外,底层的节点是有类型的,控制层⾯的服务运⾏在控制节点,平台内置了⼀个私有云,提供了完整的私有云功能。
若要使⽤私⽤云这个功能,则还需要⼀些计算节点,计算节点上会跑虚拟化相关的软件,提供私有云虚拟化的功能,总结来说就是计算节点运⾏私有云的虚拟机。
2、CPU架构简介
⼤家熟悉的服务器或者台式机都是X86架构的CPU,X86架构的CPU特点是性能⾼,并且软件的兼容性很好。
⼤家平常⼯作中使⽤的⼤部分是英特尔等提供的X86架构的CPU,对于英特尔和AMD⼤家都不陌⽣,这两家⼚商专门⽣产X86架构CPU。
另外X86 64位这种架构的CPU存在别名,例如x86_64或者amd64都代表X86架构的64位CPU。
与X86不同的是还有另⼀种称为ARM的架构,这是本⽂的主题,那么ARM架构的CPU和X86架构的CPU相⽐有何不同?
它的制造成本更低,ARM架构的芯⽚的功耗也很低,代表性的⼚商和使⽤者是苹果和华为。
苹果将⽣产的ARM芯⽚⽤到笔记本或IMAC上,ARM架构的CPU越来越普及。
国内的华为会⽣产基于ARM架构的服务器,64位ARM架构CPU也有别名,例如arm64、aarch64,这两种叫法表达同⼀个意思。
3、为什么适配ARM
因为ARM的CPU普及是⼤环境下的发展趋势,例如在国际上,苹果将ARM架构的CPU投⼊到笔记本和台式机上,在国产化⽅⾯,国内有鲲鹏和飞腾CPU,国产的基于ARM架构的服务器,现在市场上主流的是鲲鹏和飞腾。在国产化上,使⽤ARM架构也是⼀个趋势。
另外是适配了ARM架构能够提升产品的竞争⼒,竞争⼒体现在能够⽀持管理基于ARM64位和X86_64架构的虚拟化混合部署。
这种混合部署的意思是可以同时把服务运⾏在ARM和X86的服务器上,同时运⾏各⾃的虚拟化,可以通过我们平台统⼀部署和管理。
部署和管理。
x86架构和arm架构区别如何适配ARM64?
1、需要解决的关键问题
第⼀个⽅⾯是怎样把服务运⾏在64位的ARM架构上?
还有⼀⽅⾯是因为我们内置了⼀个私有云,上层的私有云业务如何⽀持64位的ARM架构?
通过架构图可以了解到服务是容器化运⾏在K8s之上的,再由K8s帮我们将服务分布式地运⾏在各个节点上,所以第⼀步是要部署异构CPU架构的K8s集,异构CPU架构的意思是K8s集要运⾏在X86和ARM架构的机器上。
具体⽽⾔就是要选择⼀个⽀持ARM架构的Linux的发⾏版。
K8s节点上的操作系统是Linux,⾸先要选择⽀持ARM架构的Linux操作系统,操作系统同时能够安装K8s必要的软件(⽐如docker-ce、kubelet等)
当我们把K8s集给部署起来之后,就要将不同架构的容器镜像统⼀制作出来。
统⼀容器镜像的意思是⼀个容器镜像中要包含X86和ARM架构,相当于同⼀个镜像名称中有两种架构的镜像。
第⼆个⼤的⽅⾯是上层私有云业务怎样⽀持ARM64。
主要是分为两个问题,第⼀个问题是要⽀持ARM的虚拟机镜像,同时上层的私有云业务能够标记宿主机,宿主机就是运⾏虚拟机的服务器。
要能够标记这种宿主机是什么架构,同时要在这个宿主机上运⾏ARM的虚拟化软件。
平台使⽤的虚拟化软件基于qemu/kvm,⽹络⽅⾯依赖openvswitch组件,要求这些组件也要能够在ARM架构上运⾏。
这就是要介绍的⼀些关键问题,解决好这些关键问题,不管是底层的业务还是上层的私有云业务,都能在ARM上运⾏。
2、Linux发⾏版选择
我们选择的发⾏版是Debian 10(开源)和统信UOS(国产系统)。
选择这两个发⾏版的第⼀个原因是客户业务上要求在ARM64服务器上运⾏“统信UOS”。
要适配的话也是⾸先适配国产统信UOS,在适配过程中,UOS中的内核和打包⼯具与Debian基本⼀致。
适配UOS之后,发现其⼯作基本相同,所以⼜选择了Debian 10(开源)进⾏适配。
如此⽆论是对于开源⽤户还是其他客户,如果不选择UOS,也可以选择Debian 10的发型版运⾏此平台,Debian系列对ARM64位的⽀持很好,
Debian发⾏版的官⽅软件仓库中已经制作好了很多ARM64的包,如果要安装docker和K8s的软件包,Debian中已经做好,直接下载安装即可。
为什么不在ARM架构上⾯再选择CentOS7发⾏版?
原因在于CentOS7官⽅即将停⽌维护,所以选择了Debian系列的发⾏版。
3、统信UOS适配认证
对统信UOS适配过后,经过其官⽅测试,为我们颁发了认证证书。
4、统⼀软件依赖—包管理⼯具
选择好发⾏版后?怎样统⼀安装这些软件?
我们能够使⽤不同发⾏版的包管理⼯具去安装相同的软件,例如在X86的CentOS上安装docker和K8s这些包时调⽤yum,在Debian上调⽤apt install 等同样的包名即可。
上游的软件仓库中已经把包做好,只需直接安装,软件直接下载即可使⽤。
在底层软件⽅⾯,ARM和X86的管理,只要发⾏版是主流的,验证Debian和X86的包没有区别,已经将底层的包安装部署好。
5、部署kubernetes集
另外⼀个话题是部署kubernetes集,如何在ARM上部署K8s集?
⾸先,我们写了名称为ocboot的部署⼯具,它的底层原理是调⽤ansible实现部署ARM64+X86_64的多节点Kubernetes,这个截图的意思是通过这个⼯具部署过后的混布的就是异构的K8s集的节点的资源。
从截图中可以发现,第⼀个节点是centos-x86-64,发⾏版名称是centos7,内核是3.10,K8s会通过打标签的⽅式标记这个节点,它是amd64。
amd64代表它是X86架构的⼀个节点,另外⼀个节点是uos-arm64,它的发⾏版是uos,内核是4.19.0-arm64的内核。K8s会通过打标签标记它是ARM64的⼀个节点,这就代表是⼀个同时基于X86和ARM64的节点构建的K8s集。
使⽤K8s集的好处是K8s 集提供了不同CPU架构节点的统⼀管理,通过⼀个API能够把所有架构的节点拿到,能够基于这些节点提供容器服务的运⾏。
6、服务统⼀镜像运⾏
部署好K8s集之后如何运⾏云联壹云的服务?
例如K8s中有daemonset的资源,这种资源表⽰在每⼀个K8s节点上都会运⾏pod容器。
它要求在这个资源中写好服务容器镜像的地址,即红框中所标记的。
只要按照这样的格式写,写好之后创建到k8s集中,K8s就会将提供的镜像以容器化的⽅式运⾏到各个节点上。
从图中可以了解到,在centos X86的节点上和UOS ARM架构的节点上都同时运⾏host的服务,这是如何做到的呢?
这就要求统⼀服务的进项,要在host:v3.6.10的镜像中包含两种架构的镜像格式,它的底层同时将X86和ARM的格式捆绑到⼀起,在实际运⾏过程中,在不同的架构的节点上,docker会根据当前节点的架构拉取镜像中不同架构的格式的镜像运⾏。
7、私有云业务如何⽀持ARM64?
•宿主机列表
在运⾏虚拟机的宿主机上去标记宿主机的架构类别,云平台的前端界⾯可以看到,给宿主机列表上⾯加了CPU架构的属性,ARM架构服务器的宿主机,会给它⼀个称为aarch64的CPU架构来标记,X86则会标记x86_64的CPU架构属性。
若要运⾏虚拟机,则需要先提供虚拟机的镜像,我们在虚拟机的镜像中也使⽤了CPU架构的属性来标记虚拟机镜像是X86架构还是ARM 64位的架构。
提供宿主机和虚拟镜像过后,即可创建虚拟机,在创建虚拟机的表单中,会让⽤户去选择是要创建ARM架构的虚拟机还是X86架构的虚拟机。
创建好虚拟机过后,平台负责将这些虚拟机根据所提供的CPU架构调⼊到不同架构的宿主机上,这是⼀个在平台同时创建的X86和ARM的虚拟机列表界⾯。
在此列表中通过CPU架构的字段了解虚拟机的架构类型,这就是在私有云这个业务做的改造并⽀持ARM64位架构。
技术细节
如何制作统⼀的容器镜像(⽀持X86_64和ARM64)?
•docker buildx⽅案
docker原⽣⽀持的多架构镜像制作⽅案
•使⽤交叉编译然后打包
分别编译打包出x86_64和arm64的容器镜像,然后捆绑到⼀起。
1、统⼀容器镜像—docker buildx
•编写适⽤buildx的 Dockerfile
docker通过读取file中的语句,制作出镜像,Dockerfile先基于⼀个称做golang:alpine的镜像,把它作为⼀个build容器。同时,其中会传两个参数,分别是TARGETPLATFORM 和BUILDPLATFORM。
TARGETPLATFORM的意思是制作出的镜像架构,BUILDPLATFORM 是当前制作镜像机器的架构,这⾥RUN的命令会读取这两个环境变量,然后将他们的值打到名称为log的⽂件中。
第⼆步,FROM alpine 的语句是到alpine这个镜像,基于这个容器镜像将build容器中的log⽂件拷贝
出来。
使⽤buildx 同时制作x86_64和arm64架构的镜像。
-t的意思是build出的镜像的名称,push代表要push到docker镜像仓库中,platform参数代表制作出来的镜像同时代表两个架构,分别是x86 64位架构和arm64位架构。
运⾏此命令,docker buildx就会基于dockerfile的步骤,同时拉取ARM架构和X86架构的基础镜像。
运⾏此命令,docker buildx就会基于dockerfile的步骤,同时拉取ARM架构和X86架构的基础镜像。
基于基础镜像运⾏⾥⾯相同的语句,例如红⾊标记的地⽅,它同时运⾏两个架构的指令,相当于同时拉取了amd64和arm64的alpine 镜像。
去build时,同⼀条build语句也在amd64和arm64这两个架构上运⾏。
通过build出的结果即可发现RUN的这个命令,是在X86架构的机器上制作的镜像build出的结果,例如第⼀条是build出了arm64的镜像,第⼆条是build出X86 64位的镜像。
build完镜像过后,把它push到镜像仓库中,即可通过docker manifest 这个命令去查询这个镜像⾥⾯的
⼀些元数据,查看刚才制作的镜像,manifest 中有两种格式,第⼀种格式是amd64,表⽰X86_64架构,第⼆种格式是ARM64,是能够运⾏在ARM上的镜像,这些都是buildx做好的,我们只需要写dockerfile。
它默认帮我们制作出来⼀个多架构的容器镜像。
buildx 如何在 x86_64的机器上制作arm64的镜像?
通过binfmt_misc模拟arm64硬件的⽤户空间,然后调⽤qemu的⽤户态模式编译程序。
最终结果是调⽤buildx的命令过后,编译进程后,会运⾏qemu-aarch64 ⼯具,相当于模拟出arm64的硬件环境,然后调⽤ARM的⼯具做编译,截图中,后端服务都是⽤Golang编写,都需要做编译。
2、统⼀容器镜像-交叉编译
交叉编译:直接在x86_64开发机上编译arm64⼆进制。
图中的Go代码,其中写了main的函数,在X86的机器上直接编译,调go build⼯具,然后把它编译成⼀个叫做t_x86_64的⼆进制可执⾏⽂件。
然后在操作系统上调⽤file去看可执⾏⽂件的内容,通过信息可知这是⼀个64位的可执⾏⽂件,并且是x86-64架构。
Go中的交叉变异很简单,指定GOARCH的环境变量,然后把它设置为arm64,然后再运⾏相同的go build的命令,即可使⽤交叉编译,编译出ARM可执⾏的⽂件。
编译出来之后,再去看t_arm64的⼆进制⽂件,即可发现它也是64位的执⾏⽂件,但其架构为ARM,此即为交叉编译的简单的⽰例。相当于在X86的开发机上使⽤交叉编译⼯具编译出ARM执⾏⽂件。
3、如何将不同架构镜像打包
将交叉编译后的x86_64和arm64容器镜像组合到⼀起。
例如已经有名称为service:v1_x86_64的容器镜像和service:v1_arm64的镜像,如何将其组合为service:v1 = service:v1_x86_64 + service:v1_arm64 的容器镜像?
⾸先创建 service:v1 的 manifest 镜像,然后将x86_64的镜像和arm64的镜像捆绑到⼀起,然后调⽤docker manifest标记镜像 service:v1_arm64 的架构为 arm64 ,标记镜像 service:v1_x86_64 的架构为 amd64 ,再调⽤docker push将service:v1 镜像上传到镜像仓库即可,如此制作出来的⼀个镜像中即可包含两个架构。
4、buildx与交叉编译打包对⽐
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论