這里記錄一些當前使用的pipeline模板和郵件模板
Java項目模板
// java項目
// 需要解析http返回結果時使用
import groovy.json.JsonSlurperClassic
import groovy.json.JsonOutput
// 按需求更改變量的值
node {
// 參數(shù)設置
def repoUrl = 'git@*****.git'
def repoBranch = 'dev'
def gitDir = ''
def workspace = ''
// docker鏡像信息
def imageName = "*****/demo"
def imageTag = "dev"
// rancher1.X部署所需變量
def rancher1Url = 'https://<rancher1_service>/v2-beta' // rancher1.X地址
def rancher1Env = '' // rancher1.X需部署服務所在環(huán)境的ID
def rancher1Service = '<stack>/<service>' // rancher1.X需部署服務的 棧/服務名
// rancher2.X部署所需變量
def rancher2Url = "https://<rancher2_service>/v3/project/<cluster_id>:<project_id>" // rancher2.X地址+project
def rancher2Namespace = "" // rancher2.X需部署服務的命名空間
def rancher2Service = "bookkeeper" // rancher2.X需部署服務的服務名
def recipients = '' // 收件人
def jenkinsHost = '' // jenkins服務器地址
// 認證ID
def gitCredentialsId = 'aa651463-c335-4488-8ff0-b82b87e11c59'
def settingsConfigId = '3ae4512e-8380-4044-9039-2b60631210fe'
def rancherAPIKey = 'd41150be-4032-4a53-be12-3024c6eb4204'
def soundsId = '69c344f1-8b11-47a1-a3b6-dfa423b94d78'
// 工具配置
def mvnHome = 'maven3.5.2'
env.SONAR_HOME = "${tool 'sonarscanner'}"
env.PATH="${env.SONAR_HOME}/bin:${env.PATH}"
try {
// 正常構建流程
stage("Preparation"){
// 拉取需要構建的代碼
git_maps = checkout scm: [$class: 'GitSCM', branches: [[name: "*/${repoBranch}"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: "${gitDir}"]], userRemoteConfigs: [[credentialsId: "${gitCredentialsId}", url: "${repoUrl}"]]]
env.GIT_REVISION = git_maps.GIT_COMMIT[0..8]
}
try {
stage('BackEndBuild') {
// maven構建生成二進制包
dir("${workspace}"){
withMaven(
maven: "${mvnHome}",
mavenSettingsConfig: "${settingsConfigId}",
options: [artifactsPublisher(disabled: true)]) {
sh "mvn -U clean package -Dmaven.test.skip=true"
}
}
}
}finally {
stage('ArchiveJar') {
// 獲取二進制包產物
archiveArtifacts allowEmptyArchive: true, artifacts: "${workspace}target/surefire-reports/TEST-*.xml"
archiveArtifacts allowEmptyArchive: true, artifacts: "${workspace}target/*.jar"
}
}
stage('CodeCheck'){
// sonar_scanner進行代碼靜態(tài)檢查
withSonarQubeEnv('sonar') {
sh """
sonar-scanner -X -Dsonar.language=java \
-Dsonar.projectKey=$JOB_NAME \
-Dsonar.projectName=$JOB_NAME \
-Dsonar.projectVersion=$GIT_REVISION \
-Dsonar.sources=src/ \
-Dsonar.sourceEncoding=UTF-8 \
-Dsonar.java.binaries=target/ \
-Dsonar.exclusions=src/test/**
"""
}
}
stage("QualityGate") {
// 獲取sonar檢查結果
timeout(time: 1, unit: "HOURS") {
def qg = waitForQualityGate()
if (qg.status != 'OK') {
error "Pipeline aborted due to quality gate failure: ${qg.status}"
}
}
}
stage('DockerBuild'){
// docker生成鏡像并push到遠程倉庫中
sh """
rm -f ${workspace}src/docker/*.jar
cp ${workspace}target/*.jar ${workspace}src/docker/
"""
dir("${workspace}src/docker/"){
def image = docker.build("${imageName}:${imageTag}")
image.push()
sh "docker rmi ${imageName}:${imageTag}"
}
}
stage('Rancher1Deploy'){
// Rancher1.X上進行服務的更新部署
rancher confirm: false, credentialId: "${rancherAPIKey}", endpoint: "${rancher1Url}", environmentId: "${rancher1Env}", environments: '', image: "${imageName}:${imageTag}", ports: '', service: "${rancher1Service}"
}
stage('Rancher2Deploy') {
// Rancher2.X上更新容器
// 獲取服務信息
def response = httpRequest acceptType: 'APPLICATION_JSON', authentication: "${rancherAPIKey}", contentType: 'APPLICATION_JSON', httpMode: 'GET', responseHandle: 'LEAVE_OPEN', timeout: 10, url: "${rancher2Url}/workloads/deployment:${rancher2Namespace}:${rancher2Service}"
def serviceInfo = new JsonSlurperClassic().parseText(response.content)
response.close()
def dockerImage = imageName+":"+imageTag
if (dockerImage.equals(serviceInfo.containers[0].image)) {
// 如果鏡像名未改變族展,直接刪除原容器
// 查詢容器名稱
response = httpRequest acceptType: 'APPLICATION_JSON', authentication: "${rancherAPIKey}", contentType: 'APPLICATION_JSON', httpMode: 'GET', responseHandle: 'LEAVE_OPEN', timeout: 10, url: "${rancher2Url}/pods/?workloadId=deployment:${rancher2Namespace}:${rancher2Service}"
def podsInfo = new JsonSlurperClassic().parseText(response.content)
def containerName = podsInfo.data[0].name
response.close()
// 刪除容器
httpRequest acceptType: 'APPLICATION_JSON', authentication: "${rancherAPIKey}", contentType: 'APPLICATION_JSON', httpMode: 'DELETE', responseHandle: 'NONE', timeout: 10, url: "${rancher2Url}/pods/${rancher2Namespace}:${containerName}"
} else {
// 如果鏡像名改變袭灯,使用新鏡像名更新服務
serviceInfo.containers[0].image = dockerImage
def updateJson = new JsonOutput().toJson(serviceInfo)
httpRequest acceptType: 'APPLICATION_JSON', authentication: "${rancherAPIKey}", contentType: 'APPLICATION_JSON', httpMode: 'PUT', requestBody: "${updateJson}", responseHandle: 'NONE', timeout: 10, url: "${rancher2Url}/workloads/deployment:${rancher2Namespace}:${rancher2Service}"
}
}
// 構建成功:聲音提示,郵件發(fā)送
httpRequest authentication:"${soundsId}", url:'http://${jenkinsHost}/sounds/playSound?src=file:///var/jenkins_home/sounds/4579.wav'
emailext attachlog:true, body: '$DEFAULT_CONTENT', subject: '$DEFAULT_SUBJECT', to: "${recipients}"
} catch(err) {
// 構建失敗:聲音提示,郵件發(fā)送
currentBuild.result = 'FAILURE'
httpRequest authentication:"${soundsId}", url:'http://${jenkinsHost}/sounds/playSound?src=file:///var/jenkins_home/sounds/8923.wav'
emailext attachlog:true, body: '$DEFAULT_CONTENT', subject: '$DEFAULT_SUBJECT', to: "${recipients}"
}
}
php+js的項目模板
與java不同的是,我們php+js的服務使用了3個容器贱鼻,php+nginx+code的方式進行部署,每次只需要更新服務中的code容器即可滋将;另外邻悬,為了防止php和js的構建污染jenkins服務,我們利用了官方的node鏡像和一個自定義已安裝好php構建環(huán)境的composer鏡像随闽,并使用docker.image('').inside{}代碼塊將構建過程放置到了docker容器當中父丰。
// php+js項目
import groovy.json.JsonSlurperClassic
import groovy.json.JsonOutput
node {
def repoUrl = 'git@******.git'
def repoBranch = 'develop'
def imageName = '***/code'
def imageTag = 'dev'
def buildEnv = 'develop'
// rancher2.X部署所需變量
def rancherUrl = 'https://<rancher2_service>/v3/project/<cluster_id>:<project_id>'
def rancherNamespace = ''
def rancherService = ''
def recipients = ''
def jenkinsHost = '' // jenkins服務器地址
// 認證ID
def gitCredentialsId = 'aa651463-c335-4488-8ff0-b82b87e11c59'
def settingsConfigId = '3ae4512e-8380-4044-9039-2b60631210fe'
def rancherAPIKey = 'd41150be-4032-4a53-be12-3024c6eb4204'
def soundsId = '69c344f1-8b11-47a1-a3b6-dfa423b94d78'
env.SONAR_HOME = "${tool 'sonarscanner'}"
env.PATH="${env.SONAR_HOME}/bin:${env.PATH}"
try {
stage("prepare") {
// 拉取需要構建的代碼
git_maps = checkout scm: [$class: 'GitSCM', branches: [[name: "*/${repoBranch}"]], doGenerateSubmoduleConfigurations: false, extensions: [], userRemoteConfigs: [[credentialsId: "${gitCredentialsId}", url: "${repoUrl}"]]]
env.GIT_REVISION = git_maps.GIT_COMMIT[0..8]
}
stage('CodeCheck'){
// sonar_scanner進行代碼靜態(tài)檢查
withSonarQubeEnv('sonar') {
sh """
sonar-scanner -X \
-Dsonar.projectKey=$JOB_NAME \
-Dsonar.projectName=$JOB_NAME \
-Dsonar.projectVersion=$GIT_REVISION \
-Dsonar.sourceEncoding=UTF-8 \
-Dsonar.modules=php-module,javascript-module \
-Dphp-module.sonar.projectName=PHP-Module \
-Dphp-module.sonar.language=php \
-Dphp-module.sonar.sources=. \
-Dphp-module.sonar.projectBaseDir=backend/app \
-Djavascript-module.sonar.projectName=JavaScript-Module \
-Djavascript-module.sonar.language=js \
-Djavascript-module.sonar.sources=. \
-Djavascript-module.sonar.projectBaseDir=front/src
"""
}
}
stage("QualityGate") {
// 獲取sonar檢查結果
timeout(time: 1, unit: "HOURS") {
def qg = waitForQualityGate()
if (qg.status != 'OK') {
error "Pipeline aborted due to quality gate failure: ${qg.status}"
}
}
}
stage("frontBuild") {
// 前端構建
docker.image("node").inside() {
sh """
cd front
npm install
npm run build ${buildEnv}
"""
}
}
stage("back-build") {
// 后端構建
docker.image("<docker_registry>/composer:v0").inside() {
sh """
cd backend
composer install
"""
}
}
stage("docker-code-build") {
// 代碼鏡像構建
sh "cp -r front/build docker/code/build"
sh "cp -r backend docker/code/api"
dir("docker/code") {
docker.build("${imageName}:${imageTag}").push()
sh "docker rmi ${imageName}:${imageTag}"
}
}
stage('rancherDeploy') {
// Rancher上更新容器
// 查詢容器名稱
def response = httpRequest acceptType: 'APPLICATION_JSON', authentication: "${rancherAPIKey}", contentType: 'APPLICATION_JSON', httpMode: 'GET', responseHandle: 'LEAVE_OPEN', timeout: 10, url: "${rancherUrl}/pods/?workloadId=deployment:${rancherNamespace}:${rancherService}"
def podsInfo = new JsonSlurperClassic().parseText(response.content)
def containerName = podsInfo.data[0].name
print(containerName)
response.close()
// 刪除容器
httpRequest acceptType: 'APPLICATION_JSON', authentication: "${rancherAPIKey}", contentType: 'APPLICATION_JSON', httpMode: 'DELETE', responseHandle: 'NONE', timeout: 10, url: "${rancherUrl}/pods/${rancherNamespace}:${containerName}"
}
stage("tear-down") {
sh "rm -rf docker"
}
httpRequest authentication:"${soundsId}", url:'http://${jenkinsHost}/sounds/playSound?src=file:///var/jenkins_home/sounds/4579.wav'
emailext attachlog:true, body: '$DEFAULT_CONTENT', subject: '$DEFAULT_SUBJECT', to: "${recipients}"
} catch(err) {
currentBuild.result = 'FAILURE'
httpRequest authentication:"${soundsId}", url:'http://${jenkinsHost}/sounds/playSound?src=file:///var/jenkins_home/sounds/8923.wav'
emailext attachlog:true, body: '$DEFAULT_CONTENT', subject: '$DEFAULT_SUBJECT', to: "${recipients}"
}
}
Android項目模板
這里android項目只是進行了單元測試和構建過程,保證本次構建能夠成功進行
// android項目
// 按需求更改變量的值
node {
// 參數(shù)設置
def repoUrl = 'git@*****.git'
def repoBranch = 'develop'
def gitDir = ''
def workspace = 'Demo/'
def version = ''
def recipients = ''
def jenkinsHost = '' // jenkins服務器地址
// 認證ID
def gitCredentialsId = 'aa651463-c335-4488-8ff0-b82b87e11c59'
def soundsId = '69c344f1-8b11-47a1-a3b6-dfa423b94d78'
// 工具配置
def gradleTool = './gradlew'
// env.GRADLE_HOME = "${tool 'Gradle3.3'}"
// env.PATH="${env.GRADLE_HOME}/bin:${env.PATH}"
try {
// 正常構建流程
stage("Preparation"){
// 拉取需要構建的代碼
checkout scm: [$class: 'GitSCM', branches: [[name: "*/${repoBranch}"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: "${gitDir}"]], userRemoteConfigs: [[credentialsId: "${gitCredentialsId}", url: "${repoUrl}"]]]
}
try {
stage('UnitTest') {
// 在清空前一次構建的結果后進行單元測試掘宪,并獲取測試報告
dir("${workspace}"){
sh "${gradleTool} clean"
sh "${gradleTool} testReleaseUnitTest --stacktrace"
}
}
stage('Build') {
// 使用Gradlew進行sdk的構建
dir("${workspace}"){
sh "${gradleTool} makeJar"
}
}
}
finally {
stage('Results'){
// 獲取單元測試報告
sh """
cd ${workspace}librasdk/build/reports/tests/
tar -zcvf test_reports.tar.gz ./test*/
"""
archiveArtifacts allowEmptyArchive: true, artifacts: "${workspace}librasdk/build/reports/tests/test_reports.tar.gz"
// 獲取sdk構建生成的jar包
archive "${workspace}librasdk/build/libs/*/*.jar"
}
}
httpRequest authentication:"${soundsId}", url:'http://10.38.162.13:8081/sounds/playSound?src=file:///var/jenkins_home/sounds/4579.wav'
emailext attachlog:true, body: '$DEFAULT_CONTENT', subject: '$DEFAULT_SUBJECT', to: "${recipients}"
} catch(err) {
// 構建失敗
currentBuild.result = 'FAILURE'
httpRequest authentication:"${soundsId}", url:'http://${jenkinsHost}/sounds/playSound?src=file:///var/jenkins_home/sounds/8923.wav'
emailext attachlog:true, body: '$DEFAULT_CONTENT', subject: '$DEFAULT_SUBJECT', to: "${recipients}"
}
}
郵件模板
標題
構建通知:${BUILD_STATUS} - ${PROJECT_NAME} - # ${BUILD_NUMBER}!
內容
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次構建日志</title>
</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>
<td>(本郵件是程序自動下發(fā)的蛾扇,請勿回復!)</td>
</tr>
<tr>
<td><h2>
<font color="#0000FF">構建結果 - ${BUILD_STATUS}</font>
</h2></td>
</tr>
<tr>
<td><br />
<b><font color="#0B610B">構建信息</font></b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td>
<ul>
<li>項目名稱 : ${PROJECT_NAME}</li>
<li>構建編號 : 第${BUILD_NUMBER}次構建</li>
<li>觸發(fā)原因: ${CAUSE}</li>
<li>構建日志: <a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
<li>構建 Url : <a href="${BUILD_URL}">${BUILD_URL}</a></li>
<li>項目 Url : <a href="${PROJECT_URL}">${PROJECT_URL}</a></li>
</ul>
</td>
</tr>
<tr>
<td><b><font color="#0B610B">Changes Since Last
Successful Build:</font></b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td>
<ul>
<li>歷史變更記錄 : <a href="${PROJECT_URL}changes">${PROJECT_URL}changes</a></li>
</ul> ${CHANGES_SINCE_LAST_SUCCESS,reverse=true, format="Changes for Build #%n:<br />%c<br />",showPaths=true,changesFormat="<pre>[%a]<br />%m</pre>",pathFormat=" %p"}
<br>
</td>
</tr>
<tr>
<td><b><font color="#0B610B">Failed Test Results</font></b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td><pre
style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">$FAILED_TESTS</pre>
<br></td>
</tr>
<tr>
<td><b><font color="#0B610B">構建日志 (最后 100行):</font></b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td><textarea id="test" cols="80" rows="30" readonly="readonly"
style="font-family: Courier New">${BUILD_LOG, maxLines=100}</textarea>
</td>
</tr>
</table>
</body>
</html>