写点什么

有赞持续集成容器化实践

  • 2020-03-15
  • 本文字数:3773 字

    阅读完需:约 12 分钟

有赞持续集成容器化实践

一、背景介绍

目前我厂 Jenkins CI 采用的是 Master-Slave 架构, Master 和 Slave 都是物理机搭建。主要用于跑单测,集成测试等。由于早期没有专人来管理 Jenkins ,随着业务的发展 Jenkins Job 越来越多,也带来了如下问题:


  1. 当 Job 越来越多时需要通过增加 Slave 机器来解决,新增 Slave 上的软件得重新安装。

  2. 资源分配不均衡有浪费,有的 Slave 上运行的 Job 出现排队等待,而有的 Slave 处于空闲状态。并且当 Slave 处于空闲状态时,也不会完全释放掉资源。

  3. 每个 Slave 总有点差异维护起来比较麻烦。

  4. 当 Master 有故障时,整个流程都不可用。

二、整体方案设计

为了解决以上问题,减少 Jenkins 维护成本降低机器成本等。我们决定采用现下比较流行的 kubernetes Jenkins CI/CD 技术,将 Jenkins Master 和 Slave 交给 k8s 动态调度。下图是基于 K8s 搭建 Jenkins 集群的简单示意图:



从上图中可以看到 Jenkins Master 和 Jenkins Slave 以 Pod 形式运行在 K8s 集群的 Node 上,Master 运行在其中一个节点,Slave 运行在各个节点上,Slave 的运行将按照需求去动态创建。


工作流程:当调用 Jenkins Master API 发起构建请求时,Jenkins k8s plugin 会根据 Job 配置的 Label 动态创建一个运行在 Pod 中的 Jenkins Slave 并注册到 Master 上,当 Job 结束后,这个 Slave 会被注销并且这个 Pod 也会自动删除,恢复到最初状态,这样集群资源得到充分的利用。


使用容器化和 K8s 动态创建 Slave 优势:


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

  • 动态伸缩合理使用资源,每次构建 Job 时,会根据配置自动创建一个 Jenkins Slave,Job 完成后,Slave 自动注销并删除容器,资源自动释放,而且 K8s 会根据每个资源的使用情况,动态分配 Slave 到空闲的节点上创建,降低出现因某节点资源利用率高,还在该节点排队等待的情况。

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

三、部署 Jenkins Master、Sonarqube

3.1 Jenkins Master 部署

由于我们采用 K8s 集群部署,首先得制作 Jenkins Master 镜像。当然也可以使用 Jenkins 官网的上镜像 jenkins/jenkins:lts,因为我们有一些需求,所以需要自己制作。下面是制作镜像中个人认为需要注意的地方:


需要 EXPOSE 2 个端口,Jenkins Web 访问端口和 JNLP 代理协议的 TCP 端口( jnlp-slave 连接 Master 使用的端口)。


JNLP 代理协议的 TCP 端口: 由于 Jenkins-Master 是在容器中启动的,所以一定要将这个端口暴露到外部,不然 Jenkins-Master 不知道 Slave 是否已经启动,会反复去创建 Pod 直到超过重试次数。


Jenkins Master 若要动态创建 Slave 需要安装配置 Kubernetes Plugin,这里可以参考 K8S 在有赞 PaaS 测试环境中的实践 里面有介绍,或在网上找资料。

3.2 Sonarqube 部署

CI/CD 中 Sonarqube 也是必不可少的,用于代码质量管理等。由于 Sonarqube 有一些规则等配置需要在启动时加载好,所以需要重新制作镜像。这里镜像制作分为 2 部分:


  • 第一部分:Mysql 镜像制作,包含 Sonar 数据库和 Sonar 用户创建,导入 Sonarqube 初始化数据,启动 Mysql。

  • 第二部分:基于上面的 Mysql 镜像再制作 Sonarqube 镜像。


这里我们是把 Mysql 和 Sonarqube 集成在一个镜像里,当然也可以分开。下面是制作 Mysql 镜像的部分 Dockerfile:


FROM mysql:5.7#设置免密登录ENV MYSQL_ALLOW_EMPTY_PASSWORD yes
#将所需文件放到容器中COPY mysqld.cnf /etc/mysql/mysql.conf.d/mysqld.cnfCOPY setup.sh /mysql/setup.shCOPY privileges.sql /mysql/privileges.sqlCOPY sonar.sql /mysql/sonar.sqlCOPY init_sonar.sql /mysql/init_sonar.sqlCOPY run-entrypoint.sh /mysql/run-entrypoint.shCOPY start.sh /mysql/start.sh........#设置容器启动时执行的命令ENTRYPOINT ["/mysql/run-entrypoint.sh", "/mysql/setup.sh"]
复制代码


  • privileges.sql 创建 sonar 数据库,配置 SonarQube Server 访问数据库用户的权限。

  • sonar.sql sonarqube 初始化配置的数据库表和数据。

  • init_sonar.sql 将 sonar 数据库表和数据导入 sonar 数据库。

  • start.sh 启动 mysql 并执行以上 sql 文本。

3.3 Jenkins Slave 制作

Jenkins Java Slave 我们参考的官网制作并添加了一些我们自己包(官方提供的 jenkins/ssh-slave,官方文档中有说明,这个镜像安装了 JDK 和 sshd,有兴趣的同学也可以自己制作),其中 Nodejs 、Python Slave 制作和 Java Slave 类似,网上也有资料这里就不详细介绍了。


制作完的镜像需推送到镜像仓库中保存, 下面是构建和推送镜像的命令:


docker build -t [IMAGE:TAG] . docker tag SOURCE_IMAGE[:TAG] harbor.xxx.com/xxx/IMAGE[:TAG]docker push harbor.xxx.com/xxx/IMAGE[:TAG]
复制代码

四、系统集成


如上图所示,有需求的同学可以在有赞 QA 平台发起创建业务线容器,后台会调用 k8s api 创建 Jenkins 、 Sonarqube 容器,并返回访问地址。如下图:





这里我们使用的 k8s 客户端是 fabric8io/kubernetes-client 项目,需要在项目的 pom 文件中加入 kubernetes-client 依赖:


    <dependency>      <groupId>io.fabric8</groupId>      <artifactId>kubernetes-client</artifactId>      <version>4.1.0</version>    </dependency>
复制代码

4.1 Create Deployment

Deployment 为 Pod 和 Replica Set(下一代 Replication Controller)提供声明式更新。只需要在 Deployment 中描述你想要的目标状态是什么,Deployment controller 就会帮你将 Pod 和 ReplicaSet 的实际状态改变到您的目标状态。

4.2 Create Service

Service 通过 Label Selector 跟服务中的 Pod 绑定,为 Pod 中的服务类应用提供了一个稳定的访问入口。通过使用 Service,我们就可以不用关心这个服务下面的 Pod 的增加和减少、故障重启等,只需通过 Service 就能够访问到对应服务的容器。

4.3 Create Ingress

Service 虽然可以 LB, NodePort 对外提供服务,但是当集群服务很多的时候,NodePort 方式最大的缺点是会占用很多集群机器的端口,LB 方式最大的缺点则是每个 Service 一个 LB 又有点浪费和麻烦,并且需要 K8s 之外的支持, 而 Ingress 则只需要一个 NodePort 或者一个 LB 就可以满足所有 service 对外服务的需求。注意点:


  • K8s 集群中,将图片或是文件上传到文件服务器上,如文件大于 1M 会报错所以 Ingress 的 Annotations 需要配置下"nginx.ingress.kubernetes.io/proxy-body-size", “600m”

  • 由于 Jenkins Master 容器起来 K8s 插件配置信息都需要初始化好,就需要知道起来后 Pod Node Ip,这里可以通过 Env 来获取 Pod Node Ip


   env:    - name: MY_POD_IP     valueFrom:      fieldRef:       fieldPath: status.podIP    status.podIP :pod IP
复制代码


  • 前端访问 Jenkins Master 时会存在跨域问题,在 Ingress 中,跨域(CORS)的配置如下:


  nginx.ingress.kubernetes.io/cors-allow-headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization  nginx.ingress.kubernetes.io/cors-allow-methods: PUT, GET, POST, OPTIONS  nginx.ingress.kubernetes.io/cors-allow-origin: '*'  nginx.ingress.kubernetes.io/enable-cors: "true"  nginx.ingress.kubernetes.io/proxy-body-size: 600m
复制代码

五、K8s Web Terminal

当需要进入容器内执行一些 shell 命令时,web terminal 可以让我们更方便的访问 container,执行 shell 命令,提高工作效率。如下图:



实现:



前端用 xterm.js 库,它是模拟一个 terminal 在浏览器中,此时并没有通讯能力。需要在后端搭建 k8s-websocket 服务。前端建立 websocket,连到后台搭建的 k8s-websocket 服务端。服务端会基于 k8s 的 remotecommand 包,建立与 container 的 ssh 长连接。我们将输入输出写入到 Websocket 流中即可,当浏览器中 terminal 大小改变了,前端应该把最新的 terminal 大小发给服务端,服务端模拟终端也要相应的 resize。


遇到的问题:


由于我们使用的 kubernetes-client 当时只提供了 pod 启动时,初始化 terminal 大小的功能,未实现 resize 功能。当浏览器中的 terminal 的大小改变时,由于与初始化时传递的行列数不同,导致显示不全或显示区域过小的问题。在查阅资料的过程中发现 k8s 的 remotecommand 实际上是提供了该功能的(详情可见 remotecommand.go)。此文件中,定义 resizeChannel 为 4,即将发送的命令 byte 数组的第一位修改为 4,就可发送 resize 的相关命令。

后记

测试的时候发现 K8s Slave 调度速度比较慢,尤其是多个同类型的 Slave 并行需要等待比较长的时间,上网查询发下默认情况下 Jenkins 保守地生成代理。如果队列中有 2 个构建,不会立即生成 2 个执行程序。会产生一个执行器并等待一段时间看第一个执行器有没有被释放,然后再决定产生第二个执行器。以确保产生的每个执行者都得到最大限度的利用。如果要覆盖此行为并立即为队列中的每个构建生成执行程序,可以在 Jenkins Mater 启动时参加一下参数:


  • Dhudson.slaves.NodeProvisioner.initialDelay=0

  • Dhudson.slaves.NodeProvisioner.MARGIN=50

  • Dhudson.slaves.NodeProvisioner.MARGIN0=0.85


总而言之 K8s 博大精深,在 CI/CD 容器化的道路上还有很多知识点需要去学习。


2020-03-15 20:20782

评论

发布
暂无评论
发现更多内容

在Mac上,按Command-X键无法剪切怎么办?

mac大玩家j

Mac软件教程 好用的软件分享

万界星空科技智能制造平台的工时管理系统

万界星空科技

MES系统 mes 万界星空科技 工时管理系统

【保姆级教程】PAI x EasyPhoto,节日氛围AI写真生成

阿里云大数据AI技术

克魔助手:方便查看iPhone应用实时日志和奔溃日志工具

群星Stellaris mac(策略游戏)v3.10.1完美激活版

mac

群星stellaris 苹果mac Windows软件 科幻策略类游戏

记录一次RPC服务有损上线的分析过程

京东科技开发者

天猫商品详情数据接口(Tmall.item_get)

tbapi

天猫数据接口 天猫商品详情数据接口 天猫商品API接口 天猫商品数据接口 天猫API

如何利用香港VPS免备案加速你的数字生活

一只扑棱蛾子

香港VPS

AI在NFT发展中的力量发挥:2024年AI将如何主导NFT场景

区块链软件开发推广运营

dapp开发 区块链开发 链游开发 NFT开发 公链开发

华为天气滑雪卡片预测滑雪好时机,轻松GET雪场信息

新消费日报

锁定1.17|PolarDB开发者大会,我们将聊些什么?

阿里云瑶池数据库

数据库 阿里云 云原生 开发者大会

语音数据集:推动智能语音技术发展的关键驱动力

来自四九城儿

The Next Generation of Apache Flink

Apache Flink

大数据 flink 实时计算

openEuler为操作系统“铸魂”,中国联通做了什么?CULinux了解下

彭飞

我与算法有个约会——精心设计的偶遇

统信软件

算法

全球校园人工智能算法精英大赛-AIOT应用赛项圆满落幕

AIRIOT

人工智能 IoT AIOT

万界星空科技MES系统中的工时管理

万界星空科技

MES系统 mes 工时管理 万界星空科技 工时管理系统

淘宝API与大数据技术在电商行业的应用研究

技术冰糖葫芦

API

淘宝API和天猫SKU API在推动电子商务创新中的作用研究

技术冰糖葫芦

API

OpenLAM | 深度势能预训练大模型DPA-2发布

新消费日报

作为一个后端必须要了解的事情

伤感汤姆布利柏

后端

软件测试/测试开发|作为测试工程师,必须掌握的CSS知识

霍格沃兹测试开发学社

MES系统中的标准工时管理

万界星空科技

低代码 低代码开发 低代码平台 mes 低代码云MES

云手机:多开群控全天在线,提高效率的最佳之选

Ogcloud

弹性云手机 手机云服务

天猫商品评论数据接口(Tmall.item_review)

tbapi

天猫API接口 天猫商品评论接口 天猫评论API接口 天猫商品评价接口 天猫商品评论数据接口

实时获取小红书笔记详情的API使用与解析

Noah

语音数据集:探索、挑战与应用

来自四九城儿

MySQL常用图形管理工具

小魏写代码

云手机引领社交平台运营新潮流

Ogcloud

跨境电商 社交媒体 社媒

有赞持续集成容器化实践_文化 & 方法_余霞_InfoQ精选文章