Jenkins简介
Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件项目可以进行持续集成。
部署安装
安装部署步骤此文不再具体讲解,具体的安装步骤可参考官方文档 安装部署
本文中演示所用为一台master节点,一台slave节点,slave节点用于代码拉取,编译打包,镜像构建,服务发布
Java服务CI/CD实现流程详解
工程页面展示
功能说明
- 触发动作包括:构建,发布至测试/预发/生产环境,回滚 共五个操作步骤
- branch:为选取分支(构建阶段此项为必填项)
- commit为代码仓库当前提交的commitid,此处非必填项,只在代码回滚时使用
功能实现详细配置
参数化构建配置
- 动作选项
- 分支选择
- groovy script脚本说明,因为本案例使用的是jenkins "流水线"类型,不能直接使用git插件来获取代码仓库分支,所以针对此类型,需要单独使用groovy脚本实现
- commitid填写(仅供回滚代码使用)
流水线功能代码详解(Pipeline script)
公共部分
import java.util.List
import java.util.ArrayList
import java.net.URLEncoder
#定义多k8s集群的kubectl命令
def kubectl(cmd,namespace="default") {
return sh(script: "kubectl --namespace=${namespace} ${cmd}", returnStdout: true)
}
def kubetest(cmd,configfile=/root/.kube/config-test,namespace="default") {
return sh(script: "kubectl --kubeconfig=${configfile} --namespace=${namespace} ${cmd}", returnStdout: true)
}
def kubepre(cmd,configfile=/root/.kube/config-pre,namespace="default") {
return sh(script: "kubectl --kubeconfig=${configfile} --namespace=${namespace} ${cmd}", returnStdout: true)
}
#在全局变量中获取构建用户ID
def user = env.BUILD_USER_ID
#模板公共参数,这对同类型pipeline创建,只需修改一下两个变量的value即可
git_repo = "https://gitee.com/xxx/.git"
project_name = "project-demo"
#获取pipeline开始时间
starttime = new Date().format('yyyy/MM/dd HH:mm:ss')
#定义消息通知发送方法(此按时使用钉钉机器人)
def SendMsg(status,envs) {
if(envs == "build"){
environment = "编译打包"
}
if(envs == "test"){
environment = "测试环境发布"
}
if(envs == "pre"){
environment = "预生产环境发布"
}
if(envs == "prod"){
environment = "生产环境发布"
}
if(envs == "refuse"){
environment = "无生产环境发布权限,请联系xx同学发布"
}
if(envs == "rollback"){
environment = "生产环境回滚"
}
if(status == "false"){
context = "组件名称: ${JOB_NAME}\\n执行结果: ${environment}失败\\n执行人:$env.BUILD_USER_ID\\n执行时间:$starttime"
}
else if(status == "reject"){
context = "组件名称: ${JOB_NAME}\\n执行结果: ${environment}\\n执行人:$env.BUILD_USER_ID\\n执行时间:$starttime"
}else{
context = "组件名称: ${JOB_NAME}\\n执行结果: ${environment}成功\\n执行人:$env.BUILD_USER_ID\\n执行时间:$starttime"
}
def msg = """{
\"msgtype\": \"text\",\"text\" : {\"content\":\"${context}\"}
}"""
response = httpRequest (consoleLogResponseBody: true,
contentType: 'APPLICATION_JSON_UTF8',
acceptType: 'APPLICATION_JSON_UTF8',
httpMode: 'POST',
requestBody: msg,
url: "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxxxxxxxxx",
validResponseCodes: '200')
return response
}
#定义一个方法来判断分支是否为空
def branch_not_null(){
if( env.branch.length()>0 ){
echo "ok"
}else{
echo "branch不允许为空,请检查参数......"
error "branch不允许为空"
}
}
构建部分
if( 触发动作 == "代码构建"){
stage('建立任务'){
try{
node('slave-be01') {
stage('拉取代码'){
branch_not_null() #判断分支是否为空
dir("${BUILD_ID}"){
checkout ( [$class: 'GitSCM',
branches: [[name: env.branch ]],
userRemoteConfigs: [[
credentialsId: 'xxx-xxx-xxx-xxx-xxxx',
url: git_repo]]])
commitid = sh(returnStdout: true, script: "git --no-pager show -s --format=%H").trim()
ref_branch = sh(returnStdout: true, script: "git branch -r --contains ${commitid}").trim()
echo commitid
sh "echo ${commitid} > /data/jenkins/current_version/${JOB_NAME}.commit" #此处这么做的目的是:commitid作为容器镜像的版本号,写文件的目的就是保证构建和部署是容器镜像版本号的一致性
}
}
stage('编译jar包'){
dir("${BUILD_ID}"){
sh "/opt/gradle-7.6.1/bin/gradle clean"
sh "/opt/gradle-7.6.1/bin/gradle bootJar"
}
}
stage('打包镜像'){
dir("${BUILD_ID}"){
sh "docker build -t ${project_name}:${commitid} ."
}
}
stage('推送镜像'){
dir("${BUILD_ID}"){
sh "docker tag ${project_name}:${commitid} swr.cn-east-3.myhuaweicloud.com/saas-prod/${project_name}:${commitid}"
sh "docker push swr.cn-east-3.myhuaweicloud.com/saas-prod/${project_name}:${commitid}"
}
}
}
SendMsg("SUCCESS","build")
}catch(all){
SendMsg("false","build")
}
}
}
各环境部署(此演示实例,项目均部署在k8s集群里面)
if( 触发动作 == "发布至测试环境" ){
try{
stage('发布至测试环境'){
node('slave-be01'){
dir("${BUILD_ID}"){
commit = sh(script: "cat /data/jenkins/current_version/${JOB_NAME}.commit", returnStdout: true)
echo "${commit}"
created = kubetest("set image deployment/${project_name} ${project_name}=swr.cn-east-3.myhuaweicloud.com/saas-prod/${project_name}:${commit}","/root/.kube/config-test","default").trim()
echo created
}
}
}
SendMsg("SUCCESS","test")
}catch(all){
SendMsg("false","test")
}
}
if( 触发动作 == "发布至预发环境" ){
try{
stage('发布至预发环境'){
node('slave-be01'){
dir("${BUILD_ID}"){
commit = sh(script: "cat /data/jenkins/current_version/${JOB_NAME}.commit", returnStdout: true)
echo "${commit}"
created = kubepre("set image deployment/${project_name} ${project_name}=swr.cn-east-3.myhuaweicloud.com/saas-prod/${project_name}:${commit}","/root/.kube/config-pre","default").trim()
echo created
}
}
}
SendMsg("SUCCESS","pre")
}catch(all){
SendMsg("false","pre")
}
}
if( 触发动作 == "发布至生产环境" ){
if ( "$user" == "zhangsan" || "$user" == "lisi" || "$user" == "admin"){
try{
stage('发布至生产环境'){
node('slave-be01'){
dir("${BUILD_ID}"){
commit = sh(script: "cat /data/jenkins/current_version/${JOB_NAME}.commit", returnStdout: true)
echo "${commit}"
created = kubectl("set image deployment/${project_name} ${project_name}=swr.cn-east-3.myhuaweicloud.com/saas-prod/${project_name}:${commit}","default").trim()
echo created
}
}
}
SendMsg("SUCCESS","prod")
}catch(all){
SendMsg("false","prod")
}
}else{
SendMsg("reject","refuse")
echo "无生产环境发布权限,请联系xx同学发布"
}
}
if( 触发动作 == "回滚" ){ #此处必须填写需要回滚版本的commit,以便在镜像仓库中找到对应的容器镜像
if ( "$user" == "zhangsan" || "$user" == "lisi" || "$user" == "admin"){ #只有指定用户才有权限进行回滚
try{
stage('生产环境代码回滚'){
node('slave-be01'){
dir("${BUILD_ID}"){
created = kubectl("set image deployment/${project_name} ${project_name}=swr.cn-east-3.myhuaweicloud.com/saas-prod/${project_name}:${commit}","default").trim()
echo created
}
}
}
SendMsg("SUCCESS","rollback")
}catch(all){
SendMsg("false","rollback")
}
}else{
SendMsg("reject","refuse")
echo "无回滚权限,请联系xx同学"
}
}