Jenkins+Maven+Git自动化部署实战:从零搭建CI/CD流水线
2026/7/22 14:04:25 网站建设 项目流程

在持续集成与持续交付(CI/CD)的实践中,Jenkins 作为自动化引擎,配合 Maven 进行项目构建,以及 Git 进行版本控制,构成了 Java 后端项目自动化部署的黄金三角。这套组合拳能显著提升开发效率、保障构建一致性并实现快速迭代。然而,从零开始搭建这套流水线,往往会遇到环境配置复杂、脚本编写晦涩、权限管理混乱等问题,导致部署过程磕磕绊绊。

本文将为你呈现一套从零到一的完整实战指南,不仅涵盖 Jenkins、Maven、Git 的基础集成,更深入多环境配置、构建优化、权限控制等工程化细节。无论你是刚接触 CI/CD 的新手,还是希望优化现有流水线的开发者,都能从中获得可直接复用于生产环境的解决方案与避坑经验。

1. 核心概念与价值:为什么需要自动化部署?

在深入实操之前,理解自动化部署的核心价值与组件职责至关重要。这能帮助我们在后续配置中做出更合理的设计决策。

1.1 传统部署 vs. 自动化部署

传统的手动部署通常包含以下步骤:本地打包 -> 通过 SCP/FTP 上传服务器 -> 登录服务器停止旧服务 -> 备份 -> 替换文件 -> 启动服务 -> 手动验证。这个过程繁琐、易错,且严重依赖个人操作,难以追溯和回滚。

自动化部署通过预设的流水线(Pipeline),将上述所有步骤脚本化、自动化。其核心价值在于:

  • 提升效率:代码提交后自动触发构建、测试、部署,释放人力。
  • 保证一致性:构建环境统一,避免“在我机器上是好的”这类问题。
  • 快速反馈:集成测试能尽早发现集成错误。
  • 可追溯与可回滚:每次构建都有唯一标识和完整日志,出问题可快速定位并回滚到上一个稳定版本。

1.2 技术栈组件职责

  • Git:作为源代码版本控制系统,是流水线的源头。Jenkins 通过轮询或 Webhook 监听代码仓库的变化。
  • Maven:作为 Java 项目的构建与依赖管理工具。它负责编译源代码、运行测试、打包(生成 JAR/WAR 文件)以及管理项目依赖。
  • Jenkins:作为自动化服务器,是整个流水线的大脑和协调者。它负责调度任务:从 Git 拉取代码 -> 调用 Maven 进行构建 -> 执行自定义的部署脚本(如 SSH 到服务器)-> 发送通知。

三者协同工作,形成了一条从代码提交到服务上线的自动化流水线。

2. 环境准备与版本说明

一个稳定、版本匹配的环境是成功的第一步。以下列出本文演示环境,你的实际环境可能有所不同,但核心思路一致。

2.1 基础环境清单

  • 操作系统:CentOS 7.9 / Ubuntu 20.04 LTS (本文命令以 CentOS 为例,Ubuntu 用户请注意包管理命令差异)
  • Java:OpenJDK 11 或 Oracle JDK 8/11。Maven 和 Jenkins(War 包方式)都依赖于 Java 环境。
  • Git:版本 2.x 以上。
  • Maven:版本 3.6.x 以上。
  • Jenkins:本文使用最新的 Jenkins LTS 版本(如 2.387.x),通过 War 包部署。同样支持 Docker 安装。
  • 目标服务器:一台或多台用于部署应用的服务。需要确保 Jenkins 所在服务器能通过 SSH 免密登录到目标服务器。

2.2 安装与验证

1. 安装 Java

# CentOS sudo yum install -y java-11-openjdk-devel # 验证 java -version

2. 安装 Git

# CentOS sudo yum install -y git # 验证 git --version

3. 安装 Maven

# 下载 wget https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz # 解压 tar -xzf apache-maven-3.9.6-bin.tar.gz -C /opt/ # 配置环境变量 echo 'export MAVEN_HOME=/opt/apache-maven-3.9.6' >> ~/.bashrc echo 'export PATH=$MAVEN_HOME/bin:$PATH' >> ~/.bashrc source ~/.bashrc # 验证 mvn -v

4. 安装 Jenkins推荐使用 War 包方式,简单可控。

# 下载最新的 LTS War 包 wget https://get.jenkins.io/war-stable/latest/jenkins.war # 启动 Jenkins(默认端口8080,可在命令中修改) nohup java -jar jenkins.war --httpPort=8080 > jenkins.log 2>&1 &

启动后,访问http://<你的服务器IP>:8080。根据提示从日志中获取初始管理员密码解锁 Jenkins,并安装推荐的插件。

3. Jenkins 基础配置与插件安装

Jenkins 安装完成后,需要进行一些基础配置,并安装必要的插件来增强其与 Git、Maven 的集成能力。

3.1 系统配置:全局工具配置

这是关键一步,告诉 Jenkins 你系统中 Git、Java、Maven 的安装路径。

  1. 进入 Jenkins 管理后台 ->系统管理->全局工具配置
  2. JDK:取消“自动安装”,在JAVA_HOME处填写你的 JDK 路径,例如/usr/lib/jvm/java-11-openjdk
  3. Git:取消“自动安装”,在Path to Git executable处填写 Git 可执行文件路径,通常为/usr/bin/git。可以通过which git命令查看。
  4. Maven:取消“自动安装”,在MAVEN_HOME处填写你的 Maven 安装路径,例如/opt/apache-maven-3.9.6

3.2 必备插件安装

进入系统管理->插件管理->可选插件,搜索并安装以下插件:

  • Git plugin:提供 Git 集成(通常已默认安装)。
  • Maven Integration plugin:提供 Maven 项目类型的支持。
  • Publish Over SSH核心插件,用于通过 SSH 将构建产物传输到远程服务器并执行部署命令。
  • Pipeline:如果你想使用更强大的“流水线即代码”(Jenkinsfile)方式,需要安装。

安装后,需要配置Publish Over SSH插件。

  1. 进入系统管理->系统配置,找到Publish over SSH区域。
  2. Passphrase:如果你的 SSH 密钥有密码,在此填写。
  3. Path to key:填写 Jenkins 用户(通常是jenkins或你启动服务的用户)的私钥文件路径,例如/var/lib/jenkins/.ssh/id_rsa确保该私钥文件对 Jenkins 进程可读
  4. 点击高级,可以测试连接。
  5. 在下方SSH Servers中,点击新增
    • Name:给目标服务器起个名字,如prod-web-01
    • Hostname:目标服务器的 IP 或域名。
    • Username:SSH 登录用户名,如rootdeploy
    • Remote Directory:远程服务器上的基准目录,后续传输文件的路径会基于此目录,如/data/app

4. 创建第一个 Maven 构建任务

我们将从一个最简单的“自由风格”项目开始,实现代码拉取、构建和归档。

4.1 任务创建与源码管理

  1. 点击 Jenkins 首页的新建任务
  2. 输入任务名称,例如demo-springboot,选择构建一个自由风格的软件项目,点击确定。
  3. 在配置页面的源码管理部分,选择Git
    • Repository URL:填写你的 Git 仓库地址,如https://github.com/yourname/demo-springboot.git。如果是私有仓库,需要在Credentials处添加用户名密码或 SSH 密钥。
    • Branches to build:指定构建的分支,例如*/main*/master

4.2 构建触发器与构建环境

  1. 构建触发器:可以选择定期构建,例如H/15 * * * *表示每15分钟检查一次代码变更。更推荐使用 Git Webhook(后续介绍)。
  2. 构建环境:暂时可以不选。

4.3 构建步骤:调用 Maven

这是核心步骤,告诉 Jenkins 如何构建你的项目。

  1. 构建部分,点击增加构建步骤,选择调用顶层 Maven 目标
  2. Maven 版本:选择你在全局工具中配置的 Maven。
  3. 目标:填写 Maven 命令。对于标准的 Spring Boot 项目,通常只需要clean package。如果需要跳过测试,可以写clean package -DskipTests

4.4 构建后操作:归档产物

构建成功后,我们需要保存生成的 JAR/WAR 包。

  1. 构建后操作部分,点击增加构建后操作步骤,选择归档构件
  2. 要归档的文件:填写构建产物的路径。Maven 默认输出在target/目录下。对于 Spring Boot JAR,可以写target/*.jar。对于 WAR 包,写target/*.war
  3. 归档后删除旧构建:可以勾选,只保留最近若干次的构建产物以节省空间。

保存配置后,点击立即构建。在构建历史中点击某次构建,查看控制台输出,你应该能看到 Jenkins 成功拉取代码并执行mvn clean package的过程。构建成功后,在构建详情页面可以看到归档的 JAR 文件,可以下载。

5. 实现自动化部署:使用 Publish Over SSH

现在我们已经能自动构建了,下一步是将构建产物自动部署到远程服务器。

5.1 准备部署脚本

在部署之前,需要在目标服务器上准备一个部署脚本。这个脚本负责备份旧版本、替换文件、重启应用等操作。这是一个 Spring Boot 应用的简单示例脚本/data/app/deploy.sh

#!/bin/bash # deploy.sh # 接收参数:应用名称、JAR包名称 APP_NAME=$1 JAR_NAME=$2 # 应用目录 APP_HOME=/data/app/$APP_NAME # 备份目录 BACKUP_HOME=/data/backup/$APP_NAME # 进入应用目录 cd $APP_HOME || exit 1 # 1. 备份当前正在运行的JAR包(如果存在) if [ -f $APP_NAME.jar ]; then BACKUP_FILE="$BACKUP_HOME/$APP_NAME.jar.$(date +%Y%m%d%H%M%S)" mkdir -p $BACKUP_HOME cp $APP_NAME.jar $BACKUP_FILE echo "Backup created: $BACKUP_FILE" fi # 2. 停止当前应用(根据实际进程管理方式调整,这里用kill) PID=$(ps -ef | grep $APP_NAME.jar | grep -v grep | awk '{print $2}') if [ -n "$PID" ]; then echo "Stopping $APP_NAME (PID: $PID)..." kill $PID sleep 5 # 强制杀死如果还在运行 if ps -p $PID > /dev/null 2>&1; then kill -9 $PID fi fi # 3. 复制新的JAR包(此步骤将由Jenkins的SSH插件完成) # 脚本假设新的JAR包已经由Jenkins传输到了 $APP_HOME/$JAR_NAME # 这里我们将其重命名为标准名称 mv $JAR_NAME $APP_NAME.jar # 4. 启动应用 echo "Starting $APP_NAME..." nohup java -jar $APP_NAME.jar --spring.profiles.active=prod > app.log 2>&1 & # 等待几秒检查是否启动成功 sleep 10 NEW_PID=$(ps -ef | grep $APP_NAME.jar | grep -v grep | awk '{print $2}') if [ -n "$NEW_PID" ]; then echo "$APP_NAME started successfully. PID: $NEW_PID" else echo "ERROR: $APP_NAME failed to start!" exit 1 fi

注意:请确保该脚本有执行权限 (chmod +x /data/app/deploy.sh),并且 Jenkins SSH 用户有权限在目标目录读写和执行。

5.2 配置 Jenkins 构建后传输与执行

回到 Jenkins 任务配置页面,修改构建后操作

  1. 删除或保留之前的“归档构件”步骤。
  2. 点击增加构建后操作步骤,选择Send build artifacts over SSH
  3. SSH Server:选择你之前配置好的服务器,如prod-web-01
  4. Transfers
    • Source files:需要传输的文件,即 Maven 构建的产物。例如target/demo-0.0.1-SNAPSHOT.jar注意路径是相对于工作空间的
    • Remove prefix:移除路径前缀。例如填写target/,那么传输到远程服务器时,就会去掉target/目录。
    • Remote directory:远程目录,此路径是基于你在 SSH Server 配置中设置的Remote Directory的相对路径。如果Remote Directory/data/app,这里留空,文件就会传到/data/app下。如果你想传到子目录,可以填写subdir/
    • Exec command:文件传输完成后,在远程服务器上执行的命令。这里调用我们的部署脚本。
      cd /data/app && ./deploy.sh demo-springboot demo-0.0.1-SNAPSHOT.jar
      这条命令先进入目录,然后执行部署脚本,并传递两个参数:应用名和 JAR 包名。

保存配置,再次执行构建。观察控制台输出,在 Maven 构建成功后,你会看到 SSH 插件开始传输文件,并执行远程命令,最终输出应用启动成功的日志。

6. 使用 Pipeline 实现更强大的流水线

自由风格项目对于简单任务足够,但 Pipeline(流水线)提供了更强大、更灵活、可版本化的方式。Pipeline 脚本(Jenkinsfile)可以随代码一起存放在 Git 仓库中,实现“流水线即代码”。

6.1 创建 Pipeline 项目

  1. 新建任务,选择Pipeline类型。
  2. Pipeline部分,定义如何获取 Jenkinsfile。
    • Definition:选择Pipeline script from SCM
    • SCM:选择Git,并配置你的仓库地址和凭据。
    • Script Path:指定 Jenkinsfile 在仓库中的路径,默认为Jenkinsfile

6.2 编写 Jenkinsfile

在项目根目录创建Jenkinsfile文件。下面是一个声明式流水线的示例:

pipeline { agent any // 在任何可用代理上执行 tools { // 指定工具版本,需在Jenkins全局工具中配置同名 maven 'Maven-3.9.6' jdk 'JDK-11' } environment { // 定义环境变量 APP_NAME = 'demo-springboot' REMOTE_SERVER = 'prod-web-01' } stages { stage('Checkout') { steps { // 拉取代码 checkout scm } } stage('Build') { steps { // 使用Maven构建,跳过测试 sh 'mvn clean package -DskipTests' } } stage('Archive') { steps { // 归档构建产物 archiveArtifacts artifacts: 'target/*.jar', fingerprint: true } } stage('Deploy to Test') { steps { // 使用SSH插件传输并部署到测试服务器 sshPublisher( publishers: [ sshPublisherDesc( configName: "${REMOTE_SERVER}", transfers: [ sshTransfer( sourceFiles: 'target/*.jar', removePrefix: 'target/', remoteDirectory: '', execCommand: "cd /data/app && ./deploy.sh ${APP_NAME} *.jar" ) ], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: true ) ] ) } } stage('Integration Test') { steps { // 这里可以添加集成测试步骤,例如调用测试API sh 'curl -f http://test-server:8080/actuator/health || exit 1' echo 'Integration test passed!' } } // 可以添加人工审核阶段,审核通过后才部署生产 stage('Manual Approval for Prod') { steps { timeout(time: 1, unit: 'HOURS') { input message: 'Deploy to production?', ok: 'Deploy' } } } stage('Deploy to Production') { steps { // 部署到生产服务器的步骤,可能使用不同的SSH配置 echo 'Deploying to production...' // ... 类似测试环境的部署命令 } } } post { always { // 无论成功失败都执行,例如清理或通知 echo 'Pipeline finished.' } success { // 构建成功时执行 emailext ( subject: "SUCCESS: Pipeline ${env.JOB_NAME} - ${env.BUILD_NUMBER}", body: "构建成功!\n详情:${env.BUILD_URL}", to: 'team@example.com' ) } failure { // 构建失败时执行 emailext ( subject: "FAILURE: Pipeline ${env.JOB_NAME} - ${env.BUILD_NUMBER}", body: "构建失败,请检查!\n详情:${env.BUILD_URL}", to: 'team@example.com' ) } } }

这个 Jenkinsfile 定义了一个完整的流水线,包含代码拉取、构建、归档、部署到测试环境、集成测试、人工审核和部署到生产环境等多个阶段。post部分用于构建后的处理,如发送邮件通知。

7. 高级配置与最佳实践

搭建起基础流水线后,以下优化能让其更健壮、更安全、更高效。

7.1 使用 Webhook 实现代码提交自动触发

轮询方式有延迟且浪费资源。配置 Git Webhook 可以在代码推送到仓库时立即通知 Jenkins 触发构建。

  1. Jenkins 端:安装GitHub pluginGitLab Plugin等。在 Pipeline 或自由风格项目的“构建触发器”中,勾选GitHub hook trigger for GITScm polling或类似选项。
  2. Git 仓库端(以 GitHub 为例)
    • 进入仓库的Settings->Webhooks->Add webhook
    • Payload URL:http://<你的Jenkins服务器IP>:8080/github-webhook/
    • Content type:application/json
    • 选择触发事件,如Just the push event
    • 保存。

这样,每次向主分支推送代码,Jenkins 就会自动开始构建。

7.2 凭据管理与安全

  • 不要使用明文密码:在 Jenkins 的凭据系统中管理 SSH 私钥、Git 仓库密码、服务器密码等。在 Pipeline 中使用withCredentials绑定凭据。
    stage('Deploy') { steps { withCredentials([sshUserPrivateKey(credentialsId: 'ssh-prod-key', keyFileVariable: 'SSH_KEY')]) { sh """ chmod 600 ${SSH_KEY} ssh -i ${SSH_KEY} user@server 'command' """ } } }
  • 最小权限原则:为 Jenkins 的 SSH 密钥、部署脚本分配尽可能小的权限。避免直接使用 root 用户。

7.3 构建优化与缓存

  • Maven 全局缓存:确保 Jenkins 工作空间或全局.m2目录被正确缓存,避免每次构建都下载全部依赖。可以使用 Jenkins 的workspace清理策略,但保留~/.m2/repository
  • 使用 Docker Agent:在 Pipeline 中指定agent { docker 'maven:3.9.6-jdk-11' },可以提供一个纯净、一致的构建环境。
  • 并行执行:如果有多模块项目或独立测试任务,可以在 Pipeline 的stage中使用parallel指令并行执行,缩短构建时间。

7.4 完善的日志与监控

  • 日志收集:确保部署脚本和应用本身将日志输出到文件(如/data/app/app.log)或集中式日志系统(如 ELK)。
  • 构建历史清理:在 Jenkins 任务配置中设置“丢弃旧的构建”,只保留最近一定数量或天数的构建记录,防止磁盘占满。
  • 健康检查:在部署脚本的最后,加入应用健康检查,例如循环调用/actuator/health端点,直到返回成功或超时,确保部署真的成功了。

8. 常见问题与排查思路

在实践过程中,你可能会遇到以下典型问题。

问题现象常见原因排查思路与解决方案
Jenkins 无法拉取 Git 代码1. 仓库地址错误或无权访问。
2. 凭据配置错误。
3. Jenkins 服务器网络问题。
1. 在 Jenkins 服务器上手动执行git clone命令测试。
2. 检查 Jenkins 中配置的凭据是否正确,特别是 SSH 密钥对。
3. 检查 Jenkins 服务器的网络和防火墙设置。
Maven 构建失败1. 依赖下载失败(网络或仓库问题)。
2. 编译错误(代码问题)。
3. 测试用例失败。
1. 查看控制台输出,确认是网络超时还是仓库地址错误。可配置国内镜像。
2. 检查代码是否有语法错误,本地先执行mvn clean compile
3. 检查测试代码,或使用-DskipTests参数临时跳过。
SSH 插件连接失败1. SSH 服务器配置错误(IP、端口、用户名)。
2. 私钥路径错误或权限不对。
3. 目标服务器防火墙阻止。
1. 在 Jenkins 系统配置的 SSH 插件部分使用“Test Configuration”测试连接。
2. 确认私钥文件路径,并确保 Jenkins 进程用户有读取权限 (chmod 600)。
3. 在 Jenkins 服务器上手动 SSH 到目标服务器测试连通性。
部署脚本执行失败1. 脚本路径错误或没有执行权限。
2. 脚本中的命令在目标服务器上不存在(如java命令)。
3. 脚本逻辑错误(如目录不存在)。
1. 在 Jenkins 控制台输出中查看具体的错误命令。登录目标服务器,手动执行部署脚本,并传入相同参数进行调试。
2. 确保目标服务器已安装所需环境(Java)。
3. 在脚本中增加set -x开启调试,或加入更多echo语句打印执行状态。
应用启动后无法访问1. 应用本身启动失败(端口占用、配置错误)。
2. 服务器防火墙未开放端口。
3. 部署脚本中的启动命令有误。
1. 登录服务器,查看应用日志 (tail -f app.log),检查启动错误。
2. 使用netstat -tlnp检查应用端口是否在监听。
3. 检查服务器防火墙(如firewalldiptables)是否允许了应用端口。
Pipeline 语法错误1. Jenkinsfile 语法不符合 Groovy 或 Declarative Pipeline 规范。
2. 使用了未定义的变量或函数。
1. 使用 Jenkins 的流水线语法工具(Pipeline Syntax)生成正确的代码片段。
2. 在 Jenkins 任务配置页面的“流水线”部分,点击“流水线语法”检查,或使用Replay功能快速迭代调试。

9. 工程化建议与扩展方向

当基础流水线稳定运行后,可以考虑以下方向进行深化和扩展,以适应更复杂的生产需求。

1. 多环境与配置管理

  • 为开发、测试、生产等不同环境创建独立的 Jenkins 任务或使用同一个 Pipeline 通过参数化构建来选择环境。
  • 使用 Spring Cloud Config、Apollo 或简单的-Dspring.profiles.active=env参数来管理不同环境的配置。
  • 在部署脚本中,根据传入的环境参数,动态决定启动命令和配置。

2. 代码质量与安全门禁

  • 在 Pipeline 中集成静态代码分析(SonarQube)、单元测试覆盖率检查、依赖漏洞扫描(OWASP Dependency-Check)。
  • 将这些检查设置为独立阶段,只有通过后才能进入部署阶段,作为质量门禁。

3. 容器化部署(Docker & Kubernetes)

  • 将构建步骤改为构建 Docker 镜像:mvn clean package && docker build -t your-image .
  • 使用docker push将镜像推送到私有仓库。
  • 添加部署阶段,通过kubectl set image或调用 Kubernetes API 更新 Pod。
  • 这实现了真正的不可变基础设施部署,是更现代和云原生的做法。

4. 回滚机制

  • 完善的部署系统必须包含快速回滚能力。
  • 在部署脚本中,备份旧版本时应保留版本号或构建号。
  • 可以编写一个单独的回滚脚本,或扩展部署脚本,使其能接收“回滚”指令,从备份目录中恢复特定版本的应用并重启。

5. 蓝绿部署/金丝雀发布

  • 对于高可用性要求高的服务,可以考虑更高级的发布策略。
  • 蓝绿部署:准备两套完全相同的生产环境(蓝和绿),一次只让一套对外服务。部署时先更新空闲的那套,测试无误后,将流量切换过去。
  • 金丝雀发布:将新版本先部署到一小部分服务器或用户,验证无误后再逐步扩大范围。
  • 这些策略可以通过结合负载均衡器(如 Nginx)的配置管理和 Jenkins Pipeline 的步骤控制来实现。

从最简单的自由风格项目到可版本化、多阶段的 Pipeline,从手动上传到全自动部署,Jenkins + Maven + Git 的自动化部署体系是提升研发效能的基础设施。关键在于理解每个组件的职责,并围绕“稳定、高效、可追溯”的目标来设计和优化你的流水线。建议从本文提供的最小可行方案开始,逐步迭代,加入适合自己团队的质量检查、安全扫描和高级发布策略,最终构建起一套可靠且高效的持续交付管道。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询