Jenkins+Maven+Git生产部署避坑清单:73次故障提炼的实操指南

Jenkins+Maven+Git生产部署避坑清单:73次故障提炼的实操指南

1. 这不是教程,是我在生产环境踩了73次坑后抄出来的部署清单

“全网最全的 Jenkins + Maven + Git 自动化部署指南”——这个标题我第一次看到时笑了。不是笑它夸张,而是笑自己三年前也信了这句话,结果在客户现场连续通宵48小时,就为了搞懂为什么mvn clean package在本地能跑,在 Jenkins 里死活报NoClassDefFoundError;为什么git clone总卡在fatal: not a git repository;为什么 SSH Publisher 传过去的 jar 包双击打不开,日志里只有一行no main manifest attribute;为什么第5次构建后服务器上堆了17个同名 jar,jps一查全是僵尸进程,端口占满却连不上……这些不是配置错误,是整套链路里埋着的、文档从不提、报错不指向、新手根本找不到入口的“暗坑”。

今天这篇,不讲“Jenkins 是什么”“Maven 有什么用”这种教科书内容。它是一份可直接粘贴进你服务器终端、按步骤执行就能跑通的生产级部署清单。核心就三件事:让代码从 Git 仓库自动拉下来、用 Maven 打出能直接运行的 jar、再一键推到目标服务器并干净启动。全程不依赖 Docker、不强求 Kubernetes、不预设云厂商,就用最朴素的三台 Linux 机器(Git 服务端、Jenkins 构建机、应用服务器),所有路径、命令、参数、权限、环境变量,全部来自我亲手部署过的21个Java Web项目真实记录。你不需要理解 CI/CD 的抽象概念,只需要知道:第3步填哪个路径、第7步改哪行 pom.xml、第12步的 clean.sh 脚本为什么必须加grep 'java -jar'而不是简单grep $appname。文末附的排查表,是我把73次故障归类后提炼出的12个高频问题+对应ssh命令+一行定位答案,比如“构建成功但访问404”,直接执行curl -v http://localhost:8088/actuator/health就能判断是应用没启、端口错、还是 Spring Boot Actuator 没开。

关键词全部落在实操层:jenkins安装与配置不是教你点几下按钮,而是告诉你/etc/init.d/jenkinsJAVA_HOME必须硬编码,否则 systemd 会忽略你的.bashrcmaven配置的核心不是settings.xml位置,而是阿里云镜像源必须加<mirrorOf>central</mirrorOf>,否则spring-boot-starter-parent会绕过镜像去连 Maven Central;git安装及配置教程的关键不在git config --global user.name,而在于 Jenkins 用的 git 用户必须有~/.ssh/id_rsa且权限为600,否则git@github.com认证永远失败。如果你正被jenkins下载插件失败卡住,别折腾代理——90%的情况是/var/lib/jenkins/cache目录属主错了,chown -R jenkins:jenkins /var/lib/jenkins/cache一行解决。这整篇,就是把热搜词背后那些“看似简单、实则致命”的细节,全给你钉死在操作步骤里。

2. 整体设计:为什么必须用这三台机器?为什么不能跳过任何一步?

2.1 架构选型的底层逻辑:隔离性不是为了炫技,是为故障兜底

看到“三台服务器”很多人第一反应是“太重了,我一台机器搞定”。但这是用运维事故换来的认知。我最早在测试环境把 GitLab、Jenkins、应用全塞进一台 4C4G 的虚拟机,结果一次git push触发构建,Jenkins 吃光内存,GitLab Web 页面直接 502,开发连代码都提交不了。后来改成两台:GitLab + Jenkins 合并在一台,应用单独一台。结果某次 Jenkins 构建时触发了 Maven 的dependency:copy-dependencies,把整个~/.m2/repository打包上传,瞬间占满磁盘,GitLab 数据库因空间不足崩溃。现在坚持三台,不是资源浪费,而是故障域物理隔离

  • GitLab 服务器:只做代码托管,不装 JDK/Maven/Git 客户端,不开放 SSH 给 Jenkins 用户。它的唯一职责是响应git clone请求,哪怕 Jenkins 服务器宕机,开发照样能push代码。
  • Jenkins 服务器:只做构建中枢,不跑任何业务应用。它装 JDK、Maven、Git、SSH 客户端,但绝不装 Tomcat 或任何应用容器。它的角色是“工厂”,只负责把源码变成 jar,然后交给“物流”(SSH Publisher)运走。这样当构建出错时,不会影响线上服务;当 Jenkins 升级卡死,业务照常运行。
  • 应用服务器:只运行最终 jar,不装 Git、不装 Maven、不装 Jenkins Agent。它只有 JRE(不是 JDK)、SSH Server、基础监控工具。这样即使 Jenkins 被误配置成rm -rf /,也删不到应用服务器上的业务数据。

这个架构的代价是多维护一台机器,收益是任何单点故障都不会导致研发流程中断。比如 GitLab 服务器硬盘坏了,Jenkins 仍能从缓存拉取上次成功的构建产物应急发布;Jenkins 服务器被攻击,应用服务器上的老版本 jar 仍在运行,用户无感知。

2.2 工具链锁定:为什么非得是 Jenkins + Maven + Git?替代方案在哪失效?

有人问:“用 GitHub Actions 不更简单?”——简单,但不可控。GitHub Actions 的 runner 是黑盒,你无法精确控制 Maven 的-Dmaven.repo.local=/opt/m2路径,无法强制使用特定版本的 JDK(比如客户要求 JDK 11,而 Actions 默认是 JDK 17),更无法在构建前执行sed -i 's/PROD/TEST/g' application.yml这种敏感配置替换。Jenkins 的价值在于每个环节都暴露给你:你可以写 Shell 脚本在Pre Steps里改配置,可以在Post Steps里用scp传证书,甚至能用groovy脚本动态读取数据库里的灰度开关。

Maven 也不是唯一选择,但它是 Java 生态里构建契约最稳定的工具。Gradle 虽快,但build.gradle里一个plugins { id 'org.springframework.boot' version '3.2.0' }版本号写错,整个构建就挂,错误信息还藏在 200 行堆栈里;而 Maven 的pom.xml是 XML,结构固定,<parent>标签在哪、<properties>怎么覆盖,IDE 和 Jenkins 插件解析起来零歧义。更重要的是,mvn clean package -DskipTests这条命令,从 Maven 2 到 Maven 4,参数含义从未变过——这种稳定性对生产环境就是生命线。

Git 的不可替代性在于分布式协作的原子性。SVN 的svn export无法保证“同一 commit ID 下所有文件版本严格一致”,而 Git 的git archive --format=zip HEAD导出的压缩包,解压后和git checkout出来的内容字节级相同。这对审计至关重要:当客户要求提供“上线版本的源码包”,你给的 zip 文件哈希值,必须和 GitLab 上该 commit 的哈希完全一致。Jenkins 的 Git 插件正是利用这一点,在构建开始前先git rev-parse HEAD记录本次构建的精确版本,后续所有日志、产物、告警都带上这个 commit ID,出了问题秒级定位。

2.3 流程设计的生死线:为什么“构建前清理”比“构建后部署”重要十倍?

几乎所有新手教程都把重点放在“怎么把 jar 传上去”,却忽略了一个事实:自动化部署最大的敌人不是失败,而是残留。我见过最惨的案例:一个电商项目,Jenkins 每次构建都把新 jar 传到/opt/app/,但没人清理旧 jar。半年后目录里有 217 个order-service-*.jar,运维手动kill -9时杀错了进程,把正在处理支付的旧版本干掉了,导致 32 笔订单状态卡在“支付中”。从此我们定下铁律:任何部署流程,Pre Steps 的权重必须占 60%

具体到本方案,“构建前清理”包含三个不可分割的层次:

  1. Jenkins 工作区清理:在 Jenkins 项目配置的General选项卡里,勾选Delete workspace before build starts。否则mvn clean只清target/,但src/main/resources/config.properties如果被上一次构建脚本修改过,就会污染本次构建。
  2. 应用服务器进程清理clean.sh脚本不是简单pkill -f jenkins-study,因为pkill会匹配到grep jenkins-study自身进程,导致误杀。必须用ps -ef | grep 'java -jar' | grep jenkins-study精确抓取,再用awk '{print $2}'提取 PID。
  3. 应用服务器文件清理rm -rf $appname/"${appname}"*.jar这行命令里,$appname是传入的参数(如jenkins-study),"${appname}"*.jar用引号包裹防止空格扩展,*通配所有版本号。如果不用引号,jenkins study-1.0.jar会被拆成两个参数,rm命令直接报错。

这三个清理动作,必须在 Jenkins 的Build阶段之前、Post Steps之后执行。顺序错了,比如把清理放在Post Steps,那构建失败时清理就不会触发,残留物永远留着。这个细节,决定了你的自动化是“省事”,还是“埋雷”。

3. 核心细节解析:从安装到配置,每一步背后的“为什么”

3.1 Jenkins 安装:为什么 WAR 包启动比 RPM 包更可控?

网络上大量教程推荐yum install jenkins,但生产环境我一律用java -jar jenkins.war启动。原因很现实:RPM 包把 Jenkins 安装到/usr/lib/jenkins,JDK 路径写死在/etc/sysconfig/jenkins,一旦你要升级 JDK,必须同时改这个文件、重启 jenkins 服务、还要担心 SELinux 是否放行新 JDK 路径。而 WAR 包方式,所有路径由你掌控:

# 创建专用用户,避免用 root 运行 useradd -m -d /var/lib/jenkins -s /bin/bash jenkins chown -R jenkins:jenkins /var/lib/jenkins # 下载 WAR 包(以 2.440.4 为例) wget https://get.jenkins.io/war-stable/2.440.4/jenkins.war -O /var/lib/jenkins/jenkins.war # 编写启动脚本 /etc/systemd/system/jenkins.service [Unit] Description=Jenkins Automation Server After=network.target [Service] Type=simple User=jenkins Group=jenkins Environment="JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64" Environment="JENKINS_HOME=/var/lib/jenkins" ExecStart=/usr/bin/java -Djava.awt.headless=true -DJENKINS_HOME=/var/lib/jenkins -jar /var/lib/jenkins/jenkins.war --httpPort=8080 --prefix=/jenkins Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target

关键点:

  • Environment="JAVA_HOME=..."必须显式声明,否则 Jenkins 启动时读不到系统级 JAVA_HOME;
  • --prefix=/jenkins设置上下文路径,避免和 Nginx 反向代理冲突;
  • RestartSec=10设置重启间隔,防止 JVM OOM 后疯狂重启打满 CPU。

启动后,首次访问http://your-ip:8080/jenkins,页面会提示输入初始密码。这个密码在/var/lib/jenkins/secrets/initialAdminPassword不要用cat直接看,用sudo -u jenkins cat,否则权限错误导致 Jenkins 启动失败。

3.2 Maven 配置:阿里云镜像源的 3 个致命陷阱

maven配置阿里云仓库是搜索热词,但 90% 的人只复制了<mirror>标签,却栽在三个隐藏坑里:

陷阱一:<mirrorOf>的值必须是central,不是*external:*
错误配置:

<mirror> <id>aliyunmaven</id> <mirrorOf>*</mirrorOf> <!-- 错!会拦截所有仓库,包括 JCenter --> <name>Aliyun Maven</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>

正确配置:

<mirror> <id>aliyunmaven</id> <mirrorOf>central</mirrorOf> <!-- 只拦截 Maven Central --> <name>Aliyun Maven</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>

为什么?Spring Boot 的 parent POM 依赖spring-boot-starter-parent,它本身又依赖spring-boot-dependencies,而后者在pom.xml中声明的仓库是<repository><id>central</id><url>https://repo.maven.apache.org/maven2</url></repository>。只有mirrorOf精确匹配central,Maven 才会把请求重定向到阿里云。用*会导致jcenter.bintray.com的依赖也被重定向,而阿里云镜像不包含 JCenter 内容,构建直接失败。

陷阱二:settings.xml必须放在 Jenkins 用户家目录下
Jenkins 用jenkins用户运行,所以 Maven 的全局配置文件路径是/var/lib/jenkins/.m2/settings.xml,不是/root/.m2/settings.xml/etc/maven/settings.xml。验证方法:在 Jenkins 的Script Consolehttp://your-jenkins/script)里执行:

println "Maven home: ${System.getenv('M2_HOME')}" println "User settings: ${new File(System.getProperty('user.home'), '.m2/settings.xml').absolutePath}"

如果输出的路径不是/var/lib/jenkins/.m2/settings.xml,说明 Jenkins 没读到你的配置。

陷阱三:<profile>激活必须显式绑定activeByDefault
很多教程漏掉这一步,导致镜像源不生效:

<profiles> <profile> <id>aliyun</id> <repositories> <repository> <id>central</id> <url>https://maven.aliyun.com/repository/public</url> </repository> </repositories> </profile> </profiles> <activeProfiles> <activeProfile>aliyun</activeProfile> <!-- 必须加这一段 --> </activeProfiles>

3.3 Git 配置:为什么git config --global在 Jenkins 里是无效的?

Jenkins 的 Git 插件默认用jenkins用户执行git clone,而git config --global是写入当前用户的~/.gitconfig。但jenkins用户的家目录是/var/lib/jenkins/var/lib/jenkins/.gitconfig文件很可能不存在或为空。更糟的是,Jenkins 有时会用--no-global参数调用 git,彻底忽略全局配置。

解决方案是在 Jenkins 项目配置里显式设置 Git 全局配置

  1. 进入Manage Jenkins>Configure System
  2. 找到Git plugin区域
  3. Global Config user.name ValueJenkins Bot
  4. Global Config user.email Valuejenkins@your-company.com

这样 Jenkins 会在每次git clone前自动执行:

git config --global user.name "Jenkins Bot" git config --global user.email "jenkins@your-company.com"

同时,SSH 密钥必须由 Jenkins 用户生成并管理

sudo -u jenkins ssh-keygen -t rsa -b 4096 -C "jenkins@your-company.com" -f /var/lib/jenkins/.ssh/id_rsa sudo -u jenkins chmod 600 /var/lib/jenkins/.ssh/id_rsa sudo -u jenkins chmod 644 /var/lib/jenkins/.ssh/id_rsa.pub

然后把公钥cat /var/lib/jenkins/.ssh/id_rsa.pub的内容,添加到 GitLab 的Deploy Keys(不是个人 Access Token),并勾选Write access allowed。这样 Jenkins 就能用 SSH 协议git@your-gitlab.com:group/project.git克隆,无需输密码。

4. 实操过程:从创建项目到稳定运行,手把手拆解每个环节

4.1 创建 Jenkins 项目:自由风格 vs Pipeline,为什么新手必须选前者?

Jenkins 有两种项目类型:Freestyle project(自由风格)和Pipeline(流水线)。新手教程常推 Pipeline,因为它用Jenkinsfile代码化,但实际落地时,95% 的故障都源于Jenkinsfile的语法错误——一个缩进空格不对,整个构建就卡在Expected a step。而自由风格项目,所有配置都在 Web 界面完成,错误即时可见,适合建立肌肉记忆。

创建步骤:

  1. Dashboard>New Item> 输入项目名(如jenkins-study)> 选择Freestyle project>OK
  2. General选项卡:
    • 勾选Discard old builds,设置Max # of builds to keep为 10(防磁盘爆满)
    • 勾选Delete workspace before build starts(前文强调的清理)
  3. Source Code Management选项卡:
    • 选择Git
    • Repository URLgit@your-gitlab.com:group/jenkins-study.git
    • CredentialsAdd>Jenkins>SSH Username with private key>Private KeyFrom the Jenkins master ~/.ssh(即刚才生成的id_rsa
  4. Build Triggers选项卡:
    • 勾选Poll SCMScheduleH/5 * * * *(每5分钟检查一次 Git 变更)
    • 注意:不要用Build when a change is pushed to GitLab,那需要 GitLab 配置 Webhook,新手极易配错导致触发失败

4.2 Maven 构建配置:Root POMGoals的精准填写

这是新手最容易填错的地方。Root POM不是pom.xml的绝对路径,而是相对于 Jenkins 工作区根目录的相对路径。假设你的 Git 仓库结构是:

jenkins-study/ ├── pom.xml ├── src/ │ └── main/ │ └── java/

那么Root POM就填pom.xml(因为工作区根目录就是jenkins-study/pom.xml就在它下面)。如果填成/var/lib/jenkins/workspace/jenkins-study/pom.xml,Jenkins 会报pom.xml not found

Goalsclean package -Dmaven.test.skip=true,解释:

  • clean:删除target/目录,确保干净构建
  • package:执行编译、测试、打包全流程
  • -Dmaven.test.skip=true:跳过测试(生产部署通常不跑单元测试,节省时间)

关键技巧:如何确认 Maven 能找到 JDK?
Build Environment选项卡,勾选Provide Node & npm bin/ folder to PATH,然后在Build步骤的Execute shell里加一行诊断命令:

echo "JAVA_HOME: $JAVA_HOME" echo "PATH: $PATH" mvn -version

构建日志里如果出现Apache Maven 3.9.6 (bc02d32b8745...Java version: 11.0.22,说明 JDK 和 Maven 都正常。

4.3 SSH Publisher 配置:远程目录、前缀、执行命令的黄金组合

Post-build Actions>Send files or execute commands over SSH是部署的核心。配置项必须严丝合缝:

配置项为什么
Nametest-server任意,但要和Manage Jenkins>Configure System>SSH Servers里定义的名字一致
Source filestarget/*.jar注意是target/不是*/target/,因为工作区根目录就是项目根目录
Remove prefixtarget/target/jenkins-study-0.0.1-SNAPSHOT.jar传过去时,去掉target/,变成jenkins-study-0.0.1-SNAPSHOT.jar
Remote directory~/jenkins-study/传到远程服务器的jenkins-study/目录下,而不是家目录根,方便管理
Exec in pty✅ 勾选否则nohup命令不生效

Exec command填:

cd ~/jenkins-study # 杀掉旧进程(clean.sh 的精简版,直接写这里) pid=$(ps -ef | grep 'java -jar' | grep jenkins-study | awk '{print $2}') if [ ! -z "$pid" ]; then kill -9 $pid; fi # 删除旧 jar rm -f jenkins-study-*.jar # 启动新 jar nohup java -jar jenkins-study-*.jar --server.port=8088 >> log.out 2>&1 &

为什么不用clean.sh外部脚本?
因为外部脚本需要chmod 777,有安全风险;而内联命令在 Jenkins 权限下执行,更可控。grep jenkins-study之所以安全,是因为nohup java -jar启动的进程命令行里必然包含jenkins-study字符串,不会误杀其他进程。

4.4 验证与调试:三步定位法,5 分钟揪出 90% 的问题

构建失败时,别急着重试。用这套标准化排查法:

第一步:看 Jenkins 控制台输出(Console Output)
重点找三类关键词:

  • Cloning into '/var/lib/jenkins/workspace/jenkins-study'...→ Git 克隆是否成功?
  • [INFO] BUILD SUCCESS→ Maven 打包是否成功?如果看到[ERROR] Failed to execute goal...,说明pom.xml有问题
  • SSH: Connecting from host [jenkins98]→ SSH 连接是否建立?如果卡在这里,检查test97的 SSH 服务是否开启(systemctl status sshd

第二步:登录 Jenkins 服务器,手动模拟构建

sudo -u jenkins bash cd /var/lib/jenkins/workspace/jenkins-study git pull # 看是否能拉取最新代码 mvn clean package -Dmaven.test.skip=true # 看是否能本地打包

如果手动失败,说明环境配置有硬伤;如果手动成功但 Jenkins 失败,说明 Jenkins 的环境变量没继承(检查JAVA_HOME是否在systemd服务里声明)。

第三步:登录应用服务器,检查部署结果

# 查看文件是否传过来 ls -l ~/jenkins-study/ # 查看进程是否启动 ps -ef | grep java # 查看日志是否有异常 tail -50 ~/jenkins-study/log.out # 本地 curl 测试 curl -v http://localhost:8088

如果curl返回Hello dev,说明部署成功;如果返回Connection refused,检查ps是否真有 java 进程;如果ps有进程但curl不通,用netstat -tuln | grep 8088看端口是否监听,再用ss -tuln | grep 8088确认(ssnetstat更准)。

5. 常见问题与排查技巧实录:73 次故障沉淀出的 12 个高频问题速查表

以下表格完全基于真实故障场景整理,每一行都是血泪教训。问题描述直击现象,解决方案给出可立即执行的命令,原理说明解释“为什么”,让你下次遇到同类问题,30 秒内定位。

问题现象解决方案(直接复制执行)原理说明
构建日志显示FATAL: Command "git fetch --tags --force --progress --prune --no-recurse-submodules --depth=1 origin +refs/heads/*:refs/remotes/origin/*" returned status code 128sudo -u jenkins ssh -T git@your-gitlab.comJenkins 用jenkins用户连接 GitLab,ssh -T测试 SSH 连通性。返回Welcome to GitLab说明密钥正确;返回Permission denied说明~/.ssh/id_rsa权限不对(应为600)或公钥没加到 GitLab 的 Deploy Keys
Maven 构建报错Could not find artifact org.springframework.boot:spring-boot-maven-plugin:jar:3.2.0sudo -u jenkins mvn help:effective-settings此命令输出 Jenkins 实际读取的settings.xml路径和内容。90% 的镜像源失效,是因为settings.xml路径错了,或<mirrorOf>值不对(必须是central
构建成功,但应用服务器上jps看不到进程,log.out为空sudo -u jenkins ssh test97 'bash -c "cd ~/jenkins-study && java -jar jenkins-study-*.jar --server.port=8088"'手动在远程服务器上执行启动命令,观察实时输出。常见原因是JAVA_HOME未在~/.bashrc里导出,或java命令不在PATHwhich java查看)
构建成功,jps能看到进程,但curl http://localhost:8088返回Connection refusedsudo -u jenkins ssh test97 'netstat -tuln | grep 8088'netstat查看 8088 端口是否被监听。如果无输出,说明 Spring Boot 应用没启动成功;如果有输出但curl不通,检查application.ymlserver.port是否被覆盖(如 profile 激活了prod,而prod配置里端口是 8080)
多次构建后,应用服务器上~/jenkins-study/目录里堆满jenkins-study-*.jar,且jps显示多个同名进程sudo -u jenkins ssh test97 'ps -ef | grep jenkins-study | grep "java -jar" | awk "{print \$2}" | xargs kill -9'此命令精确杀死所有java -jar启动的jenkins-study进程。grep "java -jar"是关键,避免grep jenkins-study匹配到grep自身进程
Jenkins 页面打开慢,插件安装总失败,日志里有java.net.SocketTimeoutException: connect timed outsudo -u jenkins sed -i 's/https:\/\/updates.jenkins-ci.org\/download/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/g' /var/lib/jenkins/hudson.model.UpdateCenter.xmlJenkins 插件中心默认连国外地址,国内网络不稳定。清华镜像源地址已验证可用,改完重启 Jenkins 服务
构建日志里mvn clean package成功,但target/目录下没有 jar 文件sudo -u jenkins ls -l /var/lib/jenkins/workspace/jenkins-study/target/检查target/目录是否存在。如果存在但无 jar,说明pom.xmlpackaging标签不是jar(如误写成war),或spring-boot-maven-plugin<executions>配置缺失
SSH Publisher 传文件时报错Failed to copy file: Permission deniedsudo -u jenkins ssh test97 'mkdir -p ~/jenkins-study && chmod 755 ~/jenkins-study'Jenkins 用户对远程目录无写权限。chmod 755确保组和其他用户可读可执行,Jenkins 用户(通常是rootjenkins)可写
构建成功,但浏览器访问http://test97:8088返回Whitelabel Error Pagesudo -u jenkins ssh test97 'tail -20 ~/jenkins-study/log.out | grep -i error'Whitelabel Error Page是 Spring Boot 默认错误页,说明应用启动了但内部出错。tail -20查看最后 20 行日志,grep -i error过滤错误,常见是数据库连接失败或 Redis 连不上
Jenkins 构建时git clone报错fatal: not a git repository (or any of the parent directories): .gitsudo -u jenkins rm -rf /var/lib/jenkins/workspace/jenkins-studyJenkins 工作区损坏。rm -rf强制删除,下次构建会重新git clone。此问题多发生在 Jenkins 服务器磁盘满后强制关机
nohup java -jar启动后,log.out文件一直为空,jps也看不到进程sudo -u jenkins ssh test97 'bash -c "cd ~/jenkins-study && nohup java -jar jenkins-study-*.jar >> log.out 2>&1 &"'手动执行nohup命令,观察是否报错。常见原因是log.out所在目录无写权限,或java命令路径不对(which java输出/usr/bin/java,但PATH里没包含/usr/bin
Jenkins 页面显示Plugin installation failed,插件列表里Maven Integration灰色不可选sudo systemctl stop jenkins && sudo rm -rf /var/lib/jenkins/plugins/maven-plugin* && sudo systemctl start jenkins插件安装一半失败,残留文件导致冲突。rm -rf彻底删除插件目录,重启 Jenkins 后重新安装

提示:以上所有命令,均可直接复制到你的终端执行。它们不是理论方案,而是我在客户现场键盘上敲过的真实命令。比如第 6 条清华镜像源,我替换了 17 台 Jenkins 服务器,无一例外提速 5 倍以上;第 11 条nohup手动测试,救过我 3 次凌晨两点的紧急发布。

6. 实战心得:那些文档里永远不会写的“脏技巧”

6.1 “一键回滚”不是神话,是三行 Shell 脚本的事

自动化部署的终极考验不是“怎么上线”,而是“上线失败怎么秒级回滚”。我给所有项目标配一个rollback.sh脚本,放在应用服务器~/jenkins-study/目录下:

#!/bin/bash # 回滚到上一个版本 cd ~/jenkins-study # 获取上一个 jar 的文件名(按修改时间倒序,取第二个) prev_jar=$(ls -t jenkins-study-*.jar | sed -n '2p') if [ -z "$prev_jar" ]; then echo "No previous version found" exit 1 fi # 杀掉当前进程 pid=$(ps -ef | grep 'java -jar' | grep jenkins-study | awk '{print $2}') if [ ! -z "$pid" ]; then kill -9 $pid; fi # 启动上一个版本 nohup java -jar "$prev_jar" --server.port=8088 >> log.out 2>&1 & echo "Rolled back to $prev_jar"

在 Jenkins 的Post Steps里,加一个Execute shell,内容是:

sudo -u jenkins ssh test97 '~/jenkins-study/rollback.sh'

这样,只要点击 Jenkins 页面的Build Now,就自动回滚。比手动登录服务器快 10 倍,且不会因手抖输错文件名。

6.2 日志切割:为什么log.out必须用logrotate,而不是>>追加?

nohup java -jar ... >> log.out看似简单,但log.out会无限增长。一个高流量项目,一天日志超 2GB,tail -f log.out直接卡死。解决方案是用logrotate

# 创建 /etc/logrotate.d/jenkins-study /home/jenkins/jenkins-study/log.out { daily missingok rotate 30 compress delaycompress notifempty create 644 jenkins jenkins sharedscripts postrotate sudo -u jenkins pkill