关于java:蚂蚁构建服务演进史

6次阅读

共计 7502 个字符,预计需要花费 19 分钟才能阅读完成。

简介:自动化构建和 CI/CD 往往是相辅相成的,能够了解为,自动化构建是饥寒问题,解决了饥寒就会有更多的进步生产力的诉求,也就是对应的 CI 平台,CI/CD 本篇文章不做扩大。

作者 | 琉克
起源 | 阿里技术公众号

一 构建平台的由来

只有是软件开发就离不开构建,构建无处不在,构建是源代码和用户出现之间的桥梁。

这里要廓清一点,构建 != 编译,构建的实质是把源代码翻译成运行环境能辨认的产物(源代码可能是 Java 代码,也可能是配置文件、资源文件等,运行环境可能是物理机,也可能是虚拟机,也可能是 mobile phone)。

所以工程师每天每时每刻都在构建,不构建就没法验证。随着布局的扩充,把构建自动化掉,提供一个“打包平台”也就是一个自然而然的事件,毕竟进步生产力是第一诉求吗,这也就是构建平台最开始的由来,把每天干的事件自动化掉,搬上平台。

自动化构建和 CI/CD 往往是相辅相成的,能够了解为,自动化构建是饥寒问题,解决了饥寒就会有更多的进步生产力的诉求,也就是对应的 CI 平台,CI/CD 本篇文章不做扩大。

二 了解构建

构建 != 编译,构建自身是一个很简单的编排过程。举两个例子:

1 Android APK 的构建过程

上图中绿色局部为工具,浅蓝色部门为源代码 + 两头产物。能够看到是一系列的工具 + 输出的编排,最终生成运行环境可辨认的产物。上诉构建过程生成的产物 (APK) 可被 Android 手机辨认并运行。

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 原始时代
其实最开始的诉求非常简单,构建工具根本都是现成的,人少,性能简略。所有构建根本都是手动。

  • 挪动端:支付宝一个利用,一个仓库,谁发版间接在个人电脑上拉取最新代码执行编译,公布 app store。
  • 服务端:一台物理机,一套构建脚本,谁要公布,间接登录,输出仓库 / 分支信息,执行构建,而后发版。

2 自动化

显然,随着人员的增长,规模的扩充,原始时代根本无法撑持进一步倒退,次要的矛盾:

  1. 多人合作艰难。
  2. 多人抢占。
  3. 集体机器构建的不稳定性,成功率极低。

这一阶段最大的诉求:项目管理 / 多人合作 / 自动化构建。应运而生两个平台:CP SCM。

  • CP 次要负责项目管理,多人合作。
  • SCM 次要负责代码治理,构建任务调度,构建机器治理运维(SCM 缓缓演变成一个大杂烩,jar 包上传治理,客户端签名,各种)。

大略长上面这个样子:

初级阶段

这里构建比拟大的难点还是在机器的治理和调度,其实做了很多事件:

  • 机器治理,增删机器。
  • 机器保活,可用性监控。
  • 机器环境一致性的保障。
  • 机器构建负载平衡。
  • 构建脚本的对立降级保护。

下面框架运行了一段时间后还是产生了很多问题,构建成功率越来越低,次要有几个问题:

  • ssh 通道是有连接数限度的,抽风性的 ssh 连贯异样。
  • 负载平衡管制艰难,机器之间负载常常不统一,构建时快时慢。
  • 机器配置治理难度大。

进阶阶段

其实这一块,开源有十分成熟的计划 — jenkins。咱们干的很多的事件 jenkins 都曾经帮咱们干了。也能很好的解决咱们遇到的痛点:

  • C/ S 架构,无 ssh 通道限度。
  • 自动控制负载平衡,机器监控。
  • 灵便的构建和工作配置。
  • 弱小的凋谢能力和丰盛的 API。

革新完大略长这样:

黑科技(填坑)

这里要抛出一个新的概念「制品库」。

Java 开发中,大家对 maven、gradle 这些工具必定不能再相熟了。后面讲构建工具的时候讲过,Java 构建工具有几代演进:Ant,Maven,Gradle。Maven 之前的上古工具,用的人应该非常少了。

在 Maven 之前,是不存在版本治理,依赖治理这种概念的,所有的货色都在你的仓库。你的工程外面用到了 gson,spring,log 等开源框架和性能时,是须要去手动下载对应的 jar 包,而后放在代码库中。如果须要更新,须要不停去我的项目对应官网,下载最新公布的包。

Maven 之后的工具,提供了弱小的依赖治理性能,只有在 pom.xml 写上你要应用的依赖,maven 会主动下载依赖,批改和降级只须要批改 GAV 坐标(groupid,artifactid,version),依赖的所有 jar 包都存储在「制品库」中。

此时的构建大略长这个样子:

  1. 物理机缓存应用 overlay 文件系统,每次构建缓存独立,避免公共缓存被刷入。
  2. 监控制品库的笼罩记录,生产 delete task,疾速告诉物理机删除公共缓存。
  3. 记录 overlay 文件系统 upper 层的减少记录,记录 add task,定时刷入物理机公共缓存。

存在的问题

  • 环境一致性保障,环境降级。
  • 简单的脚本逻辑。

了解下这两个问题,随着业务的迅速倒退,接入的零碎越来越多,APP 越来越多,构建的环境越来越“胖”。

比方:App 除了支付宝,还有口碑,财产,香港钱包等,各个产品有本人的构建逻辑,也有本人的工具,比方支付宝用 gradle4,口碑用 gradle2。

其它的技术栈也越来越多,Java,GO,C++ 等,须要不同的 JDK 版本,GO 环境等。

所有环境都塞进一台物理机,这里存在两个比较严重的问题:

频繁减少新的工具,如何确保不影响既有的环境和构建。
环境不可复制,新的构建机器,初始化艰难,很难保障和旧有环境的一致性。
历史产生过的问题

  • 新加的物理机编码异样,导致构建产物异样,运行时呈现乱码。
  • 降级 IOT 的 AndroidSDK,影响支付宝,局部手机计步性能生效。
  • 环境降级操作不当导致的各种构建失败。

3 容器化

构建是一件十分值得敬畏的事件,须要保障构建的相对正确,一旦构建异样了,结果不堪设想。

最好的保障构建正确性的形式,就是什么都不要改,不要加机器,不要改环境,什么都不要动。

然而事实是总是有越来越多新的场景冒出了,明天要反对这个,今天要反对那个,这里是一个比拟矛盾的点。

在容器技术进去之前,大家都是用的是虚拟机技术,咱们能够模仿进去一台乃至多台电脑,然而太轻便了,也不好保护。2013 年 Docker 开源,它轻量,高性能(秒级启动),隔离性,让他迅速成为焦点。

构建也尝试摸索,docker 技术非常适合在构建时应用,能够很好的解决下面的问题。革新后长上面这样:

之后降级环境再也不是痛,各种场景容器隔离,降级互不影响,物理机秒级扩容。运维人员根本只有保护 Dockerfile 就行。

当然也会带来新的问题:

  • Mac 构建没法虚拟化,依然是传统物理机构建 + Ansible 运维。
  • 容器化之后缓存生效,构建工夫暴增。
  • 镜像自身的治理,物理机磁盘空间治理。
  • 4 镜像化

后面讲的都是软件的构建过程和构建服务,这里其实还存在一个问题,除了构建的一致性,软件的运行环境一致性也至关重要。常常会产生,一个软件,在我的电脑能够,在他人的环境却跑不起来。

随着容器技术越来越火,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 应用前后端拆散的架构设计,除了 Dockerfile 也反对其余类型的前端格局。
  • 并行构建执行:对于 multistage 类型 Dockerfile,buildkit 能够实现不同 stage 之间的并行执行。
  • 反对构建缓存的多种解决形式:buildkit 解决本地缓存 snapshot,同时还提供了将构建缓存导入 / 导出到本地或者近程 registry。
  • 多种输入格局:buildkit 反对导出成 tar 包或者 oci 格局的镜像格局。
  • 引入 Dockerfile 新语法 RUN –mount 反对构建时挂载。

这里不进行扩大,有趣味的同学能够查看 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 工作等场景。

具体细节能够参考上面几篇文章:

  1. tektoncd github
  2. 继续交付基金会

下一步打算

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 极致的构建效率和体验

深度定制构建工具

  • Java 研发:Maven,Gradle,并发构建,缓存构建,增量构建,甚至秒级构建。
  • 终端研发:秒级构建,疾速本地部署验证。
  • 镜像构建:目前依然须要 30+ s 的构建工夫,须要继续做上来,缓存的命中率,镜像减速,remote cache,除了构建提效,还能够帮忙部署提效。

制品库降级

  • 颠覆现有不合理模式,比方因为 Jar release 笼罩导致的“黑科技”,晋升研发体验和研发效率。
  • 软件制品身份追踪,深刻可信研发。

原文链接
本文为阿里云原创内容,未经容许不得转载。

正文完
 0