19

5大最新云原生镜像构建工具全解析,3个来自Google,你了解几个?

 4 years ago
source link: http://dockone.io/article/10194
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

1云原生大背景下的镜像构建

在分享开始,我想先跟大家简单聊一下云原生,可能不会详细展开,而是带领大家了解一下云原生对镜像构建方面的影响。

第一,在接触云原生相关的技术时,无论是要解决开发、测试环境的问题,还是解决日常开发、测试等相关的操作和流程,我们经常都会谈到持续集成。持续集成首先要做代码的集成,不同的feature一起交付,使用持续集成的理念尽快把代码合并,保证代码没有冲突,这是持续集成最简单的一些理念。

在持续集成之后,要考虑做哪些业务的验证。验证之外,还需要有一些安全相关的策略。比如,在开发过程中是否使用了不安全的代码或依赖包。在构建的过程中,还要生产许多不同的制品。

那么问题就来了,云原生技术确实能通过容器化、K8s集群编排等提供能够复制的应用环境。无论什么语言,Java或Python等都可以非常简单地去使用docker镜像,或者Kubernetes yaml去部署环境,解决开发和生产环境的区别。

在传统开发模式下,常常会遇到在开发环境里程序好好的,到生产环境就出现各种各样的问题。K8s集群编排可以说很好地解决了这个问题,变成复用地资源。以前部署很多实例需要开很多虚拟机的情况,也成为我们不必再关心的问题。

这些听起来很多都是跟运维相关的,那么开发、测试为什么要去关心这个事情?其实大家是在合作,为了达成一个共同的目标。不管用DevOps,还是敏捷开发,我们都要去考虑从代码交付到真正上线要做哪些事情,并予以解决,而不是总面对“在我的环境里没有问题”这样的问题。同时,尽量统一使用环境的配置、资源、方案。

刚才谈到从持续集成到云原生,大量使用云原生技术,这时候要考虑的是安全。简单来说,我可以使用K8s镜像把docker socket直接部署到自己的Pod容器里。但实际上要做更多的考虑,这个环境可能不同的业务来使用,环境本身是共享的,所以不光要使用环境,还要考虑它是否稳定,有没有CVE漏洞以及容器本身的一些权限等。只有搞清楚这几件事,才能去考虑后面要讲的内容。

2无Dockerfile构建镜像工具

首先来思考为什么会有镜像构建这方面的需求?第一,对很多开发者来说,需要不断学习新的框架,新的技术,新的理念,这其实是很多开发者都不希望面临的状态。开发者希望开发环境和生产环境一致,构建的结果无差距,从而避免在后期发现问题。在没有Dockerfile的情况下,会发现不同的语言使用的构建镜像的方案完全不同,需要去这个语言的生态里面寻找相关的方案。

还有一种场景,在构建一个应用时不知道它到底安不安全,也没法控制它里面有没有Dockerfile。比如说先有一个jar包,jar包是第三方开发的,我先上传到服务器,然后再去下载、构建。这时候可能直接用大的jar包去做。

另外,企业本身对产出物或者制品要求非常高,可能会有专门的人去维护,没有那么开放。

我在这里介绍三个无Dockerfile镜像构建方案。

1、KO

Ko是Google发明的一个工具,它主要服务Golang的用户,比较容易使用。除了构建Golang本身,它还往前进了一步,集成了 Kubernetes 的使用。比如构建镜像,构建程序,相关的yaml部署到开发环境等等,一切一个命令就可以搞定,不需要再去执行其他的命令。

Ko也应用了Golang语言本身的一些特性,比如用Golang package构建不同的镜像,用package构建二进制文件。我们自己的项目里也会使用Golang,编译很多不同的二进制文件。

fInI7zE.jpg!web

这块怎么使用呢?第一,我们有对应的k8s yaml,比如开发环境可能是yaml的方案,默认的代码仓库可能会带一些k8s yaml文件,pod、deployment、services等资源。如果不使用ko,还得去做一些replace,ko能够完全解决这个问题。

在镜像地址里直接放go module的名字,或者对应的二进制文件的名字,二进制文件可能在后面再加相关的package就可以了。ko apply也会构建,根据config的一些配置来更新对应的环境。

此外,它还支持前端比较喜欢的一些技术,比如live server这样的概念,对开发体验效率非常有帮助。

它的概念也非常简单,Golang本身构建出来的是一个二进制文件,需要基础镜像的支持,在基础镜像里面做一些配置,就可以运行起来。谷歌前段时间发明了一个项目叫distroless, distroless会有一些工具,例如busybox、ls,这些是在容器里面通常会使用的工具,但是并没有package,没有办法安装新的包。如果能修改,我们不希望使用distroless默认的镜像,而是通过.ko.yaml文件去覆盖,做默认的全局配置,或者根据不同的package去覆盖。

Ko没有把目标docker镜像仓库的地址放在配置里面,它是一个环境变量,这是因为,同城市不同的人在开发时会使用自己的镜像仓库,并不希望这个信息是共享的。用户加上自己的Docker registry地址之后,可以直接开始构建。这里可以分成两部分,刚才我们提到apply、K8s相关的操作,如果只想构建一个镜像并且推送到registry,可以用一个publish命令,publish一个或者多个二进制文件或者镜像。

这个镜像本身非常简单,Golang对环境的依赖非常少。所以,除了基础镜像的这些层级,还有你的应用。构建镜像层面并不依赖Docker,我们使用docker时,会通过docker执行docker build命令,docker build会访问docker daemon,接到主机里面。用Ko的话,完全不需要docker daemon,容器本身没有docker也是可以构建的。同时,也不需要很高的成本,不需要高权限,直接就可以配置。所以从上手或者用户使用来说,又快又简单。Ko也会大量使用GO本身的缓存,以及镜像缓存。

缺点方面,Google的这个基础镜像还在国外,如果应用本身是比较复杂的,需要额外投入时间去学习它的构建。另外,也没有特别好的网站,或者文档,所有的信息都在GitHub里面。

2、Jib

国内大部分开发者可能都是基于Java的,谷歌也在前段时间发明了一个Java专用镜像工具,叫Jib。Maven、gradle、core(库)都支持Jib这个镜像工具,它可以用于任何一个java项目。它本身也是完全基于Java来实现,对java开发者来说是非常熟悉的一种用法。

BZfmYvU.jpg!web

这是我们的目标镜像仓库,基础镜像可以写入,不写入的话,也会跟Ko一样用distroless的镜像去做。也可以定制,无论更简单还是更复杂的镜像,都可以做到。

除了使用Java的生态本身,Jib也考虑了对Java应用的优化。这里包含三层:依赖、静态资源和应用jar包。

Jib还有几个优点,对Java开发者来说它非常容易,jib不是特别复杂的一个概念,就是一个插件。第二,Java容器化很占用时间。容器化的Java方案在不断优化,因为Java除了容器化,它使用jvm本来就是为了解决应用环境的一些问题,相当于带有两种虚拟化的概念。我们要去做非常优化的Java镜像要投入不少时间和精力,这块jib是一步到位的。Jib和Ko一样,不依赖docker daemon,也可以在容器里运行,不需要什么高权限。

另外Jib和Ko相比较的话,没有做和K8s的集成,如果进一步优化的话,可能会往这个方向考虑。只是不好理解的是,maven或者gradle这块的生态可能已经有相关的插件。

3、s2i

如果我不是Java,又不是Go开发的,我应该怎么做呢?s2i是红帽发明的简化的Docker构建方案 。它的主要理念是,使用builder pattern 的概念去构建程序,再用运行时的镜像包一层。s2i需要在一个专门的镜像里做构建,然后再包一个运行的环境去做镜像,这个可以用任何语言去做。

s2i的使用,需要一些经验去考虑,同时还需要有专门的人去维护。因为它需要一个镜像做构建,在这个镜像里面要维护一堆东西。简单来说,s2i的理念是有这个流程去声明一下,所以相比前面的Ko、Jib两个工具,s2i有点复杂。

第一,要有一个二进制文件把流程编排起来,除了目标镜像仓库之外,代码还需要builder一个镜像。builder 镜像有几个要求,要有assemble脚本和run脚本,这几个脚本都是为了支持要做哪些事情,包括在镜像里面做单元测试都是支持的,还有其他一些附加功能。

3eEVRrq.jpg!web

从使用来说,一个小的命令就可以搞定,但是,这个命令背后的很多事情是怎么做到的,比如builder镜像的流程、文档等,有很多需要学习的概念。

这个统一构建的过程,开发者统一去管理,对最终用户来说是无感知的。包括第三方的软件依赖、builder镜像,除了这些脚本之外,还要写dockerfile,dockerfile是统一在一个镜像里面管理的。有自己的缓存机制。支持直接克隆代码构建,支持任何语言。

s2i缺点就是,太依赖docker环境。其次,上手非常复杂,拿现成的s2i工具做业务的话会很好用,但是这之前的学习成本还是很高的。

3 Dockerfile构建镜像工具

我们其实还是更希望在K8s环境下去执行构建,这部分我也跟大家介绍两个工具。

其实更多的情况,现在很多团队学习能力很强,愿意拥抱新的技术。希望深入研究容器化的最佳实践,探索怎么优化才能做到更好,怎么进一步使用工具减少建构的时间。

从社区的角度,现在开源社区越来越庞大,很多企业会选择使用开源社区的方案去做。开源社区也提供了很多的资源,你的团队可能忽然之间就变得很大了。在使用这些工具时,企业里的不同团队也会互相分享经验。

另外,影响Dockerfile镜像工具使用的,在交付物这块一些团队定制要求很高。

1、Kaniko

第一个是kaniko,也是谷歌发明的,因为 K8s 是谷歌的,所以他们在这块做了不少东西,kaniko不需要docker daemon,在容器里面去构建镜像。Kaniko目的是推广在K8s里构建镜像,任何容器化的方案都支持。除代码仓库,还可以使用对象存储的方案。

fiuINn6.jpg!web

Kaniko也有一些自己的概念,有本地缓存,还会用registry镜像作为缓存。构建过程中,每完成一个multistage docker构建,结束一个stage,会把这个镜像上传到registry。Kaniko还有一个基础镜像的缓存,为了减少在拉取时的时间,比如说在k8s里面用PVC来保存缓存,可以用warmer镜像来维护这个缓存。

Kaniko构建时,一个命令一步到位就可以完成,完全兼容docker的config.json。那么Kaniko是怎么在容器里面构建的呢?其实,它的原理也很简单,它把镜像的内容保存在自己的容器里面,所有的run命运都在容器里面运行,再构建文件。

Kaniko支持推送到多个registry.Docker需要一个个去推送,但kaniko可以用一个命令全部搞定。除了推送到registry本身,还可以保存成一个tar包,load到docker本身,其他操作都可以使用。Kaniko有多个上下文支持,可以做的场景非常多,可以用bucket或者其他类似的协议做非常复杂的构建。

Kaniko的缺点,镜像也是在谷歌那边gcr.io,不支持v1 image tag格式,构建原理不直观,无法直接load到docker daemon。

2、makisu

Makisu是Uber公司发明的,从18年开始使用,和Kaniko要解决的问题一样,在一个容器化的环境里面执行构建,不依赖docker daemon,也不需要做很多复杂的操作。能够直接load到docker daemon。Makisu跟Kaniko不同之处在于,除了支持dockerfile,还做了一些优化,加入commit机制,可以选择最终镜像要几个层级,通过dockerfile去解决。

736RJni.jpg!web

它的缓存除了本地,还有registry Jason。还可以把layer dockerfile里面的命令和相关 layer的信息保存到redis,从而减少构建操作,也可以大量复用。Makisu有一个特点,它没有选择兼容docker的配置,而是自己发明了一个配置文件,执行命令时指定具体文件。

这两个都有它的好处,在不同的构建环境里面维护这个配置,也不需要配置参数,构建参数都是一样的。另外,可以按照它的路径去做不同的配置。比如在docker.io里面,可以用不同的用户名和密码。这点docker配置本身其实没有这方面的支持。Makisu另外一个跟kaniko不太一样的地方,是可以在主机里面直接构建。

Makisu的优点刚才也提到了,Docker使用比较快,支持优化镜像层级和空间,直接load到docker daemon,支持镜像压缩,缓存对接redis, Kubernetes 官方的pod模版。

缺点部分跟Kaniko一样,镜像在国外Google,其次,它不支持其它构建上下文类型。第三,不兼容docker配置,尽管它能够搞定这些事情提供更强化的配置,但不兼容总归是一个缺陷。另外一个不容易理解的点是,makisu把push 和 –t 分开成为两个参数,来拼接最终目标镜像仓库地址。

4总结

今天这个探索并不是为了选择最优的工具,或者得出哪个工具更好更牛的结论。而是说,这些不同的工具有自己不同的使用场景。

FrMfyyv.jpg!web

Ko和jib可能适用于你的团队Java语言和人员占据了大多数,大家可能没有足够的时间和精力去学习怎么构建docker。这会是比较容易去上手使用docker镜像、 K8s 部署的。

不想用前面两个工具或者语言不支持的情况下,不想每个人都写一遍dockerfile,可以通过s2i的方式去解决。不过这种情况,确实你要去考虑可能要专门的团队去维护。另外一个使用s2i的场景是,所有的产出物需要管理的非常细,比如出于安全,一些依赖的处理本身等原因。

Kaniko适合用于构建场景非常多,并不是一个代码仓库,还有其他一些bucket这样的对接,或者上下文去通过Kaniko去做。Makisu适用于镜像非常大,需要去优化它的空间的情况。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK