运维|devops|Jenkins Pipeline流程构建
基于 jenkins Version 2.541.3 实操案例Jenkins Pipeline
基于 Groovy 的持续交付流水线代码(Pipeline as Code)Pipeline script vs Pipeline script from SCM(实操)
Pipeline script
流水线代码直接粘贴在 Jenkins 任务页面文本框内,存储在 Jenkins 任务配置数据库中,不关联 Git 仓库Pipeline script from SCM
流水线代码(Jenkinsfile)存放在代码仓库 (Git/SVN),Jenkins 运行流水线时先拉取代码,读取仓库内的 Jenkinsfile 执行
Jenkinsfile 脚本内容的语法格式如下两种
Declarative Pipeline(声明式-实操)这套写法,语法更严格,结构也更清晰,读起来一目了然,放到绝大多数项目里都很合适。
pipeline {
agent any
stages {
stage('构建') {
steps {
echo "hello"
}
}
}
}Scripted Pipeline(脚本式):基于原生 Groovy 编写,自由度很高,尤其适合处理复杂逻辑。
node {
stage('构建') {
echo "hello"
}
}
前置条件
git、ssh、jdk、ma ven 等配置服务器端开启ssh登录
生成密钥
ssh-keygen -t rsa -b 4096将公钥添加到本机的 authorized_keys
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys配置 SSH 服务(/etc/ssh/sshd_config)
PubkeyAuthentication yes
PasswordAuthentication yes重启 SSH 服务
sudo systemctl reload sshd- 将私钥拷贝到Jenkins全局令牌凭证中

Jenkinsfile文件和脚本创建
Jenkinsfile位置放入项目pom同级别位置参照
Core(仓库根目录|父pom)
├─ pom.xml 聚合父工程
├─ common-core 公共依赖模块(无需Jenkinsfile)
│ └─ pom.xml
├─ Core.Api 业务子模块
│ ├─ pom.xml
│ └─ Jenkinsfile 当前模块流水线文件Groovy构建脚本(Jenkinsfile)
pipeline {
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}"
JA VA_HOME = '/usr/local/jdk21'
M2_HOME = '/usr/local/ma ven'
// 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. Ma ven编译打包') {
steps {
sh '''
test -d ${JA VA_HOME} || { echo " JA VA_HOME目录不存在"; exit 1; }
test -d ${M2_HOME} || { echo " M2_HOME目录不存在"; exit 1; }
${JA VA_HOME}/bin/ja va -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 StrictHostKeyChecking=no ${LOCAL_JAR_PATH} ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_FULL_DIR}/${JAR_FILE}
# 远程执行部署【修复pgrep匹配BUG】
ssh -i $SSH_KEY -o StrictHostKeyChecking=no ${REMOTE_USER}@${REMOTE_HOST} << 'EOF'
export JA VA_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 ${JA VA_HOME}/bin/ja va -jar ${JAR_FILE} > app.log 2>&1 &
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 打印、和后台应用日志pipeline {
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}"
JA VA_HOME = '/usr/local/jdk21'
M2_HOME = '/usr/local/ma ven'
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. Ma ven编译打包') {
steps {
sh '''#!/bin/bash
test -d ${JA VA_HOME} || { echo " JA VA_HOME目录不存在"; exit 1; }
test -d ${M2_HOME} || { echo " M2_HOME目录不存在"; exit 1; }
${JA VA_HOME}/bin/ja va -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 StrictHostKeyChecking=no ${LOCAL_JAR_PATH} ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_FULL_DIR}/${JAR_FILE}
ssh -i $SSH_KEY -o StrictHostKeyChecking=no ${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 "ja va.*${SERVICE_NAME}" | xargs kill -15 2>/dev/null || true
sleep 2
pgrep -f "ja va.*${SERVICE_NAME}" | xargs kill -9 2>/dev/null || true
sleep 1
echo "【2】启动新服务 >> 追加日志,不再清空日志文件"
# 远端服务器JA VA路径【硬编码】,彻底避开变量冲突大坑
nohup /usr/local/jdk-21.0.11/bin/ja va -Dfile.encoding=UTF-8 -jar ${JAR_FILE} >> app.log 2>&1 &
sleep 3
echo "========== 远端应用启动日志(尾部30行) =========="
[ -f app.log ] && tail -30 app.log || echo "app.log 文件尚未生成"
echo "===================================================="
echo "【3】校验进程状态"
if pgrep -f "ja va.*${SERVICE_NAME}" > /dev/null; then
echo " 服务进程启动成功"
else
echo " 服务启动失败!"
exit 1
fi
EOF
"""
}
echo "远程部署流程执行完毕"
}
}
}
post {
always {
echo "【Post钩子】流水线构建结束"
}
success {
cleanWs()
echo " 流水线全部执行成功!"
}
failure {
echo " 流水线执行失败,请查看日志排查问题!"
}
}
}优化脚本之后

