ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Jenkins自动化部署实战:从环境配置到Kubernetes动态节点与异常排查

Jenkins自动化部署实战:从环境配置到Kubernetes动态节点与异常排查 我几乎每天都跟Jenkins打交道从最开始只是用它跑个定时构建到后来把整套发布流程都托付给它前后也踩了无数坑。不少朋友问我Jenkins到底怎么用、怎么配置才能做到自动化部署、怎么处理那些奇奇怪怪的报错今天干脆把我这些年积累的经验整理成一篇完整的实操指南从环境准备、汉化、环境变量到构建清理、Kubernetes动态节点再到几个典型报错的排查思路一次说清楚。这篇指南适合两类人一类是刚接触Jenkins、想在本地搭一套自动化环境的新手照着步骤走就行另一类是已经在用Jenkins、但想进一步优化流程、解决具体问题的人可以直接跳到对应的章节看解决方案。1. 整体设计与思路拆解1.1 为什么选择Jenkins做自动化部署我见过很多人纠结用什么工具做持续集成和持续交付GitLab CI、GitHub Actions、Drone都有自己的拥趸但Jenkins能活到今天依然被大量团队使用靠的是两点生态插件极度丰富和部署形态极其灵活。截至现在Jenkins官方插件市场里有超过1900个插件从代码拉取、编译打包、静态检查、镜像构建到通知推送几乎你能想到的自动化步骤都有现成的插件可以用。而部署形态上你可以用war包跑在Tomcat里也可以用Docker一把梭还能在Kubernetes里动态拉起Agent节点完全看你的运维环境来决定。另外一个让我坚持用它的原因是Pipeline即代码。把构建、测试、部署过程写成Jenkinsfile放进代码仓库这就意味着环境的搭建逻辑完全版本化了换一台机器不用重新配置任务直接把Pipeline文件拉下来就能跑。对于多环境、多分支的管理场景代码化的Pipeline比在网页里疯狂点击保存配置不知道高到哪里去。1.2 自动化部署的整体架构思维在做自动化部署之前先想清楚一件事Jenkins不是一个部署工具而是一个流程调度中心。它负责把代码拉下来、按你定义的步骤执行、然后把结果送出去。真正的部署动作比如更新服务器上的应用往往是通过脚本、Ansible、Kubectl等工具来完成的Jenkins只是那个发起指令的人。我常用的部署链路长这样开发提交代码到Git仓库触发Jenkins的Webhook或轮询Jenkins从仓库拉取代码到执行节点的工作空间执行Maven/Gradle编译生成可部署的产物Jar包、镜像等推送镜像到私有仓库或用SCP把产物传到目标服务器在目标服务器上执行重启脚本完成服务更新通过飞书/钉钉/邮件把构建结果推送给相关人员这个链条里Jenkins扮演的是执棋者的角色每个步骤干什么、在哪台机器上干、失败后怎么处理这些才是你真正需要设计的内容。1.3 环境准备一台能跑的Jenkins工欲善其事必先利其器。第一次搭Jenkins我建议直接用Docker方式干净、隔离、删了重建都很方便。如果主机上已经有Docker环境一条命令就能解决docker run -d --name jenkins \ -p 8080:8080 \ -p 50000:50000 \ -v jenkins_home:/var/jenkins_home \ -v /var/run/docker.sock:/var/run/docker.sock \ jenkins/jenkins:lts说下参数的含义8080是Web访问端口50000是Master和Agent之间通信用的TCP端口容器内的/var/jenkins_home挂载到宿主机的一个Docker卷里保证容器删了配置和数据还在。额外挂载了Docker的套接字文件是为了让Jenkins能直接调用宿主机的Docker来构建镜像后面配置自动部署会用到。如果你不想用Docker也可以下载war包丢进Tomcat或者直接下载对应系统的原生安装包。个人推荐Docker因为升级、备份都太方便了。容器起来之后浏览器访问http://IP:8080在容器日志里找到初始管理员密码按引导界面初始化即可。2. 核心细节解析与实操要点2.1 Jenkins汉化与基础设置很多人第一次打开Jenkins满屏英文直接劝退。其实汉化很简单装一个插件就行在系统管理 - 插件管理 - 可选插件中搜索Locale插件安装后需要手动配置语言有些版本还要求装一个中文语言包插件。让我实际推荐的是两个组合Localization: Chinese (Simplified)界面汉化插件装完直接生效Locale语言区域控制插件用来固定语言优先级两个都装上之后进入系统管理 - 系统设置找到Locale选项把Default Language填成zh_CN同时勾上Ignore browser preference and force this language to all users这样不管访问者浏览器是什么语言界面都固定显示中文。这一步能明显降低团队上手门槛尤其是那些不太熟悉英文界面的同事。安装插件这块有个坑连不上官方插件源是家常便饭。后文专门讲插件加速器和镜像配置建议先跳到4.3节把插件源换掉再回来装汉化包否则你可能会卡在下载页面很长时间。2.2 必须搞清楚的Jenkins可用环境变量Jenkins内置了很多环境变量在Pipeline里、Shell脚本里、邮件模板里都能直接引用。我工作中最常用的几个做了一个速查表变量名含义典型使用场景JOB_NAME当前任务的名称拼镜像Tag、写日志前缀BUILD_NUMBER当前构建的版本号标识产物版本、回滚依据WORKSPACE当前任务的工作目录路径定位代码根目录JENKINS_HOMEJenkins主目录查找配置、凭据文件BUILD_URL本次构建的访问链接推送通知时提供链接GIT_COMMIT当前构建对应的Git提交哈希关联代码版本BRANCH_NAME当前构建的分支名多分支Pipeline判断环境BUILD_TAG形如jenkins-任务名-序号的组合标识唯一标识一次构建举个例子我在构建后端Java服务时要用构建号拼出镜像版本号并打印出来echo 开始构建镜像版本号${JOB_NAME}-${BUILD_NUMBER} docker build -t harbor.example.com/demo/my-app:${JOB_NAME}-${BUILD_NUMBER} .这样每次构建产出的镜像Tag都是唯一的回滚时只要指定上一个版本Tag就行特别适合配合Kubernetes做镜像版本管理。如果你想查看某次构建的全部环境变量有个最简单的方法在构建步骤里加一个Execute Shell执行env env.txt构建完成后到工作空间里打开这个文件所有变量名和值一目了然。我排查问题时经常用这招确认当前构建的上下文。2.3 凭据管理与密钥处理的正确姿势自动化部署绕不开服务器密码、SSH私钥、Token这些敏感信息。很多新手图省事直接把密码写死在脚本里这是我在团队里反复强调要禁止的事。Jenkins的凭据体系Credentials就是干这个的密码、私钥、Token都可以安全地保存在Jenkins中在脚本里通过变量引用。添加凭据的位置在系统管理 - 凭据 - 系统 - 全局凭据点击添加后选择类型Username with password适用于SSH密码登录、账号密码访问Git仓库SSH Username with private key适用于用密钥登录服务器的场景把私钥内容粘贴进去就行Secret file挂载一个敏感文件比如KubeConfigSecret text保存一段文本Token如GitLab的Access Token、Harbor的登录Token在Pipeline脚本里通过credentials()方式引用比如要拿SSH私钥去连服务器可以这样写withCredentials([sshPrivateKey( credentialsId: my-linux-server-key, keyFileVariable: SSH_KEY_PATH )]) { sh ssh -i ${SSH_KEY_PATH} -o StrictHostKeyCheckingno root10.0.0.5 cd /opt/app ./restart.sh }这样私钥只存在于Jenkins的安全存储中脚本里出现的是一个变量引用日志里也不会有明文密钥。这个习惯一定要从第一天就养成否则一旦代码仓库泄露等于把生产服务器的钥匙交了出去。3. 实操过程与核心环节实现3.1 创建第一个自动化部署任务的完整步骤我拿一个典型的Java Web项目来演示目标流程是提交代码后自动构建Jar包、传到测试服务器、重启服务。用自由风格任务演示一遍因为新手对图形界面更友好后面再介绍Pipeline方式。第一步创建任务输入任务名选择构建一个自由风格的软件项目确定。第二步源码管理选择Git填写仓库地址。如果仓库需要登录点击添加凭据选择之前配置好的用户名密码凭据。分支构建根据自己的策略填比如*/dev表示只在dev分支推送时触发。第三步构建触发器里我常用两种Poll SCMJenkins定期去Git仓库检查有没有新提交适合内网无法从外网访问Jenkins、Webhook推不进来的场景Webhook触发在GitLab/Gitee里配置Webhook地址有推送时自动通知Jenkins如果只是个人练习也可以勾选定时构建填H/15 * * * *表示每15分钟构建一次。第四步构建环境勾选Add timestamps to the Console Output这样日志里会显示每条输出的时间后期排查慢构建问题会方便很多。第五步构建步骤里点增加构建步骤选调用顶层Maven目标在Maven版本里选择服务器上配置的MavenGoals里填clean package -DskipTests。如果想自定义命令也可以选择执行Shellset -e mvn clean package -DskipTests # 构建完成后把产物传送到目标服务器 scp -i ${SSH_KEY_PATH} -o StrictHostKeyCheckingno \ target/my-app.jar root10.0.0.5:/opt/app/backup/ # 远程执行重启命令 ssh -i ${SSH_KEY_PATH} -o StrictHostKeyCheckingno root10.0.0.5 \ cd /opt/app ./deploy.sh第六步最后在构建后操作中增加Editable Email Notification配置收件人列表这样每次构建失败能第一时间收到邮件提醒。前面用到的SSH_KEY_PATH是通过withCredentials方式注入的具体参考2.3节的写法。这是我工作中最常用的一套组合拳。注意set -e这一步很重要它能让脚本在任意一条命令失败时立即退出避免出现Jar包没打出来但还继续执行部署这种失控情况。3.2 Pipeline脚本更高级的自动化写法自由风格任务适合快速上手但当你需要把流程代码化、版本化时Pipeline才是王道。Pipeline本质是把构建流程编写为Groovy脚本可以放在Jenkins任务里也可以命名为Jenkinsfile放进代码仓库。我强烈建议用后一种方式流程和代码一起评审、一起迭代相当舒服。一个精简的部署Pipeline长这样pipeline { agent any environment { DOCKER_REGISTRY harbor.example.com/demo IMAGE_NAME my-app IMAGE_TAG ${env.BUILD_NUMBER} } stages { stage(拉取代码) { steps { checkout scm } } stage(单元测试) { steps { sh mvn test } post { always { junit **/target/surefire-reports/*.xml } } } stage(构建镜像) { steps { sh mvn clean package -DskipTests docker build -t ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} . docker push ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} } } stage(部署到Kubernetes) { steps { sh kubectl set image deployment/my-app \ my-app${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} \ --record } } } post { success { echo 构建成功镜像已升级 } failure { error(构建失败请检查日志) } } }我把镜像Tag直接用了BUILD_NUMBER好处是每次构建的镜像版本可以跟Jenkins构建记录一一对应出了问题能快速定位到是哪次构建引起的这个关联关系在线上开issue的时候价值极高。如果只是个人项目想精简流程阶段设为拉取代码、构建与部署两步就够了但保留单元测试阶段这个习惯我建议不要丢它能帮你拦截很多低级问题。3.3 Jenkins 2.541.3配置Kubernetes动态Agent容器化大潮下把Jenkins的构建节点做进Kubernetes已经是很多团队的选择这也是热词里Jenkins 2.541.3配置Kubernetes的来源。原理很简单当任务进入排队后Jenkins主节点Master通过Kubernetes插件调用API动态拉起一个Pod作为Agent节点任务在那个Pod里执行执行完毕后自动销毁并释放资源。相比固定几台物理机做Slave这种模式的好处是资源按需分配、构建环境完全隔离、弹性伸缩。配置步骤按顺序来第一步安装Kubernetes插件。在插件管理中搜索Kubernetes安装并重启。第二步进入系统管理 - 系统设置找到最下面的Kubernetes配置区域点Add a new cloud。第三步填Kubernetes地址和凭据。如果Jenkins运行在Kubernetes集群内部地址填https://kubernetes.default.svc.cluster.local如果是外部访问就填集群的API Server地址。凭据选择KubeConfig或ServiceAccount Token一般建议在集群中创建一个专用ServiceAccount并绑定管理员权限不要用集群管理员的Token来配集成。第四步设置Pod模板。Jenkins URL填http://jenkins:8080这类集群内可访问的地址Pod Labels填一个名字比如jnlp-agent任务里用agent { label jnlp-agent }就能指定到这组动态节点。在Pod模板里添加Container时镜像选择很关键推荐用jenkins/inbound-agent基础镜像再在这个基础上把构建工具集成进去。比如Java项目的Agent镜像里需要包含JDK、Maven前端项目则需要Node。我一般会为不同技术栈预构建不同的Agent镜像存到私有镜像仓库避免每次执行任务都在线下载依赖。这样处理的直观效果是高峰期多个任务同时到达系统能自动拉起多个Pod并行跑构建没有任务时节点数为零不再有闲置的构建机占用资源集群运维体验会好非常多。3.4 构建清理策略别让磁盘不知不觉爆掉Jenkins跑久了最头大的问题就是JENKINS_HOME目录越来越大。家目录下主要存了三大块构建记录及产物jobs目录、插件和插件下载的缓存、工作空间残留。构建日志和Workspace是最吃磁盘的部分。先说构建记录的清理最简单的方式是在系统管理 - 系统设置里找到丢弃旧的构建勾选后配置两个策略最多保留天数为7最多保留构建数为30这样系统会自动删除超出范围的构建记录。你还可以在每个任务里单独配置比如生产环境的发布任务可以保留更久测试环境的任务保留一两天就足够。任务级配置的位置进入任务 - 配置 - 构建记录区域同样能勾选Discard old builds。我习惯对需要长期追溯的构建设置保持构建日志配合参数化构建里的版本号入参既控制磁盘占用又保留需要的信息。但构建记录只是其中之一Workspace和Docker镜像才是大头。如果你发现磁盘占用依然飙升可以去$JENKINS_HOME/workspace下看看有些被删除的任务或者长期不清理的任务工作目录会一直占着空间。我一般配合一个定时任务用Shell脚本找出超过30天没有更新过的Workspace目录并删除注意排除正在运行的任务目录就行。用完即弃的临时构建节点Kubernetes动态Agent完全不存在这个问题这也是云原生改造带来的意外好处之一。3.5 AI安装Jenkins与Jenkins AI AgentAI与CI/CD的结合是最近热度很高的方向。热词里的AI安装Jenkins和Jenkins AI Agent指向其实完全不同。前者是用AI辅助的方式完成Jenkins的安装和初始配置比如让AI生成一条docker run命令、补全Kubernetes的部署YAML后者则是在Jenkins的Pipeline里集成AI让小模型帮助分析构建日志、定位失败原因。我用过的一个场景是构建失败后把控制台的报错输出截取出来通过HTTP请求发送给本地或第三方的大模型服务让AI给出报错原因分析和修复建议然后把结果自动追加到构建记录的描述里。核心是写一个Pipeline步骤类似这样stage(AI 分析失败原因) { when { failure() } steps { script { def log sh(script: tail -200 console.log, returnStdout: true).trim() def suggestion aiChatClient.getSuggestion(这段构建日志报错了帮我分析原因$log) currentBuild.description AI 诊断${suggestion} } } }这类AI Agent工具现在已经有不少插件支持比如Jenkins官方的AIOps相关插件以及社区实现的ChatGPT插件。如果你团队里已经有大模型服务的可用接口通过Pipeline脚本直接对接是成本最低的接入方式。要提醒的是AI给出的建议只能作为参考真正的修复动作仍然需要人工确认毕竟构建失败的原因千奇百怪AI在特定上下文里并不完全可靠。4. 常见问题与排查技巧实录4.1 经典报错SSH连接时的IllegalStateException热词里有一条很典型jenkins ssh java.lang.illegalstateexception: connection is not established!。这个报错我印象非常深刻第一次遇到也是一头雾水。它通常出现在使用SSH插件如Publish Over SSH或SSH Agent插件连接远程服务器时Jenkins后台日志里抛出java.lang.IllegalStateException: Connection is not established!。这个错误的本义是代码试图在一个尚未建立或已经断开的SSH会话上执行操作。最常见的原因有服务器网络不可达Jenkins向远程主机发起的TCP握手超时SSH端口没有放通或者防火墙规则限制了Jenkins的出口IP凭据配置错误私钥不符合目标服务器的authorized_keys连接中途被拒绝在Pipeline中把SSH命令放在了错误的阶段连接在脚本执行过程中意外断开我给的排查步骤是按优先级来的先在命令行手动测试连通性ssh -p 22 usertarget -i key.pem能通说明网络和密钥没问题检查Publish Over SSH中的SSH Server配置重新点击Test Configuration看是否提示成功换用ssh命令行替代插件方式ssh -i ${SSH_KEY_PATH} -o ConnectTimeout10 -o StrictHostKeyCheckingno usertarget echo ok如果命令行能通而插件不行多半是插件配置的Hostname或端口填错了重新检查。另一个角度看这个报错还经常出现在Master与Agent之间的连接描述中。在系统管理 - 节点管理里能看到节点状态当Agent连接断开时控制台上出现这个异常排查方向是检查Agent的JNLP连接是否正常比如50000端口是否被防火墙挡掉。4.2 插件加速器解决下载安装慢的终极手段Jenkins插件下载慢、超时、失败是让国内用户最抓狂的问题之一。官方插件源在境外网络抖动下装一个插件可能要卡好几分钟甚至直接下载失败。我见过不少新手在这一步就放弃了。解决思路是把Jenkins升级站点镜像切换到国内源。打开系统管理 - 插件管理 - 高级然后把升级站点的URL改成https://mirrors.huaweicloud.com/jenkins/updates/update-center.json这是华为云维护的镜像我用了很长一段时间稳定性还不错。其他开源镜像站也有提供本质上是同步了官方Update Center内容做镜像使用方式一样。切换之后点击立即获取最新元数据让它重新拉取更新列表再去可选插件页面搜索插件就能正常安装了。还需要注意插件下载还有一层CDN如果元数据已经加载成功但下载仍然很慢可以在服务器的hosts文件里把updates.jenkins.io的域名解析指向可用的镜像IP或者用Nginx反向代理插件下载目录。4.3 常见问题速查与应对策略把工作中高频踩坑问题整理成一张速查表遇到时可以先对照排查现象可能原因解决建议构建一直卡在正在连接Agent节点失联或JNLP端口被防火墙拦截检查50000端口连通性查看节点日志Git拉取代码权限失败凭据过期或账号无分支权限进入凭据管理重新填写并验证选中正确的凭据Maven构建无法下载依赖本地仓库缓存损坏或私服地址变更清空本地仓库的.lastUpdated文件换阿里云Maven镜像构建产物无法推送到服务器SCP目标路径权限不足检查目标目录属主或用SSH命令先行测试写权限Pipeline中找不到docker命令执行节点没有安装Docker命令行工具在Agent节点安装Docker CLI或者修改Agent镜像不同构建相互影响多任务共用一个Workspace改用自定义工作空间或设置删掉旧的工作空间日志中时间不是本地时区容器未设置时区运行容器时挂载/etc/localtime或设置TZ环境变量其中不同构建相互影响是很多人容易忽略的比如A任务构建时会修改B任务所在目录的文件导致B的构建结果不可复现。我在多分支构建时会让每个分支使用单独的Workspace目录从根本上杜绝冲突。4.4 构建前必须检查的三件事经验之谈每次在Jenkins上配置新任务我建议至少检查这三项凭据是否指向正确的账号。很多人配置完任务但忘了在Git源码管理里选择凭据导致每次拉代码都用匿名身份访问权限不够自然失败。构建环境的JDK/Maven版本是否与代码匹配。不同项目依赖的JDK版本可能不一样如果全局用一个版本切换项目时很容易遇到编译错误。目标服务器的部署脚本是否具备幂等性。即重复执行能否保持结果一致、不会产生副作用。我在写deploy.sh时会先杀掉旧进程再启动配合守护进程保证重启后能正常拉起。这三点看着不起眼但在实际工作中因为它们导致的构建失败远比脚本本身写错频繁。5. 一些个人体会与扩展思路Jenkins用久了你会发现真正难的不是配置本身而是对整个自动化流程的掌控力。我见过团队把部署脚本写成一长串Shell互相之间耦合严重——一次构建失败排查半天找不到是哪个环节出的问题。按照我这几年踩坑后的习惯每个阶段尽量独立、可重试再配合Pipeline阶段之间的数据传递和产物归档整个流程的可靠性能上一个量级。如果你准备把Jenkins用于实际的团队协作我强烈建议在最初的架构阶段就考虑好三件事凭据统一管理、Agent节点资源隔离、构建产物与构建记录一一对应。后续不管是扩容、迁移还是接入Kubernetes都会顺畅很多。最后分享一个小技巧构建过程中如果发现某个步骤的性能瓶颈比如Maven下载依赖太慢可以考虑在构建脚本里加上-o离线模式前提是依赖已经缓存过或者预先在Agent镜像里把常用依赖打好。我在压榨构建速度这件事上用的最多的是把需要频繁变动的依赖缓存挂载到持久卷虽然麻烦点但效果拔群。
返回列表