转载

基于Pipeline的CI/CD在趣头条的应用实践 (编辑中)

概览

主要内容

1. 灵活定义的Pipeline

2. 多集群多环境发布控制

3. 兼容非容器部署

4. 部署结果反馈

5. 易于集成第三方功能

分享摘要

CI/CD 作为业务自动化部署流水线的重要一环,在容器化快速及频繁发布的需求下,迎来了新的挑战。

传统项目中,使用Shell或Pipeline脚本,将编译好的代码上传至服务器并启动就算完成了,而在容器环境中,则涉及到更为复杂的流程。

在今天的趣头条,随着业务需求的快速增长,部署与扩容的需求也越来越多,

为更快的响应业务需求,业务容器化也随之加速。在大量业务接入情况下,如何做到快速接入、自助接入,Jenkins 权限控制等面临更多挑战。

本次分享主要介绍在此业务场景下,服务如何进行容器化的快速接入,

使用基于Pipeline的Jenkins实现流程控制及部署, 并动态的适配多环境与多集群。

正文

流程概览

Shell or Pipeline

在初期,CI 究竟是使用基于Shell的自由风格任务还是基于Pipeline的任务,做个简单对比来看看

| - | Shell | Pipeline |

| - | - | - |

|缺点| 单个任务流程不能并行处理<br>任务无法分解至多个节点执行 |初期有一定学习成本<br>需要从头编写Groovy脚本|

|优点| 上手简单<br>对于运维无门槛<br>有大量Shell脚本可以复用 |任务可以按阶段分解至不同节点执行<br>且可并行处理<br>友好的可视化流程图|

基于以上,最终还是选择了已当前流行的Pipeline为主要任务类型,并据此重新设计了任务流程(图1)。

  • 看图
    基于Pipeline的CI/CD在趣头条的应用实践 (编辑中)
  • 看代码
    #! /usr/bin/env groovy
    def Controller(){
    Init.ExtraSettings()
    node(NODE_STAGE_INIT) {
    stage('Stage 1: Pre-Process') {
    // Fetch data from CMDB
    CMDB.FetchConfig()
    if (APP_LANG in APP_LANG_NO_COMPILE) {
    NODE_STAGE_BUILD = "master"
    } else {
    NODE_STAGE_BUILD = APP_LANG
    }
    }
    }
    node(NODE_STAGE_GIT) {
    stage('Stage 2: Checkout & SonarQube') {
    Git.Controller()
    // SonarScanner
    Init.SonarAnalyzer()
    }
    }
    node(NODE_STAGE_BUILD) {
    stage('Stage 3: Build Project') {
    // Compile
    Compile.Controller()
    }
    }
    if ( BUILD_LEGACY == false ) {
    node(NODE_STAGE_DOCKER) {
    stage('Stage 4: Build Image') {
    // Build & Push Docker image
    Docker.Controller()
    }
    }
    node(NODE_STAGE_K8S) {
    stage('Stage 5: Deploy to k8s') {
    k8s.Controller()
    }
    }
    next_stage_id = 6
    } else {
    // 兼容发布至非容器环境
    node(NODE_STAGE_BUILD) {
    stage('Stage 4: Deploy') {
    Deploy.Legacy()
    }
    }
    next_stage_id = 5
    }
    node(NODE_STAGE_INIT) {
    stage("Stage $next_stage_id: Post-Process") {
    // Report
    Notice.Report()
    // Automatic Test
    }
    }
    }
    return this
  • Code stages.groovy

对每一个关键步骤都设置一个开关,便于调试的同时也更大的增加灵活性。

// Triggers

SKIP_DOCKER = false

SKIP_KUBE = false

SKIP_COMPILE = false

SKIP_GIT = false

SKIP_TEST = false

SKIP_SONAR = true

SKIP_REPORT = false

  • 可以在任务配置中通过定义变量来改变行为。

任务配置

每一个任务都可以通过变量进行定义,可接受的变量及其作用如下:

基于Pipeline的CI/CD在趣头条的应用实践 (编辑中)

基于Pipeline的CI/CD在趣头条的应用实践 (编辑中)

是不是非常复杂?看一眼实际任务配置

基于Pipeline的CI/CD在趣头条的应用实践 (编辑中)

基于Pipeline的CI/CD在趣头条的应用实践 (编辑中)

任务的配置在开始构建时从CMDB获取,但实际工作中总会有一些看似比较合理的需求需要能支持,因此可以在任务配置中定义变量来覆盖CMDB的配置,如下图

基于Pipeline的CI/CD在趣头条的应用实践 (编辑中)

基于Pipeline的CI/CD在趣头条的应用实践 (编辑中)

现在,新建Jenkins任务就非常容易了,模块化的函数配合灵活的变量定义,可以轻松应对大部分需求。

Stage 1: Pre-Process

获取项目配置

项目配置信息直接写在Jenkins 任务配置里简单方便,但项目变多时配置维护就变得复杂了,因此需要集中统一管理配置,方便维护的同时,也能将配置权限下放至项目开发者,配置变更也能更快速。

在每次构建的时候才去获取配置,确保拿到最新配置。

拿到配置后,使用 readJSON 将配置解析为相关变量供后续流程使用。

基于Pipeline的CI/CD在趣头条的应用实践 (编辑中)

  • 对齐就是好看

权限控制

Jenkins 内置有多个权限控制方式,但在多项目+多用户下进行权限控制时,情况就变复杂了,其实也不算特别复杂,就是需要勾太多框框,随着项目与用户增多,难度呈指数级上升。

因此,在设计流程时,就完全抛弃了Jenkins内置的那几套,基于CMDB/gitlab进行权限控制, 一个很简单的思路:

- 如果你没权限访问代码,那么你可能也不能发布我的项目

- 权限检查在任务预处理阶段进行,不通过则直接终止任务

- 简单方便易于实现<del>且又能少只锅</del>

Stage 2: Checkout & SonarQube

Checkout

- #### 初始化

- 项目第一次构建自动进行初始化操作

  • #### 检查

    • 检查 git repo 是否为空
    • 检查是否选择了版本
    • 检查是否需要在指定目录下进行 checkout
    • - 检查发布环境与代码版本规则是否匹配

基于Pipeline的CI/CD在趣头条的应用实践 (编辑中)

  • 最后,输出代码相关信息,方便事后追溯

SonarQube

SonarQube 是一个开源的代码质量管理系统,支持 25+ 种语言,可以通过使用插件机制与 eclipse 和 JIRA 等其他外部工具集成,从而实现了对代码的质量的全面自动化分析和管理。

集成

SonarQube 与Jenkins集成也非常简单,构建时根据当前项目生成配置,再调用 SonarScanner 就行。

权限控制

SoanrQube 中默认情况下用户可以看到所有项目,且可查看源码,这肯定不是我们所期望的,因此也需要进行权限控制,这个思路同上面Jenkins的权限管理一致,依据代码库权限进行控制。

Stage 3: Build Project

这里比较简单,根据项目语言( APP_LANG ), 自动匹配相关节点进行构建

- PHP项目,如检测到项目根目录存在 composer.json 则调用 compoer install

- NodeJS 项目,如未提供编译命令( BUILD_COMMAND ), 则执行默认指令( npm i )

- GoLang 项目,如未提供编译命令( BUILD_COMMAND ), 则执行默认指令( make )

Stage 4: Build Image

终于跟容器有点关系了!

基于Pipeline的CI/CD在趣头条的应用实践 (编辑中)

是不是很简单?

确实很简单

- 查找并模版文件

- 在模版文件基础上加入项目信息,如

- 项目代码

- 为满足稀奇古怪的需求而特设的指令

- Build image

- Push image

基于Pipeline的CI/CD在趣头条的应用实践 (编辑中)

看到这里,也许你会明白为何不用插件非要不自量力的自己实现

就快结束了。

Stage 5: Deploy to kubernetes

部署至 k8s 相对简单多了,替换 deployment.yaml 文件, apply 一下就完了。

不信看这里

基于Pipeline的CI/CD在趣头条的应用实践 (编辑中)

等等,注意 CronJob 函数,难免会有些项目需要利用 cronjob 完成一些定时任务。

需求多的时候,CronJob 的管理相当麻烦为了<del>偷懒</del>减少不必要的麻烦, 我们针对业务需求写了个小函数( CronJob )自动化处理定时任务。

开发者在自己项目中提供一个文件,命名为 cronjob.json , 在项目发布时,Pipeline检测到这个文件即进行处理,文件格式大致如下:

[{"id":0,

"time": "00 01 * * *",

"name": "test-task",

"command": "echo 'hello world'",

"description": "Hello world",

"notify": " user1@abc.com | user2@abc.com ",

"when": "failure"

}]

  • id 为编号,从0开始,必填重要项
  • time 为执行任务时间,标准 Cron 格式
  • name 为计划任务名称,应与项目相关
  • command 为要执行的命令
  • description 为备注信息
  • notify 为接收通知者邮件地址,多个之间加 '|', 企业微信发送
  • when 为发送消息的条件:
    • always
    • never
    • failure
    • - success
  • 为避免任务失败后循环执行, 所有失败的任务都会先发送失败消息然后再以成功状态结束

如此,计划任务管理在 k8s 这部分基本无须人工干预了

原文  http://dockone.io/article/6049
正文到此结束
Loading...