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