简介: 自动化构建和CI/CD每每是相辅相成的,能够理解为,自动化构建是温饱问题,解决了温饱就会有更多的提升生产力的诉求,也就是对应的CI平台,CI/CD本篇文章不作扩展。前端
做者 | 琉克
来源 | 阿里技术公众号java
只要是软件开发就离不开构建,构建无处不在,构建是源代码和用户呈现之间的桥梁。python
这里要澄清一点,构建 != 编译,构建的本质是把源代码翻译成运行环境能识别的产物(源代码多是Java代码,也多是配置文件、资源文件等,运行环境多是物理机,也多是虚拟机,也多是mobile phone)。linux
因此工程师天天每时每刻都在构建,不构建就无法验证。随着规划的扩大,把构建自动化掉,提供一个“打包平台”也就是一个天然而然的事情,毕竟提升生产力是第一诉求吗,这也就是构建平台最开始的由来,把天天干的事情自动化掉,搬上平台。android
自动化构建和CI/CD每每是相辅相成的,能够理解为,自动化构建是温饱问题,解决了温饱就会有更多的提升生产力的诉求,也就是对应的CI平台,CI/CD本篇文章不作扩展。git
构建 != 编译,构建自己是一个很复杂的编排过程。举两个例子:github
1 Android APK的构建过程spring
上图中绿色部分为工具,浅蓝色部门为源代码+中间产物。能够看到是一系列的工具+输入的编排,最终生成运行环境可识别的产物。上诉构建过程生成的产物(APK)可被Android手机识别并运行。docker
2 Java Jar包的构建过程编程
能够看到jar包的构建过程和上面APK的差别很是大,相对来讲也是更简单。
3 构建工具
上面两个case能够看出来,构建自己很复杂,要最终构建出一个能够运行的产物须要作不少事情,咱们彻底能够手动javac copy jar等等一系列操做实现构建过程。但当工程愈来愈大,文件愈来愈多,这个事情就不是那么地使人开心了。这些命令每每都是很机械的操做。因此咱们能够把这些机械的操做交给机器去作。对应的构建工具也应运而生,毕竟提升生产力是第一诉求。
拿Java举例:
Ant
上面的示例中,Ant定义了五个任务:init、compile、build、test和clean。
每一个任务作什么都定义清楚了。在打包以前要先编译,因此经过depends来指定依赖的路径。
若是在命令行里执行ant build,那就会先执行compile,而compile又依赖于init,因此就会先执行init。
执行命令:
ant test
经过命令就能够执行编程,打包,测试。为开发者带来了很大的便利,提供了工做效率。
可是Ant有一个很致命的缺陷,那就是没办法管理依赖。
咱们一个工程,要使用不少第三方工具,不一样的工具,不一样的版本。
每次打包都要本身手动去把正确的版本拷到lib下面去,不用说,这个工做既枯燥还特别容易出错。为了解决这个问题,Maven如约而至。
Maven
Ant仅是一个构建工具,它并未对项目的中的工程依赖以及项目自己进行管理,而且Ant做为构建工具未能消除软件构建的重复性,由于不一样的项目须要编写对应的Ant任务。
Maven做为后来者,继承了Ant的项目构建功能,而且提供了依赖关系,插件机制,项目管理的功能,所以它是一个项目管理和综合工具, 其核心的依赖管理, 项目信息管理, 中央仓库,Maven的核心理念是约定大于配置。每一种类型都有固定的构建生命周期。
和ant的build.xml相对的,maven项目的核心是pom.xml,java开发同窗确定都很熟。
Gradle
Gradle已经抛弃了Ant、Maven中Xml配置的形式,取而代之的是Gradle采用了领域特定语言Groovy的配置。Gradle继承了Maven中仓库,坐标,依赖这些核心概念。文件的布局也和Maven相同。但同时,又继承了Ant中target的概念,咱们又能够从新定义本身的任务(在Gradle中叫作task)。
相比maven会更简洁,好比在maven中要引入依赖:
转换成gradle脚本:
dependencies {
compile('org.springframework:spring-core:2.5.6') compile('org.springframework:spring-beans:2.5.6') compile('org.springframework:spring-context:2.5.6') compile('com.google.code.kaptcha:kaptcha:2.3:jdk15') testCompile('junit:junit:4.7')
}
配置从原来的28行缩减至7行!效果惊人。
同时gradle在构建性能上也碾压maven,gradle在maven的基础上额外增长了增量构建、build cache、daemon等特性,大大提高构建时间。
相似的构建工具其实还有不少,基本属于百花齐放,好比facebook的BUCK,Google的bazel等,国内也有一些厂商本身的构建工具,好比腾讯的blade。不一样的工具都会有本身的优点和劣势。
1 原始时代
其实最开始的诉求很是简单,构建工具基本都是现成的,人少,功能简单。全部构建基本都是手动。
2 自动化
显然,随着人员的增加,规模的扩大,原始时代根本没法支撑进一步发展,主要的矛盾:
这一阶段最大的诉求:项目管理/多人协做/自动化构建。应运而生两个平台:CP SCM。
大概长下面这个样子:
初级阶段
这里构建比较大的难点仍是在机器的管理和调度,其实作了不少事情:
上面框架运行了一段时间后仍是发生了不少问题,构建成功率愈来愈低,主要有几个问题:
进阶阶段
其实这一块,开源有很是成熟的方案 -- jenkins。咱们干的不少的事情jenkins都已经帮咱们干了。也能很好的解决咱们遇到的痛点:
改造完大概长这样:
黑科技(填坑)
这里要抛出一个新的概念「制品库」。
Java开发中,你们对maven、gradle这些工具确定不能再熟悉了。前面讲构建工具的时候讲过,Java构建工具备几代演进:Ant,Maven,Gradle。Maven以前的上古工具,用的人应该很是少了。
在Maven以前,是不存在版本管理,依赖管理这种概念的,全部的东西都在你的仓库。你的工程里面用到了gson,spring,log等开源框架和功能时,是须要去手动下载对应的jar包,而后放在代码库中。若是须要更新,须要不停去项目对应官网,下载最新发布的包。
Maven以后的工具,提供了强大的依赖管理功能,只要在pom.xml写上你要使用的依赖,maven会自动下载依赖,修改和升级只须要修改GAV坐标(groupid,artifactid,version),依赖的全部jar包都存储在「制品库」中。
此时的构建大概长这个样子:
存在的问题
理解下这两个问题,随着业务的迅速发展,接入的系统愈来愈多,APP愈来愈多,构建的环境愈来愈“胖”。
好比:App除了支付宝,还有口碑,财富,香港钱包等,各个产品有本身的构建逻辑,也有本身的工具,好比支付宝用gradle4,口碑用gradle2。
其它的技术栈也愈来愈多,Java,GO,C++等,须要不一样的JDK版本,GO环境等。
全部环境都塞进一台物理机,这里存在两个比较严重的问题:
频繁增长新的工具,如何确保不影响既有的环境和构建。
环境不可复制,新的构建机器,初始化困难,很难保证和旧有环境的一致性。
历史发生过的问题
3 容器化
构建是一件很是值得敬畏的事情,须要保证构建的绝对正确,一旦构建异常了,后果不堪设想。
最好的保证构建正确性的方式,就是什么都不要改,不要加机器,不要改环境,什么都不要动。
可是现实是老是有愈来愈多新的场景冒出了,今天要支持这个,明天要支持那个,这里是一个比较矛盾的点。
在容器技术出来以前,你们都是用的是虚拟机技术,咱们能够模拟出来一台乃至多台电脑,可是太笨重了,也很差维护。2013年Docker开源,它轻量,高性能(秒级启动),隔离性,让他迅速成为焦点。
构建也尝试探索,docker技术很是适合在构建时使用,能够很好的解决上面的问题。改造后长下面这样:
以后升级环境不再是痛,各类场景容器隔离,升级互不影响,物理机秒级扩容。运维人员基本只要维护Dockerfile就行。
固然也会带来新的问题:
前面讲的都是软件的构建过程和构建服务,这里其实还存在一个问题,除了构建的一致性,软件的运行环境一致性也相当重要。常常会发生,一个软件,在个人电脑能够,在别人的环境却跑不起来。
随着容器技术愈来愈火,serverless技术和应用微服务架构的演进。容器正迅速成为企业应用打包和部署的基本单位,能够真正的实现build once & run everywhere。
在蚂蚁的历史中也是如此,愈来愈多的场景开始镜像化部署,因此镜像构建自己也变得愈来愈重要,镜像构建的效率,稳定性,安全性等相当重要。
镜像构建也通过两次演进:
docker build
docker build是比较简单的,咱们在以前的架构之上新增了一种镜像构建类型,主要存在下面几个弊端。
(1)对于multi-stage 的Dockerfile 构建 没法实现并行编译
(2)docker build 缓存利用效率低,改变Dockerfile 前面的一层,后面全部的层都需
要从新构建而没法使用缓存,这要求用户不得不认真控制写好本身的Dockerfile以确保镜像缓存复用。
buildkit + K8S
buildkit是从docker build分离出来的单独项目,目前buildkit已经集成到Docker 18.06以后的版本之中,核心特性:
这里不进行扩展,有兴趣的同窗能够查看buildkit的官方项目(蚂蚁目前天天运行着上万数量的高可用镜像构建服务)。
5 拥抱云原生
随着蚂蚁愈来愈多的业务serverless化,云原生慢慢成为了趋势。伴随着的是对K8s之上的构建和资源的使用诉求。
而K8s自己使用门槛又极高,同时也缺少灵活的任务编排能力。相应的构建团队也开始调研和投入云原生的构建和调度解决方案。
2019年3月份持续交付基金会(CDF)正式成立,它致力于使企业在多个 CI / CD 平台上更轻松地构建和复用 DevOps 管道。
第一批进入CDF项目的主要有四个:
Tekton 做为谷歌捐赠的 CDF 重要项目 ,是一组用于构建 CI/CD 系统的共享开源组件,与 Kubernetes 紧密相连,其重要性不言而喻。
而且jenkinsX底层也选择了tekton做为执行引擎。
Jenkins X is committing fully to Tekton as its pipeline execution engine. We are convinced that this is the right choice for Jenkins X, as a cloud-native CI/CD platform on Kubernetes, and for our users.
内部落地
综合权衡,采用tekton是一个比较合理的解决方案(站在巨人的肩膀,不重复造轮子)。
通过一段时间的探索和演进,逐步落地了云原生的资源调度和构建解决方案——ironman。服务内部天天几万的构建、代码扫描、CI任务等场景。
详细细节能够参考下面几篇文章:
下一步计划
tekton相比K8s,复杂度大大下降,而且提供了足够灵活的编排和调度能力,可是仍然有缺陷:
概念复杂,偏厚重,总体调度相比直接使用POD会更慢
使用上仍然有一些成本,对一线用户的接入使用不友好
目前正在投入POD预热等极简模式,解决上诉痛点。固然还有不少未解的难题,就不一一赘述。
6 构建中台
经历了自动化,容器化,镜像化等场景,发现用户的需求实在是千奇百怪,愈来愈多(只能说蚂蚁的业务发展太快)。
咱们有愈来愈多的业务场景(IOT,小程序,大数据,...),构建的需求差别性也很是大。有Mac构建,Linux构建,Windows构建。应付仍是有点吃力。尤为是在Mac和Windows两种没法虚拟化的场景,大量的机器分组,有点维护不动。现状大概长这样:
因为构建逻辑基本都是在构建团队维护,SCM和构建脚本中的代码逻辑也处于一个很是混乱的状态,基本就是大量的if else,伪代码大概长这样:
if (framework == "sofa") {
buildCmd = "mvn clean package" if (app == "special") { buildCmd = "mvn clean package -Ptest=true" }
} else if (framework == "android") {
buildCmd = "gradle clean assembleRelease"
} else if (framework == "jar") {
buildCmd = "mvn clean install && mvn deploy"
} else if (xxx) {
buildCmd = "xxx"
}
这个阶段在面对一些个性化的构建需求其实有点力不从心,需求千奇百怪:“我要加个额外的参数”,“我要更多的CPU”,“我须要用Mac来跑构建”,“我须要用某某软件的某某版本”。
当前阶段是无法继续支撑蚂蚁将来的业务发展的,在加上当前底下已经有很是多的资源(linux,windows,Mac,K8S)管理困难。
因此将来的构建平台,至少是能够作到下面两点:
构建可描述
全部的构建逻辑是透明的,可配置化,可代码化,可描述内容包括:
这里最关键的点是去掉大量分组维护带来的难点,让资源之间能够共用,互相流动。同时能够实现资源之间的任意切换,降级,保障构建服务的高可用(好比K8s资源下降到物理机构建)。
新的框架大概长下面这样:
业务使用方只要定义好buildspec.yaml文件,就能够实现任何个性化的构建需求。
底下执行构建的资源能够是K8S,能够是jenkins,能够是物理机,whatever,构建资源描述好本身支持的类型入场便可。
buildspec.yaml大概长下面这样:
name: android-aar-build params: - name: productLine default: alipay - name: sprintId default: ${SPRINT_ID} resources: - name: code-repo type: git url: https://code.alipay.com/xxxxx ref: master environment: type: LINUX_CONTAINER image: reg.docker.alibaba-inc.com/alipay/aarbuild:latest buildTasks: - name: Download config image: python:3 commands: - python --version - name: Install Dependency image: ruby:2.6 commands: - echo "-------2 in ruby:2.6" ruby -v artifacts: - name: pod-for-alipay type: iot-sign path: xxxx.zip
1 统一构建中台
目前还在持续的开发和演进中,做为服务蚂蚁全栈的构建服务,其稳定性,高可用,灵活性相当重要。尤为是极限生存能力。
2 云原生调度基础设施
面向K8s的CI/CD,让K8s的资源使用简单优雅。tekton的优雅升级,极简的调度方案,友好的接入成本。
3 极致的构建效率和体验
深度定制构建工具
制品库升级
原文连接本文为阿里云原创内容,未经容许不得转载。