ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Jenkins 安装部署与 CI/CD 流水线实战避坑指南

Jenkins 安装部署与 CI/CD 流水线实战避坑指南 1. 先把 Jenkins 这件事想明白别急着敲命令1.1 从一个真实的交付场景说起Jenkins 安装部署这件事我在过去几年里来来回回折腾过十几遍——从最早在虚拟机上跑 war 包到后来用包管理器装成系统服务再到现在动不动就要在容器里起一套。每次场景不同踩的坑也完全不一样。所以这篇东西不打算写成一份下一步、下一步、完成的说明书我更想把自己这几轮下来形成的判断逻辑摊开讲什么时候该装、装哪个版本、装完要改哪些默认配置以及哪些地方看着无关紧要、实际上能让你半夜被叫起来。先说清楚它是干什么的。Jenkins 是一套持续集成与持续交付的调度中枢核心能力是把拉代码、编译、跑测试、打包、传文件、重启服务这一串动作串成一条可重复执行的流水线。它最值钱的地方不是能自动构建而是可重复和可追溯——谁触发的、用的哪个 commit、构建产物在哪、日志长什么样全都有据可查。这对于改一行代码、上线要三个人配合一上午的团队来说价值是脱胎换骨的。适合读这篇的人大致分三类一类是压根没碰过、需要从零搭一套出来的运维或后端同学一类是装过但一直卡在插件下载、权限、部署脚本这些细枝末节上的还有一类是已经跑起来了但构建老是无缘无故失败想找找是不是环境没配干净。三类人关注的重点不一样我会在对应章节里标出来。先给一个结论性判断如果你团队每周发布次数少于一次、而且发布流程全靠人工文档维持那装 Jenkins 之前先把发布步骤写成一份能照着执行的清单比先装软件重要得多。工具放大的是流程流程本身就是乱的装上去只会乱得更快。1.2 版本选型与运行环境的前置判断Jenkins 的版本分两条线LTS长期支持和每周更新版。生产环境一律选 LTS这个不用犹豫。每周版是给插件开发者试水用的它的 API 和依赖会动你今天跑通的东西下周可能就报错。LTS 大约每 12 周发一个大版本中间只打补丁稳定性高得多。选完版本线紧接着是 Java 版本。这是一个高频踩坑点Jenkins 的运行依赖和你的业务项目依赖是两回事别搞混。你的项目用 Java 8 编译不代表 Jenkins 本身能跑在 Java 8 上。近两年的 LTS 已经明确要求 Java 11 或 17更新的版本要求 17 或 21。我的做法是Jenkins LTS 版本区间最低 Java 要求我的推荐2.3xx 及更早Java 8仅老环境维护用2.4xx 系列Java 11 / 17Java 172.5xx 及之后Java 17 / 21Java 17生态兼容最稳为什么推荐 17 而不是一路冲到 21因为部分老插件对 21 的支持还没跟上装上之后会出现奇怪的UnsupportedClassVersionError或者类加载失败。17 目前的插件兼容面最广属于不出彩但不出事的选择。判断服务器规格也有讲究。很多人觉得 Jenkins 就是个调度器给 2 核 4G 就够了结果一跑 Maven 构建就 OOM。真实情况是Jenkins 主进程本身吃不了多少内存真正吃资源的是它调起来的那些构建任务。如果构建和主进程在同一台机器上按并发构建数 × 单个构建峰值内存 2G 给主进程来估。比如要支持两个 Maven 项目同时构建每个峰值 1.5G那这台机器至少 8G 起步。如果构建任务放到独立的 Agent 节点上执行那 Master 给 2 核 4G 完全够用。磁盘是另一个容易忽略的地方。$JENKINS_HOME下面会堆积每次构建的日志、归档产物、工作区残留一个活跃任务跑半年攒出几十个 G 是常态。我一般会做两件事一是给$JENKINS_HOME单独挂一块盘别和系统盘混在一起二是给任务配置保留最近 10 次构建的策略别让它无限长。这两件事在装机阶段顺手做了后面能省掉大量清理工作。2. 安装前的环境准备八成问题出在这一步2.1 JDK 安装与多版本共存的隔离方案服务器上很可能已经装了别的 Java 版本被别的服务占着。直接改全局JAVA_HOME是自杀式操作风险在于你不知道哪个服务会因此挂掉。正确做法是给 Jenkins 单独指定一个 JDK不动全局环境。以 Linux 为例把 JDK 解压到独立目录然后在 Jenkins 的启动配置里显式声明路径mkdir -p /usr/local/jdk tar -zxf jdk-17_linux-x64_bin.tar.gz -C /usr/local/jdk /usr/local/jdk/jdk-17.0.10/bin/java -version验证输出里能看到17.0.10这类信息说明包解压正常。接着在 Jenkins 的启动参数中把路径写死# systemd 环境下编辑 override 配置文件 sudo mkdir -p /etc/systemd/system/jenkins.service.d sudo tee /etc/systemd/system/jenkins.service.d/override.conf EOF [Service] EnvironmentJAVA_HOME/usr/local/jdk/jdk-17.0.10 EnvironmentJENKINS_JAVA_CMD/usr/local/jdk/jdk-17.0.10/bin/java EOF sudo systemctl daemon-reload不同发行版的包配置文件路径不一样别硬套。Debian/Ubuntu 系常见的是/etc/default/jenkinsRHEL 系旧包用/etc/sysconfig/jenkins新版 RHEL 包改用了 systemd override。拿不准就用dpkg -L jenkins | grep -E default|sysconfig或者rpm -qc jenkins先查一下比到处翻文档快。注意JAVA_HOME和JENKINS_JAVA_CMD这两个我都写了。有些包只认前者有些启动脚本直接用后者。两个都设上不会冲突能省掉一次改了没生效的困惑。2.2 用户、目录与端口的规划Jenkins 的安装包默认会创建一个名为jenkins的系统用户$JENKINS_HOME一般是/var/lib/jenkins。这个默认配置大部分时候没问题但有两个地方要提前想清楚。第一个是部署权限。Jenkins 要用 SSH 或者 SCP 把构建产物传到目标服务器、重启远端服务这需要它有一个可用的密钥对。这个密钥必须以jenkins用户的身份生成放在/var/lib/jenkins/.ssh/下面权限设成 600目录设成 700。用 root 生成再挪过去权限和属主不对SSH 会直接拒绝报的错还特别含糊Permission denied (publickey)能查很久。sudo -u jenkins ssh-keygen -t rsa -b 4096 -f /var/lib/jenkins/.ssh/id_rsa -N sudo -u jenkins chmod 700 /var/lib/jenkins/.ssh sudo -u jenkins chmod 600 /var/lib/jenkins/.ssh/id_rsa第二个是端口。默认 8080。这端口太热门了Tomcat、各种管理后台都爱用它撞上就得改。改端口的位置同样取决于包的版本systemd override 里加一行EnvironmentJENKINS_PORT18080或者直接改服务单元文件。改完记得看一眼防火墙和 SELinuxsudo firewall-cmd --add-port18080/tcp --permanent sudo firewall-cmd --reload # 如果开了 SELinux还需要放行这个端口的访问 sudo semanage port -a -t http_port_t -p tcp 18080SELinux 这一关特别容易被忽略。现象是curl localhost:18080在本机通得不得了从别的机器访问就是连不上防火墙规则看着也没问题查半天查不出来。原因就是 SELinux 不允许 http 类服务监听这个非标准端口。这一条我踩过至少三次。3. Linux 环境下的完整部署过程3.1 两条安装路线的取舍包管理 vs 离线部署安装方式我分两条路讲选哪条取决于目标机器的网络状况。路线一包管理器安装。适合能直连外网的机器。以 Debian 系为例sudo wget -O /usr/share/keyrings/jenkins-keyring.asc https://更新站点/jenkins.io.key echo deb [signed-by/usr/share/keyrings/jenkins-keyring.asc] https://更新站点/debian-stable binary/ \ | sudo tee /etc/apt/sources.list.d/jenkins.list /dev/null sudo apt-get update sudo apt-get install -y jenkins这套方式的好处是服务、用户、目录、日志轮转全都自动配好了systemctl start jenkins就能起来。缺点是它会把你的 JDK 依赖也一起管起来如果系统默认 JDK 版本不对apt install会直接装一个新的进来可能影响别的东西。路线二War 包部署。适合内网隔离环境或者你想完全掌控 Java 版本的情况。下载一个jenkins.war扔到任意目录用指定 JDK 启动nohup /usr/local/jdk/jdk-17.0.10/bin/java \ -Duser.timezoneAsia/Shanghai \ -Dfile.encodingUTF-8 \ -Xms512m -Xmx2048m \ -jar /opt/jenkins/jenkins.war \ --httpPort18080 \ --prefix/jenkins \ /opt/jenkins/jenkins.log 21 这里有几个参数值得单独说。-Duser.timezone不设的话构建日志时间戳会是 UTC和你的直觉对不上排查问题时很容易被带偏。-Dfile.encodingUTF-8不设构建日志里的中文全是乱码方块。-Xmx给 2G 是主进程的堆别给太小插件多了之后光是加载就要吃不少。War 方式的代价是没服务管理得自己写 systemd 单元文件或者拿 supervisor 管着。我通常还是写个 systemd unit这样开机自启、崩溃重启、日志归集三件事都有着落。单元文件里Restarton-failure、RestartSec10这两个必须有Jenkins 偶尔会因为插件加载失败自杀自动拉起来比人工介入快得多。3.2 首次解锁与初始化配置服务起来之后浏览器打开http://ip:18080第一眼看到的是一页解锁 Jenkins要求你输入初始管理员密码。这个密码的位置# 包管理器安装 sudo cat /var/lib/jenkins/secrets/initialAdminPassword # War 包部署 cat $JENKINS_HOME/secrets/initialAdminPassword复制粘贴进去接下来的界面会让你选安装推荐的插件还是自定义。这里有个经验如果机器的网络能直连更新中心选推荐插件省事如果不能先跳过所有插件安装把更新中心地址换掉再回来装。因为插件下载这一步失败率极高卡在那里转圈十几分钟最后报一堆错非常打击人。进到主界面之后先做三件事顺序别乱。第一件改系统时区。Manage Jenkins→System→ 找时区设置选Asia/Shanghai。命令行里设了-Duser.timezone的话这里其实已经生效但界面上再确认一次不亏。第二件配 URL。同一页有个Jenkins URL的配置项默认是http://localhost:18080/。这个值会被写进邮件通知、Webhook 回调、构建链接里。如果你后面要接 GitLab 的 Webhook 或者钉钉通知这个地址填 localhost 的话回调根本没人能访问到。改成实际的机器 IP 或者域名这一步早晚要做晚做就要回头返工。第三件加管理员账号。初始化向导会让你建一个别偷懒跳过。Jenkins 默认的admin账号存在安全隐患换个用户名、设个强密码顺便把允许注册关掉。3.3 更新站点替换与插件安装提速插件装不上、装得慢是新手劝退率最高的一环。原因通常是更新中心地址在网络层面不可达界面会提示该 Jenkins 实例似乎已离线。解决办法是把更新中心地址换成一个可达的开源镜像站点。具体操作Manage Jenkins→Plugins→Advanced settings页面下方有Update Site的 URL 输入框替换成镜像站点地址然后点Check now。如果状态从离线变成显示了插件数量就说明通了。注意替换之后一定要点一次Check now触发元数据重新拉取。只改地址不触发页面还是显示旧的缓存状态你会以为没生效。如果机器完全不能出网那就走离线安装这条路。流程是在一台能出网的机器上用同样的 Jenkins 版本从插件管理页面下载jenkins-plugin-manager或者手动把.hpi文件下下来然后拷到目标机器的$JENKINS_HOME/plugins/目录下重启服务。这里有个坑.hpi文件放进 plugins 目录后必须重启才会被加载光刷新页面没用。另外插件之间有依赖关系手动下了主插件但没下依赖启动日志里会报Failed to load得去日志里找缺哪个。我一般会优先装这几个Git plugin、Pipeline、Credentials Binding、GitLab Plugin、Publish Over SSH、DingTalk或者别的通知插件、Role-based Authorization Strategy。前五个是干活用的后面两个是权限和通知按需。4. Windows 环境下安装的几个关键差异4.1 安装包方式与服务账户的选择Windows 上装 Jenkins最省事的是下 MSI 安装包一路下一步。它会默认注册成 Windows 服务JENKINS_HOME一般在C:\ProgramData\Jenkins\.jenkins初始密码在同目录的secrets\initialAdminPassword里。安装过程中会让你选运行服务的账户默认是本地系统账户。这个选项在纯构建场景下能用但一旦涉及网络访问就会出问题——本地系统账户访问网络共享目录时用的是机器账户的身份不是你的域账户权限基本为零。如果你的构建产物要往一个共享盘上丢或者要从网络位置拉依赖必须换成指定用户账户填一个有相应权限的账号。另一个必改项是安装时的端口和 JDK 路径。JDK 路径一定要选到bin的上一级目录选错了服务起不来事件查看器里能看到明确的报错。端口如果被占用安装程序会提示但有时候提示得含糊装完了服务起不来。装之前先用netstat -ano | findstr 8080看一眼省得折腾。Windows 上还有一个中文相关的小坑。构建日志里的中文如果显示成乱码需要在服务的启动参数里加-Dfile.encodingUTF-8。这个在 MSI 安装方式下要改jenkins.xml里的arguments标签改完重启服务。4.2 防火墙、路径与凭据验证的注意事项Windows 防火墙默认会拦入站。装完之后本机访问localhost:8080没问题同事从别的机器访问就超时就是这个原因。加一条入站规则放行对应端口即可New-NetFirewallRule -DisplayName Jenkins -Direction Inbound -Protocol TCP -LocalPort 8080 -Action Allow路径分隔符是另一个高频问题。流水线脚本里如果你写了sh步骤在 Windows 节点上会直接失败因为没这个命令。要么全用bat要么装一个 Git for Windows 然后用它自带的 bash。我倾向于后者因为这样 Linux 和 Windows 节点的脚本能共用大部分逻辑迁移成本低。关于凭据验证很多人第一次在 Windows 上加 SSH 凭据时会卡住填了用户名和私钥点验证按钮报一个含糊的连接错误。大多数情况是私钥格式问题。Jenkins 的 SSH 凭据要的是 OpenSSH 格式的私钥-----BEGIN OPENSSH PRIVATE KEY-----开头那种。而用 PuTTYgen 生成的.ppk文件是不认的得先转换。同理用ssh-keygen生成后如果做过-m PEM参数转换开头是-----BEGIN RSA PRIVATE KEY-----也是能用的。判断标准很简单看开头是不是 PEM 或 OPENSSH 格式.ppk直接放弃重新生成。5. 核心配置凭据、全局工具与环境变量5.1 凭据管理别把密码写在脚本里凭据管理是 Jenkins 里最值得认真对待的一块。项目里最丑的做法就是在构建脚本里明文写数据库密码、SSH 密码、API Token一旦有人有权限看任务配置就等于把生产密码发出去了。正确的姿势是Manage Jenkins→Credentials→System→Global credentials→Add Credentials。常用的类型有三种凭据类型适用场景关键字段Username with passwordGit 拉代码、HTTP 接口认证用户名 密码/TokenSSH Username with private key传文件、远程执行命令用户名 OpenSSH 私钥Secret textAPI Token、Webhook 密钥单字段密文建好之后在流水线里用withCredentials把值注入成环境变量withCredentials([usernamePassword( credentialsId: gitlab-account, usernameVariable: GIT_USER, passwordVariable: GIT_PASS )]) { sh git clone http://$GIT_USER:$GIT_PASS代码仓库地址/group/project.git }这样密码只会出现在内存里的环境变量中构建日志里会被自动打成****。要留意的是如果你的脚本把变量echo出来做调试Jenkins 的脱敏机制能盖住大部分情况但不是万能的——比如把变量做一次 base64 编码再打印脱敏就失效了。所以调试完记得把set -x或者echo语句删掉。5.2 全局工具配置与可用环境变量清单Manage Jenkins→Tools这个页面负责配置 Maven、JDK、Gradle、NodeJS 这些构建工具的安装。你可以让 Jenkins 自动下载也可以指向机器上已有的安装路径。生产环境我倾向于指向已有路径因为自动下载会去外网拉网不通就卡在那里而且版本不可控。配置好之后在流水线里用tools块引用pipeline { agent any tools { maven maven-3.9.6 jdk jdk-17 } stages { /* ... */ } }接下来是环境变量这块很多人配流水线的时候不知道有哪些现成的变量能用只能靠猜。常用的我列一下这些是开箱即用、不需要额外插件的变量名含义典型用法WORKSPACE当前构建的工作目录绝对路径定位源码和产物BUILD_NUMBER当前构建序号产物命名加版本号BUILD_ID构建 ID通常等于序号和上者类似JOB_NAME任务名称日志标记、通知内容BUILD_URL本次构建的页面地址通知里附链接GIT_COMMIT本次构建对应的 commit 短哈希记录版本、回滚依据GIT_BRANCH分支名区分环境发布BRANCH_NAME多分支流水线里的分支名仅多分支任务可用JENKINS_URL系统配置里的 Jenkins 地址拼接回调地址NODE_NAME执行本次构建的节点名排查是哪个节点出的问题BUILD_USER触发者的用户名需要 Build User Vars 插件GIT_COMMIT这个变量我几乎每次都用。用途是把构建产物命名成app-${BUILD_NUMBER}-${GIT_COMMIT}.jar出问题的时候一眼能看出这个包对应哪次代码提交回滚的时候不用翻记录。这个小习惯能省掉非常多的沟通成本。5.3 GitLab Connection 配置与回调打通如果你的代码在 GitLab 上配好这条路能实现推代码自动触发构建。整体分三步。第一步在 Jenkins 里装GitLab Plugin然后在Manage Jenkins→System里找到GitLab配置区填 GitLab 的地址和 API Token。Token 在 GitLab 的个人设置里生成权限至少给api。填完点测试连接通了会显示成功。第二步在流水线里声明使用这个连接pipeline { agent any triggers { gitlab(triggerOnPush: true, triggerOnMergeRequest: true, branchFilterType: All) } /* ... */ }第三步回到 GitLab 的项目设置里加 WebhookURL 填http://Jenkins地址/project/任务名勾上 Push events。加完可以点一下测试Jenkins 那边应该立刻起一次构建。这一步最常见的失败是 URL 填错。注意任务名如果含中文或者空格需要 URL 编码。另外如果 Jenkins 前面挂了反向代理还需要在代理配置里透传X-Forwarded-Proto和X-Forwarded-Host这两个头不然 GitLab 收到的回调地址可能是http://而实际是https://会报 403。6. 跑通一条 Java Web 的自动部署流水线6.1 任务创建与参数化设计上面配置都齐了现在把它串起来。目标很明确推代码 → 自动编译 → 打 jar 包 → 传到目标服务器 → 重启服务 → 发通知。新建任务时选流水线然后在流水线脚本里写。第一个要考虑的是参数化。加几个参数让同一条流水线能发布到不同环境parameters { choice(name: DEPLOY_ENV, choices: [test, staging, prod], description: 目标环境) string(name: APP_VERSION, defaultValue: , description: 版本号留空自动生成) booleanParam(name: SKIP_TESTS, defaultValue: false, description: 跳过单元测试) }为什么用choice而不是自由文本因为自由文本意味着有人会打成Prod、production、PROD然后你的if判断全部落空。choice从源头把可选值锁死这个设计能避免一整类诡异问题。再加个并发控制。同一个环境的部署任务不应该同时跑两个不然可能出现两次部署交叉执行、最终状态不确定的情况options { disableConcurrentBuilds() timeout(time: 30, unit: MINUTES) buildDiscarder(logRotator(numToKeepStr: 20, artifactNumToKeepStr: 5)) }disableConcurrentBuilds()是关键timeout是防止某个步骤卡死把执行器占满buildDiscarder就是前面说的清理策略日志留 20 份、产物留 5 份够了。6.2 构建、传输、部署三段式脚本实现完整的脚本我拆成三段看。第一段拉代码和编译stage(Checkout) { steps { checkout scm script { env.COMMIT sh(script: git rev-parse --short HEAD, returnStdout: true).trim() env.VERSION params.APP_VERSION ?: ${env.BUILD_NUMBER}-${env.COMMIT} } } } stage(Build) { steps { sh mvn clean package -DskipTests${params.SKIP_TESTS} \ -Dmaven.test.failure.ignorefalse } }sh步骤加了returnStdout: true之后返回值是命令输出而不是退出码所以必须.trim()一下否则会带上尾部的换行拼进文件名里会出幺蛾子。第二段推包到目标服务器stage(Deploy Package) { steps { sshagent(credentials: [deploy-ssh-key]) { sh scp -o StrictHostKeyCheckingno \ target/app-${env.VERSION}.jar \ deploy目标服务器:/opt/app/releases/ } } }sshagent这个步骤会自动把凭据里的私钥加载到 ssh-agent 里用完清理私钥不会落到磁盘。比起把私钥写死在脚本里这个做法安全得多。-o StrictHostKeyCheckingno是跳过首次连接的主机指纹确认在自动化场景下必须加不然脚本会卡在一个交互式提问上直到超时。第三段切软链、重启服务stage(Restart Service) { steps { sshagent(credentials: [deploy-ssh-key]) { sh ssh -o StrictHostKeyCheckingno deploy目标服务器 set -e cd /opt/app ln -sfn releases/app-${env.VERSION}.jar current.jar sudo systemctl restart app-server sleep 5 systemctl is-active app-server } } }这里用了软链接切换的经典做法新包放到releases/目录下current.jar是个软链指向当前生效的版本。好处是回滚只需要把软链指回旧版本再重启秒级完成不需要重新传包。set -e是必须的。不加的话中间某一步失败了脚本还会继续往下跑最后服务重启成功但用的是旧的包你会以为部署成功了。加上之后任何一步失败立刻中断Jenkins 那边也就标红了。6.3 构建成功后的通知外发部署完不通知等于没部署。通知这块我建议至少接一个即时通讯渠道。以钉钉为例装DingTalk插件在系统配置里填机器人的 Webhook 地址和加签密钥然后在流水线里加post块post { success { dingtalk( robot: deploy-robot, type: MARKDOWN, title: 部署成功, text: [ ### 部署成功, - 环境: ${params.DEPLOY_ENV}, - 版本: ${env.VERSION}, - 提交: ${env.COMMIT}, - [查看构建](${env.BUILD_URL}) ] ) } failure { dingtalk(robot: deploy-robot, type: TEXT, text: [部署失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}, 查看: ${env.BUILD_URL}]) } }经验通知里一定要带上BUILD_URL。没有链接的通知收到的人第一反应是问你哪个任务失败了你还得手动去翻加了链接这一来一回就省了。7. 常见报错排查与独家避坑清单7.1 高频报错速查表跑得久了你大概会反复遇到下面这些。我把它们和原因、处理办法整理成一张表报错关键字大概率原因处理办法UnsupportedClassVersionErrorJenkins 本身或插件的 Java 版本不匹配检查JAVA_HOME是否指向 JDK 17确认没被别的环境变量覆盖该实例似乎已离线更新中心地址不可达换成可达的镜像站点点Check now触发刷新No such file or directory指向 java启动脚本找不到 JDK在服务配置里显式写JAVA_HOME和JENKINS_JAVA_CMDPermission denied (publickey)SSH 密钥权限或属主不对确认密钥属于 jenkins 用户权限 600目录 700403 No valid crumb反向代理下 CSRF 校验失败配置代理透传X-Forwarded-*头或确认 Jenkins URL 配置正确构建日志中文乱码编码未指定加-Dfile.encodingUTF-8启动参数Out of memory堆内存不足调大-Xmx或把构建任务挪到独立 Agent构建卡住不动某个步骤在等交互输入检查是不是缺了-o StrictHostKeyCheckingno之类的非交互参数Docker 拉取镜像超时拉取源不可达配置可达的镜像加速地址或在有网机器上预先导入镜像Job configuration保存报错磁盘空间不足df -h看一下$JENKINS_HOME所在分区7.2 那些文档里不会写的隐蔽问题表格能解决的是明面上的问题下面这几个是真正折腾人的。第一磁盘写满导致的诡异现象。现象是任务打不开、配置保存失败、插件加载不全但服务本身是活的日志里也没什么明显的 ERROR。我遇到过一次查了两个小时最后df -h一看/var分区 100%。原因是某次构建的产物被归档了几百兆一个攒了几百次。所以看到 Jenkins 行为异常第一反应应该是看磁盘不是看日志。第二执行器数量配太少。默认情况下 Master 节点的执行器数是 2。如果有任务卡住另外那个名额很快也被占掉后面所有任务都在排队界面上显示等待下一个可用的执行器。一个应急办法是临时把执行器数调大但这只是治标。治本的做法是搭 Agent 节点把构建负载从 Master 上卸下来。Master 只管调度Agent 干重活这也是官方推荐的架构。第三时间不同步导致的时间戳错乱。Jenkins 主机的时区和构建节点不一致时日志时间线看起来会很混乱你按时间关联两个机器的日志根本对不上。装完就把所有机器的 NTP 对齐这是基础中的基础但真正在意的人不多。我建议装完就在流水线的第一步加一句date把机器时间打到日志里出问题时对比一目了然。第四插件升级的连锁反应。一次性升级所有插件是危险动作。插件之间有依赖某个插件升级后要求更高版本的 Jenkins 核心或者和另一个插件的新版本不兼容结果就是重启后页面加载不出来。我的做法是升级前用thinBackup之类的工具做一次完整备份然后一批只升 2 到 3 个升完重启验证确认没问题再下一批。慢一点但安全。第五构建产物的路径依赖。很多人写脚本时用了相对路径本地跑得好好的换了节点就找不到文件。原因在于WORKSPACE在不同节点上路径不一样。稳妥的做法是统一用${env.WORKSPACE}拼绝对路径或者干脆在流水线开头cd到一个固定目录。这个坑在从单机迁移到多 Agent 的时候必然遇到一次。8. 跑起来之后的运维与长期演进8.1 备份、升级与容灾恢复Jenkins 跑起来之后最该建立的例行工作就是备份。核心要备份的东西只有三样$JENKINS_HOME下的jobs任务配置和历史、users用户信息、credentials.xml和相关密钥文件、config.xml系统配置、plugins插件。最小化的备份命令长这样tar -czf /backup/jenkins-$(date %F).tar.gz \ -C /var/lib/jenkins \ jobs users config.xml credentials.xml secrets plugins nodes注意secrets目录必须带上。很多人备份的时候漏了它恢复之后所有加密的凭据全部解不开得重新加一遍。这个目录里的内容还和$JENKINS_HOME的路径绑定所以恢复的时候新机器的安装路径必须和原机器一致否则凭据一样解不开。这是最容易踩的一个坑。升级的话稳妥流程是先备份再停服务替换 war 包或升级包启动看日志有没有插件加载失败最后打开界面确认任务都在。如果升级后发现某个插件不兼容回退的方式就是把备份解回去——所以备份一定要在升级前做不是升级完看看没问题再备份。8.2 从单机走向分布式与流水线即代码单机跑到一定规模一定会遇到瓶颈构建排队、Master 负载高、不同项目对环境的依赖互相冲突一个要 JDK 8一个要 JDK 17。这时候就该上 Agent 了。Agent 的接入方式有两种。SSH方式适合静态的长跑节点在Manage Jenkins→Nodes里新建节点填好远程工作目录、启动方式选 SSH、指定凭据Jenkins 会自己去连。JNLP方式适合动态节点节点主动来连 Master配合容器编排能实现按需创建、用完销毁。我一般固定节点用前者临时的高负载任务用后者。另一个演进方向是把流水线定义从界面上挪到代码里——也就是Jenkinsfile。前面所有示例我都是直接写在任务的脚本框里的那是为了讲清楚逻辑。真到团队协作阶段脚本应该跟着代码一起提交理由有三个一是能版本化谁改的、改了什么一目了然二是能 Review走正常的合并流程三是能复用多分支流水线可以自动给每个分支生成独立任务不用手工建。Jenkinsfile的写法上我建议用声明式语法而不是脚本式。声明式的结构固定pipeline、agent、stages、post这些块写清楚了后来接手的人一眼能看懂流程走到了哪。脚本式虽然灵活但写起来自由度高两个人能写出两种完全不同的风格维护成本高。最后分享一个我自己的小习惯每个流水线的第一个 stage永远是一个叫Environment Info的信息打印步骤把 JDK 版本、Maven 版本、工作目录、节点名、当前时间全部打出来。这个 stage 不干任何实事但只要构建出了问题第一眼就能判断是不是环境变了。我靠这个步骤定位过好多次昨天还好好的今天就不行了——基本都是有人悄悄升级了机器上的 JDK 或者 Maven。
返回列表