乐趣区

关于javascript:百度APP-基于Pipeline-as-Code的持续集成实践

全文 8150 字,预计浏览工夫 21 分钟

一、概述

百度 APP 通过多年 DevOps 的建设,曾经造成了一套从打算、开发、测试、集成到交付的规范工作流和工具集。其中,继续集成(Continuous integration,简称 CI)作为 DevOps 最外围的流程之一,通过频繁地将代码集成到骨干和生产环境以执行预置的自动化工作。

CI 始终是咱们 百度挪动研发平台——Tekes,撑持百度 APP 研发流程的重要的切入点,咱们的自动化研发流程(组件主动公布、准入等)都是基于 CI 的实际,曾经反对 百度 APP 发版 50+ 次,准入 40w+ 组件和 SDK。但当这些自动化研发流程输入到其余产品线时,却遇到了一个问题:不同产品线对研发流程存在定制化需要 ,例如 百度 APP 在中台化组件公布前须要查看 API 及依赖的变更状况,并在有不兼容变更的状况产生时发动人工审批;而 难看 APP 只须要将不兼容变更展现在公布实现后的报表之中,这就导致了咱们预置的流水线模板无奈间接复用。为了解决这个问题, 一种可行的办法是让产品线用结构化的语言去形容他们研发流程须要的一组性能或个性,而后依据形容自动化地生成对应的流水线,这种思维其实就是 Pipeline as Code(流水线即代码,PaC)。

二、Pipeline as Code

Pipeline as Code 是“as Code”静止的一种,援用 Gitlab 官网对 Pipeline as Code 的解释:

Pipeline as code is a practice of defining deployment pipelines through source code, such as Git. Pipeline as code is part of a larger“as code”movement that includes infrastructure as code. Teams can configure builds, tests, and deployment in code that is trackable and stored in a centralized source repository. Teams can use a declarative YAML approach or a vendor-specific programming language, such as Jenkins and Groovy, but the premise remains the same.

提到“as Code”,咱们最容易想到 Infrastructure as Code (基础设施即代码,IaC),IaC 是将基础设施、资源及环境应用 DSL(Domain Specified Language,畛域专有语言)编码,例如 Ansible 的 playbook 就是一种基于 YML 的 DSL,一个在 macOS 零碎上标准化装置 Xcode 的 playbook 简略示例如下:

# playbook
- name: Install Xcode
  block:
    - name: check that the xcode archive is valid
      command: >
        pkgutil --check-signature {{xcode_xip_location}} |
        grep \"Status: signed Apple Software\"
    - name: Clean up existing Xcode installation
      file:
        path: /Applications/Xcode.app
        state: absent
    - name: Install Xcode from XIP file Location
      command: xip --expand {{xcode_xip_location}}
      args:
        chdir: /Applications
      poll: 5
      async: "{{xcode_xip_extraction_timeout}}" # Prevent SSH connections timing out waiting for extraction
    - name: Accept License Agreement
      command: "{{xcode_build}} -license accept"
      become: true
    - name: Run Xcode first launch
      command: "{{xcode_build}} -runFirstLaunch"
      become: true
      when: xcode_major_version | int >= 13
  when: not xcode_installed or xcode_installed_version is version(xcode_target_version, '!=')

如同 Ansible,咱们也应用了相似的计划对流水线进行 DSL 编码并纳入版本控制,除了能解决咱们遇到的不同产品线差异化配置的问题,他还有很多其余长处,例如:

  1. 让产品线团队只须要关注流水线以后版本的 DSL,不便团队外部成员独特保护和降级;
  2. 流水线自身的环境配置也是 DSL 的一部分,打消流水线环境因为配置凌乱造成的特异性;
  3. DSL 非常容易复制和链接代码片段,能够将 CI 脚本组件化之后作为一种可配置的 DSL 单元。

不过在介绍咱们的计划之前,咱们 先介绍下业内比拟有代表性的两种计划:Jenkins Pipeline Github Actions

Jenkins Pipeline 是 Jenkins2.0 推出的一套 Groovy DSL 语法,将本来独立运行于多个 Job 或者多个节点的工作对立应用代码的模式进行治理和保护。

GitHub Actions 是 GitHub 推出的自动化服务,通过在仓库配置一个基于 YML 的 DSL 文件来创立一个工作流,当仓库触发某个事件的时候,工作流就会运行。

咱们举一个简略的 Xcode 工程 编译的例子来领会两者 DSL 语法的区别,蕴含三个步骤:

  1. Checkout:从 Git 服务器下拉源码
  2. Build:执行 xcodebuild 编译命令
  3. UseMyLibrary:援用自定义的脚本办法

Jenkins Pipeline 的 DSL 如下:

// Jenkinsfile(Declarative Pipeline)
@Library('my-library') _
pipeline {
  agent {
    node {label 'MACOS'}
  }
  stages {stage('Checkout') {
      steps {checkout scm}
    }
    stage('Build') {
      steps {sh 'xcodebuild -workspace projectname.xcworkspace -scheme schemename -destination generic/platform=iOS'}
    }
    stage('UseMyLibrary') {
      steps {myCustomFunc 'Hello world'}
    }
  }
}

Github Actions 的 DSL 如下:

# .github/workflows/ios.yml
name: iOS workflow
on: [push]
jobs:
  build:
    runs-on: macos-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Build
        run: xcodebuild -workspace projectname.xcworkspace -scheme schemename -destination generic/platform=iOS
      - name: UseMyLibrary
        uses: my-library/my-custom-action@master
        with:
            args: Hello world

能够看到两者 DSL 的表述都十分清晰简练,很多语法甚至能够互相转换,实际上 DSL 不是 PaC 首要的选型根据,而是看业务应用何种继续集成系统。

Jenkins 是一个齐全自托管的继续集成系统,应用 Groovy 脚本来定义 Pipeline 也提供了极大的灵活性。而 Github Actions 与 Github 高度整合,能够间接应用,并且 Workflow 的 YML 是一种组件化设计,构造清晰,语法简略。能够说两者各有千秋。咱们业务应用的继续集成系统是百度自研的 iPipe,咱们在做 PaC 的实际时第一准则是应用公司的基础设施,防止反复造轮子,因而咱们采取了一种翻新的计划和零碎——Tekes Actions。

三、Tekes Actions

从命名能够看出 Tekes Actions 参照了 Github Actions,DSL 语法也根本照搬 Github Actions,例如 百度 APP 组件公布流程的 DSL 如下:

# baiduapp/ios/publish.ymlname: 'iOS Module Publish Workflow'author: 'zhuyusong'description: 'iOS 组件公布流程'on:  events:    topic_merge:      branches: ['master', 'release/**']      repositories: ['baidu/baidu-app/*', 'baidu/third-party/*']jobs:  publish:    name: 'publish modules using Easybox'    runs-on: macos-latest    steps:    - name: 'Checkout'      uses: actions/checkout@v2    - name: 'Setup Easybox'      uses: actions/setup-easybox@v2      with:        is_public_storage: true    - name: 'Build Task use Easybox'        uses: actions/easybox-ios-build@v1with:          component_check: true          quality_check: true    - name: 'Publish Task use Easybox'        uses: actions/easybox-ios-publish@v1    - name: 'Access Task use Easybox'        uses:actions/easybox-ios-access@v1

其实上一节提到了 DSL 自身不是 PaC 最要害的点,咱们选用 Github Actions 的 DSL 次要起因是因为其组件化的工作流程能够很好地与咱们 Tekes 平台以及公司的继续集成系统进行交融。

咱们先介绍 Github Actions 官网文档的工作流程,附上示意图:

工作流程蕴含一个或者多个作业(Job),由事件(Event)触发,在 Github 托管或自托管的运行器(Runner)上运行。上面介绍这几个外围组件:

1. 作业:每个作业由一组步骤(Step)组成,每个 Step 是能够运行操作(Action)或者 Shell 命令的单个工作,Action 能够看作封装的独立脚本,能够自定义或援用第三方。

2. 事件:事件是触发工作流程的特定流动,例如推送代码、拉取分支、创立问题等。原本 Github 就提供了十分丰盛类型,能够很不便地作为 Github Actions 的触发源。

3. 运行器:运行器是运行触发后的工作流程的服务,GitHub 提供 Linux、Windows 和 macOS 虚拟机环境的运行器,也能够创立自托管运行器运行在自定义的环境中。

Action 是组成工作流程最外围最根底的元素,能够说正是因为有了 Action 这种可复用扩大的设计,给 Github Actions 生态带来了极大的生机,Github 不仅提供了许多官网的 Action,并且还搭建了一个 Action 市场,能够搜寻到各种三方 Action,让实现一个工作流程变得非常简单。

Tekes Actions 在 作业、事件和运行器这 三个组件上都有本人特色的设计:

作业上,Tekes 通过 Action 可复用扩大的这种思维,把本人多年建设的 CI 脚本分解成 Tekes 官网的 Action,让产品线能够自在插拔到本人的研发流程中,实现定制化流水线;同时凋谢出 CI 的能力,搭建了一个公开的 Action 制品库,反对 保障组件化、品质、性能等角色 能够上传本人的 Action,独特建设 Tekes 生态。

事件上,因为 Tekes 在建设挪动 DevOps 服务时形象了本人的事件类型,咱们将此作为 Tekes Actions 工作流程的触发源。并且因为咱们的事件并不是和仓库一对一的关系,咱们还设计了一个产品线流程编排的服务和事件处理的服务,前者用来帮忙产品线管理工作流程的 DSL 文件,后者用来裁决何种事件应该触发何种产品线的何种工作流程。

运行器上,咱们齐全实现了运行器用来解释执行工作流程的 DSL,包含监听触发事件,调度 Job,下载 Action,执行脚本,上传日志等性能,并反对 Cli 命令本地调用,不便流水线开发者在本人本地的工作区中进行调试;同时远端的运行器跑在咱们虚拟机集群,当工作流程触发时咱们通过 iPipe Agent 去调度。

iPipe Agent 是一个基于 iPipe 的代理服务,能够间接调度到咱们的虚拟机集群并调配一台全新的蕴含指定零碎和运行器的虚拟机。

整个 Tekes Actions 的工程架构如下图所示:

工程上,Tekes Actions 采纳了一个齐全基于百度云的 Serverless 服务,外围的事件处理服务只是一个云函数服务,这个云函数服务负责解决两种事件源:

1. 流程编排。产品线创立和更新工作流程时会生成一个 YML 文件,上传到 DSL 文件服务 的该产品线的目录下,DSL 文件服务 的文件新增和更新事件会告诉云函数,用来新增和更新数据库服务存储的触发规定;

2. 事件。例如 DevOps 服务产生的组合入事件,这些事件会公布到音讯服务特定的 topic 中,云函数订阅这个 topic 以接管事件,用来匹配数据库服务存储的各个产品线工作流程触发规定,并在匹配胜利时调度一个运行器。

四、Tekes Runner

Tekes Runner 是运行 Tekes Actions 工作流程的工具,架构图如下:

Tekes Runner 服务层由 Template、Worker 和 WebAPI 三个模块组成,别离负责读取和校验 DSL 文件、管理工作流程以及与后端服务通信,其中 WebAPI 模块是能够在配置文件中敞开的。

Tekes Runner 在交互层的命令模块提供了四种 Cli 命令,别离是 Run、Pause、Unpause 和 Kill,都是管理工作流程生命周期的命令。工作流程生命周期如下图所示:

当执行 Run 命令时,Tekes Runner 首先会依据配置来初始化 Tekes Actions 工作流程,包含读取对应工作流程的 YML,下载并校验依赖的 Action,创立工作区等,胜利之后进入 initialized 状态。

接着会依据解析后的 工作流程对象 创立一组状态机并运行,每一个作业对应一个简略的无限状态机(Finite State Machine,FSM),此时进入到 running 状态。

状态机的现态是以后执行的阶段,触发状态迁徙的事件是脚本运行的后果。

当整个状态机组的所有状态机都迁徙到完结状态(无论是否胜利),工作流程就会完结,进入 stopped 状态。此外,如果工作流程超时或者接管到内部的 Kill 命令,也会进入 stopped 状态。

当初咱们曾经实现了一个能够在本地运行脚本的简略运行器了,但还有几个细节须要进一步论述:

丨细节 1. Action 和 Runner 的交互方式

Action 和 Runner 交互是一个十分常见的行为,例如从 Runner 获取输出写入输入,又或者将执行后果告诉 Runner。

Action 和 Runner 有三种交互方式:

  1. 环境变量:Runner 将须要传递给 Action 的参数写入环境变量中,个别包含 Action 所需的输出以及一些上下文;
  2. 工作区文件:Runner 将须要传递给 Action 的文件放入工作区特定的文件夹中,个别是 Action 所需的两头产物;
  3. Action 的打印:Runner 在执行 Action 的过程中会一直监听 Action 的打印内容,Runner 和 Action 约定了一套带有非凡命令标识符的打印语句,当 Runner 监听到此类语句时会解析并执行预设的命令,包含设置输入,打印日志和上传产物等。

丨细节 2. Pause/Unpause 的作用

工作流程有时候不可避免会插入人工审批、代码评审等须要长期期待的工作,例如组件准入产品线须要产品线负责人审批。如果工作流程没有暂停状态,那就意味着有一个 Action 始终阻塞着线程或者一直地轮询,这对资源无疑是一个微小的节约。

当执行 Pause 命令时,Runner 会长久化以后的上下文并完结过程。绝对的,当执行 Unpause 命令时,Runner 会从长久化中复原上下文,而后持续运行以后的阶段。

丨细节 3. WebAPI 的作用

在大多数状况下,Runner 是执行在远端的虚拟机中,由 ipipe agent 调度,并在执行完工作流程后被回收。因而须要一个机制将本地的日志和长久化上下文保留到一个服务中,这其实就是 WebAPI 的作用。

回顾一下 Tekes Actions 的工程架构图,日志服务其实就负责着保留工作流程日志和上下文的角色,其余的上游服务还能够通过咱们的日志服务去查问工作流程执行的状况和具体的日志。

四、结语

Pipeline as Code 既是一种高效的流水线治理模式,也是 CI/CD 转变成 DevOps 的一种新的趋势。借助于 PaC,给整个流水线带来的不堪设想的灵活性,也给团队围绕流水线的建设、沟通和合作带来了无益的变动。

建设好 PaC 须要一些前置的依赖,包含云原生平台和继续集成工具,咱们 Tekes 也是基于公司弱小的基础设施加上本身丰盛的继续集成实际,而后参考了业内成熟的计划,站在伟人的肩膀上伸手摘星。心愿咱们的这篇文章能够给大家在解决继续集成问题时带来肯定借鉴意义。

参考

[1] 继续集成维基百科

https://zh.wikipedia.org/zh-s…

[2] What is CI/CD

https://www.redhat.com/zh/top…
[3] What is pipeline as code

https://about.gitlab.com/topi…
[4] Pipeline as Code

https://www.jenkins.io/doc/bo…
[5] 流水线即代码

https://insights.thoughtworks…
[6] 解读基础设施即代码

https://insights.thoughtworks…
[7] Ansible 权威指南

https://ansible-tran.readthed…\_intro.html
[8] How to Create a Jenkins Shared Library

https//www.tutorialworks.com/jenkins-shared-library/
[9] Migrating from Jenkins to GitHub Actions

https://docs.github.com/cn/ac…
[10] Compare and contrast GitHub Actions and Azre Pipelines

https://docs.microsoft.com/en…

举荐浏览:

Go 语言应用 MySQL 的常见故障剖析和应答办法

百度交易中台之钱包零碎架构浅析

基于宽表的数据建模利用

百度评论中台的设计与摸索

基于模板配置的数据可视化平台

如何正确的评测视频画质

小程序启动性能优化实际

咱们是如何穿过低代码“⽆⼈区”的:amis 与爱速搭中的要害设计

退出移动版