在现代软件开发中,"自动化" 是提升效率的核心密码。当你还在手动编译、打包、上传、部署 Java 项目时,高效团队早已通过 CI/CD 工具实现了 "代码提交即发布" 的无缝流程。Jenkins 作为开源 CI/CD 领域的标杆工具,凭借其强大的扩展性和灵活性,成为 Java 团队实现自动化的首选。本文将带你从零开始,一步步搭建 Jenkins 环境,编写 Pipeline 脚本,最终实现 Java 项目从代码提交到自动部署的全流程自动化,让你彻底告别重复的手动操作。
一、为什么需要 Jenkins?------CI/CD 的核心价值
在没有自动化工具的年代,一个 Java 项目的发布流程可能是这样的:
- 开发人员在本地编译代码,解决依赖冲突
- 手动运行单元测试,检查是否通过
- 使用 Maven/Gradle 打包成 jar/war 包
- 通过 FTP/SCP 将包上传到服务器
- 登录服务器,停止旧服务,备份旧版本
- 启动新服务,检查日志确认是否正常
- 发现问题后,重复步骤 3-6 回滚版本
这个过程不仅耗时(中小型项目每次发布至少 15-30 分钟),更重要的是充满人为错误风险------ 可能少传一个配置文件,可能忘记备份旧版本,可能在多台服务器部署时版本不一致。
Jenkins 通过持续集成(CI) 和持续部署(CD) 解决这些问题:
- 持续集成:代码提交后自动触发编译、测试,及时发现集成问题
- 持续部署:测试通过后自动部署到目标环境,减少人工干预
- 流程标准化:将部署流程固化到脚本,确保每次执行一致
- 快速反馈:构建 / 部署结果实时通知,问题早发现早解决
对于 Java 团队,Jenkins 能带来的具体收益包括:
- 开发效率提升:开发者专注编码,无需关注部署细节
- 发布频率提高:从每周一次到每天多次,加速迭代
- 故障恢复更快:一键回滚机制,减少故障影响
- 团队协作更顺:透明的构建流程,便于跨角色协作
二、环境准备 ------ 搭建基础运行环境
工欲善其事,必先利其器。在安装 Jenkins 前,需要准备好基础环境。本文以CentOS 7为例(Windows 环境会标注差异),目标是构建一个能运行 Java 17 项目、连接 MySQL、使用 Git 管理代码的 CI/CD 环境。
2.1 安装 JDK 17
Jenkins 本身基于 Java 开发,且我们的项目需要 JDK 17,因此首先安装 JDK 17:
bash
# 下载JDK 17(注意:实际下载链接需从Oracle官网或OpenJDK镜像获取)
wget https://download.java.net/java/GA/jdk17.0.2/dfd4a8d09854a74b96786e1b17c7d6/13/GPL/openjdk-17.0.2_linux-x64_bin.tar.gz
# 解压到/usr/local目录
tar -zxvf openjdk-17.0.2_linux-x64_bin.tar.gz -C /usr/local/
# 重命名为jdk17(方便后续引用)
mv /usr/local/jdk-17.0.2 /usr/local/jdk17
# 配置环境变量
echo 'export JAVA_HOME=/usr/local/jdk17' >> /etc/profile
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> /etc/profile
echo 'export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar' >> /etc/profile
# 使环境变量生效
source /etc/profile
# 验证安装(输出java version "17.0.2"即为成功)
java -version
Windows 环境:从 Oracle 官网下载 JDK 17 安装包,按向导安装,在 "高级系统设置" 中配置 JAVA_HOME 环境变量,指向安装目录。
2.2 安装 Maven 3.8+
Maven 用于构建 Java 项目,需要与 JDK 17 兼容的版本(推荐 3.8.x 及以上):
bash
# 下载Maven
wget https://archive.apache.org/dist/maven/maven-3/3.8.8/binaries/apache-maven-3.8.8-bin.tar.gz
# 解压到/usr/local
tar -zxvf apache-maven-3.8.8-bin.tar.gz -C /usr/local/
# 配置环境变量
echo 'export MAVEN_HOME=/usr/local/apache-maven-3.8.8' >> /etc/profile
echo 'export PATH=$MAVEN_HOME/bin:$PATH' >> /etc/profile
# 生效配置
source /etc/profile
# 验证(输出Apache Maven 3.8.8即为成功)
mvn -v
配置 Maven 镜像(加速依赖下载):
bash
# 编辑Maven配置文件
vi /usr/local/apache-maven-3.8.8/conf/settings.xml
# 在<mirrors>标签内添加阿里云镜像
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
2.3 安装 Git
用于拉取代码仓库中的项目:
bash
# CentOS安装Git
yum install -y git
# 验证(输出git version 1.8.3.1及以上即可)
git --version
Windows 环境:从 Git 官网下载安装包,勾选 "Add Git to PATH" 选项,方便命令行调用。
2.4 安装 MySQL 8.0
我们的 Java 示例项目会用到 MySQL,因此需要安装数据库(若已有可跳过):
bash
# 安装MySQL源
rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
# 安装MySQL
yum install -y mysql-community-server
# 启动MySQL服务
systemctl start mysqld
# 设置开机自启
systemctl enable mysqld
# 获取初始密码(用于首次登录)
grep 'temporary password' /var/log/mysqld.log
# 登录MySQL(输入上述初始密码)
mysql -u root -p
# 修改密码(MySQL8.0要求强密码)
ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourStrongPassword123!';
# 允许远程连接(开发环境方便测试,生产环境需限制IP)
use mysql;
update user set host = '%' where user = 'root';
flush privileges;
# 创建示例项目数据库
create database jenkins_demo character set utf8mb4 collate utf8mb4_unicode_ci;
三、Jenkins 安装与初始化 ------ 搭建自动化引擎
3.1 安装 Jenkins
推荐使用 war 包方式安装(灵活且版本可控):
bash
# 下载Jenkins(LTS长期支持版,更稳定)
wget https://get.jenkins.io/war-stable/2.401.3/jenkins.war
# 启动Jenkins(--httpPort指定端口,避免与其他服务冲突)
nohup java -jar jenkins.war --httpPort=8080 > jenkins.log 2>&1 &
# 查看启动日志(确认是否成功)
tail -f jenkins.log
启动成功后,日志中会显示初始管理员密码,类似:
Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation: xxxxxxxx
3.2 首次访问与初始化
- 浏览器访问
http://服务器IP:8080
,进入解锁页面 - 输入日志中的初始密码(或通过
cat /root/.jenkins/secrets/initialAdminPassword
获取) - 选择 "安装推荐的插件"(首次使用推荐,后续可按需添加)
- 等待插件安装完成(耗时取决于网络,可跳过暂时失败的插件)
- 创建管理员用户(输入用户名、密码、邮箱)
- 确认 Jenkins URL(保持默认或修改为实际访问地址)
- 完成初始化,进入 Jenkins 首页
3.3 安装必备插件
Jenkins 的强大之处在于插件生态,针对 Java 项目自动化,需要安装以下核心插件:
- 进入 "系统管理" → "插件管理" → "可选插件"
- 搜索并安装以下插件:
- Maven Integration:支持 Maven 项目构建
- Pipeline:核心插件,支持 Pipeline 脚本
- Pipeline Utility Steps:提供 Pipeline 常用工具方法
- Git:支持从 Git 仓库拉取代码
- Publish Over SSH:通过 SSH 部署到远程服务器
- SonarQube Scanner:集成代码质量检查(可选)
- Email Extension:邮件通知(可选)
- 安装完成后重启 Jenkins(
http://IP:8080/restart
)
3.4 全局工具配置
告诉 Jenkins 我们安装的 JDK、Maven、Git 位置:
- 进入 "系统管理" → "全局工具配置"
- JDK 配置 :
- 取消 "安装自动安装"
- 名称:
JDK17
- JAVA_HOME:
/usr/local/jdk17
(Linux)或C:\Program Files\Java\jdk-17
(Windows)
- Maven 配置 :
- 取消 "安装自动安装"
- 名称:
Maven3.8
- MAVEN_HOME:
/usr/local/apache-maven-3.8.8
(Linux)或C:\apache-maven-3.8.8
(Windows)
- Git 配置 :
- 名称:
Git
- Path to Git executable:
/usr/bin/git
(Linux)或C:\Program Files\Git\bin\git.exe
(Windows)
- 名称:
- 点击 "保存"
四、Java 项目准备 ------ 构建可部署的示例应用
为了演示 Jenkins 自动部署,我们需要一个 Java 项目。下面创建一个基于 Spring Boot 3.x 的 Web 应用,包含简单的 REST 接口、数据库操作和单元测试。
4.1 项目结构
plaintext
jenkins-demo/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── JenkinsDemoApplication.java
│ │ │ ├── controller/
│ │ │ │ └── UserController.java
│ │ │ ├── service/
│ │ │ │ ├── UserService.java
│ │ │ │ └── impl/
│ │ │ │ └── UserServiceImpl.java
│ │ │ ├── mapper/
│ │ │ │ └── UserMapper.java
│ │ │ └── entity/
│ │ │ └── User.java
│ │ └── resources/
│ │ ├── application.yml
│ │ └── mybatis/
│ │ └── UserMapper.xml
│ └── test/
│ └── java/
│ └── com/
│ └── example/
│ ├── JenkinsDemoApplicationTests.java
│ └── service/
│ └── UserServiceTest.java
├── pom.xml
└── README.md
4.2 核心代码实现
pom.xml(关键依赖配置):
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.3</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>jenkins-demo</artifactId>
<version>1.0.0</version>
<name>jenkins-demo</name>
<description>Jenkins自动部署示例项目</description>
<properties>
<java.version>17</java.version>
<mybatis.version>3.5.13</mybatis.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok(简化代码,提供@Slf4j) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 测试数据库用 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Spring Boot打包插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<!-- 单元测试插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M9</version>
<configuration>
<!-- 跳过测试的参数(Jenkins中可动态控制) -->
<skipTests>${skipTests}</skipTests>
</configuration>
</plugin>
</plugins>
<!-- 打包后的文件名格式:项目名-版本号.jar -->
<finalName>${project.artifactId}-${project.version}</finalName>
</build>
</project>
实体类 User.java:
java
运行
package com.example.entity;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 用户实体类
*/
@Data
public class User {
private Long id;
private String username;
private String email;
private Integer age;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
Mapper 接口 UserMapper.java:
java
运行
package com.example.mapper;
import com.example.entity.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 用户数据访问接口
*/
@Mapper
public interface UserMapper {
/**
* 查询所有用户
*/
List<User> selectAll();
/**
* 新增用户
*/
int insert(User user);
}
UserMapper.xml(MyBatis 映射文件):
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectAll" resultType="com.example.entity.User">
select id, username, email, age, create_time, update_time from user
</select>
<insert id="insert" parameterType="com.example.entity.User">
insert into user (username, email, age, create_time, update_time)
values (#{username}, #{email}, #{age}, now(), now())
</insert>
</mapper>
服务接口 UserService.java:
java
运行
package com.example.service;
import com.example.entity.User;
import java.util.List;
/**
* 用户服务接口
*/
public interface UserService {
/**
* 获取所有用户
*/
List<User> getAllUsers();
/**
* 创建用户
*/
boolean createUser(User user);
}
服务实现类 UserServiceImpl.java:
java
运行
package com.example.service.impl;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import com.example.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 用户服务实现类
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
@Override
public List<User> getAllUsers() {
log.info("查询所有用户信息");
return userMapper.selectAll();
}
@Override
public boolean createUser(User user) {
log.info("创建新用户: {}", user.getUsername());
int rows = userMapper.insert(user);
return rows > 0;
}
}
控制器 UserController.java:
java
运行
package com.example.controller;
import com.example.entity.User;
import com.example.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 用户接口控制器
*/
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
@Slf4j
public class UserController {
private final UserService userService;
/**
* 获取所有用户
*/
@GetMapping
public List<User> getAllUsers() {
log.info("接收查询所有用户请求");
return userService.getAllUsers();
}
/**
* 创建用户
*/
@PostMapping
public String createUser(@RequestBody User user) {
log.info("接收创建用户请求: {}", user.getUsername());
boolean success = userService.createUser(user);
return success ? "用户创建成功" : "用户创建失败";
}
/**
* 健康检查接口(用于部署后验证服务是否正常)
*/
@GetMapping("/health")
public String healthCheck() {
return "Service is running";
}
}
配置文件 application.yml:
yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/jenkins_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: YourStrongPassword123!
driver-class-name: com.mysql.cj.jdbc.Driver
profiles:
active: dev
mybatis:
mapper-locations: classpath:mybatis/*.xml
type-aliases-package: com.example.entity
configuration:
map-underscore-to-camel-case: true # 下划线转驼峰
server:
port: 8081 # 避免与Jenkins的8080端口冲突
单元测试 UserServiceTest.java:
java
运行
package com.example.service;
import com.example.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* 用户服务测试类
*/
@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@Test
void getAllUsers_ShouldReturnUserList() {
List<User> users = userService.getAllUsers();
// 初始数据库为空,返回空列表(非null)
assertNotNull(users);
}
@Test
void createUser_WithValidData_ShouldReturnTrue() {
User user = new User();
user.setUsername("testuser");
user.setEmail("test@example.com");
user.setAge(25);
boolean result = userService.createUser(user);
assertTrue(result);
}
}
4.3 提交代码到 Git 仓库
将项目推送到 Git 仓库(以 Gitee 为例,GitHub 操作类似):
bash
# 初始化Git仓库
git init
# 添加文件
git add .
# 提交
git commit -m "初始化Jenkins演示项目"
# 关联远程仓库(替换为你的仓库地址)
git remote add origin https://gitee.com/your-username/jenkins-demo.git
# 推送代码
git push -u origin master
五、Jenkins Pipeline 实战 ------ 编写自动化脚本
Pipeline 是 Jenkins 中最强大的功能,通过代码(Jenkinsfile)定义整个构建部署流程,支持版本控制和复用。我们将创建一个包含 "拉取代码→编译→测试→打包→部署" 的完整 Pipeline。
5.1 创建 Pipeline 项目
- 登录 Jenkins,点击左侧 "新建 Item"
- 输入项目名称(如
jenkins-demo-pipeline
),选择 "Pipeline",点击 "确定" - 在项目配置页面,拉到 "Pipeline" 配置区域
5.2 编写基础 Pipeline 脚本
在 "Pipeline" 配置中选择 "Definition" 为 "Pipeline script",输入以下脚本:
groovy
pipeline {
// 指定在哪个节点执行(默认在Jenkins所在节点)
agent any
// 环境变量定义
environment {
// 项目名称
PROJECT_NAME = 'jenkins-demo'
// 项目版本(与pom.xml一致)
PROJECT_VERSION = '1.0.0'
// 打包后的文件名
JAR_FILE = "${PROJECT_NAME}-${PROJECT_VERSION}.jar"
// 部署目录
DEPLOY_DIR = '/opt/deploy'
// 服务端口(与application.yml一致)
SERVICE_PORT = 8081
}
// 工具配置(关联全局工具配置中的名称)
tools {
maven 'Maven3.8'
jdk 'JDK17'
git 'Git'
}
// 构建阶段定义
stages {
// 1. 拉取代码
stage('拉取代码') {
steps {
echo "开始拉取代码..."
// 替换为你的Git仓库地址
git url: 'https://gitee.com/your-username/jenkins-demo.git',
branch: 'master'
}
}
// 2. 编译代码
stage('编译代码') {
steps {
echo "开始编译代码..."
sh 'mvn clean compile'
}
}
// 3. 运行单元测试
stage('运行单元测试') {
steps {
echo "开始运行单元测试..."
sh 'mvn test'
}
post {
// 测试失败后保存测试报告
always {
junit '**/target/surefire-reports/*.xml'
}
}
}
// 4. 打包构建
stage('打包构建') {
steps {
echo "开始打包构建..."
sh 'mvn package -DskipTests'
}
}
// 5. 部署应用
stage('部署应用') {
steps {
echo "开始部署应用..."
// 创建部署目录
sh "mkdir -p ${DEPLOY_DIR}"
// 停止旧服务(如果存在)
sh """
if [ -f "${DEPLOY_DIR}/${PROJECT_NAME}.pid" ]; then
PID=\$(cat ${DEPLOY_DIR}/${PROJECT_NAME}.pid)
echo "停止旧服务,PID: \$PID"
kill -9 \$PID || true
fi
"""
// 复制新包到部署目录
sh "cp target/${JAR_FILE} ${DEPLOY_DIR}/"
// 启动新服务,记录PID
sh """
cd ${DEPLOY_DIR}
nohup java -jar ${JAR_FILE} > ${PROJECT_NAME}.log 2>&1 &
echo \$! > ${PROJECT_NAME}.pid
echo "新服务启动成功,PID: \$(cat ${PROJECT_NAME}.pid)"
"""
}
}
// 6. 验证部署
stage('验证部署') {
steps {
echo "验证服务是否正常启动..."
// 循环检查健康接口,最多等待30秒
sh """
count=0
while true; do
if curl -s "http://localhost:${SERVICE_PORT}/api/users/health" | grep -q "Service is running"; then
echo "服务验证成功"
exit 0
fi
count=\$((count + 1))
if [ \$count -ge 6 ]; then
echo "服务启动超时"
exit 1
fi
sleep 5
done
"""
}
}
}
// 构建结果通知
post {
success {
echo "构建部署成功!"
}
failure {
echo "构建部署失败!"
}
always {
echo "构建流程结束"
}
}
}
5.3 脚本关键步骤解析
- agent any:表示在任何可用的 Jenkins 节点上执行(后续可扩展为多节点)
- environment:定义环境变量,避免硬编码,方便维护
- tools:指定构建工具,关联全局配置中的 JDK、Maven、Git
- stages :核心构建阶段,每个 stage 完成一个独立任务:
- 拉取代码:从 Git 仓库克隆代码,支持指定分支
- 编译代码 :
mvn clean compile
清理并编译源码 - 运行单元测试 :
mvn test
执行测试,junit
插件收集测试报告 - 打包构建 :
mvn package
生成 jar 包,-DskipTests
跳过测试(已单独执行) - 部署应用 :
- 停止旧服务(通过 PID 文件)
- 复制新 jar 包到部署目录
- 启动新服务,将进程 ID 写入 PID 文件
- 验证部署:通过健康检查接口确认服务启动成功
- post:构建结束后执行的操作,支持成功 / 失败 / 总是执行的逻辑
5.4 运行 Pipeline 并查看结果
- 点击项目页面的 "Build Now" 触发构建
- 点击左侧 "Build History" 中的构建编号(如 #1)进入构建详情
- 点击 "Console Output" 查看实时构建日志
- 构建成功后,访问
http://服务器IP:8081/api/users/health
,显示 "Service is running" 即为部署成功
六、自动化进阶 ------ 让部署更智能
基础 Pipeline 实现了自动部署,但在实际生产中还需要更多功能,如代码提交触发构建、远程部署、代码质量检查、邮件通知等。
6.1 配置代码提交自动触发构建
无需手动点击 "Build Now",代码提交到 Git 后自动触发构建:
- 安装 "Generic Webhook Trigger" 插件
- 进入项目配置,在 "构建触发器" 中勾选 "Generic Webhook Trigger"
- 设置 "Token"(如
jenkins-demo-trigger
) - 在 Git 仓库中配置 WebHook:
- Gitee:仓库 → 管理 → WebHooks → 添加
- Payload URL:
http://JenkinsIP:8080/generic-webhook-trigger/invoke?token=jenkins-demo-trigger
- 触发事件:选择 "推送事件"
- 保存配置,后续提交代码到 master 分支会自动触发构建
6.2 远程服务器部署(通过 SSH)
实际场景中,Jenkins 和应用服务器通常分离,需要通过 SSH 部署:
- 进入 "系统管理" → "系统" → "Publish over SSH"
- 配置 SSH 服务器:
- Name:
app-server
(自定义名称) - Hostname:应用服务器 IP
- Username:登录用户名(如 root)
- 选择认证方式(密码或密钥)
- 点击 "Test Configuration" 验证连接
- Name:
- 修改 Pipeline 的 "部署应用" 阶段:
groovy
stage('部署应用') {
steps {
echo "开始部署到远程服务器..."
// 通过SSH执行命令
sshPublisher(publishers: [sshPublisherDesc(
configName: 'app-server', // 对应配置的SSH服务器名称
transfers: [sshTransfer(
cleanRemote: false,
excludes: '',
execCommand: """
# 停止旧服务
if [ -f "/opt/deploy/${PROJECT_NAME}.pid" ]; then
PID=\$(cat /opt/deploy/${PROJECT_NAME}.pid)
kill -9 \$PID || true
fi
# 启动新服务
cd /opt/deploy
nohup java -jar ${JAR_FILE} > ${PROJECT_NAME}.log 2>&1 &
echo \$! > ${PROJECT_NAME}.pid
""",
execTimeout: 120000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: '[, ]+',
remoteDirectory: '/opt/deploy', // 远程服务器目录
remoteDirectorySDF: false,
removePrefix: 'target', // 移除本地的target前缀
sourceFiles: "target/${JAR_FILE}" // 本地文件路径
)],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: true
)])
}
}
6.3 集成 SonarQube 代码质量检查
SonarQube 用于检测代码中的漏洞、异味和重复代码,在 Pipeline 中添加质量门禁:
- 安装 SonarQube 服务器(参考官方文档)
- Jenkins 中安装 "SonarQube Scanner" 插件
- 进入 "系统管理" → "系统" → "SonarQube servers" 配置:
- Name:
SonarQube
- Server URL:SonarQube 访问地址(如
http://sonarIP:9000
) - Server authentication token:在 SonarQube 中生成的令牌
- Name:
- 在 Pipeline 中添加代码质量检查阶段:
groovy
stage('代码质量检查') {
steps {
echo "开始代码质量检查..."
withSonarQubeEnv('SonarQube') {
sh """
mvn sonar:sonar \
-Dsonar.projectKey=${PROJECT_NAME} \
-Dsonar.projectName=${PROJECT_NAME} \
-Dsonar.projectVersion=${PROJECT_VERSION} \
-Dsonar.sources=src/main/java \
-Dsonar.java.binaries=target/classes
"""
}
}
}
// 添加质量门禁检查(需安装Sonar Quality Gates插件)
stage('质量门禁检查') {
steps {
script {
def qualityGate = waitForQualityGate()
if (qualityGate.status != 'OK') {
error "代码质量检查未通过: ${qualityGate.status}"
}
}
}
}
6.4 邮件通知构建结果
构建完成后自动发送邮件通知相关人员:
- 安装 "Email Extension" 插件
- 进入 "系统管理" → "系统" → "Extended E-mail Notification" 配置:
- SMTP 服务器:如
smtp.163.com
- SMTP 端口:25(或 465 用于 SSL)
- 用户名:发件人邮箱
- 密码:邮箱授权码
- 默认收件人:
developer@example.com
- SMTP 服务器:如
- 在 Pipeline 的 post 部分添加邮件通知:
groovy
post {
success {
echo "构建部署成功!"
emailext(
subject: "构建成功: ${PROJECT_NAME} #${BUILD_NUMBER}",
body: """
<h3>构建信息</h3>
<p>项目名称: ${PROJECT_NAME}</p>
<p>构建编号: ${BUILD_NUMBER}</p>
<p>构建结果: 成功</p>
<p>查看详情: <a href="${BUILD_URL}">${BUILD_URL}</a></p>
""",
to: 'developer@example.com'
)
}
failure {
echo "构建部署失败!"
emailext(
subject: "构建失败: ${PROJECT_NAME} #${BUILD_NUMBER}",
body: """
<h3>构建信息</h3>
<p>项目名称: ${PROJECT_NAME}</p>
<p>构建编号: ${BUILD_NUMBER}</p>
<p>构建结果: 失败</p>
<p>查看详情: <a href="${BUILD_URL}">${BUILD_URL}</a></p>
""",
to: 'developer@example.com'
)
}
}
6.5 版本回滚机制
部署失败时能快速回滚到上一版本:
- 在部署阶段保存历史版本:
groovy
stage('部署应用') {
steps {
echo "开始部署应用..."
// 创建历史版本目录
sh "mkdir -p ${DEPLOY_DIR}/history"
// 备份当前版本(如果存在)
sh """
if [ -f "${DEPLOY_DIR}/${JAR_FILE}" ]; then
cp ${DEPLOY_DIR}/${JAR_FILE} ${DEPLOY_DIR}/history/${JAR_FILE}.bak.${BUILD_TIMESTAMP}
echo "备份当前版本到历史目录"
fi
"""
// 后续部署步骤...
}
}
- 配置参数化构建:
- 项目配置中勾选 "This project is parameterized"
- 添加 "Choice Parameter",名称
ACTION
,选项:deploy
和rollback
- 添加 "String Parameter",名称
ROLLBACK_VERSION
,默认值为空
- 修改 Pipeline 支持回滚逻辑:
groovy
stage('部署或回滚') {
steps {
script {
if (params.ACTION == 'deploy') {
// 执行正常部署步骤
echo "执行部署操作..."
// 部署脚本...
} else if (params.ACTION == 'rollback') {
// 执行回滚操作
echo "执行回滚操作,版本: ${params.ROLLBACK_VERSION}"
sh """
# 停止当前服务
if [ -f "${DEPLOY_DIR}/${PROJECT_NAME}.pid" ]; then
PID=\$(cat ${DEPLOY_DIR}/${PROJECT_NAME}.pid)
kill -9 \$PID || true
fi
# 恢复历史版本
cp ${DEPLOY_DIR}/history/${params.ROLLBACK_VERSION} ${DEPLOY_DIR}/${JAR_FILE}
# 启动服务
cd ${DEPLOY_DIR}
nohup java -jar ${JAR_FILE} > ${PROJECT_NAME}.log 2>&1 &
echo \$! > ${PROJECT_NAME}.pid
"""
}
}
}
}
七、常见问题与解决方案
在 Jenkins 使用过程中,可能会遇到各种问题,以下是高频问题及解决方法:
7.1 构建失败:找不到 JDK/Maven
现象 :日志中出现java: command not found
或mvn: command not found
原因:全局工具配置错误,Jenkins 无法找到 JDK/Maven 路径
解决:
- 确认 JDK/Maven 实际安装路径(
which java
或which mvn
) - 进入 "全局工具配置",检查路径是否正确(绝对路径)
- 确保 Jenkins 用户有访问该路径的权限
7.2 权限问题:无法创建目录或执行命令
现象 :日志中出现Permission denied
原因:Jenkins 运行用户(通常是 jenkins)权限不足
解决:
- 查看 Jenkins 运行用户:
ps -ef | grep jenkins
- 为部署目录授权:
chown -R jenkins:jenkins /opt/deploy
- 必要时赋予 sudo 权限(谨慎操作):编辑
/etc/sudoers
添加jenkins ALL=(ALL) NOPASSWD: ALL
7.3 测试失败:单元测试不通过导致构建中断
现象 :mvn test
执行失败,构建终止
解决:
- 查看测试报告:构建详情 → "Test Result"
- 修复代码中的测试用例
- 紧急情况下可临时跳过测试(不推荐):将
mvn package
改为mvn package -DskipTests
7.4 插件冲突:安装插件后 Jenkins 无法启动
现象:Jenkins 启动失败,日志中有插件相关错误
解决:
- 进入 Jenkins 插件目录(通常是
/root/.jenkins/plugins
) - 删除冲突的插件目录(如
problem-plugin/
) - 重启 Jenkins:
systemctl restart jenkins
7.5 远程部署超时:SSH 连接失败
现象 :远程部署阶段提示Connection timed out
解决:
- 检查网络:在 Jenkins 服务器上
ping 应用服务器IP
- 检查端口:
telnet 应用服务器IP 22
确认 SSH 端口开放 - 检查认证:重新配置 "Publish over SSH" 的密钥或密码
- 增加超时时间:在 sshPublisher 中设置
execTimeout: 300000
(5 分钟)
八、总结与展望
通过本文的步骤,可以掌握从 0 搭建 Jenkins 环境、配置自动化工具、编写 Pipeline 脚本、实现 Java 项目自动部署的完整流程。一个成熟的 CI/CD 流水线能为团队带来显著的效率提升,让开发者从繁琐的部署工作中解放出来,专注于代码质量和业务逻辑。