遇到Jenkins构建一直转圈,我的习惯是先把现象确认清楚:点开构建历史,看Console Output最后几行,同时看一眼Agent上的进程状态。不要急着重启,也不要连续点取消,否则可能丢掉定位问题的重要信息。
Jenkins构建卡在某个任务上时,最常见的原因不是Jenkins本身死掉,而是某个构建步骤在等待外部资源。例如,调用Maven或npm时,如果对应的私有仓库或中央仓库网络连接缓慢,或者有未配置超时的HTTP请求被挂起,就会表现为界面上的构建进度条长时间不动。此时可以先确认构建执行的节点(Agent)上是否有对应进程仍在运行,比如ps命令能看到java或node进程但没有CPU消耗,大概率是处于I/O等待或网络阻塞状态,而不是构建逻辑本身死循环。
确认进程存在且CPU接近0后,我会先做一次网络连通性检查:在Agent上执行 curl -I --max-time 5 访问当前构建正在访问的仓库地址,看能否在超时时间内返回响应。如果请求卡住,基本就是外部网络或服务问题;如果请求正常,再往构建脚本内部排查。
用线程栈定位等待点
当构建卡在固定阶段时,先不要盲目重启Jenkins。第一步是打开该次构建的控制台输出(Console Output),查看最后几行日志是在做什么操作。如果日志停在“Downloading...”或“Running post-build step”这类位置,可以进一步检查对应的网络连接和磁盘空间。若日志里没有明显错误,但一直停在同一行,可以抓取线程栈:在Jenkins节点上执行jstack > threaddump.txt,然后查看是否有线程处于BLOCKED或WAITING状态,以及是否在等待某个锁。尤其是使用Pipeline时,卡住的经常是某个stage中的外部命令调用。
抓取线程栈后,可以先搜索java.lang.Thread.State,看是否存在多个线程同时BLOCKED。如果只有一个线程停在某段代码上,可以把线程栈里出现的类名或方法名跟当前构建的插件关联起来。比如看到等待URLConnection或HttpClient相关的栈帧,通常就是网络请求没有设置超时。
建议的处理顺序
确认卡住点后,先保留现场信息:Console Output的完整内容、线程栈文件、Agent上进程列表和网络连接状态。然后可以进入下一步操作。
对于卡住的构建,最直接的操作是先取消该次构建(Cancel),然后限流重试。但取消前要注意,如果构建里启动了子进程或外发任务,取消Jenkins任务并不会自动终止这些子进程,可能会导致资源被占用。建议在Pipeline中为关键步骤设置timeout,例如timeout(time: 10, unit: 'MINUTES') { sh '...' },这样即使外部命令异常挂起,构建也会在指定时间后自动终止。如果经常卡在同一个位置,优先排查该步骤依赖的服务是否可用,而不是反复人工干预。
取消构建后,需要到Agent上确认残留子进程。在Linux上可以用ps -ef | grep java或者ps -ef | grep node找一下是否还有任务进程在跑。如果有,先确定这个进程是否属于刚才取消的构建,确认后手动终止。这里不建议一上来就重启Jenkins,因为重启会丢失所有正在构建的任务,而且可能留下锁文件。
注意区分UI假死和构建真正卡住
一个容易被忽略的坑是Jenkins的JVM参数或GC停顿导致UI假死,但构建实际还在运行。表现为界面点击没反应,但构建进度还在变,或者极慢。这时检查Jenkins主机的CPU和内存使用,如果发现频繁Full GC,可能是堆太小或发生了内存泄漏。另外,如果卡在“Waiting for next available executor”,那不是构建本身卡住,而是没有可用的执行器,需要检查是否有大量并发的Pipeline在等待。还有,使用Parallel Test Executor等插件时,如果报告文件路径写错,也会导致构建停在等待测试结果生成的位置。
判别的办法是:登录Jenkins主机,直接查看进程的CPU时间。如果jenkins主进程的CPU使用率在正常波动,而构建进度也在缓慢变化,那就是UI或网络延迟问题;如果进程CPU完全不动,构建日志也不再变化,才是真正的等待或阻塞。
哪些操作有风险
处理Jenkins构建卡住时,有两类操作要特别谨慎:一是强制杀掉构建进程,尤其是在Windows节点上,随意kill可能会导致文件句柄未释放或Git仓库索引损坏;二是重启Jenkins服务,虽然能清掉卡住的队列,但正在运行的构建会直接丢失,而且可能造成工作空间里残留锁文件或半写的产物。如果构建卡在Pipeline的某个stage,且该stage调用了外部的部署脚本,强行终止可能会让部署留下不一致状态。所以最好先保留现场信息(日志、线程栈),确认卡住点后再决定是否终止。
如果必须强制终止,建议先在Jenkins界面取消构建,等待一段时间,看子进程是否被回收。如果子进程一直存在,再针对具体PID做kill。在Windows节点上,优先考虑taskkill /F /PID <pid>前先观察是否有关联进程树,避免单独杀掉父进程后留下一堆子进程继续占用文件。
减少再次卡住的配置习惯
对于反复出现的卡住,我会把重点放在超时和依赖上。Pipeline步骤设置timeout是最直接的方法;对于Shell步骤,还可以给命令自身加超时,比如Linux的timeout 600 ./deploy.sh。同时,检查Jenkins全局安全配置里的Agent 的 Launch method相关设置,确认JVM参数是否合理,特别是堆内存不要设得太小。
另外,给构建节点留出足够的磁盘空间和inode,日志文件过大也会导致IO卡顿。定期查看系统日志和Jenkins的系统日志,寻找OOM或GC长时间停顿的记录。这样至少能在下一次卡住之前多拿到一些线索。