維|devops|Jenkins Pipeline流程構(gòu)建)
運(yùn)維|devops|Jenkins Pipeline流程構(gòu)建 目錄1. 運(yùn)維|devops|Jenkins Pipeline流程構(gòu)建1.1 Jenkins Pipeline1.1.1 Pipeline script vs Pipeline script from SCM(實(shí)操)1.1.2 Jenkinsfile 腳本內(nèi)容的語法格式如下兩種1.2 前置條件1.2.1 服務(wù)器端開啟ssh登錄1.2.2 Jenkinsfile文件和腳本創(chuàng)建1.3 創(chuàng)建Pipeline任務(wù)1.3.1 構(gòu)建相關(guān)pipeline1.4 執(zhí)行構(gòu)建結(jié)果1.5 優(yōu)化腳本1.5.1 優(yōu)化腳本之后基于 jenkins Version 2.541.3 實(shí)操案例Jenkins Pipeline基于 Groovy 的持續(xù)交付流水線代碼Pipeline as CodePipeline script vs Pipeline script from SCM(實(shí)操)Pipeline script流水線代碼直接粘貼在 Jenkins 任務(wù)頁面文本框內(nèi)存儲(chǔ)在 Jenkins 任務(wù)配置數(shù)據(jù)庫中不關(guān)聯(lián) Git 倉庫Pipeline script from SCM流水線代碼Jenkinsfile存放在代碼倉庫 (Git/SVN)Jenkins 運(yùn)行流水線時(shí)先拉取代碼讀取倉庫內(nèi)的 Jenkinsfile 執(zhí)行Jenkinsfile 腳本內(nèi)容的語法格式如下兩種Declarative Pipeline聲明式-實(shí)操語法嚴(yán)格、易讀適合絕大多數(shù)項(xiàng)目bashpipeline { agent any stages { stage(構(gòu)建) { steps { echo hello } } } }Scripted Pipeline腳本式原生 Groovy自由度高適合復(fù)雜邏輯bashnode { stage(構(gòu)建) { echo hello } }前置條件git、ssh、jdk、maven 等配置服務(wù)器端開啟ssh登錄生成密鑰bashssh-keygen -t rsa -b 4096將公鑰添加到本機(jī)的 authorized_keysbashcat ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys配置 SSH 服務(wù)/etc/ssh/sshd_config)bashPubkeyAuthentication yes PasswordAuthentication yes重啟 SSH 服務(wù)bashsudo systemctl reload sshd將私鑰拷貝到Jenkins全局令牌憑證中Jenkinsfile文件和腳本創(chuàng)建Jenkinsfile位置放入項(xiàng)目pom同級(jí)別位置參照baseCore倉庫根目錄父pom ├─ pom.xml 聚合父工程 ├─ common-core 公共依賴模塊無需Jenkinsfile │ └─ pom.xml ├─ Core.Api 業(yè)務(wù)子模塊 │ ├─ pom.xml │ └─ Jenkinsfile ? 當(dāng)前模塊流水線文件Groovy構(gòu)建腳本(Jenkinsfile)bashpipeline { agent any options { disableConcurrentBuilds() timeout(time: 20, unit: MINUTES) timestamps() buildDiscarder(logRotator(numToKeepStr: 10)) } parameters { booleanParam( name: AUTO_DEPLOY, defaultValue: false, description: 構(gòu)建完成自動(dòng)推送Jar并重啟遠(yuǎn)程服務(wù) ) } environment { SERVICE_DIR app SERVICE_NAME app JAR_VERSION 1.0.0-SNAPSHOT JAR_FILE ${SERVICE_NAME}-${JAR_VERSION}.jar REMOTE_SUB_DIR ${SERVICE_NAME} REMOTE_FULL_DIR /usr/local/${REMOTE_SUB_DIR} LOCAL_JAR_PATH ${SERVICE_DIR}/target/${JAR_FILE} JAVA_HOME /usr/local/jdk21 M2_HOME /usr/local/maven // SSH 配置 REMOTE_HOST 192.168.0.66 REMOTE_USER test SSH_CREDENTIALS_ID test } stages { stage(1. 環(huán)境信息打印) { steps { echo 【構(gòu)建信息】服務(wù)名${env.SERVICE_NAME}Jar包${env.JAR_FILE} echo 本地路徑${env.LOCAL_JAR_PATH} echo 遠(yuǎn)端目錄${env.REMOTE_FULL_DIR} } } stage(2. Maven編譯打包) { steps { sh test -d ${JAVA_HOME} || { echo ? JAVA_HOME目錄不存在; exit 1; } test -d ${M2_HOME} || { echo ? M2_HOME目錄不存在; exit 1; } ${JAVA_HOME}/bin/java -version ${M2_HOME}/bin/mvn -v cd ${SERVICE_DIR} ${M2_HOME}/bin/mvn clean package -DskipTests } } stage(3. 歸檔構(gòu)建產(chǎn)物) { steps { archiveArtifacts artifacts: ${env.LOCAL_JAR_PATH}, fingerprint: true, onlyIfSuccessful: true } } stage(4. 遠(yuǎn)程發(fā)布部署) { when { expression { params.AUTO_DEPLOY } } steps { echo 開始推送Jar包并遠(yuǎn)程重啟服務(wù) withCredentials([sshUserPrivateKey( credentialsId: ${env.SSH_CREDENTIALS_ID}, keyFileVariable: SSH_KEY, usernameVariable: SSH_USER )]) { sh set -e -x # 傳輸 JAR scp -i \$SSH_KEY -o StrictHostKeyCheckingno ${LOCAL_JAR_PATH} ${REMOTE_USER}${REMOTE_HOST}:${REMOTE_FULL_DIR}/${JAR_FILE} # 遠(yuǎn)程執(zhí)行部署【修復(fù)pgrep匹配BUG】 ssh -i \$SSH_KEY -o StrictHostKeyCheckingno ${REMOTE_USER}${REMOTE_HOST} EOF export JAVA_HOME/usr/local/jdk-21.0.11 SERVICE_NAME${SERVICE_NAME} REMOTE_FULL_DIR${REMOTE_FULL_DIR} JAR_FILE${JAR_FILE} mkdir -p \${REMOTE_FULL_DIR} cd \${REMOTE_FULL_DIR} # 根據(jù)服務(wù)名殺死進(jìn)程不再寫死jar名稱 pkill -f \${SERVICE_NAME} || true sleep 1 # 后臺(tái)啟動(dòng)服務(wù) nohup \${JAVA_HOME}/bin/java -jar \${JAR_FILE} app.log 21 sleep 3 # 校驗(yàn)進(jìn)程匹配服務(wù)名稱不再匹配jar短文件名 if pgrep -f \${SERVICE_NAME} /dev/null; then echo ? 服務(wù)啟動(dòng)成功 else echo ? 服務(wù)啟動(dòng)失敗打印最近日志 [ -f app.log ] tail -15 app.log || echo app.log 文件尚未生成 exit 1 fi EOF } echo 遠(yuǎn)程部署流程執(zhí)行完畢 } } } post { always { echo 【Post鉤子】流水線構(gòu)建結(jié)束 } success { cleanWs() echo 流水線全部執(zhí)行成功 } failure { echo 流水線執(zhí)行失敗請(qǐng)查看日志排查問題 } } }創(chuàng)建Pipeline任務(wù)構(gòu)建相關(guān)pipeline腳本指向路徑執(zhí)行構(gòu)建結(jié)果優(yōu)化腳本增加git 打印、和后臺(tái)應(yīng)用日志bashpipeline { agent any options { disableConcurrentBuilds() timeout(time: 20, unit: MINUTES) timestamps() buildDiscarder(logRotator(numToKeepStr: 10)) } parameters { booleanParam( name: AUTO_DEPLOY, defaultValue: false, description: 構(gòu)建完成自動(dòng)推送Jar并重啟遠(yuǎn)程服務(wù) ) } environment { SERVICE_DIR app SERVICE_NAME app JAR_VERSION 1.0.0-SNAPSHOT JAR_FILE ${SERVICE_NAME}-${JAR_VERSION}.jar REMOTE_SUB_DIR ${SERVICE_NAME} REMOTE_FULL_DIR /usr/local/${REMOTE_SUB_DIR} LOCAL_JAR_PATH ${SERVICE_DIR}/target/${JAR_FILE} JAVA_HOME /usr/local/jdk21 M2_HOME /usr/local/maven REMOTE_HOST 192.168.0.66 REMOTE_USER test SSH_CREDENTIALS_ID test } stages { stage(1. 環(huán)境信息 Git提交日志打印) { steps { sh #!/bin/bash echo Git 最新提交信息 echo 短提交號(hào): $(git rev-parse --short HEAD) echo 完整commit: $(git rev-parse HEAD) echo 提交人姓名: $(git log -1 --pretty%an) echo 提交備注: git log -1 --pretty%B echo echo 【構(gòu)建信息】服務(wù)名${env.SERVICE_NAME}Jar包${env.JAR_FILE} echo 本地路徑${env.LOCAL_JAR_PATH} echo 遠(yuǎn)端目錄${env.REMOTE_FULL_DIR} } } stage(2. Maven編譯打包) { steps { sh #!/bin/bash test -d ${JAVA_HOME} || { echo ? JAVA_HOME目錄不存在; exit 1; } test -d ${M2_HOME} || { echo ? M2_HOME目錄不存在; exit 1; } ${JAVA_HOME}/bin/java -version ${M2_HOME}/bin/mvn -v cd ${SERVICE_DIR} ${M2_HOME}/bin/mvn clean package -DskipTests } } stage(3. 歸檔構(gòu)建產(chǎn)物) { steps { archiveArtifacts artifacts: ${env.LOCAL_JAR_PATH}, fingerprint: true, onlyIfSuccessful: true } } stage(4. 遠(yuǎn)程發(fā)布部署) { when { expression { params.AUTO_DEPLOY } } steps { echo 開始推送Jar包并遠(yuǎn)程重啟服務(wù) withCredentials([sshUserPrivateKey( credentialsId: ${env.SSH_CREDENTIALS_ID}, keyFileVariable: SSH_KEY, usernameVariable: SSH_USER )]) { // 使用三雙引號(hào) 允許Groovy解析Jenkins環(huán)境變量 sh #!/bin/bash set -e -x scp -i \$SSH_KEY -o StrictHostKeyCheckingno ${LOCAL_JAR_PATH} ${REMOTE_USER}${REMOTE_HOST}:${REMOTE_FULL_DIR}/${JAR_FILE} ssh -i \$SSH_KEY -o StrictHostKeyCheckingno ${REMOTE_USER}${REMOTE_HOST} EOF SERVICE_NAME${SERVICE_NAME} REMOTE_FULL_DIR${REMOTE_FULL_DIR} JAR_FILE${JAR_FILE} mkdir -p \${REMOTE_FULL_DIR} cd \${REMOTE_FULL_DIR} echo 【1】優(yōu)雅停止舊服務(wù)進(jìn)程 pgrep -f java.*\${SERVICE_NAME} | xargs kill -15 2/dev/null || true sleep 2 pgrep -f java.*\${SERVICE_NAME} | xargs kill -9 2/dev/null || true sleep 1 echo 【2】啟動(dòng)新服務(wù) 追加日志不再清空日志文件 # 遠(yuǎn)端服務(wù)器JAVA路徑【硬編碼】徹底避開變量沖突大坑 nohup /usr/local/jdk-21.0.11/bin/java -Dfile.encodingUTF-8 -jar \${JAR_FILE} app.log 21 sleep 3 echo 遠(yuǎn)端應(yīng)用啟動(dòng)日志尾部30行 [ -f app.log ] tail -30 app.log || echo app.log 文件尚未生成 echo echo 【3】校驗(yàn)進(jìn)程狀態(tài) if pgrep -f java.*\${SERVICE_NAME} /dev/null; then echo ? 服務(wù)進(jìn)程啟動(dòng)成功 else echo ? 服務(wù)啟動(dòng)失敗 exit 1 fi EOF } echo 遠(yuǎn)程部署流程執(zhí)行完畢 } } } post { always { echo 【Post鉤子】流水線構(gòu)建結(jié)束 } success { cleanWs() echo 流水線全部執(zhí)行成功 } failure { echo 流水線執(zhí)行失敗請(qǐng)查看日志排查問題 } } }優(yōu)化腳本之后