企业级扫描平台EOS-Jenkins集群进阶之路

背景

Jenkins因其强大灵活可扩展的特性,在持续集成系统中一直被广泛使用。EOS—代码质量检测平台,选用Jenkins实现远程任务调度,完成扫描流程。但随着EOS扫描工程类型及服务拓展,资源复用,job数量、构建频率及服务压力激增,问题随之而来。

  • 如果单纯的使用 Master 去构建,除了要承担项目上的编译、扫描分析等开销外,还会影响Jenkins 应用本身占用 memory 和 CPU 资源。

  • Jenkins支持Master-Slave分布式部署,将不同的Job下发到多台机器执行,提高jenkins处理能力,但是无法解决服务分布式部署。

  • 随着Job增多,增加Node Slave,面临重新接入与配置的风险。

  • 不同的业务类型,需要不同的Node配置,维护成本较大。

  • 各业务线单独维护Jenkins,存在资源重复使用的问题。

容器化实践方案

为解决以上问题,我们设计了一套高可用的Jenkins分布式集群搭建方案。整体方案设计图如下所示:

  • 采用Jenkins分布式架构,Slave集群基于Lable管理: Jenkins Master 上管理Job,构建任务分配到不同的 Slave Node 上运行,提升了Master性能,提高构建速度。使用Label管理Slave集群,增强并发能力,提高了Slave 集群综合资源利用率(Disk,CPU)。

  • 基于Kubernetes的容器化实践:基于容器化去构建 Jenkins , 实现按需弹性收缩;资源共享(所有的容器slave可以共享 Kubernetes cluster)。

  • 基于LVS负载均衡:整体方案采用LVS和Centos操作系统实现一个高性能,高可用的服务器群集。

Master-Slave 分布式架构

该模块是增强Jenkins并发能力、缓解构建压力的核心。在介绍jenkins分布式架构之前,先来看下简化版的EOS-Server端扫描主流程:

  • 用户(手动或自动)在server端触发指定项目开始扫描
  • server端调起Jenkins服务
  • clone代码
  • 执行代码扫描脚本,脚本端分析代码(略。。。)
  • 扫描结果返回server

可以清楚的看出,jenkins在整个扫描流程中承担了任务调度的作用。但

  • 随着接入项目的增加,Jenkins需要维护的Job数量,扫描分析压力随之增加。
  • 随着业务类型的拓展,各业务间编译环境不统一,不能跨业务共享。

针对以上问题,我们通过Master-Slave分布式架构,jenkins-Master创建Job,分配Job到指定Slave进行扫描分析。

该设计方案中以下问题值得思考:

1.节点如何连接?

Jenkins Slave 连接方式官网提供两种方式:

  • 通过 SSH 启动 Slave 代理

    在 Jenkins 上直接配置,相当于从 Master 往 Slave 上连接,从 Master 上主动发起的请求。

  • 通过 JNLP 启动代理

    基于 Java Web Start 的 JNLP 的连接,从 Slave 往 Master 主动的方式。

结合后面的容器化方案,我们自己实现一套节点创建及节点连接自动化脚本,支持特殊节点及本地节点创建连接。

  • 创建node

根据Lable自动创建node节点。

  • 连接 master

创建node之后,在Jenkins Slave触发connect_master脚本,使用JNIP方式主动连接Master。

连接完成效果展示:

2.slaves如何提高资源利用率?

为解决业务间,编译环境不统一,不能跨业务共享问题,我们挂载了较多Slave,但这一策略出现了资源利用率低的问题。
为提高资源利用率,我们将同编译环境的 slaves 添加相同 Label,同类型 jobs 使用 Lable 进行构建,适量增大 slave 的 executor 数目。

Kubernetes的容器化实践

Kubernetes 作为一个用来对应用进行自动化部署、扩容和管理的容器编排引擎。将其与Jenkins分布式架构相结合,实现集群高可用,可扩展,有效降低维护及节点扩展成本。

制作 Jenkins Master 镜像。可以使用 Jenkins 官网的上镜像 jenkins/jenkins:lts;

制作Jenkins Slave 镜像,可以使用官方提供的 jenkins/ssh-slave;

我们结合EOS业务实际情况制作了自己的Master和Slave镜像。

镜像制作部署流程:

  1. 编写 BaseDockerfile (基于Centos7安装基础依赖运行环境、安装jenkins环境
  2. FROM basedocker,编写dockerfile
  3. 构建打包 Docker 镜像
  4. 推送 Docker 镜像到仓库
  5. 编写 Kubernetes YAML 文件
  6. 更改 YAML 文件中 Docker 镜像 TAG
  7. 利用jdos部署

集群设计如下:

从上图中可以看到 Jenkins Master 和 Jenkins Slave 以 Pod 形式运行在 K8s 集群的 Node 上。Master 运行在指定节点, 当Server触发构建请求时,会触发Jenkins Master API ,Jenkins k8s plugin 会根据 Job 配置的 Label 动态创建一个运行在 Pod 中的 Jenkins Slave 并注册到 Master 上,当 Job 结束后,这个 Slave 会被注销并且这个 Pod 也会自动删除,恢复到最初状态,这样集群资源得到充分的利用。

LVS负载均衡

监控整个构建流程,发现存在构建时间长、部分Job构建不稳定的问题,其中很大一部分原因是拉取外部依赖耗时且失败率较高。

为了让这个过程更加稳定,采用以下方案:
采用LVS(负载均衡+高可用,采用技术:ipvsadm+keepalived;工作模式为:DR加权轮询,即:DR+wrr);它工作在网络层,实现高性能,高可用的服务器集群,为客户提供服务。

  • LSV(主)调度机提供数据源(即:CFS)供集群中所有Jenkins Server使用;
    (完善不同业务类型所需数据源
    在不同业务类型对的基础镜像中放入优先使用内部源的配置)
  • LVS(从)做热备调度机,同时做主数据源的异地备份(采用技术:Rsync+Inotify)

  • 所有Jenkins Server需要挂载CFS 主提供的数据源工作(采用技术:Linux Mount 挂载CFS)

成效

使用容器化方案以后,收益如下:

  • 服务高可用。当 Jenkins Master 出现故障时, 会自动创建一个新的 Jenkins Master 容器。

  • 资源共享。同一个 job,通过配置 Lable可以运行在不同的 Docker host。同一个Docker host 上配置多个 Docker image,不同的模板供不同的job使用。

  • Slave 资源合理分配。Slave 按需弹性收缩,根据扫描需求去动态的生成一个 Docker 容器挂载为 Jenkins Slave 进行构建,构建结束以后,这个容器就会被自动销毁。整个过程不再是原来那种普通的 Slave 长连接的方式挂载。实现容器Slave生命周期自动化,自动创建、使用、销毁。

  • Slave易扩展。当 K8s 集群的资源严重不足而导致 Job 排队等待时,可以很容易的添加一个 Kubernetes Node 到集群中,从而实现扩展。

本文主要以 EOS 调度中心为例介绍了 Jenkins 集群的容器化历程 ,进阶之路尚未结束,如何使用 Jenkins 创建出高效稳定、灵活的流水线仍需继续探索。