目录
2、Library工作模式
3、Library代码结构
二、提前准备工作
1、jenkins添加harbor登录凭证
2、jenkins配置library
3、jenkins独立配置robot流水线
4、jenkins配置gitlab
三、涉及功能点实现
1、library整体环境准备
1、jenkinsfile全局配置
2、异常捕获
3、计时器与循环
4、library解析yaml文件
2、library集成镜像构建及推送
1、jenkinsfile调用片段
2、docker login实现
3、build or push完成推送消息
3、library集成k8s服务部署
四、源码
1、原出处
2、简化版
由于公司内部项目众多,大量的项目使用同一套流程做CICD
那么势必会存在大量的重复代码
一旦某个公共的地方需要做调整,每个项目都需要修改
主要通过使用groovy实现Jenkins的sharedLibrary的开发,以提取项目在CICD实践过程中的公共逻辑,提供一系列的流程的接口供公司内各项目调用。
开发完成后,对项目进行Jenkinsfile的改造,最后仅需通过简单的Jenkinsfile的配置,即可优雅的完成CICD流程的整个过程,此方式已在大型企业内部落地应用。
由于流水线被组织中越来越多的项目所采用,常见的模式很可能会出现。 在多个项目之间共享流水线有助于减少冗余并保持代码 "DRY"。
流水线支持引用 "共享库" ,可以在外部源代码控制仓库中定义并加载到现有的流水线中。
@Library('my-shared-library') _
在实际运行过程中,会把library中定义的groovy功能添加到构建目录中:
/var/jenkins_home/jobs/test-maven-build/branches/feature-CDN-2904.cm507o/builds/2/libs/my-shared-library/vars/devops.groovy
共享库的目录结构如下:
(root) +- src # Groovy source files | +- org | +- foo | +- Bar.groovy # for org.foo.Bar class +- vars | +- foo.groovy # for global 'foo' variable | +- foo.txt # help for 'foo' variable
src
目录应该看起来像标准的 Java 源目录结构。当执行流水线时,该目录被添加到类路径下。
vars
目录定义可从流水线访问的全局变量的脚本。 每个 *.groovy
文件的基名应该是一个 Groovy (~ Java) 标识符, 通常是 camelCased
。
无论是传统流水线还是library构建,登陆或者钉钉token等私密数据都建议采用全局凭证方式调用,类似k8s中的secret资源对象。
[系统管理] -> [系统设置] -> [ **Global Pipeline Libraries** ],配置jenkins调用library,library可以看作是gitlab上面的一套独立代码类。
robot集成测试一般用于测试流水线,不适合放在项目中,我们将测试用户单独创建一个流水线,library直接调用即可
@Library('rui-devops') _ #jenkins调用library
pipeline {
agent { label 'jnlp-slave'} #k8s动态生成jenkins-slave
options {
timeout(time: 20, unit: 'MINUTES') #循环检测超时
gitLabConnection('gitlab') #调用gitlab
}
environment {
IMAGE_REPO = "192.168.0.121:5000/myblog/myblog" #镜像地址
IMAGE_CREDENTIAL = "credential-registry" #用户密码模式全局变量,harbor登录
DINGTALK_CREDS = credentials('dingTalk') #token模式全局变量,钉钉通知token
PROJECT = "myblog"
}
def exceptionDemo(){
try {
def val = 10 / 0
println(val)
}catch(Exception e) {
println(e.toString())
throw e
}
}
exceptionDemo()
主要用于集群更新容器版本后,循环检测pog启动状态是否为running
import groovy.time.TimeCategory
use( TimeCategory ) {
def endTime = TimeCategory.plus(new Date(), TimeCategory.getSeconds(15))
def counter = 0
while(true) {
println(counter++)
sleep(1000)
if (new Date() >= endTime) {
println("done")
break
}
}
}
library代码中需要获取容器yaml文件中的变量,例如获取namespace、label、副本数等,可以导入三方类org.yaml.snakeyaml.Yaml实现
import org.yaml.snakeyaml.Yaml
def readYaml(){
def content = new File('myblog.yaml').text
Yaml parser = new Yaml()
def data = parser.load(content)
def kind = data["kind"]
def name = data["metadata"]["name"]
println(kind)
println(name)
}
readYaml()
stage('docker-image') {
steps {
container('tools') {
script{
devops.docker(
"${IMAGE_REPO}",
"${GIT_COMMIT}",
IMAGE_CREDENTIAL
).build().push()
}
}
}
//groovy获取jenkins全局凭证ID,并将对应的账号密码赋值给自定义变量
withCredentials([usernamePassword(credentialsId: this.credentialsId, usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')])
sh "docker login ${regs} -u $USERNAME -p $PASSWORD"
sh "docker push ${this.fullAddress}"
//把当前推送的镜像地址记录在环境变量中
env.CURRENT_IMAGE = this.fullAddress
isSuccess = true
// check if build success
def stage = env.STAGE_NAME + '-push'
if(isSuccess){
updateGitlabCommitStatus(name: "${stage}", state: 'success')
this.msg.updateBuildMessage(env.BUILD_TASKS, "${stage} OK... √")
检查myblog应用的pod是否部署正常,人工检查的大致步骤:
kubectl -n luffy get pod
,查看pod列表找到列表中带有myblog关键字的running的pod
查看上述running pod数,是否和myblog的deployment中定义的replicas副本数一致
若一致,则检查结束,若不一致,可能稍等几秒钟,再次执行相同的检查操作
如果5分钟了还没有检查通过,则大概率是pod有问题,通过查看日志进一步排查
如何通过library代码实现上述过程:
library如何获取myblog的pod列表?
首先要知道本次部署的是哪个workload,因此需要调用者传递workload的yaml文件路径
library解析workload.yaml文件,找到如下值:
pod所在的namespace
pod中使用的
labels
标签
使用如下命令查找该workload关联的pod
$ kubectl -nget po -l -l # 如查找myblog的pod $ kubectl -n luffy get po -l app=myblog
如何确定步骤1中的pod的状态?
# 或者可以直接进行提取状态 $ kubectl -n luffy get po -l app=myblog -ojsonpath='{.items[0].status.phase}' # 以json数组的形式存储 $ kubectl -n luffy get po -l app=myblog -o json
如何检测所有的副本数都是正常的?
# 以json数组的形式存储 $ kubectl -n luffy get po -l app=myblog -o json # 遍历数组,检测每一个pod查看是否均正常(terminating和evicted除外)
如何实现在5分钟的时间内,若pod状态符合预期,则退出检测循环,若不符合预期则继续检测
use( TimeCategory ) {
def endTime = TimeCategory.plus(new Date(), TimeCategory.getMinutes(timeoutMinutes,5))
while (true) {
if (new Date() >= endTime) {
//超时了,则宣告pod状态不对
updateGitlabCommitStatus(name: 'deploy', state: 'failed')
throw new Exception("deployment timed out...")
}
//循环检测当前deployment下的pod的状态
try {
if (this.isDeploymentReady()) {
readyCount++
if(readyCount > 5){
updateGitlabCommitStatus(name: 'deploy', state: 'success')
break;
}
}else {
readyCount = 0
}catch (Exception exc){
echo exc.toString()
}
//每次检测若不满足所有pod均正常,则sleep 5秒钟后继续检测
sleep(5)
}
}
原作者源码,有能力者可以直接git clone学习
YongxinLi/jenkins-shared-library
自己练习时简化的代码,基本功能实现,主要去除了多项目逻辑判断,sonarqube代码扫描、通知方式判断等,添加了一些注释。
配套源码:
CSDNhttps://mp.csdn.net/mp_download/manage/download/UpDetailed
配套镜像:
王宏瑞 (wanghongruihaha) - Gitee.comhttps://gitee.com/wanghongruihaha
docker pull registry.cn-hangzhou.aliyuncs.com/devlop01/myblog:v1
docker pull registry.cn-hangzhou.aliyuncs.com/devlop01/mysql:v1
docker pull registry.cn-hangzhou.aliyuncs.com/devlop01/tools:v1