写这篇东西之前,我先把话放这:Jenkins的安装使用,网上教程一搜一大把,但多数都是“点到为止”——装完就没了,插件装不上、Webhook不触发、构建产物传不到服务器上,这些问题没人告诉你。我这些年从零搭了不知道多少套Jenkins环境,从单机到几十个节点的集群都折腾过,踩坑踩到怀疑人生。这篇文章我不打算写成官方文档的复读机,就按实际动手的顺序,把从下载Jenkins到跑通自动化部署的完整链路写一遍,顺带把我遇到过的问题和排查思路全部交代清楚。适合谁看?准备在公司内部搭CI流程的运维同学、被临时抓壮丁搭环境的后端开发,以及想彻底搞明白Jenkins自动部署原理的测试和前端工程师。不管你是第一次听到Jenkins这个名字,还是已经建过几个Job但经常被各种诡异报错卡住,这篇应该都能帮到你。
1. 安装前的准备:版本选型和目录规划
1.1 JDK版本不对,后面全白搭
先说一个很多人还没走到安装就开始翻车的事情:JDK版本。
Jenkins本身是一个Java应用,机器上没有JDK它压根跑不起来。但问题不在“有没有”,而在“版本对不对”。从Jenkins 2.357版本开始,官方要求必须跑在Java 11或Java 17上,如果你机器上还是JDK8,下载最新的war包或者用官方源安装,启动的时候要么直接给你报一个unsupported class version错误,要么起来之后一堆插件加载异常,日志里全是一眼看不明白的NoClassDefFoundError。
我现在的习惯是直接上OpenJDK 17。Jenkins的LTS版本对Java 17支持已经很成熟,而且这两年新版本的插件也在往JDK17靠。装完以后务必检查一下环境变量,别省这一步:
java -version echo $JAVA_HOME如果JAVA_HOME没输出,在/etc/profile里加上:
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 export PATH=$PATH:$JAVA_HOME/bin执行source /etc/profile让配置生效。
补充一个高频场景:公司老项目还是JDK8的,但Jenkins必须用JDK17跑,这两者其实不冲突。Jenkins支持在“Manage Jenkins -> Tools -> JDK”里配置多个JDK安装,然后在每个Job里单独指定用哪个JDK去构建。前提是你机器上装了多个版本,并且能用which java找到对应的路径。这个配置项很多人不知道,导致了“为了Jenkins不敢升级JDK”的误会。
1.2 war包、rpm包、Docker三种方式怎么选
安装方式上,我的建议非常直白:先想清楚你准备把这台服务器当成什么。
如果只是临时测试,或者公司已经有Tomcat在跑,想顺手挂一个Jenkins上去,那用war包最省事。下载jenkins.war,丢到Tomcat的webapps下,启动Tomcat就完事了。更直接一点,不用Tomcat也行,Jenkins本身可以独立运行:
java -jar jenkins.war --httpPort=8080如果这是一台专职CI服务器,我更推荐用官方提供的rpm或者deb包。它会帮你把Jenkins注册成systemd服务,开机自启、日志管理、重启服务都方便。CentOS系大致是这么装:
sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key sudo yum install jenkins -y sudo systemctl start jenkins sudo systemctl enable jenkinsDocker方式我也用过不少,但它有一个必须正视的问题:Jenkins的配置、插件、构建记录全部都在文件系统里,容器一删就全没了。除非你把/var/jenkins_home挂载成持久化卷,否则千万别在生产环境用裸容器跑Jenkins。Docker更适合的场景是Kubernetes里跑动态构建节点,这个后面有机会单独写一篇。
这里还得强调一个路径概念,rpm方式安装后,Jenkins的默认目录结构长这样:
- /var/lib/jenkins:Jenkins主目录,所有配置、插件、构建记录都在这,备份就备份它
- /var/log/jenkins:日志目录
- /etc/sysconfig/jenkins:启动参数文件,改内存、改端口都在这
搞清楚这三个位置,后面所有排查工作都不抓瞎。
1.3 内网环境怎么离线安装
很多公司CI服务器是内网隔离的,装不了外网包,这是很现实的需求。离线安装的核心就两个:war包能拷进去,插件也能装进去。
war包没什么好说的,移动硬盘也好、内网共享目录也好,拷进去就能跑。离线装插件有两个路子。
第一个路子,在能联网的电脑上,从Jenkins插件更新中心把需要的.hpi文件一个个下载下来,拷贝到/var/lib/jenkins/plugins目录下,注意目录权限,然后重启Jenkins。这个方法看起来简单,实际上有个大坑:插件之间是有依赖关系的。你装A插件,结果它依赖B和C,B又依赖D,手动下很容易漏。
我前几年吃过这个亏之后,换了一个更聪明的做法:先在一台能联网的机器上正常装一遍Jenkins,把需要的插件都装好,然后把整个/var/lib/jenkins/plugins目录打包,拷到内网机器解压。这个做法虽然土,但能一次性解决依赖问题,内网机器重启Jenkins后插件全部就位。唯一要注意的是插件版本要和Jenkins主版本匹配,不然启动时控制台会一片红。
2. 初始化配置:解锁管理员、换国内插件源
2.1 第一次启动必须处理的三件事
服务起来之后,浏览器访问http://你的IP:8080,会看到一个解锁页面。初始管理员密码在文件里,直接读:
cat /var/lib/jenkins/secrets/initialAdminPassword把密码复制进去,进入下一步。
这里要特别注意:到了“Customize Jenkins”这一步,一定选“Select plugins to install”,不要选“Install suggested plugins”。推荐插件列表里塞了一堆你可能根本用不到的东西,安装时间极长,而且在国内网络环境下大概率装到一半就失败。选“Select plugins to install”进去以后,先不勾选任何插件,直接点右上角的保存,进去之后再按需安装。这个操作能帮你省掉大量等待时间。
然后创建管理员账号。这里我不厌其烦地提醒一句:账号密码一定记好,这个账号就是整个Jenkins的超级管理员,后面开账号、配权限都靠它。密码强度不要太弱,Jenkins面板暴露在办公网的话,弱口令被扫到就是一场灾难。
2.2 插件源换成国内源的正确姿势
这是新手卡住重灾区。Jenkins默认的插件更新中心地址是updates.jenkins.io,国内访问这个地址速度极其不稳定,装插件经常失败。解决办法是把更新中心换成国内镜像。
图形化路径:“Manage Jenkins -> Plugins -> Advanced settings”,把Update Site里的URL改成清华源:
https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json或者华为云源:
https://mirrors.huaweicloud.com/jenkins/updates/update-center.json但是这里有个超级隐蔽的坑,我估计80%的人都被坑过:只修改Update Site的URL是不够的。Jenkins会把这个update-center.json下载到本地并解析,解析出来的每个插件的下载地址仍然指向官方国外的服务器。你看到的现象是插件列表刷出来了,但安装时依然慢如蜗牛,最后超时失败。
正确的做法是改完后还要处理本地解析出来的配置文件。具体来说,修改/var/lib/jenkins/hudson.model.UpdateCenter.xml,把URL换成国内镜像地址。然后重启Jenkins,等它重新拉取更新中心数据。接着编辑/var/lib/jenkins/updates/default.json,把这个文件里所有包含官方下载地址的地方批量替换成国内镜像地址。这个文件很大,用sed一把梭:
sed -i 's#https://updates.jenkins.io/download#https://mirrors.tuna.tsinghua.edu.cn/jenkins#g' /var/lib/jenkins/updates/default.json sed -i 's#http://updates.jenkins.io/download#https://mirrors.tuna.tsinghua.edu.cn/jenkins#g' /var/lib/jenkins/updates/default.json替换完重启Jenkins,再去装插件,效果立竿见影。
注意:如果镜像源上的插件版本和当前Jenkins版本不兼容,安装时会出现红色警告。这种时候优先升级Jenkins主版本,别强行装,不然运行期各种诡异报错会让你怀疑人生。
2.3 插件我一向只装这些
插件本身不该贪多,装多了全是负担。但有几类插件属于基础建设,几乎每个团队都躲不开。我列一下我的基础清单:
- Git:源码管理必备,没有它Job里连仓库地址都填不了
- Pipeline:流水线任务支持,写Jenkinsfile就靠它
- Publish Over SSH:远程传文件和执行命令,部署必备
- Allure:测试报告插件
- DingTalk:钉钉通知
- Credentials Binding:在Pipeline里安全使用凭据
这些插件在“Manage Jenkins -> Plugins -> Available plugins”里搜索安装就行。换完国内源之后,安装过程通常一两分钟就结束。
3. 第一个自动化任务:从自由风格任务到流水线
3.1 新建一个能跑的Freestyle任务
插件就绪,我们正式建第一个自动化构建任务。我故意先讲自由风格任务,因为它操作直观,适合把“源码管理、构建触发器、构建步骤”这三个核心概念讲清楚。
在Jenkins主页点“新建任务”,输入名称,选择“Freestyle project”。进入配置页面后,核心配置有这几项:
源码管理选Git,Repository URL填仓库地址。如果仓库是私有的,需要添加凭据。点“Add”,选“Username with password”或者“SSH key”。SSH key方式要注意:把Jenkins服务器的公钥添加到GitLab的部署密钥列表里。
构建触发器这里,没有Webhook之前可以先选“Poll SCM”,填一个轮询表达式:
H/2 * * * *意思是每两分钟检查一次仓库有没有新提交,有变化就触发。这种方式实时性不如Webhook,但起步阶段好排查,什么东西都能从日志里看到。
构建步骤里选“执行shell”,写实际命令。Java项目可以写:
mvn clean package -DskipTestsNode项目:
npm ci && npm run build保存之后点“立即构建”,然后点击构建号,查看控制台输出。第一次构建跑通的感觉,还是挺有成就感的。从这一刻起,你的Jenkins才算是真正“用起来”了。
3.2 环境变量和参数化构建
构建脚本里如果全写死路径和名称,过两个月你自己都会看着那一堆硬编码头疼。Jenkins内置了环境变量,帮你在脚本里动态获取Job名、构建号、工作目录等信息。我用过最频繁的变量先列几个:
| 变量名 | 含义 | 示例值 |
|---|---|---|
| JOB_NAME | 任务名 | my-job |
| BUILD_NUMBER | 构建号 | 42 |
| WORKSPACE | 工作目录 | /var/lib/jenkins/workspace/my-job |
| BUILD_URL | 本次构建完整URL | http://jenkins:8080/job/my-job/42/ |
| GIT_COMMIT | 当前代码提交号 | a1b2c3d... |
| GIT_BRANCH | 当前分支 | origin/main |
在“执行shell”里直接echo这些变量就能看到值。想看看还有哪些变量可用,加一行env命令,把整个环境变量表打印出来,然后删掉就行。
参数化构建是我强烈建议掌握的功能。在Job配置里勾选“参数化构建过程”,添加一个String Parameter,名字比如叫ENV,默认值填dev。保存后构建时选择“Build with Parameters”,可以自由修改参数,在构建脚本里直接能用$ENV取到。这套机制在做多环境发布时是救命级的,一个Job配合不同参数就能部署到测试、预发、生产,不用复制三套任务出来。
3.3 从自由风格迁移到流水线
自由风格任务用多了,你很快会遇到问题:构建步骤散落在网页配置区,每次改动都得登录Jenkins去点,没法做代码审查,也没法放进Git里做历史追溯。这时候就应该上流水线了。
流水线有两种语法,声明式和脚本式。新手直接学声明式,结构清晰,可读性好。一个最基础的声明式流水线长这样:
pipeline { agent any parameters { string(name: 'ENV', defaultValue: 'dev', description: '部署环境') } stages { stage('拉取代码') { steps { git url: 'http://gitlab.example.com/group/demo-api.git', branch: 'main', credentialsId: 'gitlab-ssh-key' } } stage('代码构建') { steps { sh 'mvn clean package -DskipTests' } } stage('输出产物') { steps { sh 'ls -lh target/*.jar' } } } }写完之后,新建任务时选“Pipeline”,把脚本粘贴到Pipeline script里,保存构建即可。但更专业的做法是:把这段脚本保存为仓库根目录的Jenkinsfile文件,然后任务配置里选“Pipeline script from SCM”,指向存放Jenkinsfile的仓库。这样一来,构建流程成了代码,每人改都有记录,出问题能回退。团队协作层面,这比在网页上改配置靠谱一个量级。
注意:声明式流水线里,stage名用中文没问题,但agent、steps这些关键字必须是英文。如果脚本里要使用凭据,推荐用withCredentials块,不要把账号密码明文写在脚本里。
4. 进阶实践:GitLab自动触发、部署包上传、Allure报告和钉钉通知
4.1 对接GitLab,提交代码后自动构建
轮询SCM说到底还是被动检查,实时性差,对GitLab服务器也是种负担。标准做法是用Webhook,代码一提交,GitLab主动通知Jenkins触发构建。
先安装GitLab插件。装完后在“Manage Jenkins -> Configure System”里找到GitLab配置区域,填两件事:Connection name,自己起一个比如gitlab-conn;GitLab API Token,这个Token要去GitLab个人设置里生成,权限勾上api和read_repository。
然后在Job配置的“构建触发器”里勾选“Build when a change is pushed to GitLab”,页面下方会显示一个Webhook URL:
http://你的Jenkins地址/project/my-job复制这个URL,去GitLab仓库设置里的Webhooks页面添加,同时勾选Push events。保存后,提交一次代码试试Webhook有没有生效。
这里有个我反复踩的坑:新版Jenkins默认开启CSRF防护,GitLab推过来的请求校验不通过的话,Webhook会返回403。解决办法是:在Job触发器的Webhook URL下方有一个“Secret token”生成按钮,点一下自动生成Token,复制到GitLab Webhook配置里的Secret Token字段。两边配好后,403基本不再出现。
还有个容易忽略的点:Jenkins填写的URL必须是GitLab能访问到的地址。如果Jenkins装在开发机,用localhost配置Webhook,那GitLab自然是访问不了的。这个错误很低级,但每次出问题都有人犯。
4.2 构建产物怎么传上目标服务器
构建机把包打出来,接下来就要面对我们真正关心的问题——部署。最常用的插件是Publish Over SSH。
先在“Manage Jenkins -> Configure System”里的“Publish over SSH”区域添加一个SSH Server。需要填:主机名、端口、用户名、私钥。私钥建议用Jenkins服务器生成的专用密钥对,把公钥追加到目标服务器对应用户的authorized_keys文件里。填完后点“Test Configuration”,返回success才算通过。
然后在Job的“构建后操作”里选择“Send build artifacts over SSH”,配置传输规则。Source files填相对于工作目录的路径,比如target/demo.jar;Remote directory填目标服务器上的目录;Exec command填传输完成后执行的命令,比如:
systemctl restart demo-api这套流程非常直接,而且传输结果和命令执行结果都会回传到Jenkins控制台,排查问题方便。
确实也有不用插件的做法,直接在shell里scp:
scp target/demo.jar deploy@192.168.1.10:/opt/app/demo/ ssh deploy@192.168.1.10 "systemctl restart demo-api"这种方式的好处是不依赖插件,坏处是得提前配好SSH免密,而且报错信息没有Publish Over SSH那么清晰。我还是推荐插件方案,尤其是团队里有人对Linux命令不熟的时候。
4.3 集成Allure,测试报告自动生成
测试跑完之后,一堆XML原始结果几乎没人看,大家想要的是带趋势、带失败分类、能一眼定位问题的HTML报告,这就是Allure的价值。
系统层面需要先装Allure命令行工具,装完确认allure命令能执行。Jenkins里装Allure插件。然后在流水线脚本里加一个Stage:
stage('测试报告') { steps { allure includeProperties: false, jdk: 'default', report: 'target/allure-report', results: [[path: 'target/allure-results']] } }这里有一个必须注意的前提:results路径下必须真实存在allure-results目录,否则插件直接报错。我习惯在测试阶段先执行mvn test,生成原始结果,再跑Allure聚合。构建完成后Job页面左侧会出现Allure Report入口,点开就是清晰的HTML报告。
补充一个细节:Allure报告默认会保留每次构建的数据,时间长了会积累大量文件。需要在流水线里定期清理旧的报告目录,或者找时间手动清一下,不然磁盘容易被悄悄占满。
4.4 钉钉通知,构建结果主动找上门
构建失败不能只挂在Jenkins页面里等着有人来看,得主动通知到人。钉钉机器人是团队协作里最常见的接收端。
先去钉钉群里添加自定义机器人。安全设置选“自定义关键词”或“加签”都可以,选加签的话会得到一个密钥,后续请求需要用签名。创建成功后拿到Webhook地址,填到Jenkins的DingTalk插件配置里。
在流水线里发通知很简单:
post { success { dingtalk(robot: 'my-bot', notifyEveryone: true, message: '构建成功,可以发布') } failure { dingtalk(robot: 'my-bot', notifyEveryone: true, message: "构建失败:${env.BUILD_URL}") } }如果插件默认的消息格式满足不了需求,更灵活的方式是用curl直接调钉钉机器人接口。钉钉的机器人消息支持markdown格式,拼一个自定义消息发出去:
curl -X POST "$WEBHOOK_URL" \ -H 'Content-Type: application/json' \ -d '{"msgtype":"markdown","markdown":{"title":"构建通知","text":"### 构建失败\n 分支: main\n 构建号: 42\n [查看日志]('"$BUILD_URL"')"},"at":{"isAtAll":false}}'这种方式的好处是消息内容可以完全自定义,塞入提交人、代码变更、测试通过率等任何你关心的信息。我这边生产上的通知脚本,早期就是从这条curl命令长出来的。
5. 常见问题与排查技巧实录
5.1 内存不足导致构建卡死
Jenkins本身是Java应用,默认堆内存往往只有512MB,并发一上来就卡。修改/etc/sysconfig/jenkins里的JENKINS_JAVA_OPTIONS,把堆内存调大:
JENKINS_JAVA_OPTIONS="-Djava.awt.headless=true -Xmx2048m -Xms512m"改完重启。如果服务器内存本身就不大,建议把并发执行器数量调小。在“Manage Nodes”里把内置节点的Number of executors从默认的2改成1。要不然多个构建同时跑,CPU和内存瞬间被打满,构建互相拖累,看起来就像卡死。
5.2 插件装不上或者列表都刷不出来
插件安装失败九成是更新源的问题。按前面2.2节的步骤把更新中心和插件下载地址全部换成国内源,再配合sed命令把本地解析出的JSON文件里的地址也一并替换。如果还是失败,去日志文件/var/log/jenkins/jenkins.log看具体报错,常见的原因有两个:一是插件与Jenkins版本不兼容,二是插件之间冲突。前者去插件管理页面换个兼容版本,后者只能逐个排查,卸载最近安装的插件试试。
5.3 定时任务不执行,或者时间差8小时
Jenkins的定时表达式是基于服务器本地时区的。很多服务器默认是UTC时间,于是你配的定时任务会比预期晚8小时执行。解决方案:在“Manage Jenkins -> Configure System”里找“User Defined Time Zone”,填Asia/Shanghai,保存后重启。
另外提醒一个容易误解的地方:Jenkins的cron和Linux crontab虽然很像,但它支持一个H字母,表示“为一个哈希运算出的随机值”。比如H/30 * * * *并不是固定每小时的00分、30分执行,而是每30分钟执行一次,具体几分由Jenkins自己算。如果业务上必须精确在00分和30分,就写成0,30 * * * *。
5.4 Webhook不触发或者一直403
Webhook是高频踩坑点。403大概率是CSRF没过,按4.1节配置Secret Token。不触发的排查路径我理一下:第一,在GitLab的Webhook设置页面点击Test,看发送结果是200还是其他状态码;第二,确认Jenkins地址是GitLab能访问的,别填localhost;第三,确认Job里勾选了构建触发器对应选项,别只保存忘了勾。
5.5 聊聊Jenkins与AI的新生态
最近AI编程工具火得一塌糊涂,里面有个词也传到了CI/CD圈子:MCP,全称Model Context Protocol。它是用来让AI助手调用外部工具的一套协议。社区里已经有人做了Jenkins的MCP服务端,把查询构建状态、触发构建、获取日志这些操作封装成标准工具。配合支持MCP的AI客户端,你可以直接说“帮我触发某某任务并看看结果”,AI自己完成调用。
这个生态还比较年轻,但方向很明显:未来CI/CD平台的日常操作会越来越轻,AI充当操作员,人来负责判断和兜底。不过我的建议始终是,先把Jenkins本身的基础体系玩明白,再去接这些新工具,底子不稳的话,AI只会帮你制造更多错误。
6. 最后再分享几条实在的经验
Jenkins用久了你会发现,它本质上就是一个“把重复事情自动化”的调度中心,真正的难度从来不在装上这个服务,而在于把构建、测试、部署流程梳理清楚,用流水线固化下来。我踩过无数坑之后的三个体会,这里毫无保留地分享一下。
第一,Jenkinsfile一定要纳入Git版本管理。任何人对流水线的改动都留痕,出了问题能明确知道是谁改的、改了什么,回退也方便。把构建流程当成代码来管,收益远超想象。
第二,从第一天就把权限控制好。管理员账号不要到处共享,普通开发给Job级别的权限就够,别的一律不给。等团队扩到几十人再回来收拾权限摊子,会非常痛苦。
第三,定期备份/var/lib/jenkins目录。配置、插件、构建记录全在这一个目录里,服务器挂了直接恢复。我见过太多人重装系统之后对着空白的Jenkins发呆,那种绝望没必要经历。
最后分享一个压箱底的小技巧:在构建脚本开头加一行set -ex。作用很简单——shell每执行一条命令,先把命令本身打印出来;一旦任何一条命令失败,立即终止整个构建。排查“构建失败但不知道卡在哪一步”的时候,这个参数比任何调试工具都好用。Jenkins的坑是踩不完的,但每解决一个,整套流程就会顺滑一点。希望这篇能帮你少走几条弯路,尽早把自动化流水线跑起来。