CI/CD并不是陌生的东西,大部分企业都有自己的CI/CD,不过今天我要介绍的是使用Jenkins和GitOps实现CI/CD。
整体架构如下:
devops.png
涉及的软件以及版本信息如下:
软件 | 版本 |
---|---|
kubernetes | 1.17.9 |
docker | 19.03.13 |
jenkins | 2.249.3 |
argocd | 1.8.0 |
gitlab | 社区版11.8.1 |
sonarqube | 社区版8.5.1 |
traefik | 2.3.3 |
代码仓库 | 阿里云仓库 |
涉及的技术:
- Jenkins shareLibrary
- Jenkins pipeline
- Jenkinsfile
- Argocd
- sonarqube api操作
软件安装
软件安装我这里不贴具体的安装代码了,所有的代码我都放在了github上,地址:https://github.com/cool-ops/kubernetes-software-yaml.git
所以这里默认你已经安装好所以软件了。
在Jenkins上安装如下插件
- kubernetes
- AnsiColor
- HTTP Request
- SonarQube Scanner
- Utility Steps
- Email Extension Template
- Gitlab Hook
- Gitlab
在Jenkins上配置Kubernetes集群信息
在系统管理-->系统配置-->cloud
image.png
在Jenkins上配置邮箱地址
系统设置-->系统配置-->Email
(1)设置管理员邮箱
image.png
配置SMTP服务
image.png
在Gitlab上准备一个测试代码
我这里有一个简单的java测试代码,地址如下:https://gitee.com/jokerbai/springboot-helloworld.git
可以将其导入到自己的gitlab仓库。
在Gitlab上创建一个共享库
首先在gitlab上创建一个共享库,我这里取名叫shareLibrary,如下:
image.png
然后创建src/org/devops目录,并在该目录下创建一下文件。
image.png
它们的内容分别如下:
build.groovy
代码语言:javascript复制package org.devops
// docker容器直接build
def DockerBuild(buildShell){
sh """
${buildShell}
"""
}
sendEmail.groovy
代码语言:javascript复制package org.devops
//定义邮件内容
def SendEmail(status,emailUser){
emailext body: """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0">
<table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
<tr>
本邮件由系统自动发出,无需回复!<br/>
各位同事,大家好,以下为${JOB_NAME}项目构建信息</br>
<td><font color="#CC0000">构建结果 - ${status}</font></td>
</tr>
<tr>
<td><br />
<b><font color="#0B610B">构建信息</font></b>
</td>
</tr>
<tr>
<td>
<ul>
<li>项目名称:${JOB_NAME}</li>
<li>构建编号:${BUILD_ID}</li>
<li>构建状态: ${status} </li>
<li>项目地址:<a href="${BUILD_URL}">${BUILD_URL}</a></li>
<li>构建日志:<a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
</ul>
</td>
</tr>
<tr>
</table>
</body>
</html> """,
subject: "Jenkins-${JOB_NAME}项目构建信息 ",
to: emailUser
}
sonarAPI.groovy
代码语言:javascript复制package ore.devops
// 封装HTTP请求
def HttpReq(requestType,requestUrl,requestBody){
// 定义sonar api接口
def sonarServer = "http://sonar.devops.svc.cluster.local:9000/api"
result = httpRequest authentication: 'sonar-admin-user',
httpMode: requestType,
contentType: "APPLICATION_JSON",
consoleLogResponseBody: true,
ignoreSslErrors: true,
requestBody: requestBody,
url: "${sonarServer}/${requestUrl}"
return result
}
// 获取soanr项目的状态
def GetSonarStatus(projectName){
def apiUrl = "project_branches/list?project=${projectName}"
// 发请求
response = HttpReq("GET",apiUrl,"")
// 对返回的文本做JSON解析
response = readJSON text: """${response.content}"""
// 获取状态值
result = response["branches"][0]["status"]["qualityGateStatus"]
return result
}
// 获取sonar项目,判断项目是否存在
def SearchProject(projectName){
def apiUrl = "projects/search?projects=${projectName}"
// 发请求
response = HttpReq("GET",apiUrl,"")
println "搜索的结果:${response}"
// 对返回的文本做JSON解析
response = readJSON text: """${response.content}"""
// 获取total字段,该字段如果是0则表示项目不存在,否则表示项目存在
result = response["paging"]["total"]
// 对result进行判断
if (result.toString() == "0"){
return "false"
}else{
return "true"
}
}
// 创建sonar项目
def CreateProject(projectName){
def apiUrl = "projects/create?name=${projectName}&project=${projectName}"
// 发请求
response = HttpReq("POST",apiUrl,"")
println(response)
}
// 配置项目质量规则
def ConfigQualityProfiles(projectName,lang,qpname){
def apiUrl = "qualityprofiles/add_project?language=${lang}&project=${projectName}&qualityProfile=${qpname}"
// 发请求
response = HttpReq("POST",apiUrl,"")
println(response)
}
// 获取质量阈ID
def GetQualityGateId(gateName){
def apiUrl = "qualitygates/show?name=${gateName}"
// 发请求
response = HttpReq("GET",apiUrl,"")
// 对返回的文本做JSON解析
response = readJSON text: """${response.content}"""
// 获取total字段,该字段如果是0则表示项目不存在,否则表示项目存在
result = response["id"]
return result
}
// 更新质量阈规则
def ConfigQualityGate(projectKey,gateName){
// 获取质量阈id
gateId = GetQualityGateId(gateName)
apiUrl = "qualitygates/select?projectKey=${projectKey}&gateId=${gateId}"
// 发请求
response = HttpReq("POST",apiUrl,"")
println(response)
}
//获取Sonar质量阈状态
def GetProjectStatus(projectName){
apiUrl = "project_branches/list?project=${projectName}"
response = HttpReq("GET",apiUrl,'')
response = readJSON text: """${response.content}"""
result = response["branches"][0]["status"]["qualityGateStatus"]
//println(response)
return result
}
sonarqube.groovy
代码语言:javascript复制package ore.devops
def SonarScan(projectName,projectDesc,projectPath){
// sonarScanner安装地址
def sonarHome = "/opt/sonar-scanner"
// sonarqube服务端地址
def sonarServer = "http://sonar.devops.svc.cluster.local:9000/"
// 以时间戳为版本
def scanTime = sh returnStdout: true, script: 'date %Y%m%d%H%m%S'
scanTime = scanTime - "n"
sh """
${sonarHome}/bin/sonar-scanner -Dsonar.host.url=${sonarServer}
-Dsonar.projectKey=${projectName}
-Dsonar.projectName=${projectName}
-Dsonar.projectVersion=${scanTime}
-Dsonar.login=admin
-Dsonar.password=admin
-Dsonar.ws.timeout=30
-Dsonar.projectDescription="${projectDesc}"
-Dsonar.links.homepage=http://www.baidu.com
-Dsonar.sources=${projectPath}
-Dsonar.sourceEncoding=UTF-8
-Dsonar.java.binaries=target/classes
-Dsonar.java.test.binaries=target/test-classes
-Dsonar.java.surefire.report=target/surefire-reports -X
echo "${projectName} scan success!"
"""
}
tools.groovy
代码语言:javascript复制package org.devops
//格式化输出
def PrintMes(value,color){
colors = ['red' : "